From 650f93bb2a794afabb3b8f3dea4a83e15515e593 Mon Sep 17 00:00:00 2001 From: Arturo Herrero Date: Fri, 3 Jul 2020 21:36:52 +0000 Subject: [PATCH 1/9] Add issues_enabled column to jira_tracker_data table This will support the checkbox to enable/disable the Jira issues page. Display commits search in mobile & adjust text Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/214039 Update placeholder text per Pajamas guidelines (https://design.gitlab.com/components/search/) Unhide search bar in smaller screens. --- app/assets/stylesheets/utilities.scss | 5 +++++ app/models/project_services/jira_service.rb | 2 +- app/views/projects/commits/show.html.haml | 10 +++++----- .../214039-display-filter-commit-in-mobile.yml | 5 +++++ .../225706-add-issues-enabled-to-jira-tracker-data.yml | 5 +++++ ...03154822_add_issues_enabled_to_jira_tracker_data.rb | 9 +++++++++ db/structure.sql | 2 ++ locale/gitlab.pot | 6 +++--- 8 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/214039-display-filter-commit-in-mobile.yml create mode 100644 changelogs/unreleased/225706-add-issues-enabled-to-jira-tracker-data.yml create mode 100644 db/migrate/20200703154822_add_issues_enabled_to_jira_tracker_data.rb diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index 94af1df2ccb5..e02b4eff9f0b 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -117,3 +117,8 @@ .gl-border-b-2 { border-bottom-width: $gl-border-size-2; } + +// Remove once this MR has been merged in GitLab UI > https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1539 +.gl-min-w-full { + min-width: 100%; +} diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 7ba5f1d01f96..c3f9d8346854 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -23,7 +23,7 @@ class JiraService < IssueTrackerService # TODO: we can probably just delegate as part of # https://gitlab.com/gitlab-org/gitlab/issues/29404 - data_field :username, :password, :url, :api_url, :jira_issue_transition_id, :project_key + data_field :username, :password, :url, :api_url, :jira_issue_transition_id, :project_key, :issues_enabled before_update :reset_password diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 7722a3523a13..d65563a6eba6 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -14,18 +14,18 @@ %ul.breadcrumb.repo-breadcrumb = commits_breadcrumbs #js-author-dropdown{ data: { 'commits_path': project_commits_path(@project), 'project_id': @project.id } } - .tree-controls.d-none.d-sm-none.d-md-block + .tree-controls - if @merge_request.present? - .control + .control.d-none.d-md-block = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' - elsif create_mr_button?(@repository.root_ref, @ref) - .control + .control.d-none.d-md-block = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' .control = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do - = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } - .control + = search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control search-text-input input-short gl-mt-3 mt-sm-0 gl-min-w-full', spellcheck: false } + .control.d-none.d-md-block = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do = icon("rss") diff --git a/changelogs/unreleased/214039-display-filter-commit-in-mobile.yml b/changelogs/unreleased/214039-display-filter-commit-in-mobile.yml new file mode 100644 index 000000000000..a9e1727ac4d3 --- /dev/null +++ b/changelogs/unreleased/214039-display-filter-commit-in-mobile.yml @@ -0,0 +1,5 @@ +--- +title: Display commits search in mobile & adjust text +merge_request: 35702 +author: +type: changed diff --git a/changelogs/unreleased/225706-add-issues-enabled-to-jira-tracker-data.yml b/changelogs/unreleased/225706-add-issues-enabled-to-jira-tracker-data.yml new file mode 100644 index 000000000000..54161c0982cc --- /dev/null +++ b/changelogs/unreleased/225706-add-issues-enabled-to-jira-tracker-data.yml @@ -0,0 +1,5 @@ +--- +title: Add issues_enabled column to jira_tracker_data table +merge_request: 35987 +author: +type: other diff --git a/db/migrate/20200703154822_add_issues_enabled_to_jira_tracker_data.rb b/db/migrate/20200703154822_add_issues_enabled_to_jira_tracker_data.rb new file mode 100644 index 000000000000..e56f6f6ee11b --- /dev/null +++ b/db/migrate/20200703154822_add_issues_enabled_to_jira_tracker_data.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddIssuesEnabledToJiraTrackerData < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + add_column :jira_tracker_data, :issues_enabled, :boolean, default: false, null: false + end +end diff --git a/db/structure.sql b/db/structure.sql index 68a318fcf69e..47de909bb477 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12404,6 +12404,7 @@ CREATE TABLE public.jira_tracker_data ( encrypted_password_iv character varying, jira_issue_transition_id character varying, project_key text, + issues_enabled boolean DEFAULT false NOT NULL, CONSTRAINT check_214cf6a48b CHECK ((char_length(project_key) <= 255)) ); @@ -23539,5 +23540,6 @@ COPY "schema_migrations" (version) FROM STDIN; 20200626060151 20200626130220 20200702123805 +20200703154822 \. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e20caddaf826..293ff00a0725 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10042,9 +10042,6 @@ msgstr "" msgid "Filter by Git revision" msgstr "" -msgid "Filter by commit message" -msgstr "" - msgid "Filter by issues that are currently closed." msgstr "" @@ -19926,6 +19923,9 @@ msgstr "" msgid "Search by author" msgstr "" +msgid "Search by message" +msgstr "" + msgid "Search by name" msgstr "" -- GitLab From b81dcbe19a3ec8fcad85265ba15dd252454ace75 Mon Sep 17 00:00:00 2001 From: Serena Fang Date: Wed, 1 Jul 2020 22:15:52 +0000 Subject: [PATCH 2/9] Multiple clusters moving to core --- doc/user/project/clusters/index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 16d78751f402..beca463b8383 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -64,11 +64,9 @@ to: (EKS) using GitLab's UI. - Add an integration to an existing cluster from any Kubernetes platform. -### Multiple Kubernetes clusters **(PREMIUM)** +### Multiple Kubernetes clusters -> Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3. - -With GitLab Premium, you can associate more than one Kubernetes cluster to your +You can associate more than one Kubernetes cluster to your project. That way you can have different clusters for different environments, like dev, staging, production, and so on. -- GitLab From 312de8ce32b711459cf49804d0221bda64e06ac5 Mon Sep 17 00:00:00 2001 From: serenafang Date: Wed, 1 Jul 2020 17:53:25 -0500 Subject: [PATCH 3/9] Fix multiple clusters premium anchor --- doc/README.md | 4 ++-- doc/topics/autodevops/index.md | 4 ++-- doc/topics/autodevops/quick_start_guide.md | 2 +- .../projects/clusters/_multiple_clusters_message.html.haml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/README.md b/doc/README.md index 65044edff53e..5b53068de095 100644 --- a/doc/README.md +++ b/doc/README.md @@ -314,11 +314,11 @@ The following documentation relates to the DevOps **Configure** stage: | [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. | | [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. | | [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. | -| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters-premium) **(PREMIUM)** | Associate more than one Kubernetes clusters to your project. | +| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters) | Associate more than one Kubernetes clusters to your project. | | [Protected variables](ci/variables/README.md#protect-a-custom-variable) | Restrict variables to protected branches and tags. | | [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. | | [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. | -| [Manage your infrastructure with Terraform](user/infrastructure/index.md) | Manage your infrastructure as you run your CI/CD pipeline. | +| [Manage your infrastructure with Terraform](user/infrastructure/index.md) | Manage your infrastructure as you run your CI/CD pipeline. |
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index e9b59b8c2223..5deedd1dcfab 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -240,11 +240,11 @@ TIP: **Tip:** Use the [blue-green deployment](../../ci/environments/incremental_rollouts.md#blue-green-deployment) technique to minimize downtime and risk. -## Using multiple Kubernetes clusters **(PREMIUM)** +## Using multiple Kubernetes clusters When using Auto DevOps, you can deploy different environments to different Kubernetes clusters, due to the 1:1 connection -[existing between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters-premium). +[existing between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters). The [Deploy Job template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml) used by Auto DevOps currently defines 3 environment names: diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md index ec5191dd4ac4..39fe7c796eba 100644 --- a/doc/topics/autodevops/quick_start_guide.md +++ b/doc/topics/autodevops/quick_start_guide.md @@ -305,7 +305,7 @@ all within GitLab. Despite its automatic nature, Auto DevOps can also be configu and customized to fit your workflow. Here are some helpful resources for further reading: 1. [Auto DevOps](index.md) -1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **(PREMIUM)** +1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters) 1. [Incremental rollout to production](customize.md#incremental-rollout-to-production-premium) **(PREMIUM)** 1. [Disable jobs you don't need with environment variables](customize.md#environment-variables) 1. [Use a static IP for your cluster](../../user/clusters/applications.md#using-a-static-ip) diff --git a/ee/app/views/projects/clusters/_multiple_clusters_message.html.haml b/ee/app/views/projects/clusters/_multiple_clusters_message.html.haml index 0e5703e3852a..da3e128ba327 100644 --- a/ee/app/views/projects/clusters/_multiple_clusters_message.html.haml +++ b/ee/app/views/projects/clusters/_multiple_clusters_message.html.haml @@ -1,4 +1,4 @@ -- autodevops_help_url = help_page_path('topics/autodevops/index.md', anchor: 'using-multiple-kubernetes-clusters-premium') +- autodevops_help_url = help_page_path('topics/autodevops/index.md', anchor: 'using-multiple-kubernetes-clusters') - help_link_start = ''.html_safe - help_link_end = ''.html_safe -- GitLab From 30621e5b74ad103f1f4c9469227e9c85c2681f69 Mon Sep 17 00:00:00 2001 From: serenafang Date: Tue, 30 Jun 2020 16:44:00 -0500 Subject: [PATCH 4/9] Set has_multiple_clusters to true in core Remove ee override has_multiple_clusters, set to always true in core Move presenter and create service to core Remove clusterable presenter and create service from ee, add to core. Move deployment platform to core ee spec tests should be failing 'when multiple clsuters license is unavailable' because multiple_clusters already in core, but is currently passing. Need to investigate why. Remove multiple_clusters from license.rb Remove multiple_clusters from EEP_FEATURES --- app/helpers/clusters_helper.rb | 4 +-- app/models/concerns/deployment_platform.rb | 20 +++++++++++--- app/presenters/clusterable_presenter.rb | 2 +- app/services/clusters/create_service.rb | 5 ++-- ee/app/helpers/ee/clusters_helper.rb | 7 ++--- .../models/concerns/ee/deployment_platform.rb | 26 ------------------- ee/app/models/ee/project.rb | 2 +- ee/app/models/license.rb | 1 - ee/app/presenters/ee/clusterable_presenter.rb | 7 ----- ee/app/services/ee/clusters/create_service.rb | 14 ---------- ee/spec/helpers/ee/clusters_helper_spec.rb | 8 +++--- .../concerns/ee/deployment_platform_spec.rb | 2 +- .../ee/clusters/create_service_spec.rb | 13 ---------- spec/helpers/clusters_helper_spec.rb | 6 +++++ spec/services/clusters/create_service_spec.rb | 6 ++--- .../clusters/create_service_shared.rb | 9 +++++-- 16 files changed, 47 insertions(+), 85 deletions(-) delete mode 100644 ee/app/models/concerns/ee/deployment_platform.rb delete mode 100644 ee/app/services/ee/clusters/create_service.rb diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index 1204f8827073..fa6c594aa8af 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true module ClustersHelper - # EE overrides this + def has_multiple_clusters? - false + true end def create_new_cluster_label(provider: nil) diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb index 3b893a56bd62..03e0efaeb5cc 100644 --- a/app/models/concerns/deployment_platform.rb +++ b/app/models/concerns/deployment_platform.rb @@ -21,15 +21,29 @@ def find_deployment_platform(environment) end # EE would override this and utilize environment argument - def find_platform_kubernetes_with_cte(_environment) - Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?).base_and_ancestors + def find_platform_kubernetes_with_cte(environment) + if !environment + Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?).base_and_ancestors .enabled.default_environment .first&.platform_kubernetes + + else + ::Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?) + .base_and_ancestors + .enabled + .on_environment(environment, relevant_only: true) + .first&.platform_kubernetes + end end # EE would override this and utilize environment argument def find_instance_cluster_platform_kubernetes(environment: nil) - Clusters::Instance.new.clusters.enabled.default_environment + if !environment + Clusters::Instance.new.clusters.enabled.default_environment + .first&.platform_kubernetes + else + ::Clusters::Instance.new.clusters.enabled.on_environment(environment, relevant_only: true) .first&.platform_kubernetes + end end end diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb index 5e669ff2e503..d50a0dc9f593 100644 --- a/app/presenters/clusterable_presenter.rb +++ b/app/presenters/clusterable_presenter.rb @@ -86,7 +86,7 @@ def learn_more_link # Overridden on EE module def multiple_clusters_available? - false + true end def has_no_clusters? diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb index 7b5bf6b32c2a..8a971583a433 100644 --- a/app/services/clusters/create_service.rb +++ b/app/services/clusters/create_service.rb @@ -9,6 +9,7 @@ def initialize(user = nil, params = {}) end def execute(access_token: nil) + raise ArgumentError, 'Unknown clusterable provided' unless clusterable cluster_params = params.merge(global_params).merge(clusterable_params) @@ -57,7 +58,7 @@ def clusterable_params # EE would override this method def can_create_cluster? - clusterable.clusters.empty? + true end def validate_management_project_permissions(cluster) @@ -67,4 +68,4 @@ def validate_management_project_permissions(cluster) end end -Clusters::CreateService.prepend_if_ee('EE::Clusters::CreateService') + diff --git a/ee/app/helpers/ee/clusters_helper.rb b/ee/app/helpers/ee/clusters_helper.rb index b781b6ef68d6..5632905d09a0 100644 --- a/ee/app/helpers/ee/clusters_helper.rb +++ b/ee/app/helpers/ee/clusters_helper.rb @@ -2,11 +2,8 @@ module EE module ClustersHelper - extend ::Gitlab::Utils::Override - - override :has_multiple_clusters? - def has_multiple_clusters? - clusterable.feature_available?(:multiple_clusters) + def show_cluster_health_graphs? + clusterable.feature_available?(:cluster_health) end end end diff --git a/ee/app/models/concerns/ee/deployment_platform.rb b/ee/app/models/concerns/ee/deployment_platform.rb deleted file mode 100644 index 4dd505bdb4cd..000000000000 --- a/ee/app/models/concerns/ee/deployment_platform.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module EE - module DeploymentPlatform - extend ::Gitlab::Utils::Override - - override :find_platform_kubernetes_with_cte - def find_platform_kubernetes_with_cte(environment) - return super unless environment && feature_available?(:multiple_clusters) - - ::Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?) - .base_and_ancestors - .enabled - .on_environment(environment, relevant_only: true) - .first&.platform_kubernetes - end - - override :find_instance_cluster_platform_kubernetes - def find_instance_cluster_platform_kubernetes(environment: nil) - return super unless environment && feature_available?(:multiple_clusters) - - ::Clusters::Instance.new.clusters.enabled.on_environment(environment, relevant_only: true) - .first&.platform_kubernetes - end - end -end diff --git a/ee/app/models/ee/project.rb b/ee/app/models/ee/project.rb index e38ddb829718..e0fd58f79e25 100644 --- a/ee/app/models/ee/project.rb +++ b/ee/app/models/ee/project.rb @@ -798,4 +798,4 @@ def requirements_ci_variables end EE::Project.include_if_ee('::EE::GitlabRoutingHelper') -EE::Project.include_if_ee('::EE::DeploymentPlatform') + diff --git a/ee/app/models/license.rb b/ee/app/models/license.rb index 3367d8ff4023..503c4459d5f1 100644 --- a/ee/app/models/license.rb +++ b/ee/app/models/license.rb @@ -89,7 +89,6 @@ class License < ApplicationRecord merge_trains metrics_reports multiple_approval_rules - multiple_clusters multiple_group_issue_boards object_storage operations_dashboard diff --git a/ee/app/presenters/ee/clusterable_presenter.rb b/ee/app/presenters/ee/clusterable_presenter.rb index 822081229ed2..a5618af153c0 100644 --- a/ee/app/presenters/ee/clusterable_presenter.rb +++ b/ee/app/presenters/ee/clusterable_presenter.rb @@ -2,7 +2,6 @@ module EE module ClusterablePresenter - extend ::Gitlab::Utils::Override def metrics_cluster_path(cluster, params = {}) raise NotImplementedError @@ -12,11 +11,5 @@ def metrics_dashboard_path(cluster) raise NotImplementedError end - private - - override :multiple_clusters_available? - def multiple_clusters_available? - clusterable.feature_available?(:multiple_clusters) - end end end diff --git a/ee/app/services/ee/clusters/create_service.rb b/ee/app/services/ee/clusters/create_service.rb deleted file mode 100644 index f06b81b28b58..000000000000 --- a/ee/app/services/ee/clusters/create_service.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module EE - module Clusters - module CreateService - extend ::Gitlab::Utils::Override - - override :can_create_cluster? - def can_create_cluster? - super || clusterable.feature_available?(:multiple_clusters) - end - end - end -end diff --git a/ee/spec/helpers/ee/clusters_helper_spec.rb b/ee/spec/helpers/ee/clusters_helper_spec.rb index e582b98ff0b3..f6497f5e6167 100644 --- a/ee/spec/helpers/ee/clusters_helper_spec.rb +++ b/ee/spec/helpers/ee/clusters_helper_spec.rb @@ -27,19 +27,19 @@ end end - describe '#has_multiple_clusters?' do - subject { helper.has_multiple_clusters? } + describe '#show_cluster_health_graphs?' do + subject { helper.show_cluster_health_graphs? } context 'project level' do let(:clusterable) { instance_double(Project) } - it_behaves_like 'feature availablilty', :multiple_clusters + it_behaves_like 'feature availablilty', :cluster_health end context 'group level' do let(:clusterable) { instance_double(Group) } - it_behaves_like 'feature availablilty', :multiple_clusters + it_behaves_like 'feature availablilty', :cluster_health end end end diff --git a/ee/spec/models/concerns/ee/deployment_platform_spec.rb b/ee/spec/models/concerns/ee/deployment_platform_spec.rb index 4b7d281b746c..23214fafb06d 100644 --- a/ee/spec/models/concerns/ee/deployment_platform_spec.rb +++ b/ee/spec/models/concerns/ee/deployment_platform_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EE::DeploymentPlatform do +RSpec.describe DeploymentPlatform do describe '#deployment_platform' do let(:group) { create(:group) } let(:project) { create(:project, group: group) } diff --git a/ee/spec/services/ee/clusters/create_service_spec.rb b/ee/spec/services/ee/clusters/create_service_spec.rb index 3168b2df510f..bd226fc4547d 100644 --- a/ee/spec/services/ee/clusters/create_service_spec.rb +++ b/ee/spec/services/ee/clusters/create_service_spec.rb @@ -56,17 +56,4 @@ include_examples 'create cluster service error' end end - - context 'when license does not have multiple clusters feature' do - include_context 'valid cluster create params' - - before do - allow(project).to receive(:feature_available?).with(:multiple_clusters).and_return(false) - end - - it 'does not create a cluster' do - expect(ClusterProvisionWorker).not_to receive(:perform_async) - expect { subject }.to raise_error(ArgumentError).and change { Clusters::Cluster.count }.by(0) - end - end end diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index 2b820cd540cb..dad72bd9a167 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -101,6 +101,12 @@ end end + describe '#has_multiple_clusters?' do + subject { helper.has_multiple_clusters? } + + it { is_expected.to be_truthy } + end + describe '#cluster_type_label' do subject { helper.cluster_type_label(cluster_type) } diff --git a/spec/services/clusters/create_service_spec.rb b/spec/services/clusters/create_service_spec.rb index d45749b52d08..9aa405845828 100644 --- a/spec/services/clusters/create_service_spec.rb +++ b/spec/services/clusters/create_service_spec.rb @@ -53,9 +53,9 @@ include_context 'valid cluster create params' let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) } - it 'does not create a cluster' do - expect(ClusterProvisionWorker).not_to receive(:perform_async) - expect { subject }.to raise_error(ArgumentError).and change { Clusters::Cluster.count }.by(0) + it 'creates another cluster' do + expect(ClusterProvisionWorker).to receive(:perform_async) + expect { subject }.to change { Clusters::Cluster.count }.by(1) end end end diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb index 31aee08baec6..95c63df03110 100644 --- a/spec/support/services/clusters/create_service_shared.rb +++ b/spec/support/services/clusters/create_service_shared.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true RSpec.shared_context 'valid cluster create params' do + let(:clusterable) { Clusters::Instance.new } let(:params) do { name: 'test-cluster', @@ -11,12 +12,14 @@ num_nodes: 1, machine_type: 'machine_type-a', legacy_abac: 'true' - } + }, + clusterable: clusterable } end end RSpec.shared_context 'invalid cluster create params' do + let(:clusterable) { Clusters::Instance.new } let(:params) do { name: 'test-cluster', @@ -26,7 +29,9 @@ zone: 'us-central1-a', num_nodes: 1, machine_type: 'machine_type-a' - } + }, + clusterable: clusterable + } end end -- GitLab From b94181e2b4831f70cfe8e0aca2af20d2a355ff9b Mon Sep 17 00:00:00 2001 From: serenafang Date: Wed, 24 Jun 2020 10:36:11 -0500 Subject: [PATCH 5/9] Add multiple_clusters back into ee --- ee/app/models/license.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ee/app/models/license.rb b/ee/app/models/license.rb index 503c4459d5f1..3367d8ff4023 100644 --- a/ee/app/models/license.rb +++ b/ee/app/models/license.rb @@ -89,6 +89,7 @@ class License < ApplicationRecord merge_trains metrics_reports multiple_approval_rules + multiple_clusters multiple_group_issue_boards object_storage operations_dashboard -- GitLab From 05ba446cf13edd033089ac6b779355e311047cd6 Mon Sep 17 00:00:00 2001 From: serenafang Date: Wed, 24 Jun 2020 17:29:08 -0500 Subject: [PATCH 6/9] Change environment scope param Spec was failing in pipeline but not locally on duplicated environment scope error, set cluster_params scope to test/* to hopefully fix Fix static analysis failure Mostly whitespace issues Move specs from ee to core Add some new specs to core, delete some ee files Remove comments, swap conditionals Move cluster specs to core Apply MR review suggestions Move specs around, change wording Remove instances of multiple_clusters Remove whitespace for static analysis Remove trailing whitespace again Move environment_scope to core files Fix clashing environment scope Move multiple Kubernetes clusters to core Fix whitespace again Add changelog yml file Revert "Multiple clusters moving to core" This reverts commit 2038063cddd41445dde976ea3ee4a59b06f07211. Move multiple clusters from EE to core Remove instance of multiple-kubernetes-cluster-premium Add ee changelog Fix doc anchoring Revert "Fix doc anchoring" This reverts commit 59826511c4c9583e9d9162beb6609f0e38db3032. Revert "Revert "Fix doc anchoring"" This reverts commit f7ab53c05f8b0fe4081f22083dd0e9644468e74c. Apply MR review suggestions Apply MR review suggestions to spec and doc Restore can_create_cluster Removing the method caused some rspec errors, will iterate on it in the future and remove it then. Move more specs from ee to core Remove unused file Remove trailing whitespace Remove reference to unused EE method Revert "Move more specs from ee to core" This reverts commit 3db0e57b86ba5c60807ee037a33c0caeca69e282. Revert "Remove unused file" Accidentally deleted a file that I needed Re add prepend if ee Remove unused file Remove merge conflict markers Move multiple clusters message Remove changes to db/structure.sql Delete changes to structure.sql Render multiple clusters message Render message in sidebar always Change environment scope in specs --- app/helpers/clusters_helper.rb | 3 - app/models/clusters/cluster.rb | 8 + app/models/concerns/deployment_platform.rb | 20 +- app/presenters/clusterable_presenter.rb | 14 +- app/services/clusters/create_service.rb | 12 - .../_multiple_clusters_message.html.haml | 0 .../clusters/clusters/_sidebar.html.haml | 2 +- ...s-to-core-multiple-kubernetes-clusters.yml | 5 + db/structure.sql | 3 - doc/ci/variables/README.md | 4 +- doc/user/group/clusters/index.md | 9 +- doc/user/project/clusters/index.md | 3 + ee/app/helpers/ee/clusters_helper.rb | 9 - ee/app/models/ee/clusters/cluster.rb | 11 - ee/app/models/ee/project.rb | 1 - ee/app/models/license.rb | 1 - ee/app/presenters/ee/clusterable_presenter.rb | 2 - .../features/projects/clusters/gcp_spec.rb | 30 -- ee/spec/features/projects/clusters_spec.rb | 202 ------------ ee/spec/helpers/ee/clusters_helper_spec.rb | 45 --- .../concerns/ee/deployment_platform_spec.rb | 289 ------------------ ee/spec/models/ee/clusters/cluster_spec.rb | 84 ----- ee/spec/models/project_spec.rb | 70 ++--- ee/spec/requests/api/group_clusters_spec.rb | 19 -- ee/spec/requests/api/project_clusters_spec.rb | 4 +- .../ee/clusters/create_service_spec.rb | 59 ---- .../prometheus/alerts/notify_service_spec.rb | 2 - locale/gitlab.pot | 3 - spec/features/projects/clusters/gcp_spec.rb | 26 +- spec/features/projects/clusters_spec.rb | 162 ++++++++++ spec/helpers/clusters_helper_spec.rb | 2 +- spec/models/clusters/cluster_spec.rb | 74 +++++ .../concerns/deployment_platform_spec.rb | 235 ++++++++++++++ spec/models/project_spec.rb | 75 ++++- spec/requests/api/group_clusters_spec.rb | 48 ++- spec/requests/api/project_clusters_spec.rb | 51 +++- spec/services/clusters/create_service_spec.rb | 41 +++ .../prometheus/alerts/notify_service_spec.rb | 35 +++ .../clusters/create_service_shared.rb | 2 +- 39 files changed, 759 insertions(+), 906 deletions(-) rename {ee/app/views/projects => app/views/clusters}/clusters/_multiple_clusters_message.html.haml (100%) create mode 100644 changelogs/unreleased/212229-move-features-to-core-multiple-kubernetes-clusters.yml delete mode 100644 ee/app/helpers/ee/clusters_helper.rb delete mode 100644 ee/spec/features/projects/clusters/gcp_spec.rb delete mode 100644 ee/spec/features/projects/clusters_spec.rb delete mode 100644 ee/spec/helpers/ee/clusters_helper_spec.rb delete mode 100644 ee/spec/models/concerns/ee/deployment_platform_spec.rb delete mode 100644 ee/spec/models/ee/clusters/cluster_spec.rb delete mode 100644 ee/spec/services/ee/clusters/create_service_spec.rb diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index fa6c594aa8af..005070cca5cd 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true module ClustersHelper - def has_multiple_clusters? true end @@ -95,5 +94,3 @@ def can_admin_cluster?(user, cluster) can?(user, :admin_cluster, cluster) end end - -ClustersHelper.prepend_if_ee('EE::ClustersHelper') diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index bde7a2104bab..a8c7e28cf27d 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -2,6 +2,7 @@ module Clusters class Cluster < ApplicationRecord + prepend HasEnvironmentScope include Presentable include Gitlab::Utils::StrongMemoize include FromUnion @@ -81,6 +82,7 @@ def self.has_one_cluster_application(name) # rubocop:disable Naming/PredicateNam validate :no_groups, unless: :group_type? validate :no_projects, unless: :project_type? validate :unique_management_project_environment_scope + validate :unique_environment_scope after_save :clear_reactive_cache! @@ -352,6 +354,12 @@ def unique_management_project_environment_scope end end + def unique_environment_scope + if clusterable.present? && clusterable.clusters.where(environment_scope: environment_scope).where.not(id: id).exists? + errors.add(:environment_scope, 'cannot add duplicated environment scope') + end + end + def managed_namespace(environment) Clusters::KubernetesNamespaceFinder.new( self, diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb index 03e0efaeb5cc..02f7711e9274 100644 --- a/app/models/concerns/deployment_platform.rb +++ b/app/models/concerns/deployment_platform.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true module DeploymentPlatform - # EE would override this and utilize environment argument # rubocop:disable Gitlab/ModuleWithInstanceVariables def deployment_platform(environment: nil) @deployment_platform ||= {} @@ -20,29 +19,26 @@ def find_deployment_platform(environment) find_instance_cluster_platform_kubernetes(environment: environment) end - # EE would override this and utilize environment argument def find_platform_kubernetes_with_cte(environment) - if !environment - Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?).base_and_ancestors - .enabled.default_environment - .first&.platform_kubernetes - - else + if environment ::Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?) .base_and_ancestors .enabled .on_environment(environment, relevant_only: true) .first&.platform_kubernetes + else + Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?).base_and_ancestors + .enabled.default_environment + .first&.platform_kubernetes end end - # EE would override this and utilize environment argument def find_instance_cluster_platform_kubernetes(environment: nil) - if !environment - Clusters::Instance.new.clusters.enabled.default_environment + if environment + ::Clusters::Instance.new.clusters.enabled.on_environment(environment, relevant_only: true) .first&.platform_kubernetes else - ::Clusters::Instance.new.clusters.enabled.on_environment(environment, relevant_only: true) + Clusters::Instance.new.clusters.enabled.default_environment .first&.platform_kubernetes end end diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb index d50a0dc9f593..7054b3a3652d 100644 --- a/app/presenters/clusterable_presenter.rb +++ b/app/presenters/clusterable_presenter.rb @@ -13,8 +13,7 @@ def self.fabricate(clusterable, **attributes) end def can_add_cluster? - can?(current_user, :add_cluster, clusterable) && - (has_no_clusters? || multiple_clusters_available?) + can?(current_user, :add_cluster, clusterable) end def can_create_cluster? @@ -81,17 +80,6 @@ def sidebar_text def learn_more_link raise NotImplementedError end - - private - - # Overridden on EE module - def multiple_clusters_available? - true - end - - def has_no_clusters? - clusterable.clusters.empty? - end end ClusterablePresenter.prepend_if_ee('EE::ClusterablePresenter') diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb index 8a971583a433..6693a58683fb 100644 --- a/app/services/clusters/create_service.rb +++ b/app/services/clusters/create_service.rb @@ -9,7 +9,6 @@ def initialize(user = nil, params = {}) end def execute(access_token: nil) - raise ArgumentError, 'Unknown clusterable provided' unless clusterable cluster_params = params.merge(global_params).merge(clusterable_params) @@ -20,10 +19,6 @@ def execute(access_token: nil) cluster = Clusters::Cluster.new(cluster_params) - unless can_create_cluster? - cluster.errors.add(:base, _('Instance does not support multiple Kubernetes clusters')) - end - validate_management_project_permissions(cluster) return cluster if cluster.errors.present? @@ -56,16 +51,9 @@ def clusterable_params end end - # EE would override this method - def can_create_cluster? - true - end - def validate_management_project_permissions(cluster) Clusters::Management::ValidateManagementProjectPermissionsService.new(current_user) .execute(cluster, params[:management_project_id]) end end end - - diff --git a/ee/app/views/projects/clusters/_multiple_clusters_message.html.haml b/app/views/clusters/clusters/_multiple_clusters_message.html.haml similarity index 100% rename from ee/app/views/projects/clusters/_multiple_clusters_message.html.haml rename to app/views/clusters/clusters/_multiple_clusters_message.html.haml diff --git a/app/views/clusters/clusters/_sidebar.html.haml b/app/views/clusters/clusters/_sidebar.html.haml index 24a74c59b975..31add011bfa4 100644 --- a/app/views/clusters/clusters/_sidebar.html.haml +++ b/app/views/clusters/clusters/_sidebar.html.haml @@ -5,4 +5,4 @@ %p = clusterable.learn_more_link -= render_if_exists 'clusters/multiple_clusters_message' += render 'clusters/clusters/multiple_clusters_message' diff --git a/changelogs/unreleased/212229-move-features-to-core-multiple-kubernetes-clusters.yml b/changelogs/unreleased/212229-move-features-to-core-multiple-kubernetes-clusters.yml new file mode 100644 index 000000000000..9898b6449b66 --- /dev/null +++ b/changelogs/unreleased/212229-move-features-to-core-multiple-kubernetes-clusters.yml @@ -0,0 +1,5 @@ +--- +title: 'Multiple Kubernetes clusters now available in GitLab core' +merge_request: 35094 +author: +type: changed diff --git a/db/structure.sql b/db/structure.sql index 3f200f765715..bb5dcbbe68c5 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -23571,9 +23571,6 @@ COPY "schema_migrations" (version) FROM STDIN; 20200626130220 20200702123805 20200703154822 -<<<<<<< HEAD -======= 20200706005325 ->>>>>>> d20e41cae30cef71bfe776768a4c6b898c0a6d3b \. diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 42a238148e9e..7ea53c190e5e 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -896,8 +896,8 @@ if [[ -d "/builds/gitlab-examples/ci-debug-trace/.git" ]]; then ++ CI_SERVER_VERSION_PATCH=0 ++ export CI_SERVER_REVISION=f4cc00ae823 ++ CI_SERVER_REVISION=f4cc00ae823 -++ export GITLAB_FEATURES=audit_events,burndown_charts,code_owners,contribution_analytics,description_diffs,elastic_search,group_bulk_edit,group_burndown_charts,group_webhooks,issuable_default_templates,issue_weights,jenkins_integration,ldap_group_sync,member_lock,merge_request_approvers,multiple_issue_assignees,multiple_ldap_servers,multiple_merge_request_assignees,protected_refs_for_users,push_rules,related_issues,repository_mirrors,repository_size_limit,scoped_issue_board,usage_quotas,visual_review_app,wip_limits,adjourned_deletion_for_projects_and_groups,admin_audit_log,auditor_user,batch_comments,blocking_merge_requests,board_assignee_lists,board_milestone_lists,ci_cd_projects,cluster_deployments,code_analytics,code_owner_approval_required,commit_committer_check,cross_project_pipelines,custom_file_templates,custom_file_templates_for_namespace,custom_project_templates,custom_prometheus_metrics,cycle_analytics_for_groups,db_load_balancing,default_project_deletion_protection,dependency_proxy,deploy_board,design_management,email_additional_text,extended_audit_events,external_authorization_service_api_management,feature_flags,file_locks,geo,github_project_service_integration,group_allowed_email_domains,group_project_templates,group_saml,issues_analytics,jira_dev_panel_integration,ldap_group_sync_filter,merge_pipelines,merge_request_performance_metrics,merge_trains,metrics_reports,multiple_approval_rules,multiple_clusters,multiple_group_issue_boards,object_storage,operations_dashboard,packages,productivity_analytics,project_aliases,protected_environments,reject_unsigned_commits,required_ci_templates,scoped_labels,service_desk,smartcard_auth,group_timelogs,type_of_work_analytics,unprotection_restrictions,ci_project_subscriptions,container_scanning,dast,dependency_scanning,epics,group_ip_restriction,incident_management,insights,license_management,personal_access_token_expiration_policy,pod_logs,prometheus_alerts,pseudonymizer,report_approver_rules,sast,security_dashboard,tracing,web_ide_terminal -++ GITLAB_FEATURES=audit_events,burndown_charts,code_owners,contribution_analytics,description_diffs,elastic_search,group_bulk_edit,group_burndown_charts,group_webhooks,issuable_default_templates,issue_weights,jenkins_integration,ldap_group_sync,member_lock,merge_request_approvers,multiple_issue_assignees,multiple_ldap_servers,multiple_merge_request_assignees,protected_refs_for_users,push_rules,related_issues,repository_mirrors,repository_size_limit,scoped_issue_board,usage_quotas,visual_review_app,wip_limits,adjourned_deletion_for_projects_and_groups,admin_audit_log,auditor_user,batch_comments,blocking_merge_requests,board_assignee_lists,board_milestone_lists,ci_cd_projects,cluster_deployments,code_analytics,code_owner_approval_required,commit_committer_check,cross_project_pipelines,custom_file_templates,custom_file_templates_for_namespace,custom_project_templates,custom_prometheus_metrics,cycle_analytics_for_groups,db_load_balancing,default_project_deletion_protection,dependency_proxy,deploy_board,design_management,email_additional_text,extended_audit_events,external_authorization_service_api_management,feature_flags,file_locks,geo,github_project_service_integration,group_allowed_email_domains,group_project_templates,group_saml,issues_analytics,jira_dev_panel_integration,ldap_group_sync_filter,merge_pipelines,merge_request_performance_metrics,merge_trains,metrics_reports,multiple_approval_rules,multiple_clusters,multiple_group_issue_boards,object_storage,operations_dashboard,packages,productivity_analytics,project_aliases,protected_environments,reject_unsigned_commits,required_ci_templates,scoped_labels,service_desk,smartcard_auth,group_timelogs,type_of_work_analytics,unprotection_restrictions,ci_project_subscriptions,cluster_health,container_scanning,dast,dependency_scanning,epics,group_ip_restriction,incident_management,insights,license_management,personal_access_token_expiration_policy,pod_logs,prometheus_alerts,pseudonymizer,report_approver_rules,sast,security_dashboard,tracing,web_ide_terminal +++ export GITLAB_FEATURES=audit_events,burndown_charts,code_owners,contribution_analytics,description_diffs,elastic_search,group_bulk_edit,group_burndown_charts,group_webhooks,issuable_default_templates,issue_weights,jenkins_integration,ldap_group_sync,member_lock,merge_request_approvers,multiple_issue_assignees,multiple_ldap_servers,multiple_merge_request_assignees,protected_refs_for_users,push_rules,related_issues,repository_mirrors,repository_size_limit,scoped_issue_board,usage_quotas,visual_review_app,wip_limits,adjourned_deletion_for_projects_and_groups,admin_audit_log,auditor_user,batch_comments,blocking_merge_requests,board_assignee_lists,board_milestone_lists,ci_cd_projects,cluster_deployments,code_analytics,code_owner_approval_required,commit_committer_check,cross_project_pipelines,custom_file_templates,custom_file_templates_for_namespace,custom_project_templates,custom_prometheus_metrics,cycle_analytics_for_groups,db_load_balancing,default_project_deletion_protection,dependency_proxy,deploy_board,design_management,email_additional_text,extended_audit_events,external_authorization_service_api_management,feature_flags,file_locks,geo,github_project_service_integration,group_allowed_email_domains,group_project_templates,group_saml,issues_analytics,jira_dev_panel_integration,ldap_group_sync_filter,merge_pipelines,merge_request_performance_metrics,merge_trains,metrics_reports,multiple_approval_rules,multiple_group_issue_boards,object_storage,operations_dashboard,packages,productivity_analytics,project_aliases,protected_environments,reject_unsigned_commits,required_ci_templates,scoped_labels,service_desk,smartcard_auth,group_timelogs,type_of_work_analytics,unprotection_restrictions,ci_project_subscriptions,container_scanning,dast,dependency_scanning,epics,group_ip_restriction,incident_management,insights,license_management,personal_access_token_expiration_policy,pod_logs,prometheus_alerts,pseudonymizer,report_approver_rules,sast,security_dashboard,tracing,web_ide_terminal +++ GITLAB_FEATURES=audit_events,burndown_charts,code_owners,contribution_analytics,description_diffs,elastic_search,group_bulk_edit,group_burndown_charts,group_webhooks,issuable_default_templates,issue_weights,jenkins_integration,ldap_group_sync,member_lock,merge_request_approvers,multiple_issue_assignees,multiple_ldap_servers,multiple_merge_request_assignees,protected_refs_for_users,push_rules,related_issues,repository_mirrors,repository_size_limit,scoped_issue_board,usage_quotas,visual_review_app,wip_limits,adjourned_deletion_for_projects_and_groups,admin_audit_log,auditor_user,batch_comments,blocking_merge_requests,board_assignee_lists,board_milestone_lists,ci_cd_projects,cluster_deployments,code_analytics,code_owner_approval_required,commit_committer_check,cross_project_pipelines,custom_file_templates,custom_file_templates_for_namespace,custom_project_templates,custom_prometheus_metrics,cycle_analytics_for_groups,db_load_balancing,default_project_deletion_protection,dependency_proxy,deploy_board,design_management,email_additional_text,extended_audit_events,external_authorization_service_api_management,feature_flags,file_locks,geo,github_project_service_integration,group_allowed_email_domains,group_project_templates,group_saml,issues_analytics,jira_dev_panel_integration,ldap_group_sync_filter,merge_pipelines,merge_request_performance_metrics,merge_trains,metrics_reports,multiple_approval_rules,multiple_group_issue_boards,object_storage,operations_dashboard,packages,productivity_analytics,project_aliases,protected_environments,reject_unsigned_commits,required_ci_templates,scoped_labels,service_desk,smartcard_auth,group_timelogs,type_of_work_analytics,unprotection_restrictions,ci_project_subscriptions,cluster_health,container_scanning,dast,dependency_scanning,epics,group_ip_restriction,incident_management,insights,license_management,personal_access_token_expiration_policy,pod_logs,prometheus_alerts,pseudonymizer,report_approver_rules,sast,security_dashboard,tracing,web_ide_terminal ++ export CI_PROJECT_ID=17893 ++ CI_PROJECT_ID=17893 ++ export CI_PROJECT_NAME=ci-debug-trace diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md index 8dcc08bce46a..89e0c4898fb5 100644 --- a/doc/user/group/clusters/index.md +++ b/doc/user/group/clusters/index.md @@ -38,10 +38,11 @@ the project. In the case of sub-groups, GitLab uses the cluster of the closest ancestor group to the project, provided the cluster is not disabled. -## Multiple Kubernetes clusters **(PREMIUM)** +## Multiple Kubernetes clusters -With [GitLab Premium](https://about.gitlab.com/pricing/premium/), you can associate -more than one Kubernetes cluster to your group, and maintain different clusters +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35094) to GitLab Core in 13.2. + +You can associate more than one Kubernetes cluster to your group, and maintain different clusters for different environments, such as development, staging, and production. When adding another cluster, @@ -93,7 +94,7 @@ To clear the cache: > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24580) in GitLab 11.8. Domains at the cluster level permit support for multiple domains -per [multiple Kubernetes clusters](#multiple-kubernetes-clusters-premium). When specifying a domain, +per [multiple Kubernetes clusters](#multiple-kubernetes-clusters) When specifying a domain, this will be automatically set as an environment variable (`KUBE_INGRESS_BASE_DOMAIN`) during the [Auto DevOps](../../../topics/autodevops/index.md) stages. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index beca463b8383..ff0aaca40b7d 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -66,6 +66,9 @@ to: ### Multiple Kubernetes clusters +> - Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3 +> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35094) to GitLab core in 13.2. + You can associate more than one Kubernetes cluster to your project. That way you can have different clusters for different environments, like dev, staging, production, and so on. diff --git a/ee/app/helpers/ee/clusters_helper.rb b/ee/app/helpers/ee/clusters_helper.rb deleted file mode 100644 index 5632905d09a0..000000000000 --- a/ee/app/helpers/ee/clusters_helper.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module EE - module ClustersHelper - def show_cluster_health_graphs? - clusterable.feature_available?(:cluster_health) - end - end -end diff --git a/ee/app/models/ee/clusters/cluster.rb b/ee/app/models/ee/clusters/cluster.rb index 4264d4fba994..c4efde030d59 100644 --- a/ee/app/models/ee/clusters/cluster.rb +++ b/ee/app/models/ee/clusters/cluster.rb @@ -6,23 +6,12 @@ module Cluster extend ActiveSupport::Concern prepended do - prepend HasEnvironmentScope include UsageStatistics - - validate :unique_environment_scope end def prometheus_adapter application_prometheus end - - private - - def unique_environment_scope - if clusterable.present? && clusterable.clusters.where(environment_scope: environment_scope).where.not(id: id).exists? - errors.add(:environment_scope, 'cannot add duplicated environment scope') - end - end end end end diff --git a/ee/app/models/ee/project.rb b/ee/app/models/ee/project.rb index e0fd58f79e25..57f3837d7afd 100644 --- a/ee/app/models/ee/project.rb +++ b/ee/app/models/ee/project.rb @@ -798,4 +798,3 @@ def requirements_ci_variables end EE::Project.include_if_ee('::EE::GitlabRoutingHelper') - diff --git a/ee/app/models/license.rb b/ee/app/models/license.rb index 3367d8ff4023..503c4459d5f1 100644 --- a/ee/app/models/license.rb +++ b/ee/app/models/license.rb @@ -89,7 +89,6 @@ class License < ApplicationRecord merge_trains metrics_reports multiple_approval_rules - multiple_clusters multiple_group_issue_boards object_storage operations_dashboard diff --git a/ee/app/presenters/ee/clusterable_presenter.rb b/ee/app/presenters/ee/clusterable_presenter.rb index a5618af153c0..89b22ac0819c 100644 --- a/ee/app/presenters/ee/clusterable_presenter.rb +++ b/ee/app/presenters/ee/clusterable_presenter.rb @@ -2,7 +2,6 @@ module EE module ClusterablePresenter - def metrics_cluster_path(cluster, params = {}) raise NotImplementedError end @@ -10,6 +9,5 @@ def metrics_cluster_path(cluster, params = {}) def metrics_dashboard_path(cluster) raise NotImplementedError end - end end diff --git a/ee/spec/features/projects/clusters/gcp_spec.rb b/ee/spec/features/projects/clusters/gcp_spec.rb deleted file mode 100644 index 9175afb56aca..000000000000 --- a/ee/spec/features/projects/clusters/gcp_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Gcp Cluster', :js do - include GoogleApi::CloudPlatformHelpers - - let(:project) { create(:project) } - let(:user) { create(:user) } - - before do - project.add_maintainer(user) - gitlab_sign_in(user) - allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } - end - - context 'when a user has a licence to use multiple clusers' do - before do - stub_licensed_features(multiple_clusters: true) - visit project_clusters_path(project) - - click_link 'Add Kubernetes cluster' - click_link 'Add existing cluster' - end - - it 'user sees the "Environment scope" field' do - expect(page).to have_css('#cluster_environment_scope') - end - end -end diff --git a/ee/spec/features/projects/clusters_spec.rb b/ee/spec/features/projects/clusters_spec.rb deleted file mode 100644 index 95e3f192d354..000000000000 --- a/ee/spec/features/projects/clusters_spec.rb +++ /dev/null @@ -1,202 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'EE Clusters', :js do - include GoogleApi::CloudPlatformHelpers - - let(:project) { create(:project) } - let(:user) { create(:user) } - - before do - project.add_maintainer(user) - gitlab_sign_in(user) - stub_feature_flags(clusters_list_redesign: false) - end - - context 'when user has a cluster' do - context 'when license has multiple clusters feature' do - before do - allow(License).to receive(:feature_available?).and_call_original - allow(License).to receive(:feature_available?).with(:multiple_clusters).and_return(true) - allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected) - end - - context 'when user adds an existing cluster' do - before do - create(:cluster, :provided_by_user, name: 'default-cluster', environment_scope: '*', projects: [project]) - visit project_clusters_path(project) - end - - it 'user sees a add cluster button ' do - expect(page).not_to have_selector('.js-add-cluster.readonly') - expect(page).to have_selector('.js-add-cluster') - end - - context 'when user filled form with environment scope' do - before do - click_link 'Add Kubernetes cluster' - click_link 'Add existing cluster' - fill_in 'cluster_name', with: 'staging-cluster' - fill_in 'cluster_environment_scope', with: 'staging/*' - click_button 'Add Kubernetes cluster' - end - - it 'user sees a cluster details page' do - expect(page.find_field('cluster[name]').value).to eq('staging-cluster') - expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*') - end - end - - context 'when user updates environment scope' do - before do - click_link 'default-cluster' - fill_in 'cluster_environment_scope', with: 'production/*' - within '.js-cluster-integration-form' do - click_button 'Save changes' - end - end - - it 'user sees a cluster details page' do - expect(page.find_field('cluster[environment_scope]').value).to eq('production/*') - end - end - - context 'when user updates duplicated environment scope' do - before do - click_link 'Add Kubernetes cluster' - click_link 'Add existing cluster' - fill_in 'cluster_name', with: 'staging-cluster' - fill_in 'cluster_environment_scope', with: '*' - fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'https://0.0.0.0' - fill_in 'cluster_platform_kubernetes_attributes_token', with: 'token' - - click_button 'Add Kubernetes cluster' - end - - it 'users sees an environment scope validation error' do - expect(page).to have_content('cannot add duplicated environment scope') - end - end - end - - context 'when user adds an Google Kubernetes Engine cluster' do - before do - allow_any_instance_of(Projects::ClustersController) - .to receive(:token_in_session).and_return('token') - allow_any_instance_of(Projects::ClustersController) - .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s) - - allow_any_instance_of(Projects::ClustersController).to receive(:authorize_google_project_billing) - allow_any_instance_of(Projects::ClustersController).to receive(:google_project_billing_status).and_return(true) - - allow_any_instance_of(GoogleApi::CloudPlatform::Client) - .to receive(:projects_zones_clusters_create) do - OpenStruct.new( - self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123', - status: 'RUNNING' - ) - end - - allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil) - - create(:cluster, :provided_by_gcp, name: 'default-cluster', environment_scope: '*', projects: [project]) - visit project_clusters_path(project) - end - - it 'user sees a add cluster button ' do - expect(page).not_to have_selector('.js-add-cluster.readonly') - expect(page).to have_selector('.js-add-cluster') - end - - context 'when user filled form with environment scope' do - before do - click_link 'Add Kubernetes cluster' - click_link 'Create new cluster' - click_link 'Google GKE' - - sleep 2 # wait for ajax - execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")') - execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")') - execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")') - execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")') - - fill_in 'cluster_name', with: 'staging-cluster' - fill_in 'cluster_environment_scope', with: 'staging/*' - fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123' - fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a' - fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2' - click_button 'Create Kubernetes cluster' - - # The frontend won't show the details until the cluster is - # created, and we don't want to make calls out to GCP. - provider = Clusters::Cluster.last.provider - provider.make_created - end - - it 'user sees a cluster details page' do - expect(page).to have_content('GitLab Integration') - expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*') - end - end - - context 'when user updates environment scope' do - before do - click_link 'default-cluster' - fill_in 'cluster_environment_scope', with: 'production/*' - within ".js-cluster-integration-form" do - click_button 'Save changes' - end - end - - it 'user sees a cluster details page' do - expect(page.find_field('cluster[environment_scope]').value).to eq('production/*') - end - end - - context 'when user updates duplicated environment scope' do - before do - click_link 'Add Kubernetes cluster' - click_link 'Create new cluster' - click_link 'Google GKE' - - sleep 2 # wait for ajax - execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")') - execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")') - execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")') - execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")') - - fill_in 'cluster_name', with: 'staging-cluster' - fill_in 'cluster_environment_scope', with: '*' - fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123' - fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a' - fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2' - click_button 'Create Kubernetes cluster' - end - - it 'users sees an environment scope validation error' do - expect(page).to have_content('cannot add duplicated environment scope') - end - end - end - end - - context 'when license does not have multiple clusters feature' do - before do - allow(License).to receive(:feature_available?).and_call_original - allow(License).to receive(:feature_available?).with(:multiple_clusters).and_return(false) - create(:cluster, :provided_by_user, name: 'default-cluster', environment_scope: '*', projects: [project]) - end - - context 'when user visits cluster index page' do - before do - visit project_clusters_path(project) - end - - it 'user sees a disabled add cluster button ' do - expect(page).to have_selector('.js-add-cluster.disabled') - end - end - end - end -end diff --git a/ee/spec/helpers/ee/clusters_helper_spec.rb b/ee/spec/helpers/ee/clusters_helper_spec.rb deleted file mode 100644 index f6497f5e6167..000000000000 --- a/ee/spec/helpers/ee/clusters_helper_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe ClustersHelper do - shared_examples 'feature availablilty' do |feature| - before do - # clusterable is provided as a `helper_method` - allow(helper).to receive(:clusterable).and_return(clusterable) - - 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 'feature available' do - let(:feature_available) { false } - - it { is_expected.to be_falsey } - end - end - - describe '#show_cluster_health_graphs?' do - subject { helper.show_cluster_health_graphs? } - - 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 -end diff --git a/ee/spec/models/concerns/ee/deployment_platform_spec.rb b/ee/spec/models/concerns/ee/deployment_platform_spec.rb deleted file mode 100644 index 23214fafb06d..000000000000 --- a/ee/spec/models/concerns/ee/deployment_platform_spec.rb +++ /dev/null @@ -1,289 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe DeploymentPlatform do - describe '#deployment_platform' do - let(:group) { create(:group) } - let(:project) { create(:project, group: group) } - - shared_examples 'matching environment scope' do - context 'when multiple clusters license is available' do - before do - stub_licensed_features(multiple_clusters: true) - end - - it 'returns environment specific cluster' do - is_expected.to eq(cluster.platform_kubernetes) - end - end - - context 'when multiple clusters licence is unavailable' do - before do - stub_licensed_features(multiple_clusters: false) - end - - it 'returns a kubernetes platform' do - is_expected.not_to eq(cluster.platform_kubernetes) - is_expected.to be_kind_of(Clusters::Platforms::Kubernetes) - end - end - end - - shared_examples 'not matching environment scope' do - context 'when multiple clusters license is available' do - before do - stub_licensed_features(multiple_clusters: true) - end - - it 'returns default cluster' do - is_expected.to eq(default_cluster.platform_kubernetes) - end - end - - context 'when multiple clusters license is unavailable' do - before do - stub_licensed_features(multiple_clusters: false) - end - - it 'returns default cluster' do - is_expected.to eq(default_cluster.platform_kubernetes) - end - end - end - - context 'multiple clusters use the same management project' do - let(:management_project) { create(:project, group: group) } - - let!(:default_cluster) do - create(:cluster_for_group, groups: [group], environment_scope: '*', management_project: management_project) - end - - let!(:cluster) do - create(:cluster_for_group, groups: [group], environment_scope: 'review/*', management_project: management_project) - end - - let(:environment) { 'review/name' } - - subject { management_project.deployment_platform(environment: environment) } - - it_behaves_like 'matching environment scope' - end - - context 'when project does not have a cluster but has group clusters' do - let!(:default_cluster) do - create(:cluster, :provided_by_user, - cluster_type: :group_type, groups: [group], environment_scope: '*') - end - - let!(:cluster) do - create(:cluster, :provided_by_user, - cluster_type: :group_type, environment_scope: 'review/*', groups: [group]) - end - - let(:environment) { 'review/name' } - - subject { project.deployment_platform(environment: environment) } - - context 'when environment scope is exactly matched' do - before do - cluster.update!(environment_scope: 'review/name') - end - - it_behaves_like 'matching environment scope' - end - - context 'when environment scope is matched by wildcard' do - before do - cluster.update!(environment_scope: 'review/*') - end - - it_behaves_like 'matching environment scope' - end - - context 'when environment scope does not match' do - before do - cluster.update!(environment_scope: 'review/*/special') - end - - it_behaves_like 'not matching environment scope' - end - - context 'when group belongs to a parent group' do - let(:parent_group) { create(:group) } - let(:group) { create(:group, parent: parent_group) } - - context 'when parent_group has a cluster with default scope' do - let!(:parent_group_cluster) do - create(:cluster, :provided_by_user, - cluster_type: :group_type, environment_scope: '*', groups: [parent_group]) - end - - it_behaves_like 'matching environment scope' - end - - context 'when parent_group has a cluster that is an exact match' do - let!(:parent_group_cluster) do - create(:cluster, :provided_by_user, - cluster_type: :group_type, environment_scope: 'review/name', groups: [parent_group]) - end - - it_behaves_like 'matching environment scope' - end - end - end - - context 'with instance clusters' do - let!(:default_cluster) do - create(:cluster, :provided_by_user, :instance, environment_scope: '*') - end - - let!(:cluster) do - create(:cluster, :provided_by_user, :instance, environment_scope: 'review/*') - end - - let(:environment) { 'review/name' } - - subject { project.deployment_platform(environment: environment) } - - context 'when environment scope is exactly matched' do - before do - cluster.update!(environment_scope: 'review/name') - end - - it_behaves_like 'matching environment scope' - end - - context 'when environment scope is matched by wildcard' do - before do - cluster.update!(environment_scope: 'review/*') - end - - it_behaves_like 'matching environment scope' - end - - context 'when environment scope does not match' do - before do - cluster.update!(environment_scope: 'review/*/special') - end - - it_behaves_like 'not matching environment scope' - end - end - - context 'when environment is specified' do - let!(:default_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: '*') } - let!(:cluster) { create(:cluster, :provided_by_user, environment_scope: 'review/*', projects: [project]) } - - let!(:group_default_cluster) do - create(:cluster, :provided_by_user, - cluster_type: :group_type, groups: [group], environment_scope: '*') - end - - let(:environment) { 'review/name' } - - subject { project.deployment_platform(environment: environment) } - - context 'when environment scope is exactly matched' do - before do - cluster.update!(environment_scope: 'review/name') - end - - it_behaves_like 'matching environment scope' - end - - context 'when environment scope is matched by wildcard' do - before do - cluster.update!(environment_scope: 'review/*') - end - - it_behaves_like 'matching environment scope' - end - - context 'when environment scope does not match' do - before do - cluster.update!(environment_scope: 'review/*/special') - end - - it_behaves_like 'not matching environment scope' - end - - context 'when environment scope has _' do - before do - stub_licensed_features(multiple_clusters: true) - end - - it 'does not treat it as wildcard' do - cluster.update!(environment_scope: 'foo_bar/*') - - is_expected.to eq(default_cluster.platform_kubernetes) - end - - context 'when environment name contains an underscore' do - let(:environment) { 'foo_bar/test' } - - it 'matches literally for _' do - cluster.update!(environment_scope: 'foo_bar/*') - - is_expected.to eq(cluster.platform_kubernetes) - end - end - end - - # The environment name and scope cannot have % at the moment, - # but we're considering relaxing it and we should also make sure - # it doesn't break in case some data sneaked in somehow as we're - # not checking this integrity in database level. - context 'when environment scope has %' do - before do - stub_licensed_features(multiple_clusters: true) - end - - it 'does not treat it as wildcard' do - cluster.update_attribute(:environment_scope, '*%*') - - is_expected.to eq(default_cluster.platform_kubernetes) - end - - context 'when environment name contains a percent char' do - let(:environment) { 'foo%bar/test' } - - it 'matches literally for %' do - cluster.update_attribute(:environment_scope, 'foo%bar/*') - - is_expected.to eq(cluster.platform_kubernetes) - end - end - end - - context 'when perfectly matched cluster exists' do - let!(:perfectly_matched_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'review/name') } - - before do - stub_licensed_features(multiple_clusters: true) - end - - it 'returns perfectly matched cluster as highest precedence' do - is_expected.to eq(perfectly_matched_cluster.platform_kubernetes) - end - end - end - - context 'with multiple clusters and multiple environments' do - let!(:cluster_1) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'staging/*') } - let!(:cluster_2) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'test/*') } - - let(:environment_1) { 'staging/name' } - let(:environment_2) { 'test/name' } - - before do - stub_licensed_features(multiple_clusters: true) - end - - it 'returns the appropriate cluster' do - expect(project.deployment_platform(environment: environment_1)).to eq(cluster_1.platform_kubernetes) - expect(project.deployment_platform(environment: environment_2)).to eq(cluster_2.platform_kubernetes) - end - end - end -end diff --git a/ee/spec/models/ee/clusters/cluster_spec.rb b/ee/spec/models/ee/clusters/cluster_spec.rb deleted file mode 100644 index 8c6c54465eac..000000000000 --- a/ee/spec/models/ee/clusters/cluster_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Clusters::Cluster do - it { is_expected.to include_module(HasEnvironmentScope) } - - describe 'validation' do - subject { cluster.valid? } - - context 'when validates unique_environment_scope' do - context 'for a project cluster' do - let(:project) { create(:project) } - - before do - create(:cluster, projects: [project], environment_scope: 'product/*') - end - - context 'when identical environment scope exists in project' do - let(:cluster) { build(:cluster, projects: [project], environment_scope: 'product/*') } - - it { is_expected.to be_falsey } - end - - context 'when identical environment scope does not exist in project' do - let(:cluster) { build(:cluster, projects: [project], environment_scope: '*') } - - it { is_expected.to be_truthy } - end - - context 'when identical environment scope exists in different project' do - let(:project2) { create(:project) } - let(:cluster) { build(:cluster, projects: [project2], environment_scope: 'product/*') } - - it { is_expected.to be_truthy } - end - end - - context 'for a group cluster' do - let(:group) { create(:group) } - - before do - create(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') - end - - context 'when identical environment scope exists in group' do - let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') } - - it { is_expected.to be_falsey } - end - - context 'when identical environment scope does not exist in group' do - let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: '*') } - - it { is_expected.to be_truthy } - end - - context 'when identical environment scope exists in different group' do - let(:cluster) { build(:cluster, :group, environment_scope: 'product/*') } - - it { is_expected.to be_truthy } - end - end - - context 'for an instance cluster' do - before do - create(:cluster, :instance, environment_scope: 'product/*') - end - - context 'identical environment scope exists' do - let(:cluster) { build(:cluster, :instance, environment_scope: 'product/*') } - - it { is_expected.to be_falsey } - end - - context 'identical environment scope does not exist' do - let(:cluster) { build(:cluster, :instance, environment_scope: '*') } - - it { is_expected.to be_truthy } - end - end - end - end -end diff --git a/ee/spec/models/project_spec.rb b/ee/spec/models/project_spec.rb index dad8b95317f8..e9f234c4a65b 100644 --- a/ee/spec/models/project_spec.rb +++ b/ee/spec/models/project_spec.rb @@ -442,52 +442,44 @@ end describe '#deployment_variables' do - context 'when project has a deployment platforms' do - context 'when multiple clusters (EEP) is enabled' do - before do - stub_licensed_features(multiple_clusters: true) - end - - let(:project) { create(:project) } + let(:project) { create(:project) } - let!(:default_cluster) do - create(:cluster, - :not_managed, - platform_type: :kubernetes, - projects: [project], - environment_scope: '*', - platform_kubernetes: default_cluster_kubernetes) - end + let!(:default_cluster) do + create(:cluster, + :not_managed, + platform_type: :kubernetes, + projects: [project], + environment_scope: '*', + platform_kubernetes: default_cluster_kubernetes) + end - let!(:review_env_cluster) do - create(:cluster, - :not_managed, - platform_type: :kubernetes, - projects: [project], - environment_scope: 'review/*', - platform_kubernetes: review_env_cluster_kubernetes) - end + let!(:review_env_cluster) do + create(:cluster, + :not_managed, + platform_type: :kubernetes, + projects: [project], + environment_scope: 'review/*', + platform_kubernetes: review_env_cluster_kubernetes) + end - let(:default_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'default-AAA') } - let(:review_env_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'review-AAA') } + let(:default_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'default-AAA') } + let(:review_env_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'review-AAA') } - context 'when environment name is review/name' do - let!(:environment) { create(:environment, project: project, name: 'review/name') } + context 'when environment name is review/name' do + let!(:environment) { create(:environment, project: project, name: 'review/name') } - it 'returns variables from this service' do - expect(project.deployment_variables(environment: 'review/name')) - .to include(key: 'KUBE_TOKEN', value: 'review-AAA', public: false, masked: true) - end - end + it 'returns variables from this service' do + expect(project.deployment_variables(environment: 'review/name')) + .to include(key: 'KUBE_TOKEN', value: 'review-AAA', public: false, masked: true) + end + end - context 'when environment name is other' do - let!(:environment) { create(:environment, project: project, name: 'staging/name') } + context 'when environment name is other' do + let!(:environment) { create(:environment, project: project, name: 'staging/name') } - it 'returns variables from this service' do - expect(project.deployment_variables(environment: 'staging/name')) - .to include(key: 'KUBE_TOKEN', value: 'default-AAA', public: false, masked: true) - end - end + it 'returns variables from this service' do + expect(project.deployment_variables(environment: 'staging/name')) + .to include(key: 'KUBE_TOKEN', value: 'default-AAA', public: false, masked: true) end end end diff --git a/ee/spec/requests/api/group_clusters_spec.rb b/ee/spec/requests/api/group_clusters_spec.rb index 9be6be7bf751..b7c53d0c756c 100644 --- a/ee/spec/requests/api/group_clusters_spec.rb +++ b/ee/spec/requests/api/group_clusters_spec.rb @@ -52,25 +52,6 @@ expect(json_response['environment_scope']).to eq('*') end end - - context 'when license has multiple clusters feature' do - before do - stub_licensed_features(multiple_clusters: true) - - create(:cluster, :provided_by_gcp, :group, - groups: [group]) - - post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params - end - - it 'responds with 201' do - expect(response).to have_gitlab_http_status(:created) - end - - it 'allows multiple clusters to be associated to group' do - expect(group.reload.clusters.count).to eq(2) - end - end end describe 'PUT /groups/:id/clusters/:cluster_id' do diff --git a/ee/spec/requests/api/project_clusters_spec.rb b/ee/spec/requests/api/project_clusters_spec.rb index 75c60ecc9ae7..a496fdc277f1 100644 --- a/ee/spec/requests/api/project_clusters_spec.rb +++ b/ee/spec/requests/api/project_clusters_spec.rb @@ -78,10 +78,8 @@ end end - context 'when license has multiple clusters feature' do + context 'when another cluster exists' do before do - stub_licensed_features(multiple_clusters: true) - create(:cluster, :provided_by_gcp, :project, projects: [project]) diff --git a/ee/spec/services/ee/clusters/create_service_spec.rb b/ee/spec/services/ee/clusters/create_service_spec.rb deleted file mode 100644 index bd226fc4547d..000000000000 --- a/ee/spec/services/ee/clusters/create_service_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Clusters::CreateService do - let(:access_token) { 'xxx' } - let(:project) { create(:project) } - let(:user) { create(:user) } - let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) } - - subject { described_class.new(user, params).execute(access_token: access_token) } - - before do - allow(project).to receive(:feature_available?).and_call_original - end - - context 'when license has multiple clusters feature' do - before do - allow(project).to receive(:feature_available?).with(:multiple_clusters).and_return(true) - end - - context 'when correct params' do - let(:params) do - { - name: 'test-cluster', - provider_type: :gcp, - provider_gcp_attributes: { - gcp_project_id: 'gcp-project', - zone: 'us-central1-a', - num_nodes: 1, - machine_type: 'machine_type-a', - legacy_abac: 'true' - }, - clusterable: project - } - end - - include_examples 'create cluster service success' - end - - context 'when invalid params' do - let(:params) do - { - name: 'test-cluster', - provider_type: :gcp, - provider_gcp_attributes: { - gcp_project_id: '!!!!!!!', - zone: 'us-central1-a', - num_nodes: 1, - machine_type: 'machine_type-a' - }, - clusterable: project - } - end - - include_examples 'create cluster service error' - end - end -end diff --git a/ee/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/ee/spec/services/projects/prometheus/alerts/notify_service_spec.rb index 9f312ba14bb1..05fa8a0d2b74 100644 --- a/ee/spec/services/projects/prometheus/alerts/notify_service_spec.rb +++ b/ee/spec/services/projects/prometheus/alerts/notify_service_spec.rb @@ -118,8 +118,6 @@ end before do - stub_licensed_features(multiple_clusters: true) - create(:clusters_applications_prometheus, :installed, cluster: prd_cluster, alert_manager_token: token) create(:clusters_applications_prometheus, :installed, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 3b28b67ca0d0..407deba0d43f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12394,9 +12394,6 @@ msgstr "" msgid "Instance administrators group already exists" msgstr "" -msgid "Instance does not support multiple Kubernetes clusters" -msgstr "" - msgid "Instance license" msgstr "" diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 3e1006920e7d..2e6a366f77ac 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -139,6 +139,19 @@ def submit_form end end + context 'when a user adds an existing cluster' do + before do + visit project_clusters_path(project) + + click_link 'Add Kubernetes cluster' + click_link 'Add existing cluster' + end + + it 'user sees the "Environment scope" field' do + expect(page).to have_css('#cluster_environment_scope') + end + end + context 'when user destroys the cluster' do before do click_link 'Advanced Settings' @@ -155,19 +168,6 @@ def submit_form end end - context 'when a user cannot edit the environment scope' do - before do - visit project_clusters_path(project) - - click_link 'Add Kubernetes cluster' - click_link 'Add existing cluster' - end - - it 'user does not see the "Environment scope" field' do - expect(page).not_to have_css('#cluster_environment_scope') - end - end - context 'when user has not dismissed GCP signup offer' do before do visit project_clusters_path(project) diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index 1cf214a5c4eb..c56a1ed1711b 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -25,6 +25,168 @@ end end + context 'when user has a cluster' do + before do + allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected) + end + + context 'when user adds an existing cluster' do + before do + create(:cluster, :provided_by_user, name: 'default-cluster', environment_scope: '*', projects: [project]) + visit project_clusters_path(project) + end + + it 'user sees an add cluster button' do + expect(page).to have_selector('.js-add-cluster:not(.readonly)') + end + + context 'when user filled form with environment scope' do + before do + click_link 'Add Kubernetes cluster' + click_link 'Add existing cluster' + fill_in 'cluster_name', with: 'staging-cluster' + fill_in 'cluster_environment_scope', with: 'staging/*' + click_button 'Add Kubernetes cluster' + end + + it 'user sees a cluster details page' do + expect(page.find_field('cluster[name]').value).to eq('staging-cluster') + expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*') + end + end + + context 'when user updates environment scope' do + before do + click_link 'default-cluster' + fill_in 'cluster_environment_scope', with: 'production/*' + within '.js-cluster-integration-form' do + click_button 'Save changes' + end + end + + it 'updates the environment scope' do + expect(page.find_field('cluster[environment_scope]').value).to eq('production/*') + end + end + + context 'when user updates duplicated environment scope' do + before do + click_link 'Add Kubernetes cluster' + click_link 'Add existing cluster' + fill_in 'cluster_name', with: 'staging-cluster' + fill_in 'cluster_environment_scope', with: '*' + fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'https://0.0.0.0' + fill_in 'cluster_platform_kubernetes_attributes_token', with: 'token' + + click_button 'Add Kubernetes cluster' + end + + it 'users sees an environment scope validation error' do + expect(page).to have_content('cannot add duplicated environment scope') + end + end + end + + context 'when user adds a Google Kubernetes Engine cluster' do + before do + allow_any_instance_of(Projects::ClustersController) + .to receive(:token_in_session).and_return('token') + allow_any_instance_of(Projects::ClustersController) + .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s) + + allow_any_instance_of(Projects::ClustersController).to receive(:authorize_google_project_billing) + allow_any_instance_of(Projects::ClustersController).to receive(:google_project_billing_status).and_return(true) + + allow_any_instance_of(GoogleApi::CloudPlatform::Client) + .to receive(:projects_zones_clusters_create) do + OpenStruct.new( + self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123', + status: 'RUNNING' + ) + end + + allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil) + + create(:cluster, :provided_by_gcp, name: 'default-cluster', environment_scope: '*', projects: [project]) + visit project_clusters_path(project) + end + + it 'user sees a add cluster button ' do + expect(page).to have_selector('.js-add-cluster:not(.readonly)') + end + + context 'when user filled form with environment scope' do + before do + click_link 'Add Kubernetes cluster' + click_link 'Create new cluster' + click_link 'Google GKE' + + sleep 2 # wait for ajax + execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")') + execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")') + execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")') + execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")') + + fill_in 'cluster_name', with: 'staging-cluster' + fill_in 'cluster_environment_scope', with: 'staging/*' + fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123' + fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a' + fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2' + click_button 'Create Kubernetes cluster' + + # The frontend won't show the details until the cluster is + # created, and we don't want to make calls out to GCP. + provider = Clusters::Cluster.last.provider + provider.make_created + end + + it 'user sees a cluster details page' do + expect(page).to have_content('GitLab Integration') + expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*') + end + end + + context 'when user updates environment scope' do + before do + click_link 'default-cluster' + fill_in 'cluster_environment_scope', with: 'production/*' + within ".js-cluster-integration-form" do + click_button 'Save changes' + end + end + + it 'updates the environment scope' do + expect(page.find_field('cluster[environment_scope]').value).to eq('production/*') + end + end + + context 'when user updates duplicated environment scope' do + before do + click_link 'Add Kubernetes cluster' + click_link 'Create new cluster' + click_link 'Google GKE' + + sleep 2 # wait for ajax + execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")') + execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")') + execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")') + execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")') + + fill_in 'cluster_name', with: 'staging-cluster' + fill_in 'cluster_environment_scope', with: '*' + fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123' + fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a' + fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2' + click_button 'Create Kubernetes cluster' + end + + it 'users sees an environment scope validation error' do + expect(page).to have_content('cannot add duplicated environment scope') + end + end + end + end + context 'when user has a cluster and visits cluster index page' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index dad72bd9a167..cebf6235f442 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -103,7 +103,7 @@ describe '#has_multiple_clusters?' do subject { helper.has_multiple_clusters? } - + it { is_expected.to be_truthy } end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 8b1afd271daa..83060434b7c1 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -10,6 +10,7 @@ subject { build(:cluster) } + it { is_expected.to include_module(HasEnvironmentScope) } it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:management_project).class_name('::Project') } it { is_expected.to have_many(:cluster_projects) } @@ -289,6 +290,79 @@ describe 'validations' do subject { cluster.valid? } + context 'when validates unique_environment_scope' do + context 'for a project cluster' do + let(:project) { create(:project) } + + before do + create(:cluster, projects: [project], environment_scope: 'product/*') + end + + context 'when identical environment scope exists in project' do + let(:cluster) { build(:cluster, projects: [project], environment_scope: 'product/*') } + + it { is_expected.to be_falsey } + end + + context 'when identical environment scope does not exist in project' do + let(:cluster) { build(:cluster, projects: [project], environment_scope: '*') } + + it { is_expected.to be_truthy } + end + + context 'when identical environment scope exists in different project' do + let(:project2) { create(:project) } + let(:cluster) { build(:cluster, projects: [project2], environment_scope: 'product/*') } + + it { is_expected.to be_truthy } + end + end + + context 'for a group cluster' do + let(:group) { create(:group) } + + before do + create(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') + end + + context 'when identical environment scope exists in group' do + let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') } + + it { is_expected.to be_falsey } + end + + context 'when identical environment scope does not exist in group' do + let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: '*') } + + it { is_expected.to be_truthy } + end + + context 'when identical environment scope exists in different group' do + let(:cluster) { build(:cluster, :group, environment_scope: 'product/*') } + + it { is_expected.to be_truthy } + end + end + + context 'for an instance cluster' do + before do + create(:cluster, :instance, environment_scope: 'product/*') + end + + context 'identical environment scope exists' do + let(:cluster) { build(:cluster, :instance, environment_scope: 'product/*') } + + it { is_expected.to be_falsey } + end + + context 'identical environment scope does not exist' do + let(:cluster) { build(:cluster, :instance, environment_scope: '*') } + + it { is_expected.to be_truthy } + end + end + end + context 'when validates name' do context 'when provided by user' do let!(:cluster) { build(:cluster, :provided_by_user, name: name) } diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb index b5b7efa0c478..2bb6aa27e215 100644 --- a/spec/models/concerns/deployment_platform_spec.rb +++ b/spec/models/concerns/deployment_platform_spec.rb @@ -8,6 +8,241 @@ describe '#deployment_platform' do subject { project.deployment_platform } + context 'multiple clusters' do + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + + shared_examples 'matching environment scope' do + it 'returns environment specific cluster' do + is_expected.to eq(cluster.platform_kubernetes) + end + end + + shared_examples 'not matching environment scope' do + it 'returns default cluster' do + is_expected.to eq(default_cluster.platform_kubernetes) + end + end + + context 'multiple clusters use the same management project' do + let(:management_project) { create(:project, group: group) } + + let!(:default_cluster) do + create(:cluster_for_group, groups: [group], environment_scope: '*', management_project: management_project) + end + + let!(:cluster) do + create(:cluster_for_group, groups: [group], environment_scope: 'review/*', management_project: management_project) + end + + let(:environment) { 'review/name' } + + subject { management_project.deployment_platform(environment: environment) } + + it_behaves_like 'matching environment scope' + end + + context 'when project does not have a cluster but has group clusters' do + let!(:default_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, groups: [group], environment_scope: '*') + end + + let!(:cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, environment_scope: 'review/*', groups: [group]) + end + + let(:environment) { 'review/name' } + + subject { project.deployment_platform(environment: environment) } + + context 'when environment scope is exactly matched' do + before do + cluster.update!(environment_scope: 'review/name') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope is matched by wildcard' do + before do + cluster.update!(environment_scope: 'review/*') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope does not match' do + before do + cluster.update!(environment_scope: 'review/*/special') + end + + it_behaves_like 'not matching environment scope' + end + + context 'when group belongs to a parent group' do + let(:parent_group) { create(:group) } + let(:group) { create(:group, parent: parent_group) } + + context 'when parent_group has a cluster with default scope' do + let!(:parent_group_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, environment_scope: '*', groups: [parent_group]) + end + + it_behaves_like 'matching environment scope' + end + + context 'when parent_group has a cluster that is an exact match' do + let!(:parent_group_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, environment_scope: 'review/name', groups: [parent_group]) + end + + it_behaves_like 'matching environment scope' + end + end + end + + context 'with instance clusters' do + let!(:default_cluster) do + create(:cluster, :provided_by_user, :instance, environment_scope: '*') + end + + let!(:cluster) do + create(:cluster, :provided_by_user, :instance, environment_scope: 'review/*') + end + + let(:environment) { 'review/name' } + + subject { project.deployment_platform(environment: environment) } + + context 'when environment scope is exactly matched' do + before do + cluster.update!(environment_scope: 'review/name') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope is matched by wildcard' do + before do + cluster.update!(environment_scope: 'review/*') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope does not match' do + before do + cluster.update!(environment_scope: 'review/*/special') + end + + it_behaves_like 'not matching environment scope' + end + end + + context 'when environment is specified' do + let!(:default_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: '*') } + let!(:cluster) { create(:cluster, :provided_by_user, environment_scope: 'review/*', projects: [project]) } + + let!(:group_default_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, groups: [group], environment_scope: '*') + end + + let(:environment) { 'review/name' } + + subject { project.deployment_platform(environment: environment) } + + context 'when environment scope is exactly matched' do + before do + cluster.update!(environment_scope: 'review/name') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope is matched by wildcard' do + before do + cluster.update!(environment_scope: 'review/*') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope does not match' do + before do + cluster.update!(environment_scope: 'review/*/special') + end + + it_behaves_like 'not matching environment scope' + end + + context 'when environment scope has _' do + it 'does not treat it as wildcard' do + cluster.update!(environment_scope: 'foo_bar/*') + + is_expected.to eq(default_cluster.platform_kubernetes) + end + + context 'when environment name contains an underscore' do + let(:environment) { 'foo_bar/test' } + + it 'matches literally for _' do + cluster.update!(environment_scope: 'foo_bar/*') + + is_expected.to eq(cluster.platform_kubernetes) + end + end + end + + # The environment name and scope cannot have % at the moment, + # but we're considering relaxing it and we should also make sure + # it doesn't break in case some data sneaked in somehow as we're + # not checking this integrity in database level. + context 'when environment scope has %' do + it 'does not treat it as wildcard' do + cluster.update_attribute(:environment_scope, '*%*') + + is_expected.to eq(default_cluster.platform_kubernetes) + end + + context 'when environment name contains a percent char' do + let(:environment) { 'foo%bar/test' } + + it 'matches literally for %' do + cluster.update_attribute(:environment_scope, 'foo%bar/*') + + is_expected.to eq(cluster.platform_kubernetes) + end + end + end + + context 'when perfectly matched cluster exists' do + let!(:perfectly_matched_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'review/name') } + + it 'returns perfectly matched cluster as highest precedence' do + is_expected.to eq(perfectly_matched_cluster.platform_kubernetes) + end + end + end + + context 'with multiple clusters and multiple environments' do + let!(:cluster_1) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'staging/*') } + let!(:cluster_2) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'test/*') } + + let(:environment_1) { 'staging/name' } + let(:environment_2) { 'test/name' } + + it 'returns the appropriate cluster' do + expect(project.deployment_platform(environment: environment_1)).to eq(cluster_1.platform_kubernetes) + expect(project.deployment_platform(environment: environment_2)).to eq(cluster_2.platform_kubernetes) + end + end + end + context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service' do it { is_expected.to be_nil } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 94b0b55d32c0..da3e1b7d611f 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2905,28 +2905,73 @@ subject { project.deployment_variables(environment: environment, kubernetes_namespace: namespace) } - before do - expect(project).to receive(:deployment_platform).with(environment: environment) - .and_return(deployment_platform) - end + context 'when the deployment platform is stubbed' do + before do + expect(project).to receive(:deployment_platform).with(environment: environment) + .and_return(deployment_platform) + end + + context 'when project has a deployment platform' do + let(:platform_variables) { %w(platform variables) } + let(:deployment_platform) { double } + + before do + expect(deployment_platform).to receive(:predefined_variables) + .with(project: project, environment_name: environment, kubernetes_namespace: namespace) + .and_return(platform_variables) + end + + it { is_expected.to eq platform_variables } + end - context 'when project has no deployment platform' do - let(:deployment_platform) { nil } + context 'when project has no deployment platform' do + let(:deployment_platform) { nil } - it { is_expected.to eq [] } + it { is_expected.to eq [] } + end end - context 'when project has a deployment platform' do - let(:platform_variables) { %w(platform variables) } - let(:deployment_platform) { double } + context 'when project has a deployment platforms' do + let(:project) { create(:project) } - before do - expect(deployment_platform).to receive(:predefined_variables) - .with(project: project, environment_name: environment, kubernetes_namespace: namespace) - .and_return(platform_variables) + let!(:default_cluster) do + create(:cluster, + :not_managed, + platform_type: :kubernetes, + projects: [project], + environment_scope: '*', + platform_kubernetes: default_cluster_kubernetes) + end + + let!(:review_env_cluster) do + create(:cluster, + :not_managed, + platform_type: :kubernetes, + projects: [project], + environment_scope: 'review/*', + platform_kubernetes: review_env_cluster_kubernetes) + end + + let(:default_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'default-AAA') } + let(:review_env_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'review-AAA') } + + context 'when environment name is review/name' do + let!(:environment) { create(:environment, project: project, name: 'review/name') } + + it 'returns variables from this service' do + expect(project.deployment_variables(environment: 'review/name')) + .to include(key: 'KUBE_TOKEN', value: 'review-AAA', public: false, masked: true) + end end - it { is_expected.to eq platform_variables } + context 'when environment name is other' do + let!(:environment) { create(:environment, project: project, name: 'staging/name') } + + it 'returns variables from this service' do + expect(project.deployment_variables(environment: 'staging/name')) + .to include(key: 'KUBE_TOKEN', value: 'default-AAA', public: false, masked: true) + end + end end end diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb index 0e695cc64a23..068af1485e28 100644 --- a/spec/requests/api/group_clusters_spec.rb +++ b/spec/requests/api/group_clusters_spec.rb @@ -266,29 +266,51 @@ end end - context 'when user tries to add multiple clusters' do + context 'non-authorized user' do before do - create(:cluster, :provided_by_gcp, :group, - groups: [group]) - - post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params + post api("/groups/#{group.id}/clusters/user", developer_user), params: cluster_params end - it 'responds with 400' do - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['message']['base'].first).to eq(_('Instance does not support multiple Kubernetes clusters')) + it 'responds with 403' do + expect(response).to have_gitlab_http_status(:forbidden) + + expect(json_response['message']).to eq('403 Forbidden') end end + end - context 'non-authorized user' do + describe 'PUT /groups/:id/clusters/:cluster_id' do + let(:api_url) { 'https://kubernetes.example.com' } + + let(:platform_kubernetes_attributes) do + { + api_url: api_url, + token: 'sample-token' + } + end + + let(:cluster_params) do + { + name: 'test-cluster', + environment_scope: 'test/*', + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + context 'when another cluster exists' do before do - post api("/groups/#{group.id}/clusters/user", developer_user), params: cluster_params + create(:cluster, :provided_by_gcp, :group, + groups: [group]) + + post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params end - it 'responds with 403' do - expect(response).to have_gitlab_http_status(:forbidden) + it 'responds with 201' do + expect(response).to have_gitlab_http_status(:created) + end - expect(json_response['message']).to eq('403 Forbidden') + it 'allows multiple clusters to be associated to group' do + expect(group.reload.clusters.count).to eq(2) end end end diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index f01b3bc9d5f7..2413be022d7a 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -258,29 +258,52 @@ end end - context 'when user tries to add multiple clusters' do + context 'non-authorized user' do before do - create(:cluster, :provided_by_gcp, :project, - projects: [project]) - - post api("/projects/#{project.id}/clusters/user", current_user), params: cluster_params + post api("/projects/#{project.id}/clusters/user", developer_user), params: cluster_params end - it 'responds with 400' do - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['message']['base'].first) - .to eq(_('Instance does not support multiple Kubernetes clusters')) + it 'responds with 403' do + expect(response).to have_gitlab_http_status(:forbidden) + expect(json_response['message']).to eq('403 Forbidden') end end + end - context 'non-authorized user' do + describe 'POST /projects/:id/clusters/user with multiple clusters' do + let(:api_url) { 'https://kubernetes.example.com' } + let(:namespace) { project.path } + + let(:platform_kubernetes_attributes) do + { + api_url: api_url, + token: 'sample-token', + namespace: namespace + } + end + + let(:cluster_params) do + { + name: 'test-cluster', + environment_scope: 'test/*', + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + context 'when another cluster exists' do before do - post api("/projects/#{project.id}/clusters/user", developer_user), params: cluster_params + create(:cluster, :provided_by_gcp, :project, + projects: [project]) + + post api("/projects/#{project.id}/clusters/user", current_user), params: cluster_params end - it 'responds with 403' do - expect(response).to have_gitlab_http_status(:forbidden) - expect(json_response['message']).to eq('403 Forbidden') + it 'responds with 201' do + expect(response).to have_gitlab_http_status(:created) + end + + it 'allows multiple clusters to be associated to project' do + expect(project.reload.clusters.count).to eq(2) end end end diff --git a/spec/services/clusters/create_service_spec.rb b/spec/services/clusters/create_service_spec.rb index 9aa405845828..6e252bee7c0d 100644 --- a/spec/services/clusters/create_service_spec.rb +++ b/spec/services/clusters/create_service_spec.rb @@ -60,6 +60,47 @@ end end + context 'when another cluster exists' do + let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) } + + context 'when correct params' do + let(:params) do + { + name: 'test-cluster', + provider_type: :gcp, + provider_gcp_attributes: { + gcp_project_id: 'gcp-project', + zone: 'us-central1-a', + num_nodes: 1, + machine_type: 'machine_type-a', + legacy_abac: 'true' + }, + clusterable: project + } + end + + include_examples 'create cluster service success' + end + + context 'when invalid params' do + let(:params) do + { + name: 'test-cluster', + provider_type: :gcp, + provider_gcp_attributes: { + gcp_project_id: '!!!!!!!', + zone: 'us-central1-a', + num_nodes: 1, + machine_type: 'machine_type-a' + }, + clusterable: project + } + end + + include_examples 'create cluster service error' + end + end + context 'when params includes :management_project_id' do subject(:cluster) { described_class.new(user, params).execute(access_token: access_token) } diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb index 852382dd0d84..557bf2162775 100644 --- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb +++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb @@ -102,6 +102,41 @@ let(:payload_alert_firing) { payload_raw['alerts'].first } let(:token) { 'token' } + context 'with environment specific clusters' do + let(:prd_cluster) do + cluster + end + + let(:stg_cluster) do + create(:cluster, :provided_by_user, projects: [project], enabled: true, environment_scope: 'stg/*') + end + + let(:stg_environment) do + create(:environment, project: project, name: 'stg/1') + end + + let(:alert_firing) do + create(:prometheus_alert, project: project, environment: stg_environment) + end + + before do + create(:clusters_applications_prometheus, :installed, + cluster: prd_cluster, alert_manager_token: token) + create(:clusters_applications_prometheus, :installed, + cluster: stg_cluster, alert_manager_token: nil) + end + + context 'without token' do + let(:token_input) { nil } + + it_behaves_like 'notifies alerts' + end + + context 'with token' do + it_behaves_like 'no notifications', http_status: :unauthorized + end + end + context 'with project specific cluster' do using RSpec::Parameterized::TableSyntax diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb index 95c63df03110..f8a58a828ce4 100644 --- a/spec/support/services/clusters/create_service_shared.rb +++ b/spec/support/services/clusters/create_service_shared.rb @@ -12,7 +12,7 @@ num_nodes: 1, machine_type: 'machine_type-a', legacy_abac: 'true' - }, + }, clusterable: clusterable } end -- GitLab From 3e17c2cebabf2c37860eb0ff29c630df7023cd59 Mon Sep 17 00:00:00 2001 From: serenafang Date: Tue, 7 Jul 2020 17:03:13 -0500 Subject: [PATCH 7/9] Change spec environment scope --- spec/requests/api/project_clusters_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index 2413be022d7a..6ce03f52ba01 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -285,7 +285,7 @@ let(:cluster_params) do { name: 'test-cluster', - environment_scope: 'test/*', + environment_scope: 'test', platform_kubernetes_attributes: platform_kubernetes_attributes } end -- GitLab From 121519eef5fb4f331006d81e2d31f5c018ae7baa Mon Sep 17 00:00:00 2001 From: serenafang Date: Tue, 7 Jul 2020 17:48:31 -0500 Subject: [PATCH 8/9] Add puts to inspect ci --- spec/requests/api/project_clusters_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index 6ce03f52ba01..806197a752a8 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -299,6 +299,8 @@ end it 'responds with 201' do + puts cluster_params + puts project.clusters.first.inspect expect(response).to have_gitlab_http_status(:created) end -- GitLab From ec27efa78071f637d33783a039bf41f0b9a29860 Mon Sep 17 00:00:00 2001 From: serenafang Date: Tue, 7 Jul 2020 20:04:55 -0500 Subject: [PATCH 9/9] Add environment scope param Add optional environment scope param to group_clusters.rb and project_clusters.rb, used to only be in the ee files --- ee/lib/ee/api/group_clusters.rb | 21 --------------------- ee/lib/ee/api/project_clusters.rb | 21 --------------------- lib/api/group_clusters.rb | 16 ++-------------- lib/api/project_clusters.rb | 16 ++-------------- spec/requests/api/project_clusters_spec.rb | 6 ++---- 5 files changed, 6 insertions(+), 74 deletions(-) delete mode 100644 ee/lib/ee/api/group_clusters.rb delete mode 100644 ee/lib/ee/api/project_clusters.rb diff --git a/ee/lib/ee/api/group_clusters.rb b/ee/lib/ee/api/group_clusters.rb deleted file mode 100644 index b0bdc8567d1e..000000000000 --- a/ee/lib/ee/api/group_clusters.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module EE - module API - module GroupClusters - extend ActiveSupport::Concern - - prepended do - helpers do - params :create_params_ee do - optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' - end - - params :update_params_ee do - optional :environment_scope, type: String, desc: 'The associated environment to the cluster' - end - end - end - end - end -end diff --git a/ee/lib/ee/api/project_clusters.rb b/ee/lib/ee/api/project_clusters.rb deleted file mode 100644 index 6bcced7f8ed6..000000000000 --- a/ee/lib/ee/api/project_clusters.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module EE - module API - module ProjectClusters - extend ActiveSupport::Concern - - prepended do - helpers do - params :create_params_ee do - optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' - end - - params :update_params_ee do - optional :environment_scope, type: String, desc: 'The associated environment to the cluster' - end - end - end - end - end -end diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb index c6d10f22bb45..ae41d9f13b81 100644 --- a/lib/api/group_clusters.rb +++ b/lib/api/group_clusters.rb @@ -6,18 +6,6 @@ class GroupClusters < Grape::API::Instance before { authenticate! } - # EE::API::GroupClusters will - # override these methods - helpers do - params :create_params_ee do - end - - params :update_params_ee do - end - end - - prepend_if_ee('EE::API::GroupClusters') # rubocop: disable Cop/InjectEnterpriseEditionModule - params do requires :id, type: String, desc: 'The ID of the group' end @@ -52,6 +40,7 @@ class GroupClusters < Grape::API::Instance params do requires :name, type: String, desc: 'Cluster name' optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' + optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' optional :domain, type: String, desc: 'Cluster base domain' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' @@ -62,7 +51,6 @@ class GroupClusters < Grape::API::Instance optional :namespace, type: String, desc: 'Unique namespace related to Group' optional :authorization_type, type: String, values: ::Clusters::Platforms::Kubernetes.authorization_types.keys, default: 'rbac', desc: 'Cluster authorization type, defaults to RBAC' end - use :create_params_ee end post ':id/clusters/user' do authorize! :add_cluster, user_group @@ -85,6 +73,7 @@ class GroupClusters < Grape::API::Instance requires :cluster_id, type: Integer, desc: 'The cluster ID' optional :name, type: String, desc: 'Cluster name' optional :domain, type: String, desc: 'Cluster base domain' + optional :environment_scope, type: String, desc: 'The associated environment to the cluster' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do optional :api_url, type: String, desc: 'URL to access the Kubernetes API' @@ -92,7 +81,6 @@ class GroupClusters < Grape::API::Instance optional :ca_cert, type: String, desc: 'TLS certificate (needed if API is using a self-signed TLS certificate)' optional :namespace, type: String, desc: 'Unique namespace related to Group' end - use :update_params_ee end put ':id/clusters/:cluster_id' do authorize! :update_cluster, cluster diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb index e1dfb647fa08..0e5605984e63 100644 --- a/lib/api/project_clusters.rb +++ b/lib/api/project_clusters.rb @@ -6,18 +6,6 @@ class ProjectClusters < Grape::API::Instance before { authenticate! } - # EE::API::ProjectClusters will - # override these methods - helpers do - params :create_params_ee do - end - - params :update_params_ee do - end - end - - prepend_if_ee('EE::API::ProjectClusters') # rubocop: disable Cop/InjectEnterpriseEditionModule - params do requires :id, type: String, desc: 'The ID of the project' end @@ -56,6 +44,7 @@ class ProjectClusters < Grape::API::Instance requires :name, type: String, desc: 'Cluster name' optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' optional :domain, type: String, desc: 'Cluster base domain' + optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' requires :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do @@ -65,7 +54,6 @@ class ProjectClusters < Grape::API::Instance optional :namespace, type: String, desc: 'Unique namespace related to Project' optional :authorization_type, type: String, values: ::Clusters::Platforms::Kubernetes.authorization_types.keys, default: 'rbac', desc: 'Cluster authorization type, defaults to RBAC' end - use :create_params_ee end post ':id/clusters/user' do authorize! :add_cluster, user_project @@ -89,6 +77,7 @@ class ProjectClusters < Grape::API::Instance requires :cluster_id, type: Integer, desc: 'The cluster ID' optional :name, type: String, desc: 'Cluster name' optional :domain, type: String, desc: 'Cluster base domain' + optional :environment_scope, type: String, desc: 'The associated environment to the cluster' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do optional :api_url, type: String, desc: 'URL to access the Kubernetes API' @@ -96,7 +85,6 @@ class ProjectClusters < Grape::API::Instance optional :ca_cert, type: String, desc: 'TLS certificate (needed if API is using a self-signed TLS certificate)' optional :namespace, type: String, desc: 'Unique namespace related to Project' end - use :update_params_ee end put ':id/clusters/:cluster_id' do authorize! :update_cluster, cluster diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index 806197a752a8..ff35e3804761 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -40,7 +40,7 @@ expect(response).to include_pagination_headers end - it 'onlies include authorized clusters' do + it 'only includes authorized clusters' do cluster_ids = json_response.map { |cluster| cluster['id'] } expect(response).to have_gitlab_http_status(:ok) @@ -285,7 +285,7 @@ let(:cluster_params) do { name: 'test-cluster', - environment_scope: 'test', + environment_scope: 'production/*', platform_kubernetes_attributes: platform_kubernetes_attributes } end @@ -299,8 +299,6 @@ end it 'responds with 201' do - puts cluster_params - puts project.clusters.first.inspect expect(response).to have_gitlab_http_status(:created) end -- GitLab