Commit c7691cf6 authored by Nick Thomas's avatar Nick Thomas

Merge branch 'instance_level_operator_view' into 'master'

Operator can see all projects using an instance level cluster

See merge request gitlab-org/gitlab!18173
parents 4b4c676f c92616f2
...@@ -12,3 +12,5 @@ module Clusters ...@@ -12,3 +12,5 @@ module Clusters
end end
end end
end end
Clusters::InstancePolicy.prepend_if_ee('EE::Clusters::InstancePolicy')
...@@ -42,6 +42,6 @@ ...@@ -42,6 +42,6 @@
= render 'banner' = render 'banner'
- if cluster_environments_path.present? - if cluster_environments_path.present?
= render_if_exists 'clusters/clusters/group_cluster_environments', expanded: expanded = render_if_exists 'clusters/clusters/cluster_environments', expanded: expanded
- else - else
= render 'configure', expanded: expanded = render 'configure', expanded: expanded
...@@ -145,6 +145,7 @@ Rails.application.routes.draw do ...@@ -145,6 +145,7 @@ Rails.application.routes.draw do
get :metrics, format: :json get :metrics, format: :json
get :metrics_dashboard get :metrics_dashboard
get :'/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api get :'/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api
get :environments, format: :json
end end
scope :applications do scope :applications do
......
...@@ -10,12 +10,6 @@ deployed to the Kubernetes cluster and it: ...@@ -10,12 +10,6 @@ deployed to the Kubernetes cluster and it:
## Overview ## Overview
NOTE: **Note:**
Cluster environments are only available for
[group-level clusters](../group/clusters/index.md).
Support for [instance-level](../instance/clusters/index.md) clusters is
[planned](https://gitlab.com/gitlab-org/gitlab-foss/issues/63985).
With cluster environments, you can gain insight into: With cluster environments, you can gain insight into:
- Which projects are deployed to the cluster. - Which projects are deployed to the cluster.
...@@ -37,7 +31,7 @@ In order to: ...@@ -37,7 +31,7 @@ In order to:
- Show pod usage correctly, you must - Show pod usage correctly, you must
[enable Deploy Boards](../project/deploy_boards.md#enabling-deploy-boards). [enable Deploy Boards](../project/deploy_boards.md#enabling-deploy-boards).
Once you have successful deployments to your group-level cluster: Once you have successful deployments to your group-level or instance-level cluster:
1. Navigate to your group's **Kubernetes** page. 1. Navigate to your group's **Kubernetes** page.
1. Click on the **Environments** tab. 1. Click on the **Environments** tab.
......
...@@ -139,7 +139,9 @@ The result will then be: ...@@ -139,7 +139,9 @@ The result will then be:
## Cluster environments **(PREMIUM)** ## Cluster environments **(PREMIUM)**
Please see the documentation for [cluster environments](../../clusters/environments.md). For a consolidated view of which CI [environments](../../../ci/environments.md)
are deployed to the Kubernetes cluster, see the documentation for
[cluster environments](../../clusters/environments.md).
## Security of Runners ## Security of Runners
......
...@@ -20,3 +20,9 @@ GitLab will try match to clusters in the following order: ...@@ -20,3 +20,9 @@ GitLab will try match to clusters in the following order:
To be selected, the cluster must be enabled and To be selected, the cluster must be enabled and
match the [environment selector](../../../ci/environments.md#scoping-environments-with-specs). match the [environment selector](../../../ci/environments.md#scoping-environments-with-specs).
## Cluster environments **(PREMIUM)**
For a consolidated view of which CI [environments](../../../ci/environments.md)
are deployed to the Kubernetes cluster, see the documentation for
[cluster environments](../../clusters/environments.md).
...@@ -7,6 +7,7 @@ module EE ...@@ -7,6 +7,7 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
before_action :expire_etag_cache, only: [:show]
before_action :authorize_read_prometheus!, only: :prometheus_proxy before_action :authorize_read_prometheus!, only: :prometheus_proxy
end end
...@@ -51,8 +52,40 @@ module EE ...@@ -51,8 +52,40 @@ module EE
end end
end end
def environments
respond_to do |format|
format.json do
::Gitlab::PollingInterval.set_header(response, interval: 5_000)
environments = ::Clusters::EnvironmentsFinder.new(cluster, current_user).execute
render json: serialize_environments(
environments.preload_for_cluster_environment_entity,
request,
response
)
end
end
end
private private
def expire_etag_cache
return if request.format.json? || !clusterable.environments_cluster_path(cluster)
# this forces to reload json content
::Gitlab::EtagCaching::Store.new.tap do |store|
store.touch(clusterable.environments_cluster_path(cluster))
end
end
def serialize_environments(environments, request, response)
::Clusters::EnvironmentSerializer
.new(cluster: cluster, current_user: current_user)
.with_pagination(request, response)
.represent(environments)
end
def prometheus_adapter def prometheus_adapter
return unless cluster&.application_prometheus_available? return unless cluster&.application_prometheus_available?
......
...@@ -5,44 +5,6 @@ module EE ...@@ -5,44 +5,6 @@ module EE
module ClustersController module ClustersController
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do
before_action :expire_etag_cache, only: [:show]
end
def environments
respond_to do |format|
format.json do
::Gitlab::PollingInterval.set_header(response, interval: 5_000)
environments = ::Clusters::EnvironmentsFinder.new(cluster, current_user).execute
render json: serialize_environments(
environments.preload_for_cluster_environment_entity,
request,
response
)
end
end
end
private
def expire_etag_cache
return if request.format.json?
# this forces to reload json content
::Gitlab::EtagCaching::Store.new.tap do |store|
store.touch(environments_group_cluster_path(group, cluster))
end
end
def serialize_environments(environments, request, response)
::Clusters::EnvironmentSerializer
.new(cluster: cluster, current_user: current_user)
.with_pagination(request, response)
.represent(environments)
end
def metrics_dashboard_params def metrics_dashboard_params
{ {
cluster: cluster, cluster: cluster,
......
...@@ -57,6 +57,8 @@ module EE ...@@ -57,6 +57,8 @@ module EE
if cluster&.group_type? if cluster&.group_type?
::Gitlab::Routing.url_helpers.environments_group_cluster_path(cluster.group, cluster) ::Gitlab::Routing.url_helpers.environments_group_cluster_path(cluster.group, cluster)
elsif cluster&.instance_type?
::Gitlab::Routing.url_helpers.environments_admin_cluster_path(cluster)
end end
end end
end end
......
# frozen_string_literal: true
module EE
module Clusters
module InstancePolicy
extend ActiveSupport::Concern
prepended do
condition(:cluster_deployments_available) do
License.feature_available?(:cluster_deployments)
end
rule { can?(:read_cluster) & cluster_deployments_available }
.enable :read_cluster_environments
end
end
end
end
...@@ -9,9 +9,22 @@ module EE ...@@ -9,9 +9,22 @@ module EE
metrics_admin_cluster_path(cluster, params) metrics_admin_cluster_path(cluster, params)
end end
override :environments_cluster_path
def environments_cluster_path(cluster)
return super unless can_read_cluster_environments?
environments_admin_cluster_path(cluster)
end
override :metrics_dashboard_path override :metrics_dashboard_path
def metrics_dashboard_path(cluster) def metrics_dashboard_path(cluster)
metrics_dashboard_admin_cluster_path(cluster) metrics_dashboard_admin_cluster_path(cluster)
end end
private
def can_read_cluster_environments?
can?(current_user, :read_cluster_environments, clusterable)
end
end end
end end
- is_configure_active = !params[:tab] || params[:tab] == 'configure' - is_configure_active = !params[:tab] || params[:tab] == 'configure'
- is_group_type = @cluster.cluster_type.in? 'group_type' - is_project_type = @cluster.cluster_type.in? 'project_type'
- is_creating = @cluster.status_name.in? %i/scheduled creating/ - is_creating = @cluster.status_name.in? %i/scheduled creating/
- if is_group_type && !is_creating - if !is_project_type && !is_creating
.js-toggle-container .js-toggle-container
%ul.nav-links.mobile-separator.nav.nav-tabs{ role: 'tablist' } %ul.nav-links.mobile-separator.nav.nav-tabs{ role: 'tablist' }
%li.nav-item{ role: 'presentation' } %li.nav-item{ role: 'presentation' }
%a.nav-link{ class: active_when(is_configure_active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'configure'}), id: 'group-cluster-configure-tab' } %a.nav-link{ class: active_when(is_configure_active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'configure'}), id: 'cluster-configure-tab' }
%span= _('Configuration') %span= _('Configuration')
%li.nav-item{ role: 'presentation' } %li.nav-item{ role: 'presentation' }
%a.nav-link{ class: active_when(!is_configure_active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'environments'}), id: 'group-cluster-environments-tab' } %a.nav-link{ class: active_when(!is_configure_active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'environments'}), id: 'cluster-environments-tab' }
%span.js-cluster-nav-environments= _('Environments') %span.js-cluster-nav-environments= _('Environments')
.tab-content .tab-content
- if is_configure_active - if is_configure_active
.tab-pane.active{ id: 'group-cluster-configure-pane', role: 'tabpanel' } .tab-pane.active{ id: 'cluster-configure-pane', role: 'tabpanel' }
= render 'configure', expanded: expanded = render 'configure', expanded: expanded
- else - else
.tab-pane.active{ id: 'group-cluster-environments-pane', role: 'tabpanel' } .tab-pane.active{ id: 'cluster-environments-pane', role: 'tabpanel' }
#js-cluster-environments #js-cluster-environments
- else - else
......
---
title: Operator can see all projects using an instance level cluster
merge_request: 18173
author:
type: added
...@@ -31,12 +31,6 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -31,12 +31,6 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end end
end end
resources :clusters, only: [] do
member do
get :environments, format: :json
end
end
resource :ldap, only: [] do resource :ldap, only: [] do
member do member do
put :sync put :sync
......
...@@ -3,8 +3,15 @@ ...@@ -3,8 +3,15 @@
require 'spec_helper' require 'spec_helper'
describe Admin::ClustersController do describe Admin::ClustersController do
it_behaves_like 'cluster metrics' do include AccessMatchersForController
let(:user) { create(:admin) } let(:user) { create(:admin) }
before do
sign_in(user)
end
it_behaves_like 'cluster metrics' do
let(:clusterable) { Clusters::Instance.new } let(:clusterable) { Clusters::Instance.new }
let(:cluster) do let(:cluster) do
...@@ -50,13 +57,55 @@ describe Admin::ClustersController do ...@@ -50,13 +57,55 @@ describe Admin::ClustersController do
end end
describe 'GET #metrics_dashboard' do describe 'GET #metrics_dashboard' do
let(:user) { create(:admin) } it_behaves_like 'the default dashboard'
end
end
describe 'GET environments' do
let(:cluster) { create(:cluster, :instance, :provided_by_gcp) }
before do before do
sign_in(user) create(:deployment, :success, cluster: cluster)
end end
it_behaves_like 'the default dashboard' def get_cluster_environments
get :environments,
params: { id: cluster },
format: :json
end
describe 'functionality' do
it 'responds successfully' do
get_cluster_environments
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Poll-Interval']).to eq("5000")
end
end
describe 'security' do
it { expect { get_cluster_environments }.to be_allowed_for(:admin) }
it { expect { get_cluster_environments }.to be_denied_for(:user) }
it { expect { get_cluster_environments }.to be_denied_for(:external) }
end
end
describe 'GET show' do
let(:cluster) { create(:cluster, :instance, :provided_by_gcp) }
def get_cluster
get :show, params: { id: cluster }
end
it 'expires etag cache to force reload environments list' do
stub_licensed_features(cluster_deployments: true)
expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
expect(store).to receive(:touch)
.with(environments_admin_cluster_path(cluster))
.and_call_original
end
get_cluster
end end
end end
......
...@@ -151,6 +151,7 @@ describe Groups::ClustersController do ...@@ -151,6 +151,7 @@ describe Groups::ClustersController do
end end
it 'expires etag cache to force reload environments list' do it 'expires etag cache to force reload environments list' do
stub_licensed_features(cluster_deployments: true)
expect_next_instance_of(Gitlab::EtagCaching::Store) do |store| expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
expect(store).to receive(:touch) expect(store).to receive(:touch)
.with(environments_group_cluster_path(cluster.group, cluster)) .with(environments_group_cluster_path(cluster.group, cluster))
......
...@@ -46,4 +46,16 @@ describe 'Clusterable > Show page' do ...@@ -46,4 +46,16 @@ describe 'Clusterable > Show page' do
expect(page).to have_selector('.js-cluster-nav-environments', text: 'Environments') expect(page).to have_selector('.js-cluster-nav-environments', text: 'Environments')
end end
end end
context 'when clusterable is an instance' do
let(:current_user) { create(:admin) }
let(:cluster_path) { admin_cluster_path(cluster) }
let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
it 'shows the environments tab' do
visit cluster_path
expect(page).to have_selector('.js-cluster-nav-environments', text: 'Environments')
end
end
end end
...@@ -185,6 +185,28 @@ describe Environment, :use_clean_rails_memory_store_caching do ...@@ -185,6 +185,28 @@ describe Environment, :use_clean_rails_memory_store_caching do
subject subject
end end
end end
context 'with an instance cluster' do
let(:cluster) { create(:cluster, :instance) }
before do
create(:deployment, :success, environment: environment, cluster: cluster)
end
it 'expires the environments path for the group cluster' do
expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
expect(store).to receive(:touch)
.with(::Gitlab::Routing.url_helpers.project_environments_path(project, format: :json))
.and_call_original
expect(store).to receive(:touch)
.with(::Gitlab::Routing.url_helpers.environments_admin_cluster_path(cluster))
.and_call_original
end
subject
end
end
end end
describe '#rollout_status' do describe '#rollout_status' do
......
...@@ -4,25 +4,21 @@ require 'spec_helper' ...@@ -4,25 +4,21 @@ require 'spec_helper'
describe Clusters::InstancePolicy do describe Clusters::InstancePolicy do
let(:user) { create(:admin) } let(:user) { create(:admin) }
let(:policy) { described_class.new(user, Clusters::Instance.new) } subject { described_class.new(user, Clusters::Instance.new) }
describe 'rules' do context 'when cluster deployments is available' do
context 'when admin' do before do
it { expect(policy).to be_allowed :read_cluster } stub_licensed_features(cluster_deployments: true)
it { expect(policy).to be_allowed :add_cluster }
it { expect(policy).to be_allowed :create_cluster }
it { expect(policy).to be_allowed :update_cluster }
it { expect(policy).to be_allowed :admin_cluster }
end end
context 'when not admin' do it { is_expected.to be_allowed(:read_cluster_environments) }
let(:user) { create(:user) } end
it { expect(policy).to be_disallowed :read_cluster } context 'when cluster deployments is not available' do
it { expect(policy).to be_disallowed :add_cluster } before do
it { expect(policy).to be_disallowed :create_cluster } stub_licensed_features(cluster_deployments: false)
it { expect(policy).to be_disallowed :update_cluster }
it { expect(policy).to be_disallowed :admin_cluster }
end end
it { is_expected.not_to be_allowed(:read_cluster_environments) }
end end
end end
...@@ -14,10 +14,6 @@ describe 'Clusterable > Show page' do ...@@ -14,10 +14,6 @@ describe 'Clusterable > Show page' do
end end
shared_examples 'show page' do shared_examples 'show page' do
before do
clusterable.add_maintainer(current_user)
end
it 'allow the user to set domain' do it 'allow the user to set domain' do
visit cluster_path visit cluster_path
...@@ -63,7 +59,6 @@ describe 'Clusterable > Show page' do ...@@ -63,7 +59,6 @@ describe 'Clusterable > Show page' do
shared_examples 'editing a GCP cluster' do shared_examples 'editing a GCP cluster' do
before do before do
clusterable.add_maintainer(current_user)
visit cluster_path visit cluster_path
end end
...@@ -92,7 +87,6 @@ describe 'Clusterable > Show page' do ...@@ -92,7 +87,6 @@ describe 'Clusterable > Show page' do
shared_examples 'editing a user-provided cluster' do shared_examples 'editing a user-provided cluster' do
before do before do
stub_kubeclient_discover(cluster.platform.api_url) stub_kubeclient_discover(cluster.platform.api_url)
clusterable.add_maintainer(current_user)
visit cluster_path visit cluster_path
end end
...@@ -123,6 +117,10 @@ describe 'Clusterable > Show page' do ...@@ -123,6 +117,10 @@ describe 'Clusterable > Show page' do
let(:cluster_path) { project_cluster_path(clusterable, cluster) } let(:cluster_path) { project_cluster_path(clusterable, cluster) }
let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) } let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) }
before do
clusterable.add_maintainer(current_user)
end
it_behaves_like 'show page' it_behaves_like 'show page'
it_behaves_like 'editing a GCP cluster' it_behaves_like 'editing a GCP cluster'
...@@ -137,6 +135,10 @@ describe 'Clusterable > Show page' do ...@@ -137,6 +135,10 @@ describe 'Clusterable > Show page' do
let(:cluster_path) { group_cluster_path(clusterable, cluster) } let(:cluster_path) { group_cluster_path(clusterable, cluster) }
let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) } let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) }
before do
clusterable.add_maintainer(current_user)
end
it_behaves_like 'show page' it_behaves_like 'show page'
it_behaves_like 'editing a GCP cluster' it_behaves_like 'editing a GCP cluster'
...@@ -145,4 +147,18 @@ describe 'Clusterable > Show page' do ...@@ -145,4 +147,18 @@ describe 'Clusterable > Show page' do
let(:cluster) { create(:cluster, :provided_by_user, :group, groups: [clusterable]) } let(:cluster) { create(:cluster, :provided_by_user, :group, groups: [clusterable]) }
end end
end end
context 'when clusterable is an instance' do
let(:current_user) { create(:admin) }
let(:cluster_path) { admin_cluster_path(cluster) }
let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
it_behaves_like 'show page'
it_behaves_like 'editing a GCP cluster'
it_behaves_like 'editing a user-provided cluster' do
let(:cluster) { create(:cluster, :provided_by_user, :instance) }
end
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