Commit bbaff070 authored by Mikolaj Wawrzyniak's avatar Mikolaj Wawrzyniak

Move cluster health dashboard to the core

In order to make clustr health dashbaord available to all users
we need to move #mertics_dashboard endpoint out of ee module
parent 0e405512
......@@ -10,6 +10,11 @@ class Admin::ClustersController < Clusters::ClustersController
def clusterable
@clusterable ||= InstanceClusterablePresenter.fabricate(Clusters::Instance.new, current_user: current_user)
end
end
Admin::ClustersController.prepend_if_ee('EE::Admin::ClustersController')
def metrics_dashboard_params
{
cluster: cluster,
cluster_type: :admin
}
end
end
......@@ -3,6 +3,7 @@
class Clusters::ClustersController < Clusters::BaseController
include RoutableActions
include Metrics::Dashboard::PrometheusApiProxy
include MetricsDashboard
before_action :cluster, only: [:cluster_status, :show, :update, :destroy, :clear_cache]
before_action :generate_gcp_authorize_url, only: [:new]
......
......@@ -17,6 +17,12 @@ class Groups::ClustersController < Clusters::ClustersController
def group
@group ||= find_routable!(Group, params[:group_id] || params[:id])
end
end
Groups::ClustersController.prepend_if_ee('EE::Groups::ClustersController')
def metrics_dashboard_params
{
cluster: cluster,
cluster_type: :group,
group: group
}
end
end
......@@ -23,6 +23,13 @@ class Projects::ClustersController < Clusters::ClustersController
def repository
@repository ||= project.repository
end
end
Projects::ClustersController.prepend_if_ee('EE::Projects::ClustersController')
def metrics_dashboard_params
params.permit(:embedded, :group, :title, :y_label).merge(
{
cluster: cluster,
cluster_type: :project
}
)
end
end
......@@ -344,6 +344,10 @@ module Clusters
Feature.enabled?(:managed_apps_local_tiller, clusterable, default_enabled: true)
end
def prometheus_adapter
application_prometheus
end
private
def unique_management_project_environment_scope
......
......@@ -64,6 +64,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
raise NotImplementedError
end
def metrics_dashboard_path(cluster)
raise NotImplementedError
end
# Will be overridden in EE
def environments_cluster_path(cluster)
nil
......
......@@ -64,8 +64,27 @@ module Clusters
!cluster.provided_by_user?
end
def health_data(clusterable)
{
'clusters-path': clusterable.index_path,
'dashboard-endpoint': clusterable.metrics_dashboard_path(cluster),
'documentation-path': help_page_path('user/project/clusters/index', anchor: 'monitoring-your-kubernetes-cluster-ultimate'),
'empty-getting-started-svg-path': image_path('illustrations/monitoring/getting_started.svg'),
'empty-loading-svg-path': image_path('illustrations/monitoring/loading.svg'),
'empty-no-data-svg-path': image_path('illustrations/monitoring/no_data.svg'),
'empty-unable-to-connect-svg-path': image_path('illustrations/monitoring/unable_to_connect.svg'),
'settings-path': '',
'project-path': '',
'tags-path': ''
}
end
private
def image_path(path)
ActionController::Base.helpers.image_path(path)
end
def clusterable
if cluster.group_type?
cluster.group
......
......@@ -43,6 +43,10 @@ class GroupClusterablePresenter < ClusterablePresenter
def learn_more_link
link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/group/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
metrics_dashboard_group_cluster_path(clusterable, cluster)
end
end
GroupClusterablePresenter.prepend_if_ee('EE::GroupClusterablePresenter')
......@@ -81,6 +81,10 @@ class InstanceClusterablePresenter < ClusterablePresenter
def learn_more_link
link_to(s_('ClusterIntegration|Learn more about instance Kubernetes clusters'), help_page_path('user/instance/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
metrics_dashboard_admin_cluster_path(cluster)
end
end
InstanceClusterablePresenter.prepend_if_ee('EE::InstanceClusterablePresenter')
......@@ -38,6 +38,10 @@ class ProjectClusterablePresenter < ClusterablePresenter
def learn_more_link
link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
metrics_dashboard_project_cluster_path(clusterable, cluster)
end
end
ProjectClusterablePresenter.prepend_if_ee('EE::ProjectClusterablePresenter')
---
title: Open source cluster health dashboard and make it available to all users
merge_request: 35721
author:
type: changed
......@@ -190,7 +190,6 @@ Rails.application.routes.draw do
member do
Gitlab.ee do
get :metrics, format: :json
get :metrics_dashboard
get :environments, format: :json
end
......@@ -200,6 +199,7 @@ Rails.application.routes.draw do
delete '/:application', to: 'clusters/applications#destroy', as: :uninstall_applications
end
get :metrics_dashboard
get :'/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api
get :cluster_status, format: :json
delete :clear_cache
......
# frozen_string_literal: true
module EE
module Admin
module ClustersController
def metrics_dashboard_params
{
cluster: cluster,
cluster_type: :admin
}
end
end
end
end
......@@ -3,7 +3,6 @@
module EE
module Clusters
module ClustersController
include MetricsDashboard
extend ActiveSupport::Concern
prepended do
......
# frozen_string_literal: true
module EE
module Groups
module ClustersController
extend ActiveSupport::Concern
def metrics_dashboard_params
{
cluster: cluster,
cluster_type: :group,
group: group
}
end
end
end
end
# frozen_string_literal: true
module EE
module Projects
module ClustersController
def metrics_dashboard_params
params.permit(:embedded, :group, :title, :y_label).merge(
{
cluster: cluster,
cluster_type: :project
}
)
end
end
end
end
......@@ -8,10 +8,6 @@ module EE
prepended do
include UsageStatistics
end
def prometheus_adapter
application_prometheus
end
end
end
end
......@@ -5,9 +5,5 @@ module EE
def metrics_cluster_path(cluster, params = {})
raise NotImplementedError
end
def metrics_dashboard_path(cluster)
raise NotImplementedError
end
end
end
......@@ -3,30 +3,19 @@
module EE
module Clusters
module ClusterPresenter
extend ::Gitlab::Utils::Override
override :health_data
def health_data(clusterable)
{
'clusters-path': clusterable.index_path,
super.merge(
'metrics-endpoint': clusterable.metrics_cluster_path(cluster, format: :json),
'dashboard-endpoint': clusterable.metrics_dashboard_path(cluster),
'documentation-path': help_page_path('user/project/clusters/index', anchor: 'monitoring-your-kubernetes-cluster'),
'empty-getting-started-svg-path': image_path('illustrations/monitoring/getting_started.svg'),
'empty-loading-svg-path': image_path('illustrations/monitoring/loading.svg'),
'empty-no-data-svg-path': image_path('illustrations/monitoring/no_data.svg'),
'empty-unable-to-connect-svg-path': image_path('illustrations/monitoring/unable_to_connect.svg'),
'settings-path': '',
'project-path': '',
'tags-path': '',
'alerts-endpoint': alerts_endpoint,
'prometheus-alerts-available': prometheus_alerts_available
}
)
end
private
def image_path(path)
ActionController::Base.helpers.image_path(path)
end
def alerts_endpoint
'/' if ::Feature.enabled?(:prometheus_computed_alerts)
end
......
......@@ -16,11 +16,6 @@ module EE
environments_group_cluster_path(clusterable, cluster)
end
override :metrics_dashboard_path
def metrics_dashboard_path(cluster)
metrics_dashboard_group_cluster_path(clusterable, cluster)
end
private
def can_read_cluster_environments?
......
......@@ -16,11 +16,6 @@ module EE
environments_admin_cluster_path(cluster)
end
override :metrics_dashboard_path
def metrics_dashboard_path(cluster)
metrics_dashboard_admin_cluster_path(cluster)
end
private
def can_read_cluster_environments?
......
......@@ -8,10 +8,5 @@ module EE
def metrics_cluster_path(cluster, params = {})
metrics_project_cluster_path(clusterable, cluster, params)
end
override :metrics_dashboard_path
def metrics_dashboard_path(cluster)
metrics_dashboard_project_cluster_path(clusterable, cluster)
end
end
end
......@@ -11,8 +11,7 @@ module EE
override :permissions_by_route
def permissions_by_route
super.concat([
ROUTE.new(::Gitlab::Metrics::Dashboard::Url.alert_regex, :read_prometheus_alerts),
ROUTE.new(::Gitlab::Metrics::Dashboard::Url.clusters_regex, :read_cluster)
ROUTE.new(::Gitlab::Metrics::Dashboard::Url.alert_regex, :read_prometheus_alerts)
])
end
end
......
......@@ -10,7 +10,6 @@ module EE
def metrics_filters
[
::Banzai::Filter::InlineAlertMetricsFilter,
::Banzai::Filter::InlineClusterMetricsFilter,
*super
]
end
......
......@@ -27,10 +27,6 @@ RSpec.describe Admin::ClustersController do
before do
allow(::Clusters::Instance).to receive(:new).and_return(cluster.instance)
end
describe 'GET #metrics_dashboard' do
it_behaves_like 'the default dashboard'
end
end
describe 'GET environments' do
......
......@@ -33,34 +33,6 @@ RSpec.describe Groups::ClustersController do
clusterable.add_maintainer(user)
end
context 'with inappropriate requests' do
context 'with anoymous user' do
before do
sign_out(user)
end
it 'renders not found' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(:not_found)
end
context 'with invalid clusterable id' do
before do
sign_in(user)
end
let(:other_clusterable) { create(:group) }
it 'returns 404' do
get :prometheus_proxy, params: prometheus_proxy_params(id: other_clusterable.id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
describe 'security' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: true, query: nil) }
......@@ -78,28 +50,6 @@ RSpec.describe Groups::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
describe 'GET #metrics_dashboard' do
let(:user) { create(:user) }
before do
clusterable.add_maintainer(user)
sign_in(user)
end
it_behaves_like 'the default dashboard'
end
end
private
def prometheus_proxy_params(params = {})
{
id: cluster.id.to_s,
group_id: group.name,
proxy_path: 'query',
query: '1'
}.merge(params)
end
describe 'GET environments' do
......
......@@ -47,28 +47,5 @@ RSpec.describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
describe 'GET #metrics_dashboard' do
let(:user) { create(:user) }
before do
clusterable.add_maintainer(user)
sign_in(user)
end
it_behaves_like 'the default dashboard'
end
end
private
def prometheus_proxy_params(params = {})
{
id: cluster.id.to_s,
namespace_id: project.namespace.full_path,
project_id: project.name,
proxy_path: 'query',
query: '1'
}.merge(params)
end
end
......@@ -27,24 +27,4 @@ RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter do
it_behaves_like 'redacts the embed placeholder'
it_behaves_like 'retains the embed placeholder when applicable'
end
context 'for a cluster metric embed' do
let_it_be(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [project]) }
let(:params) { [project.namespace.path, project.path, cluster.id] }
let(:query_params) { { group: 'Cluster Health', title: 'CPU Usage', y_label: 'CPU (cores)' } }
let(:url) { urls.metrics_namespace_project_cluster_url(*params, **query_params) }
context 'with user who can read cluster' do
it_behaves_like 'redacts the embed placeholder'
it_behaves_like 'retains the embed placeholder when applicable'
end
context 'without user who can read cluster' do
let(:doc) { filter(input, current_user: create(:user)) }
it 'redacts the embed placeholder' do
expect(doc.to_s).to be_empty
end
end
end
end
......@@ -17,18 +17,8 @@ RSpec.describe Clusters::ClusterPresenter do
subject { cluster_presenter.health_data(clusterable_presenter) }
it do
is_expected.to match(
'clusters-path': clusterable_presenter.index_path,
is_expected.to include(
'metrics-endpoint': clusterable_presenter.metrics_cluster_path(cluster, format: :json),
'dashboard-endpoint': clusterable_presenter.metrics_dashboard_path(cluster),
'documentation-path': help_page_path('user/project/clusters/index', anchor: 'monitoring-your-kubernetes-cluster'),
'empty-getting-started-svg-path': match_asset_path('/assets/illustrations/monitoring/getting_started.svg'),
'empty-loading-svg-path': match_asset_path('/assets/illustrations/monitoring/loading.svg'),
'empty-no-data-svg-path': match_asset_path('/assets/illustrations/monitoring/no_data.svg'),
'empty-unable-to-connect-svg-path': match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'),
'settings-path': '',
'project-path': '',
'tags-path': '',
'alerts-endpoint': '/',
'prometheus-alerts-available': 'true'
)
......
......@@ -14,10 +14,4 @@ RSpec.describe InstanceClusterablePresenter do
it { is_expected.to eq(metrics_admin_cluster_path(cluster)) }
end
describe '#metrics_dashboard_path' do
subject { presenter.metrics_dashboard_path(cluster) }
it { is_expected.to eq(metrics_dashboard_admin_cluster_path(cluster)) }
end
end
......@@ -40,10 +40,4 @@ RSpec.describe GroupClusterablePresenter do
it { is_expected.to be_nil }
end
end
describe '#metrics_dashboard_path' do
subject { presenter.metrics_dashboard_path(cluster) }
it { is_expected.to eq(metrics_dashboard_group_cluster_path(group, cluster)) }
end
end
......@@ -14,10 +14,4 @@ RSpec.describe ProjectClusterablePresenter do
it { is_expected.to eq(metrics_project_cluster_path(project, cluster)) }
end
describe '#metrics_dashboard_path' do
subject { presenter.metrics_dashboard_path(cluster) }
it { is_expected.to eq(metrics_dashboard_project_cluster_path(project, cluster)) }
end
end
......@@ -74,21 +74,6 @@ RSpec.shared_examples 'cluster metrics' do
end
end
shared_examples 'the default dashboard' do
it 'returns a json object with the correct keys' do
get :metrics_dashboard, params: metrics_params, format: :json
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.keys).to contain_exactly('dashboard', 'status', 'metrics_data')
end
it 'is the default dashboard' do
get :metrics_dashboard, params: metrics_params, format: :json
expect(json_response['dashboard']['dashboard']).to eq('Cluster health')
end
end
private
def go
......
......@@ -6,7 +6,6 @@ module Banzai
def embed_params(node)
url = node['href']
@query_params = query_params(url)
return unless [:group, :title, :y_label].all? do |param|
@query_params.include?(param)
end
......
......@@ -77,6 +77,10 @@ module Banzai
Route.new(
::Gitlab::Metrics::Dashboard::Url.grafana_regex,
:read_project
),
Route.new(
::Gitlab::Metrics::Dashboard::Url.clusters_regex,
:read_cluster
)
]
end
......
......@@ -48,7 +48,8 @@ module Banzai
def self.metrics_filters
[
Filter::InlineMetricsFilter,
Filter::InlineGrafanaMetricsFilter
Filter::InlineGrafanaMetricsFilter,
Filter::InlineClusterMetricsFilter
]
end
......
......@@ -171,6 +171,16 @@ RSpec.describe Admin::ClustersController do
end
end
it_behaves_like 'GET #metrics_dashboard for dashboard', 'Cluster health' do
let(:cluster) { create(:cluster, :instance, :provided_by_gcp) }
let(:metrics_dashboard_req_params) do
{
id: cluster.id
}
end
end
describe 'GET #prometheus_proxy' do
let(:user) { admin }
let(:proxyable) do
......
......@@ -192,6 +192,17 @@ RSpec.describe Groups::ClustersController do
end
end
it_behaves_like 'GET #metrics_dashboard for dashboard', 'Cluster health' do
let(:cluster) { create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) }
let(:metrics_dashboard_req_params) do
{
id: cluster.id,
group_id: group.name
}
end
end
describe 'GET #prometheus_proxy' do
let(:proxyable) do
create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group])
......
......@@ -230,6 +230,18 @@ RSpec.describe Projects::ClustersController do
end
end
it_behaves_like 'GET #metrics_dashboard for dashboard', 'Cluster health' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:metrics_dashboard_req_params) do
{
id: cluster.id,
namespace_id: project.namespace.full_path,
project_id: project.name
}
end
end
describe 'POST create for new cluster' do
let(:legacy_abac_param) { 'true' }
let(:params) do
......
......@@ -2,8 +2,9 @@
require 'spec_helper'
RSpec.describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
RSpec.describe 'Metrics rendering', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline do
include PrometheusHelpers
include KubernetesHelpers
include GrafanaApiHelpers
include MetricsDashboardUrlHelpers
......@@ -166,6 +167,41 @@ RSpec.describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching,
end
end
context 'for GitLab embedded cluster health metrics' do
before do
project.add_maintainer(user)
import_common_metrics
stub_any_prometheus_request_with_response
allow(Prometheus::ProxyService).to receive(:new).and_call_original
create(:clusters_applications_prometheus, :installed, cluster: cluster)
stub_kubeclient_discover(cluster.platform.api_url)
stub_prometheus_request(/prometheus-prometheus-server/, body: prometheus_values_body)
stub_prometheus_request(/prometheus\/api\/v1/, body: prometheus_values_body)
end
let_it_be(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [project], user: user) }
let(:params) { [project.namespace.path, project.path, cluster.id] }
let(:query_params) { { group: 'Cluster Health', title: 'CPU Usage', y_label: 'CPU (cores)' } }
let(:metrics_url) { urls.namespace_project_cluster_url(*params, **query_params) }
let(:description) { "# Summary \n[](#{metrics_url})" }
it 'shows embedded metrics' do
visit project_issue_path(project, issue)
expect(page).to have_css('div.prometheus-graph')
expect(page).to have_text(query_params[:title])
expect(page).to have_text(query_params[:y_label])
expect(page).not_to have_text(metrics_url)
expect(Prometheus::ProxyService)
.to have_received(:new)
.with(cluster, 'GET', 'query_range', hash_including('start', 'end', 'step'))
.at_least(:once)
end
end
def import_common_metrics
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end
......
......@@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::InlineClusterMetricsFilter do
let!(:project) { create(:project) }
let(:params) { [project.namespace.path, project.path, cluster.id] }
let(:query_params) { { group: 'Food metrics', title: 'Pizza Consumption', y_label: 'Slice Count' } }
let(:trigger_url) { urls.metrics_namespace_project_cluster_url(*params, **query_params) }
let(:trigger_url) { urls.namespace_project_cluster_url(*params, **query_params) }
let(:dashboard_url) do
urls.metrics_dashboard_namespace_project_cluster_url(
*params,
......
......@@ -29,6 +29,26 @@ RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter do
it_behaves_like 'retains the embed placeholder when applicable'
end
context 'for a cluster metric embed' do
let_it_be(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [project]) }
let(:params) { [project.namespace.path, project.path, cluster.id] }
let(:query_params) { { group: 'Cluster Health', title: 'CPU Usage', y_label: 'CPU (cores)' } }
let(:url) { urls.metrics_dashboard_namespace_project_cluster_url(*params, **query_params) }
context 'with user who can read cluster' do
it_behaves_like 'redacts the embed placeholder'
it_behaves_like 'retains the embed placeholder when applicable'
end
context 'without user who can read cluster' do
let(:doc) { filter(input, current_user: create(:user)) }
it 'redacts the embed placeholder' do
expect(doc.to_s).to be_empty
end
end
end
context 'the user has requisite permissions' do
let(:user) { create(:user) }
let(:doc) { filter(input, current_user: user) }
......
......@@ -249,4 +249,44 @@ RSpec.describe Clusters::ClusterPresenter do
it { is_expected.to be_truthy }
end
end
describe '#health_data' do
shared_examples 'cluster health data' do
let(:user) { create(:user) }
let(:cluster_presenter) { cluster.present(current_user: user) }
let(:clusterable_presenter) do
ClusterablePresenter.fabricate(clusterable, current_user: user)
end
subject { cluster_presenter.health_data(clusterable_presenter) }
it do
is_expected.to include('clusters-path': clusterable_presenter.index_path,
'dashboard-endpoint': clusterable_presenter.metrics_dashboard_path(cluster),
'documentation-path': help_page_path('user/project/clusters/index', anchor: 'monitoring-your-kubernetes-cluster-ultimate'),
'empty-getting-started-svg-path': match_asset_path('/assets/illustrations/monitoring/getting_started.svg'),
'empty-loading-svg-path': match_asset_path('/assets/illustrations/monitoring/loading.svg'),
'empty-no-data-svg-path': match_asset_path('/assets/illustrations/monitoring/no_data.svg'),
'empty-unable-to-connect-svg-path': match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'),
'settings-path': '',
'project-path': '',
'tags-path': '')
end
end
context 'with project cluster' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:clusterable) { cluster.project }
it_behaves_like 'cluster health data'
end
context 'with group cluster' do
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
let(:clusterable) { cluster.group }
it_behaves_like 'cluster health data'
end
end
end
......@@ -94,4 +94,10 @@ RSpec.describe GroupClusterablePresenter do
it { is_expected.to eq(group_cluster_path(group, cluster)) }
end
describe '#metrics_dashboard_path' do
subject { presenter.metrics_dashboard_path(cluster) }
it { is_expected.to eq(metrics_dashboard_group_cluster_path(group, cluster)) }
end
end
......@@ -26,4 +26,10 @@ RSpec.describe InstanceClusterablePresenter do
it { is_expected.to eq(clear_cache_admin_cluster_path(cluster)) }
end
describe '#metrics_dashboard_path' do
subject { presenter.metrics_dashboard_path(cluster) }
it { is_expected.to eq(metrics_dashboard_admin_cluster_path(cluster)) }
end
end
......@@ -94,4 +94,10 @@ RSpec.describe ProjectClusterablePresenter do
it { is_expected.to eq(project_cluster_path(project, cluster)) }
end
describe '#metrics_dashboard_path' do
subject { presenter.metrics_dashboard_path(cluster) }
it { is_expected.to eq(metrics_dashboard_project_cluster_path(project, cluster)) }
end
end
......@@ -4,7 +4,8 @@ RSpec.shared_examples_for 'GET #metrics_dashboard correctly formatted response'
it 'returns a json object with the correct keys' do
get :metrics_dashboard, params: metrics_dashboard_req_params, format: :json
# Exlcude `all_dashboards` to handle separately.
# Exclude `all_dashboards` to handle separately, at spec/controllers/projects/environments_controller_spec.rb:565
# because `all_dashboards` key is not part of expected shared behavior
found_keys = json_response.keys - ['all_dashboards']
expect(response).to have_gitlab_http_status(status_code)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment