Commit f0a7653a authored by Peter Leitzen's avatar Peter Leitzen Committed by Bob Van Landuyt

Show health graphs on group-level

Tweak cluster helper and refactor its specs.
parent 66b0fad6
......@@ -235,3 +235,5 @@ class Clusters::ClustersController < Clusters::BaseController
@cluster.applications.each(&:schedule_status_update)
end
end
Clusters::ClustersController.prepend(EE::Clusters::ClustersController)
......@@ -22,5 +22,3 @@ class Projects::ClustersController < Clusters::ClustersController
@repository ||= project.repository
end
end
Projects::ClustersController.prepend(EE::Projects::ClustersController)
......@@ -56,6 +56,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
raise NotImplementedError
end
def clusters_path(params = {})
raise NotImplementedError
end
def empty_state_help_text
nil
end
......@@ -68,3 +72,5 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
raise NotImplementedError
end
end
ClusterablePresenter.prepend EE::ClusterablePresenter
......@@ -44,3 +44,5 @@ class GroupClusterablePresenter < ClusterablePresenter
link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/group/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
end
GroupClusterablePresenter.prepend EE::GroupClusterablePresenter
......@@ -39,3 +39,5 @@ class ProjectClusterablePresenter < ClusterablePresenter
link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
end
ProjectClusterablePresenter.prepend EE::ProjectClusterablePresenter
# frozen_string_literal: true
module EE
module Projects
module Clusters
module ClustersController
extend ActiveSupport::Concern
def metrics
return render_404 unless prometheus_adapter&.can_query?
......
......@@ -9,17 +9,14 @@ module EE
clusterable.feature_available?(:multiple_clusters)
end
override :show_cluster_health_graphs?
def show_cluster_health_graphs?(cluster)
cluster.project_type? && cluster.project.feature_available?(:cluster_health)
def show_cluster_health_graphs?
clusterable.feature_available?(:cluster_health)
end
def cluster_health_data(cluster)
project = cluster.project
{
'metrics-endpoint': metrics_project_cluster_path(project, cluster, format: :json),
'clusters-path': project_clusters_path(project),
'clusters-path': clusterable.clusters_path,
'metrics-endpoint': clusterable.metrics_cluster_path(cluster, format: :json),
'documentation-path': help_page_path('administration/monitoring/prometheus/index.md'),
'empty-getting-started-svg-path': image_path('illustrations/monitoring/getting_started.svg'),
'empty-loading-svg-path': image_path('illustrations/monitoring/loading.svg'),
......
# frozen_string_literal: true
module EE
module ClusterablePresenter
def metrics_cluster_path(cluster, params = {})
raise NotImplementedError
end
end
end
# frozen_string_literal: true
module EE
module GroupClusterablePresenter
extend ::Gitlab::Utils::Override
override :metrics_cluster_path
def metrics_cluster_path(cluster, params = {})
metrics_group_cluster_path(clusterable, cluster, params)
end
end
end
# frozen_string_literal: true
module EE
module ProjectClusterablePresenter
extend ::Gitlab::Utils::Override
override :metrics_cluster_path
def metrics_cluster_path(cluster, params = {})
metrics_project_cluster_path(clusterable, cluster, params)
end
end
end
- return unless show_cluster_health_graphs?
%section.settings.no-animate.expanded.cluster-health-graphs#cluster-health
%h4= s_('ClusterIntegration|Cluster health')
......
# frozen_string_literal: true
require 'spec_helper'
describe Groups::ClustersController do
set(:group) { create(:group) }
it_behaves_like 'cluster metrics' do
let(:clusterable) { group }
let(:cluster) do
create(:cluster, :group, :provided_by_gcp, groups: [group])
end
let(:metrics_params) do
{
group_id: group,
id: cluster
}
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Projects::ClustersController do
include AccessMatchersForController
set(:project) { create(:project) }
describe 'GET metrics' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
describe 'functionality' do
let(:user) { create(:user) }
it_behaves_like 'cluster metrics' do
let(:clusterable) { project }
before do
project.add_maintainer(user)
sign_in(user)
let(:cluster) do
create(:cluster, :project, :provided_by_gcp, projects: [project])
end
context "Can't query Prometheus" do
it 'returns not found' do
go
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'can query Prometheus' do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true, query: nil) }
before do
allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
end
it 'queries cluster metrics' do
go
expect(prometheus_adapter).to have_received(:query).with(:cluster)
end
context 'when response has content' do
let(:query_response) { { response: nil } }
before do
allow(prometheus_adapter).to receive(:query).and_return(query_response)
end
it 'returns prometheus query response' do
go
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to eq(query_response.to_json)
end
end
context 'when response has no content' do
let(:query_response) { {} }
before do
allow(prometheus_adapter).to receive(:query).and_return(query_response)
end
it 'returns prometheus query response' do
go
expect(response).to have_gitlab_http_status(:no_content)
end
end
end
end
def go
get :metrics, params: {
let(:metrics_params) do
{
namespace_id: project.namespace,
project_id: project,
id: cluster
},
format: :json
end
describe 'security' do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true, query: nil) }
before do
allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
end
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
}
end
end
end
......@@ -3,84 +3,105 @@
require 'spec_helper'
describe ClustersHelper do
describe '#has_multiple_clusters?' do
let(:project) { build(:project) }
subject { helper.has_multiple_clusters? }
shared_examples 'feature availablilty' do |feature|
before do
# clusterable is provided as a `helper_method`
allow(helper).to receive(:clusterable).and_return(project)
end
allow(helper).to receive(:clusterable).and_return(clusterable)
context 'license is premium' do
before do
expect(project).to receive(:feature_available?).with(:multiple_clusters).and_return(true)
expect(clusterable)
.to receive(:feature_available?)
.with(feature)
.and_return(feature_available)
end
context 'feature unavailable' do
let(:feature_available) { true }
it { is_expected.to be_truthy }
end
context 'license is starter' do
before do
expect(project).to receive(:feature_available?).with(:multiple_clusters).and_return(false)
end
context 'feature available' do
let(:feature_available) { false }
it { is_expected.to be_falsey }
end
end
describe '#show_cluster_health_graphs?' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:cluster_presenter) { cluster.present }
describe '#has_multiple_clusters?' do
subject { helper.has_multiple_clusters? }
before do
stub_licensed_features(cluster_health: true)
end
context 'project level' do
let(:clusterable) { instance_double(Project) }
context 'with project level cluster' do
it 'returns true' do
expect(helper.show_cluster_health_graphs?(cluster_presenter)).to eq(true)
end
it_behaves_like 'feature availablilty', :multiple_clusters
end
context 'with group level cluster' do
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
context 'group level' do
let(:clusterable) { instance_double(Group) }
it 'returns false' do
expect(helper.show_cluster_health_graphs?(cluster_presenter)).to eq(false)
it_behaves_like 'feature availablilty', :multiple_clusters
end
end
context 'without cluster_health license' do
before do
stub_licensed_features(cluster_health: false)
end
describe '#show_cluster_health_graphs?' do
subject { helper.show_cluster_health_graphs? }
it 'returns false' do
expect(helper.show_cluster_health_graphs?(cluster_presenter)).to eq(false)
context 'project level' do
let(:clusterable) { instance_double(Project) }
it_behaves_like 'feature availablilty', :cluster_health
end
context 'group level' do
let(:clusterable) { instance_double(Group) }
it_behaves_like 'feature availablilty', :cluster_health
end
end
describe '#cluster_health_data' do
let(:project) { cluster.project }
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:cluster_presenter) { cluster.present }
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 { helper.cluster_health_data(cluster_presenter) }
before do
allow(helper).to receive(:clusterable).and_return(clusterable_presenter)
end
it 'returns graph configuration' do
expect(cluster_health_data(cluster_presenter)).to eq(
'clusters-path': project_clusters_path(project),
it do
is_expected.to match(
'clusters-path': clusterable_presenter.clusters_path,
'metrics-endpoint': clusterable_presenter.metrics_cluster_path(cluster, format: :json),
'documentation-path': help_page_path('administration/monitoring/prometheus/index.md'),
'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'),
'metrics-endpoint': metrics_project_cluster_path(project, cluster, format: :json),
'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
# frozen_string_literal: true
require 'spec_helper'
describe GroupClusterablePresenter do
include Gitlab::Routing.url_helpers
let(:presenter) { described_class.new(group) }
let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
let(:group) { cluster.group }
describe '#metrics_cluster_path' do
subject { presenter.metrics_cluster_path(cluster) }
it { is_expected.to eq(metrics_group_cluster_path(group, cluster)) }
end
end
# frozen_string_literal: true
require 'spec_helper'
describe ProjectClusterablePresenter do
include Gitlab::Routing.url_helpers
let(:presenter) { described_class.new(project) }
let(:cluster) { create(:cluster, :provided_by_gcp, :project) }
let(:project) { cluster.project }
describe '#metrics_cluster_path' do
subject { presenter.metrics_cluster_path(cluster) }
it { is_expected.to eq(metrics_project_cluster_path(project, cluster)) }
end
end
# frozen_string_literal: true
require 'spec_helper'
shared_examples 'cluster metrics' do
include AccessMatchersForController
describe 'GET #metrics' do
before do
allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
end
describe 'functionality' do
let(:user) { create(:user) }
before do
clusterable.add_maintainer(user)
sign_in(user)
end
context 'can query Prometheus' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: true, query: nil) }
it 'queries cluster metrics' do
go
expect(prometheus_adapter).to have_received(:query).with(:cluster)
end
context 'when response has content' do
let(:query_response) { { non_empty: :response } }
before do
allow(prometheus_adapter).to receive(:query).and_return(query_response)
end
it 'returns prometheus query response' do
go
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to eq(query_response.to_json)
end
end
context 'when response has no content' do
let(:query_response) { nil }
before do
allow(prometheus_adapter).to receive(:query).and_return(query_response)
end
it 'returns prometheus query response' do
go
expect(response).to have_gitlab_http_status(:no_content)
end
end
end
context 'without Prometheus' do
let(:prometheus_adapter) { nil }
it 'returns not found' do
go
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'cannot query Prometheus' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: false) }
it 'returns not found' do
go
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'security' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: true, query: nil) }
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(clusterable) }
it { expect { go }.to be_allowed_for(:maintainer).of(clusterable) }
it { expect { go }.to be_denied_for(:developer).of(clusterable) }
it { expect { go }.to be_denied_for(:reporter).of(clusterable) }
it { expect { go }.to be_denied_for(:guest).of(clusterable) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
private
def go
get :metrics, params: metrics_params, format: :json
end
end
end
......@@ -3,24 +3,21 @@
require 'spec_helper'
describe 'clusters/clusters/show' do
let(:user) { create(:user) }
let(:project) { create(:project) }
set(:user) { create(:user) }
before do
allow(controller).to receive(:current_user).and_return(user)
shared_examples 'cluster health section' do
let(:cluster_presenter) { cluster.present(current_user: user) }
let(:clusterable_presenter) do
ClusterablePresenter.fabricate(clusterable, current_user: user)
end
context 'when the cluster details page is opened' do
before do
assign(:cluster, cluster_presenter)
allow(view).to receive(:clusterable).and_return(clusterable)
allow(view).to receive(:clusterable).and_return(clusterable_presenter)
end
context 'with project level cluster' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:clusterable) { ClusterablePresenter.fabricate(project, current_user: user) }
let(:cluster_presenter) { cluster.present(current_user: user) }
context 'with feature cluster_health available' do
before do
stub_licensed_features(cluster_health: true)
end
......@@ -33,16 +30,12 @@ describe 'clusters/clusters/show' do
end
end
context 'with group level cluster' do
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
let(:clusterable) { ClusterablePresenter.fabricate(cluster.group, current_user: user) }
let(:cluster_presenter) { cluster.present(current_user: user) }
context 'without feature cluster_health available' do
before do
stub_licensed_features(cluster_health: true)
stub_licensed_features(cluster_health: false)
end
it 'does not display cluster health section' do
it 'does not show the Cluster health section' do
render
expect(rendered).not_to have_selector('#cluster-health')
......@@ -50,4 +43,24 @@ describe 'clusters/clusters/show' do
end
end
end
before do
allow(controller).to receive(:current_user).and_return(user)
end
context 'when the cluster details page is opened' do
context 'with project level cluster' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:clusterable) { cluster.project }
it_behaves_like 'cluster health section'
end
context 'with group level cluster' do
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
let(:clusterable) { cluster.group }
it_behaves_like 'cluster health section'
end
end
end
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