Commit 5b915f5d authored by Thong Kuah's avatar Thong Kuah Committed by Igor Drozdov

Add (BYO) Prometheus as cluster integration

parent c36cf6d0
......@@ -141,6 +141,9 @@ export default {
isInstalling() {
return this.status === APPLICATION_STATUS.INSTALLING;
},
isExternallyInstalled() {
return this.status === APPLICATION_STATUS.EXTERNALLY_INSTALLED;
},
canInstall() {
return (
this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
......@@ -193,10 +196,17 @@ export default {
label = __('Installing');
} else if (this.installed) {
label = __('Installed');
} else if (this.isExternallyInstalled) {
label = __('Externally installed');
}
return label;
},
buttonGridCellClass() {
return this.showManageButton || this.status === APPLICATION_STATUS.EXTERNALLY_INSTALLED
? 'section-25'
: 'section-15';
},
showManageButton() {
return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
},
......@@ -427,8 +437,7 @@ export default {
</div>
</div>
<div
:class="{ 'section-25': showManageButton, 'section-15': !showManageButton }"
class="table-section table-button-footer section-align-top"
:class="[buttonGridCellClass, 'table-section', 'table-button-footer', 'section-align-top']"
role="gridcell"
>
<div v-if="showManageButton" class="btn-group table-action-buttons">
......
......@@ -26,6 +26,7 @@ export const APPLICATION_STATUS = {
ERROR: 'errored',
PRE_INSTALLED: 'pre_installed',
UNINSTALLED: 'uninstalled',
EXTERNALLY_INSTALLED: 'externally_installed',
};
/*
......
import Vue from 'vue';
import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
import IntegrationForm from '../components/integration_form.vue';
import { createStore } from '../stores';
export default () => {
dirtySubmitFactory(document.querySelectorAll('.js-cluster-integrations-form'));
const entryPoint = document.querySelector('#js-cluster-details-form');
if (!entryPoint) {
......
......@@ -15,6 +15,7 @@ const {
UNINSTALL_ERRORED,
PRE_INSTALLED,
UNINSTALLED,
EXTERNALLY_INSTALLED,
} = APPLICATION_STATUS;
const applicationStateMachine = {
......@@ -71,6 +72,9 @@ const applicationStateMachine = {
[UNINSTALLED]: {
target: UNINSTALLED,
},
[EXTERNALLY_INSTALLED]: {
target: EXTERNALLY_INSTALLED,
},
},
},
[NOT_INSTALLABLE]: {
......
# frozen_string_literal: true
class Admin::Clusters::IntegrationsController < Clusters::IntegrationsController
include EnforcesAdminAuthentication
private
def clusterable
@clusterable ||= InstanceClusterablePresenter.fabricate(Clusters::Instance.new, current_user: current_user)
end
end
......@@ -60,6 +60,9 @@ class Clusters::ClustersController < Clusters::BaseController
end
def show
if params[:tab] == 'integrations'
@prometheus_integration = Clusters::IntegrationPresenter.new(@cluster.find_or_build_application(Clusters::Applications::Prometheus))
end
end
def update
......
# frozen_string_literal: true
module Clusters
class IntegrationsController < ::Clusters::BaseController
before_action :cluster
before_action :authorize_admin_cluster!, only: [:create_or_update]
def create_or_update
service_response = Clusters::Integrations::CreateService
.new(container: clusterable, cluster: cluster, current_user: current_user, params: cluster_integration_params)
.execute
if service_response.success?
redirect_to cluster.show_path(params: { tab: 'integrations' }), notice: service_response.message
else
redirect_to cluster.show_path(params: { tab: 'integrations' }), alert: service_response.message
end
end
private
def clusterable
raise NotImplementedError
end
def cluster_integration_params
params.require(:integration).permit(:application_type, :enabled)
end
def cluster
@cluster ||= clusterable.clusters.find(params[:cluster_id]).present(current_user: current_user)
end
end
end
# frozen_string_literal: true
class Groups::Clusters::IntegrationsController < Clusters::IntegrationsController
include ControllerWithCrossProjectAccessCheck
prepend_before_action :group
requires_cross_project_access
private
def clusterable
@clusterable ||= ClusterablePresenter.fabricate(group, current_user: current_user)
end
def group
@group ||= find_routable!(Group, params[:group_id] || params[:id])
end
end
# frozen_string_literal: true
class Projects::Clusters::IntegrationsController < ::Clusters::IntegrationsController
prepend_before_action :project
private
def clusterable
@clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user)
end
def project
@project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]))
end
end
......@@ -71,6 +71,8 @@ module ClustersHelper
render_if_exists 'clusters/clusters/health'
when 'apps'
render 'applications'
when 'integrations'
render 'integrations'
when 'settings'
render 'advanced_settings_container'
else
......
......@@ -32,7 +32,7 @@ module Clusters
end
state_machine :status do
after_transition any => [:installed] do |application|
after_transition any => [:installed, :externally_installed] do |application|
application.run_after_commit do
Clusters::Applications::ActivateServiceWorker
.perform_async(application.cluster_id, ::PrometheusService.to_param) # rubocop:disable CodeReuse/ServiceClass
......
......@@ -9,6 +9,7 @@ module Clusters
scope :available, -> do
where(
status: [
self.state_machines[:status].states[:externally_installed].value,
self.state_machines[:status].states[:installed].value,
self.state_machines[:status].states[:updated].value
]
......@@ -28,6 +29,7 @@ module Clusters
state :uninstalling, value: 7
state :uninstall_errored, value: 8
state :uninstalled, value: 10
state :externally_installed, value: 11
# Used for applications that are pre-installed by the cluster,
# e.g. Knative in GCP Cloud Run enabled clusters
......@@ -37,7 +39,7 @@ module Clusters
state :pre_installed, value: 9
event :make_externally_installed do
transition any => :installed
transition any => :externally_installed
end
event :make_externally_uninstalled do
......@@ -79,7 +81,7 @@ module Clusters
transition [:scheduled] => :uninstalling
end
before_transition any => [:scheduled, :installed, :uninstalled] do |application, _|
before_transition any => [:scheduled, :installed, :uninstalled, :externally_installed] do |application, _|
application.status_reason = nil
end
......@@ -114,7 +116,7 @@ module Clusters
end
def available?
pre_installed? || installed? || updated?
pre_installed? || installed? || externally_installed? || updated?
end
def update_in_progress?
......
......@@ -49,13 +49,25 @@ module Clusters
end
end
def show_path
def show_path(params: {})
if cluster.project_type?
project_cluster_path(project, cluster)
project_cluster_path(project, cluster, params)
elsif cluster.group_type?
group_cluster_path(group, cluster)
group_cluster_path(group, cluster, params)
elsif cluster.instance_type?
admin_cluster_path(cluster)
admin_cluster_path(cluster, params)
else
raise NotImplementedError
end
end
def integrations_path
if cluster.project_type?
create_or_update_project_cluster_integration_path(project, cluster)
elsif cluster.group_type?
create_or_update_group_cluster_integration_path(group, cluster)
elsif cluster.instance_type?
create_or_update_admin_cluster_integration_path(cluster)
else
raise NotImplementedError
end
......
# frozen_string_literal: true
module Clusters
class IntegrationPresenter < Gitlab::View::Presenter::Delegated
presents :integration
def integration?
integration.new_record? || integration.externally_installed? || integration.uninstalled?
end
def application_type
integration.class.application_name
end
def enabled
integration.externally_installed?
end
end
end
# frozen_string_literal: true
module Clusters
module Integrations
class CreateService < BaseContainerService
InvalidApplicationError = Class.new(StandardError)
attr_accessor :cluster
def initialize(container:, cluster:, current_user: nil, params: {})
@cluster = cluster
super(container: container, current_user: current_user, params: params)
end
def execute
return ServiceResponse.error(message: 'Unauthorized') unless authorized?
application_class = Clusters::Cluster::APPLICATIONS[params[:application_type]]
application = cluster.find_or_build_application(application_class)
if params[:enabled]
application.make_externally_installed!
ServiceResponse.success(message: s_('ClusterIntegration|Integration enabled'), payload: { application: application })
else
application.make_externally_uninstalled!
ServiceResponse.success(message: s_('ClusterIntegration|Integration disabled'), payload: { application: application })
end
end
private
def authorized?
Ability.allowed?(current_user, :admin_cluster, cluster)
end
end
end
end
.settings.expanded.border-0.m-0
%p
= s_('ClusterIntegration|Integrations enable you to integrate your cluster as part of your GitLab workflow.')
= link_to _('Learn more'), help_page_path('user/clusters/integrations.md'), target: '_blank'
.settings-content#advanced-settings-section
- if can?(current_user, :admin_cluster, @cluster) && @prometheus_integration.integration?
.sub-section.form-group
= form_for @prometheus_integration, url: @cluster.integrations_path, as: :integration, method: :post, html: { class: 'js-cluster-integrations-form' } do |form|
= form.hidden_field :application_type
.form-group
.gl-form-checkbox.custom-control.custom-checkbox
= form.check_box :enabled, { class: 'custom-control-input'}, true, false
= form.label :enabled, s_('ClusterIntegration|Enable Prometheus integration'), class: 'custom-control-label'
.gl-form-group
.form-text.text-gl-muted
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path("user/clusters/integrations", anchor: "prometheus-cluster-integration") }
- link_end = '</a>'.html_safe
= html_escape(s_('ClusterIntegration|Before you enable this integration, follow the %{link_start}documented process%{link_end}.')) % { link_start: link_start, link_end: link_end }
= form.submit _('Save changes'), class: 'btn gl-button btn-success'
- tab_name = 'integrations'
- active = params[:tab] == tab_name
%li.nav-item{ role: 'presentation' }
%a#cluster-apps-tab.nav-link{ class: active_when(active), href: clusterable.cluster_path(@cluster.id, params: {tab: tab_name}) }
%span= _('Integrations')
......@@ -59,6 +59,7 @@
= render_if_exists 'clusters/clusters/environments_tab'
= render 'clusters/clusters/health_tab'
= render 'applications_tab'
= render 'integrations_tab'
= render 'advanced_settings_tab'
.tab-content.py-3
......
---
title: Ability to add Prometheus as cluster integration
merge_request: 55244
author:
type: added
......@@ -220,6 +220,12 @@ Rails.application.routes.draw do
post :authorize_aws_role
end
resource :integration, controller: 'clusters/integrations', only: [] do
collection do
post :create_or_update
end
end
member do
Gitlab.ee do
get :metrics, format: :json
......
......@@ -1001,8 +1001,8 @@ Logs produced by pods running **GitLab Managed Apps** can be browsed using
## Install with one click
WARNING:
The one click installation method is scheduled for removal in GitLab 14.0. The removal
of this feature from GitLab does not affect installed applications to avoid breaking
The one-click installation method is deprecated and scheduled for removal in [GitLab 14.0](https://gitlab.com/groups/gitlab-org/-/epics/4280).
This removal does not affect installed applications to avoid breaking
changes. Following GitLab 14.0, users can take ownership of already installed applications
using our documentation.
......
---
stage: Configure
group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Cluster integrations **(FREE)**
GitLab provides several ways to integrate applications to your
Kubernetes cluster.
To enable cluster integrations, first add a Kubernetes cluster to a GitLab
[project](../project/clusters/add_remove_clusters.md) or [group](../group/clusters/index.md#group-level-kubernetes-clusters).
## Prometheus cluster integration
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55244) in GitLab 13.11.
You can integrate your Kubernetes cluster with
[Prometheus](https://prometheus.io/) for monitoring key metrics of your
apps directly from the GitLab UI.
Once enabled, you will see metrics from services available in the
[metrics library](../project/integrations/prometheus_library/index.md).
Prerequisites:
To benefit from this integration, you must have Prometheus
installed in your cluster with the following requirements:
1. Prometheus must be installed inside the `gitlab-managed-apps` namespace.
1. The `Service` resource for Prometheus must be named `prometheus-prometheus-server`.
You can use the following commands to install Prometheus to meet the requirements for cluster integrations:
```shell
# Create the require Kubernetes namespace
kubectl create ns gitlab-managed-apps
# Download Helm chart values that is compatible with the requirements above.
# You should substitute the tag that corresponds to the GitLab version in the url
# - https://gitlab.com/gitlab-org/gitlab/-/raw/<tag>/vendor/prometheus/values.yaml
#
wget https://gitlab.com/gitlab-org/gitlab/-/raw/v13.9.0-ee/vendor/prometheus/values.yaml
# Add the Prometheus community helm repo
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
# Install Prometheus
helm install prometheus prometheus-community/prometheus -n gitlab-managed-apps --values values.yaml
```
Alternatively, you can use your preferred installation method to install
Prometheus as long as you meet the requirements above.
### Enable Prometheus integration for your cluster
To enable the Prometheus integration for your cluster:
1. Go to the cluster's page:
- For a [project-level cluster](../project/clusters/index.md), navigate to your project's
**Operations > Kubernetes**.
- For a [group-level cluster](../group/clusters/index.md), navigate to your group's
**Kubernetes** page.
1. Select the **Integrations** tab.
1. Check the **Enable Prometheus integration** checkbox.
1. Click **Save changes**.
1. Go to the **Health** tab to see your cluster's metrics.
......@@ -31,6 +31,10 @@ Once enabled, GitLab detects metrics from known services in the
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/28916) in GitLab 10.5.
**Deprecated:** Managed Prometheus on Kubernetes is deprecated, and
scheduled for removal in [GitLab
14.0](https://gitlab.com/groups/gitlab-org/-/epics/4280).
GitLab can seamlessly deploy and manage Prometheus on a
[connected Kubernetes cluster](../clusters/index.md), to help you monitor your apps.
......
......@@ -6678,6 +6678,9 @@ msgstr ""
msgid "ClusterIntegration|Base domain"
msgstr ""
msgid "ClusterIntegration|Before you enable this integration, follow the %{link_start}documented process%{link_end}."
msgstr ""
msgid "ClusterIntegration|Blocking mode"
msgstr ""
......@@ -6843,6 +6846,9 @@ msgstr ""
msgid "ClusterIntegration|Enable Cloud Run for Anthos"
msgstr ""
msgid "ClusterIntegration|Enable Prometheus integration"
msgstr ""
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
msgstr ""
......@@ -6981,6 +6987,15 @@ msgstr ""
msgid "ClusterIntegration|Integrate with a cluster certificate"
msgstr ""
msgid "ClusterIntegration|Integration disabled"
msgstr ""
msgid "ClusterIntegration|Integration enabled"
msgstr ""
msgid "ClusterIntegration|Integrations enable you to integrate your cluster as part of your GitLab workflow."
msgstr ""
msgid "ClusterIntegration|Issuer Email"
msgstr ""
......@@ -12777,6 +12792,9 @@ msgstr ""
msgid "ExternalWikiService|The URL of the external wiki"
msgstr ""
msgid "Externally installed"
msgstr ""
msgid "Facebook"
msgstr ""
......
......@@ -546,20 +546,30 @@ RSpec.describe Admin::ClustersController do
describe 'GET #show' do
let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
def get_show
def get_show(tab: nil)
get :show,
params: {
id: cluster
id: cluster,
tab: tab
}
end
describe 'functionality' do
render_views
it 'responds successfully' do
get_show
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:cluster)).to eq(cluster)
end
it 'renders integration tab view' do
get_show(tab: 'integrations')
expect(response).to render_template('clusters/clusters/_integrations')
expect(response).to have_gitlab_http_status(:ok)
end
end
describe 'security' do
......
......@@ -641,21 +641,31 @@ RSpec.describe Groups::ClustersController do
describe 'GET show' do
let(:cluster) { create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) }
def go
def go(tab: nil)
get :show,
params: {
group_id: group,
id: cluster
id: cluster,
tab: tab
}
end
describe 'functionality' do
render_views
it 'renders view' do
go
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:cluster)).to eq(cluster)
end
it 'renders integration tab view', :aggregate_failures do
go(tab: 'integrations')
expect(response).to render_template('clusters/clusters/_integrations')
expect(response).to have_gitlab_http_status(:ok)
end
end
describe 'security' do
......
......@@ -674,22 +674,32 @@ RSpec.describe Projects::ClustersController do
describe 'GET show' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
def go
def go(tab: nil)
get :show,
params: {
namespace_id: project.namespace,
project_id: project,
id: cluster
id: cluster,
tab: tab
}
end
describe 'functionality' do
render_views
it "renders view" do
go
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:cluster)).to eq(cluster)
end
it 'renders integration tab view' do
go(tab: 'integrations')
expect(response).to render_template('clusters/clusters/_integrations')
expect(response).to have_gitlab_http_status(:ok)
end
end
describe 'security' do
......
......@@ -69,6 +69,10 @@ FactoryBot.define do
status { 10 }
end
trait :externally_installed do
status { 11 }
end
trait :timed_out do
installing
updated_at { ClusterWaitForAppInstallationWorker::TIMEOUT.ago }
......
......@@ -89,6 +89,12 @@ describe('Application Row', () => {
checkButtonState('Install', false, true);
});
it('has disabled "Externally installed" when APPLICATION_STATUS.EXTERNALLY_INSTALLED', () => {
mountComponent({ status: APPLICATION_STATUS.EXTERNALLY_INSTALLED });
checkButtonState('Externally installed', false, true);
});
it('has disabled "Installed" when application is installed and not uninstallable', () => {
mountComponent({
status: APPLICATION_STATUS.INSTALLED,
......
......@@ -20,6 +20,8 @@ const {
UNINSTALLING,
UNINSTALL_ERRORED,
UNINSTALLED,
PRE_INSTALLED,
EXTERNALLY_INSTALLED,
} = APPLICATION_STATUS;
const NO_EFFECTS = 'no effects';
......@@ -29,19 +31,21 @@ describe('applicationStateMachine', () => {
describe(`current state is ${NO_STATUS}`, () => {
it.each`
expectedState | event | effects
${INSTALLING} | ${SCHEDULED} | ${NO_EFFECTS}
${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
${INSTALLABLE} | ${INSTALLABLE} | ${NO_EFFECTS}
${INSTALLING} | ${INSTALLING} | ${NO_EFFECTS}
${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
${UPDATING} | ${UPDATING} | ${NO_EFFECTS}
${INSTALLED} | ${UPDATED} | ${NO_EFFECTS}
${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
${UNINSTALLING} | ${UNINSTALLING} | ${NO_EFFECTS}
${INSTALLED} | ${UNINSTALL_ERRORED} | ${{ uninstallFailed: true }}
${UNINSTALLED} | ${UNINSTALLED} | ${NO_EFFECTS}
expectedState | event | effects
${INSTALLING} | ${SCHEDULED} | ${NO_EFFECTS}
${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
${INSTALLABLE} | ${INSTALLABLE} | ${NO_EFFECTS}
${INSTALLING} | ${INSTALLING} | ${NO_EFFECTS}
${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
${UPDATING} | ${UPDATING} | ${NO_EFFECTS}
${INSTALLED} | ${UPDATED} | ${NO_EFFECTS}
${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
${UNINSTALLING} | ${UNINSTALLING} | ${NO_EFFECTS}
${INSTALLED} | ${UNINSTALL_ERRORED} | ${{ uninstallFailed: true }}
${UNINSTALLED} | ${UNINSTALLED} | ${NO_EFFECTS}
${PRE_INSTALLED} | ${PRE_INSTALLED} | ${NO_EFFECTS}
${EXTERNALLY_INSTALLED} | ${EXTERNALLY_INSTALLED} | ${NO_EFFECTS}
`(`transitions to $expectedState on $event event and applies $effects`, (data) => {
const { expectedState, event, effects } = data;
const currentAppState = {
......
......@@ -39,6 +39,19 @@ RSpec.describe Clusters::Applications::Prometheus do
end
end
describe 'transition to externally_installed' do
let(:project) { create(:project) }
let(:cluster) { create(:cluster, :with_installed_helm) }
let(:application) { create(:clusters_applications_prometheus, :installing, cluster: cluster) }
it 'schedules post installation job' do
expect(Clusters::Applications::ActivateServiceWorker)
.to receive(:perform_async).with(cluster.id, 'prometheus')
application.make_externally_installed!
end
end
describe 'transition to updating' do
let(:project) { create(:project) }
let(:cluster) { create(:cluster, projects: [project]) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Admin::Clusters::IntegrationsController, :enable_admin_mode do
include AccessMatchersForController
shared_examples 'a secure endpoint' do
context 'it is allowed for admins only' do
it { expect { subject }.to be_allowed_for(:admin) }
it { expect { subject }.to be_denied_for(:user) }
it { expect { subject }.to be_denied_for(:external) }
end
end
describe 'POST create_or_update' do
let(:cluster) { create(:cluster, :instance, :provided_by_gcp) }
let(:user) { create(:admin) }
it_behaves_like '#create_or_update action' do
let(:path) { create_or_update_admin_cluster_integration_path(cluster) }
let(:redirect_path) { admin_cluster_path(cluster, params: { tab: 'integrations' }) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::Clusters::IntegrationsController do
include AccessMatchersForController
shared_examples 'a secure endpoint' do
it 'is allowed for admin when admin mode enabled', :enable_admin_mode do
expect { subject }.to be_allowed_for(:admin)
end
it 'is denied for admin when admin mode disabled' do
expect { subject }.to be_denied_for(:admin)
end
context 'it is allowed for group maintainers' do
it { expect { subject }.to be_allowed_for(:owner).of(group) }
it { expect { subject }.to be_allowed_for(:maintainer).of(group) }
it { expect { subject }.to be_denied_for(:developer).of(group) }
it { expect { subject }.to be_denied_for(:reporter).of(group) }
it { expect { subject }.to be_denied_for(:guest).of(group) }
it { expect { subject }.to be_denied_for(:user) }
it { expect { subject }.to be_denied_for(:external) }
end
end
describe 'POST create_or_update' do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:member) { create(:group_member, user: user, group: group) }
let(:cluster) { create(:cluster, :group, :provided_by_gcp, groups: [group]) }
it_behaves_like '#create_or_update action' do
let(:path) { create_or_update_group_cluster_integration_path(group, cluster) }
let(:redirect_path) { group_cluster_path(group, cluster, params: { tab: 'integrations' }) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Clusters::IntegrationsController do
include AccessMatchersForController
shared_examples 'a secure endpoint' do
it 'is allowed for admin when admin mode enabled', :enable_admin_mode do
expect { subject }.to be_allowed_for(:admin)
end
it 'is denied for admin when admin mode disabled' do
expect { subject }.to be_denied_for(:admin)
end
context 'it is allowed for project maintainers' do
it { expect { subject }.to be_allowed_for(:owner).of(project) }
it { expect { subject }.to be_allowed_for(:maintainer).of(project) }
it { expect { subject }.to be_denied_for(:developer).of(project) }
it { expect { subject }.to be_denied_for(:reporter).of(project) }
it { expect { subject }.to be_denied_for(:guest).of(project) }
it { expect { subject }.to be_denied_for(:user) }
it { expect { subject }.to be_denied_for(:external) }
end
end
describe 'POST create_or_update' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let(:user) { project.owner }
it_behaves_like '#create_or_update action' do
let(:path) { create_or_update_project_cluster_integration_path(project, cluster) }
let(:redirect_path) { project_cluster_path(project, cluster, params: { tab: 'integrations' }) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Clusters::Integrations::CreateService, '#execute' do
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:params) do
{ application_type: 'prometheus', enabled: true }
end
let(:service) do
described_class.new(container: project, cluster: cluster, current_user: project.owner, params: params)
end
it 'creates a new Prometheus instance' do
expect(service.execute).to be_success
expect(cluster.application_prometheus).to be_present
expect(cluster.application_prometheus).to be_persisted
expect(cluster.application_prometheus).to be_externally_installed
end
context 'enabled param is false' do
let(:params) do
{ application_type: 'prometheus', enabled: false }
end
it 'creates a new uninstalled Prometheus instance' do
expect(service.execute).to be_success
expect(cluster.application_prometheus).to be_present
expect(cluster.application_prometheus).to be_persisted
expect(cluster.application_prometheus).to be_uninstalled
end
end
context 'unauthorized user' do
let(:service) do
unauthorized_user = create(:user)
described_class.new(container: project, cluster: cluster, current_user: unauthorized_user, params: params)
end
it 'does not create a new Prometheus instance' do
expect(service.execute).to be_error
expect(cluster.application_prometheus).to be_nil
end
end
context 'prometheus record exists' do
before do
create(:clusters_applications_prometheus, cluster: cluster)
end
it 'updates the Prometheus instance' do
expect(service.execute).to be_success
expect(cluster.application_prometheus).to be_present
expect(cluster.application_prometheus).to be_persisted
expect(cluster.application_prometheus).to be_externally_installed
end
context 'enabled param is false' do
let(:params) do
{ application_type: 'prometheus', enabled: false }
end
it 'updates the Prometheus instance as uninstalled' do
expect(service.execute).to be_success
expect(cluster.application_prometheus).to be_present
expect(cluster.application_prometheus).to be_persisted
expect(cluster.application_prometheus).to be_uninstalled
end
end
end
context 'for an un-supported application type' do
let(:params) do
{ application_type: 'something_else', enabled: true }
end
it 'errors' do
expect { service.execute}.to raise_error(ArgumentError)
end
end
end
......@@ -138,7 +138,7 @@ RSpec.shared_examples 'cluster application status specs' do |application_name|
it 'is installed' do
subject.make_externally_installed
expect(subject).to be_installed
expect(subject).to be_externally_installed
end
context 'helm record does not exist' do
......@@ -170,7 +170,7 @@ RSpec.shared_examples 'cluster application status specs' do |application_name|
it 'is installed' do
subject.make_externally_installed
expect(subject).to be_installed
expect(subject).to be_externally_installed
end
end
......@@ -180,7 +180,7 @@ RSpec.shared_examples 'cluster application status specs' do |application_name|
it 'is installed' do
subject.make_externally_installed
expect(subject).to be_installed
expect(subject).to be_externally_installed
end
it 'clears #status_reason' do
......@@ -317,6 +317,7 @@ RSpec.shared_examples 'cluster application status specs' do |application_name|
:uninstall_errored | false
:uninstalled | false
:timed_out | false
:externally_installed | true
end
with_them do
......
# frozen_string_literal: true
RSpec.shared_examples '#create_or_update action' do
let(:params) do
{ integration: { application_type: Clusters::Applications::Prometheus.application_name, enabled: true } }
end
let(:path) { raise NotImplementedError }
let(:redirect_path) { raise NotImplementedError }
describe 'authorization' do
subject do
post path, params: params
end
it_behaves_like 'a secure endpoint'
end
describe 'functionality' do
before do
sign_in(user)
end
it 'redirects on success' do
post path, params: params
expect(response).to have_gitlab_http_status(:redirect)
expect(response).to redirect_to(redirect_path)
expect(flash[:notice]).to be_present
end
it 'redirects on error' do
error = ServiceResponse.error(message: 'failed')
expect_next_instance_of(Clusters::Integrations::CreateService) do |service|
expect(service).to receive(:execute).and_return(error)
end
post path, params: params
expect(response).to have_gitlab_http_status(:redirect)
expect(response).to redirect_to(redirect_path)
expect(flash[:alert]).to eq(error.message)
end
end
end
......@@ -41,7 +41,7 @@ RSpec.shared_examples 'parse cluster applications artifact' do |release_name|
end.to change(application_class, :count)
expect(cluster_application).to be_persisted
expect(cluster_application).to be_installed
expect(cluster_application).to be_externally_installed
end
end
......@@ -53,7 +53,7 @@ RSpec.shared_examples 'parse cluster applications artifact' do |release_name|
it 'marks the application as installed' do
described_class.new(job, user).execute(artifact)
expect(cluster_application).to be_installed
expect(cluster_application).to be_externally_installed
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