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
end
end
end
Clusters::InstancePolicy.prepend_if_ee('EE::Clusters::InstancePolicy')
......@@ -42,6 +42,6 @@
= render 'banner'
- 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
= render 'configure', expanded: expanded
......@@ -145,6 +145,7 @@ Rails.application.routes.draw do
get :metrics, format: :json
get :metrics_dashboard
get :'/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api
get :environments, format: :json
end
scope :applications do
......
......@@ -10,12 +10,6 @@ deployed to the Kubernetes cluster and it:
## 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:
- Which projects are deployed to the cluster.
......@@ -37,7 +31,7 @@ In order to:
- Show pod usage correctly, you must
[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. Click on the **Environments** tab.
......
......@@ -139,7 +139,9 @@ The result will then be:
## 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
......
......@@ -20,3 +20,9 @@ GitLab will try match to clusters in the following order:
To be selected, the cluster must be enabled and
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
extend ActiveSupport::Concern
prepended do
before_action :expire_etag_cache, only: [:show]
before_action :authorize_read_prometheus!, only: :prometheus_proxy
end
......@@ -51,8 +52,40 @@ module EE
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
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
return unless cluster&.application_prometheus_available?
......
......@@ -5,44 +5,6 @@ module EE
module ClustersController
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
{
cluster: cluster,
......
......@@ -57,6 +57,8 @@ module EE
if cluster&.group_type?
::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
......
# 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
metrics_admin_cluster_path(cluster, params)
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
def metrics_dashboard_path(cluster)
metrics_dashboard_admin_cluster_path(cluster)
end
private
def can_read_cluster_environments?
can?(current_user, :read_cluster_environments, clusterable)
end
end
end
- 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/
- if is_group_type && !is_creating
- if !is_project_type && !is_creating
.js-toggle-container
%ul.nav-links.mobile-separator.nav.nav-tabs{ role: 'tablist' }
%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')
%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')
.tab-content
- 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
- else
.tab-pane.active{ id: 'group-cluster-environments-pane', role: 'tabpanel' }
.tab-pane.active{ id: 'cluster-environments-pane', role: 'tabpanel' }
#js-cluster-environments
- 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
end
end
resources :clusters, only: [] do
member do
get :environments, format: :json
end
end
resource :ldap, only: [] do
member do
put :sync
......
......@@ -3,8 +3,15 @@
require 'spec_helper'
describe Admin::ClustersController do
it_behaves_like 'cluster metrics' do
include AccessMatchersForController
let(:user) { create(:admin) }
before do
sign_in(user)
end
it_behaves_like 'cluster metrics' do
let(:clusterable) { Clusters::Instance.new }
let(:cluster) do
......@@ -50,13 +57,55 @@ describe Admin::ClustersController do
end
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
sign_in(user)
create(:deployment, :success, cluster: cluster)
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
......
......@@ -151,6 +151,7 @@ describe Groups::ClustersController do
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_group_cluster_path(cluster.group, cluster))
......
......@@ -46,4 +46,16 @@ describe 'Clusterable > Show page' do
expect(page).to have_selector('.js-cluster-nav-environments', text: 'Environments')
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
......@@ -185,6 +185,28 @@ describe Environment, :use_clean_rails_memory_store_caching do
subject
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
describe '#rollout_status' do
......
......@@ -4,25 +4,21 @@ require 'spec_helper'
describe Clusters::InstancePolicy do
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 admin' do
it { expect(policy).to be_allowed :read_cluster }
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 }
context 'when cluster deployments is available' do
before do
stub_licensed_features(cluster_deployments: true)
end
context 'when not admin' do
let(:user) { create(:user) }
it { is_expected.to be_allowed(:read_cluster_environments) }
end
it { expect(policy).to be_disallowed :read_cluster }
it { expect(policy).to be_disallowed :add_cluster }
it { expect(policy).to be_disallowed :create_cluster }
it { expect(policy).to be_disallowed :update_cluster }
it { expect(policy).to be_disallowed :admin_cluster }
context 'when cluster deployments is not available' do
before do
stub_licensed_features(cluster_deployments: false)
end
it { is_expected.not_to be_allowed(:read_cluster_environments) }
end
end
......@@ -14,10 +14,6 @@ describe 'Clusterable > Show page' do
end
shared_examples 'show page' do
before do
clusterable.add_maintainer(current_user)
end
it 'allow the user to set domain' do
visit cluster_path
......@@ -63,7 +59,6 @@ describe 'Clusterable > Show page' do
shared_examples 'editing a GCP cluster' do
before do
clusterable.add_maintainer(current_user)
visit cluster_path
end
......@@ -92,7 +87,6 @@ describe 'Clusterable > Show page' do
shared_examples 'editing a user-provided cluster' do
before do
stub_kubeclient_discover(cluster.platform.api_url)
clusterable.add_maintainer(current_user)
visit cluster_path
end
......@@ -123,6 +117,10 @@ describe 'Clusterable > Show page' do
let(:cluster_path) { project_cluster_path(clusterable, cluster) }
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 'editing a GCP cluster'
......@@ -137,6 +135,10 @@ describe 'Clusterable > Show page' do
let(:cluster_path) { group_cluster_path(clusterable, cluster) }
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 'editing a GCP cluster'
......@@ -145,4 +147,18 @@ describe 'Clusterable > Show page' do
let(:cluster) { create(:cluster, :provided_by_user, :group, groups: [clusterable]) }
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
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