diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-24 12:06:03 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-24 12:06:03 +0000 |
commit | 33813f993b49da58426d33a148ee70952e6835bb (patch) | |
tree | a8310742d6eb7e1dc83f72ceba1fefb3d5b8a030 | |
parent | dc0622dbe3cd552abca4107557c6c09edb23625c (diff) | |
download | gitlab-ce-33813f993b49da58426d33a148ee70952e6835bb.tar.gz |
Add latest changes from gitlab-org/gitlab@master
32 files changed, 474 insertions, 93 deletions
@@ -1 +1 @@ -12.3.0-pre +12.5.0-pre diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue index 19a2db2db25..aa270a374ae 100644 --- a/app/assets/javascripts/repository/components/last_commit.vue +++ b/app/assets/javascripts/repository/components/last_commit.vue @@ -125,19 +125,21 @@ export default { </div> <div class="commit-actions flex-row"> <div v-if="commit.signatureHtml" v-html="commit.signatureHtml"></div> - <gl-link - v-if="commit.latestPipeline" - v-gl-tooltip - :href="commit.latestPipeline.detailedStatus.detailsPath" - :title="statusTitle" - class="js-commit-pipeline" - > - <ci-icon - :status="commit.latestPipeline.detailedStatus" - :size="24" - :aria-label="statusTitle" - /> - </gl-link> + <div class="ci-status-link"> + <gl-link + v-if="commit.latestPipeline" + v-gl-tooltip + :href="commit.latestPipeline.detailedStatus.detailsPath" + :title="statusTitle" + class="js-commit-pipeline" + > + <ci-icon + :status="commit.latestPipeline.detailedStatus" + :size="24" + :aria-label="statusTitle" + /> + </gl-link> + </div> <div class="commit-sha-group d-flex"> <div class="label label-monospace monospace"> {{ showCommitId }} diff --git a/app/assets/stylesheets/framework/memory_graph.scss b/app/assets/stylesheets/framework/memory_graph.scss index 81cdf6b59e4..c84010c6f10 100644 --- a/app/assets/stylesheets/framework/memory_graph.scss +++ b/app/assets/stylesheets/framework/memory_graph.scss @@ -1,11 +1,7 @@ .memory-graph-container { svg { background: $white-light; - cursor: pointer; - - &:hover { - box-shadow: 0 0 4px $gray-darkest inset; - } + border: 1px solid $gray-200; } path { diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index bb47ccb72b3..abec237dd1d 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -142,6 +142,7 @@ class Clusters::ClustersController < Clusters::BaseController :environment_scope, :managed, :base_domain, + :management_project_id, platform_kubernetes_attributes: [ :api_url, :token, @@ -155,6 +156,7 @@ class Clusters::ClustersController < Clusters::BaseController :environment_scope, :managed, :base_domain, + :management_project_id, platform_kubernetes_attributes: [ :namespace ] diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index c600717f5df..e3e2f06c313 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -117,6 +117,8 @@ module Clusters scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) } + scope :for_project_namespace, -> (namespace_id) { joins(:projects).where(projects: { namespace_id: namespace_id }) } + def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc) return [] if clusterable.is_a?(Instance) diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb index a906eb2888b..c9c18d8c96a 100644 --- a/app/models/clusters/clusters_hierarchy.rb +++ b/app/models/clusters/clusters_hierarchy.rb @@ -20,7 +20,7 @@ module Clusters .with .recursive(cte.to_arel) .from(cte_alias) - .order(DEPTH_COLUMN => :asc) + .order(depth_order_clause) end private @@ -40,7 +40,7 @@ module Clusters end if clusterable.is_a?(::Project) && include_management_project - cte << management_clusters_query + cte << same_namespace_management_clusters_query end cte << base_query @@ -49,13 +49,42 @@ module Clusters cte end + # Returns project-level clusters where the project is the management project + # for the cluster. The management project has to be in the same namespace / + # group as the cluster's project. + # + # Support for management project in sub-groups is planned in + # https://gitlab.com/gitlab-org/gitlab/issues/34650 + # + # NB: group_parent_id is un-used but we still need to match the same number of + # columns as other queries in the CTE. + def same_namespace_management_clusters_query + clusterable.management_clusters + .project_type + .select([clusters_star, 'NULL AS group_parent_id', "0 AS #{DEPTH_COLUMN}"]) + .for_project_namespace(clusterable.namespace_id) + end + # Management clusters should be first in the hierarchy so we use 0 for the # depth column. # - # group_parent_id is un-used but we still need to match the same number of - # columns as other queries in the CTE. - def management_clusters_query - clusterable.management_clusters.select([clusters_star, 'NULL AS group_parent_id', "0 AS #{DEPTH_COLUMN}"]) + # Only applicable if the clusterable is a project (most especially when + # requesting project.deployment_platform). + def depth_order_clause + return { DEPTH_COLUMN => :asc } unless clusterable.is_a?(::Project) && include_management_project + + order = <<~SQL + (CASE clusters.management_project_id + WHEN :project_id THEN 0 + ELSE #{DEPTH_COLUMN} + END) ASC + SQL + + values = { + project_id: clusterable.id + } + + model.sanitize_sql_array([Arel.sql(order), values]) end def group_clusters_base_query diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb index fe8e9609820..d0b10fd8671 100644 --- a/app/models/concerns/deployment_platform.rb +++ b/app/models/concerns/deployment_platform.rb @@ -12,7 +12,7 @@ module DeploymentPlatform private def cluster_management_project_enabled? - Feature.enabled?(:cluster_management_project, default_enabled: true) + Feature.enabled?(:cluster_management_project, self) end def find_deployment_platform(environment) diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb index 25d26e761b1..98dd6b26a47 100644 --- a/app/services/clusters/update_service.rb +++ b/app/services/clusters/update_service.rb @@ -9,7 +9,55 @@ module Clusters end def execute(cluster) - cluster.update(params) + if validate_params(cluster) + cluster.update(params) + else + false + end + end + + private + + def can_admin_pipeline_for_project?(project) + Ability.allowed?(current_user, :admin_pipeline, project) + end + + def validate_params(cluster) + if params[:management_project_id] + management_project = management_project_scope(cluster).find_by_id(params[:management_project_id]) + + unless management_project + cluster.errors.add(:management_project_id, _('Project does not exist or you don\'t have permission to perform this action')) + + return false + end + + unless can_admin_pipeline_for_project?(management_project) + # Use same message as not found to prevent enumeration + cluster.errors.add(:management_project_id, _('Project does not exist or you don\'t have permission to perform this action')) + + return false + end + end + + true + end + + def management_project_scope(cluster) + return ::Project.all if cluster.instance_type? + + group = + if cluster.group_type? + cluster.first_group + elsif cluster.project_type? + cluster.first_project&.namespace + end + + # Prevent users from selecting nested projects until + # https://gitlab.com/gitlab-org/gitlab/issues/34650 is resolved + include_subgroups = cluster.group_type? + + ::GroupProjectsFinder.new(group: group, current_user: current_user, options: { only_owned: true, include_subgroups: include_subgroups }).execute end end end diff --git a/changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml b/changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml new file mode 100644 index 00000000000..0bbbcb11523 --- /dev/null +++ b/changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml @@ -0,0 +1,5 @@ +--- +title: Remove pointer cursor from MemoryUsage chart on MR widget deployment +merge_request: 18599 +author: +type: fixed diff --git a/changelogs/unreleased/cluster_management_projects_updating.yml b/changelogs/unreleased/cluster_management_projects_updating.yml new file mode 100644 index 00000000000..8f1876fc5a1 --- /dev/null +++ b/changelogs/unreleased/cluster_management_projects_updating.yml @@ -0,0 +1,5 @@ +--- +title: Adds ability to set management project for cluster via API +merge_request: 18429 +author: +type: added diff --git a/changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml b/changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml new file mode 100644 index 00000000000..daafd2539fd --- /dev/null +++ b/changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml @@ -0,0 +1,5 @@ +--- +title: Fixed admin geo collapsed sidebar fly out not showing +merge_request: 19012 +author: +type: fixed diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md index e878bb5fa4d..6eb2cef4097 100644 --- a/doc/api/group_clusters.md +++ b/doc/api/group_clusters.md @@ -53,6 +53,16 @@ Example response: "api_url":"https://104.197.68.152", "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + }, + "management_project": + { + "id":2, + "description":null, + "name":"project2", + "name_with_namespace":"John Doe8 / project2", + "path":"project2", + "path_with_namespace":"namespace2/project2", + "created_at":"2019-10-11T02:55:54.138Z" } }, { @@ -111,6 +121,16 @@ Example response: "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" }, + "management_project": + { + "id":2, + "description":null, + "name":"project2", + "name_with_namespace":"John Doe8 / project2", + "path":"project2", + "path_with_namespace":"namespace2/project2", + "created_at":"2019-10-11T02:55:54.138Z" + }, "group": { "id":26, @@ -135,6 +155,7 @@ Parameters: | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | | `name` | String | yes | The name of the cluster | | `domain` | String | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster | +| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster | | `enabled` | Boolean | no | Determines if cluster is active or not, defaults to true | | `managed` | Boolean | no | Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true | | `platform_kubernetes_attributes[api_url]` | String | yes | The URL to access the Kubernetes API | @@ -178,6 +199,7 @@ Example response: "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" }, + "management_project":null, "group": { "id":26, @@ -248,6 +270,16 @@ Example response: "authorization_type":"rbac", "ca_cert":null }, + "management_project": + { + "id":2, + "description":null, + "name":"project2", + "name_with_namespace":"John Doe8 / project2", + "path":"project2", + "path_with_namespace":"namespace2/project2", + "created_at":"2019-10-11T02:55:54.138Z" + }, "group": { "id":26, diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md index 633ef20deb4..e733cd7ea7b 100644 --- a/doc/api/project_clusters.md +++ b/doc/api/project_clusters.md @@ -54,6 +54,16 @@ Example response: "namespace":"cluster-1-namespace", "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + }, + "management_project": + { + "id":2, + "description":null, + "name":"project2", + "name_with_namespace":"John Doe8 / project2", + "path":"project2", + "path_with_namespace":"namespace2/project2", + "created_at":"2019-10-11T02:55:54.138Z" } }, { @@ -113,6 +123,16 @@ Example response: "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" }, + "management_project": + { + "id":2, + "description":null, + "name":"project2", + "name_with_namespace":"John Doe8 / project2", + "path":"project2", + "path_with_namespace":"namespace2/project2", + "created_at":"2019-10-11T02:55:54.138Z" + }, "project": { "id":26, @@ -205,6 +225,7 @@ Example response: "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" }, + "management_project":null, "project": { "id":26, @@ -253,6 +274,7 @@ Parameters: | `cluster_id` | integer | yes | The ID of the cluster | | `name` | String | no | The name of the cluster | | `domain` | String | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster | +| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster | | `platform_kubernetes_attributes[api_url]` | String | no | The URL to access the Kubernetes API | | `platform_kubernetes_attributes[token]` | String | no | The token to authenticate against Kubernetes | | `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | @@ -300,6 +322,16 @@ Example response: "authorization_type":"rbac", "ca_cert":null }, + "management_project": + { + "id":2, + "description":null, + "name":"project2", + "name_with_namespace":"John Doe8 / project2", + "path":"project2", + "path_with_namespace":"namespace2/project2", + "created_at":"2019-10-11T02:55:54.138Z" + }, "project": { "id":26, diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md index 37308ad7175..2c3c6850ea5 100644 --- a/doc/user/clusters/management_project.md +++ b/doc/user/clusters/management_project.md @@ -4,7 +4,7 @@ CAUTION: **Warning:** This is an _alpha_ feature, and it is subject to change at any time without prior notice. -> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/17866) in GitLab 12.4 +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/32810) in GitLab 12.5 A project can be designated as the management project for a cluster. A management project can be used to run deployment jobs with @@ -22,6 +22,14 @@ This can be useful for: Only the management project will receive `cluster-admin` privileges. All other projects will continue to receive [namespace scoped `edit` level privileges](../project/clusters/index.md#rbac-cluster-resources). +Management projects are restricted to the following: + +- For project-level clusters, the management project must in the same + namespace (or descendants) as the cluster's project. +- For group-level clusters, the management project must in the same + group (or descendants) as as the cluster's group. +- For instance-level clusters, there are no such restrictions. + ## Usage ### Selecting a cluster management project @@ -87,9 +95,9 @@ configure production cluster: name: production ``` -## Disabling this feature +## Enabling this feature -This feature is enabled by default. To disable this feature, disable the +This feature is disabled by default. To enable this feature, enable the feature flag `:cluster_management_project`. To check if the feature flag is enabled on your GitLab instance, diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 933a571c17d..de12695af37 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -1791,6 +1791,7 @@ module API expose :user, using: Entities::UserBasic expose :platform_kubernetes, using: Entities::Platform::Kubernetes expose :provider_gcp, using: Entities::Provider::Gcp + expose :management_project, using: Entities::ProjectIdentity end class ClusterProject < Cluster diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb index a70ac63cc6e..abfe10b7fa1 100644 --- a/lib/api/group_clusters.rb +++ b/lib/api/group_clusters.rb @@ -84,6 +84,7 @@ module API 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 :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' optional :token, type: String, desc: 'Token to authenticate against Kubernetes' diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index 4c575381d30..dfac777e4a1 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -140,7 +140,8 @@ module API { repository: repository.gitaly_repository, address: Gitlab::GitalyClient.address(project.repository_storage), - token: Gitlab::GitalyClient.token(project.repository_storage) + token: Gitlab::GitalyClient.token(project.repository_storage), + features: Feature::Gitaly.server_feature_flags } end end diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb index 45c800d7d1e..8e35914f48a 100644 --- a/lib/api/project_clusters.rb +++ b/lib/api/project_clusters.rb @@ -88,6 +88,7 @@ module API 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 :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' optional :token, type: String, desc: 'Token to authenticate against Kubernetes' diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1c8669deef3..323efe2e6eb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12701,6 +12701,9 @@ msgstr "" msgid "Project details" msgstr "" +msgid "Project does not exist or you don't have permission to perform this action" +msgstr "" + msgid "Project export could not be deleted." msgstr "" diff --git a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap index 08173f4f0c4..706c26403c0 100644 --- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap +++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap @@ -62,19 +62,23 @@ exports[`Repository last commit component renders commit widget 1`] = ` > <!----> - <gllink-stub - class="js-commit-pipeline" - data-original-title="Commit: failed" - href="https://test.com/pipeline" - title="" + <div + class="ci-status-link" > - <ciicon-stub - aria-label="Commit: failed" - cssclasses="" - size="24" - status="[object Object]" - /> - </gllink-stub> + <gllink-stub + class="js-commit-pipeline" + data-original-title="Commit: failed" + href="https://test.com/pipeline" + title="" + > + <ciicon-stub + aria-label="Commit: failed" + cssclasses="" + size="24" + status="[object Object]" + /> + </gllink-stub> + </div> <div class="commit-sha-group d-flex" @@ -165,19 +169,23 @@ exports[`Repository last commit component renders the signature HTML as returned </button> </div> - <gllink-stub - class="js-commit-pipeline" - data-original-title="Commit: failed" - href="https://test.com/pipeline" - title="" + <div + class="ci-status-link" > - <ciicon-stub - aria-label="Commit: failed" - cssclasses="" - size="24" - status="[object Object]" - /> - </gllink-stub> + <gllink-stub + class="js-commit-pipeline" + data-original-title="Commit: failed" + href="https://test.com/pipeline" + title="" + > + <ciicon-stub + aria-label="Commit: failed" + cssclasses="" + size="24" + status="[object Object]" + /> + </gllink-stub> + </div> <div class="commit-sha-group d-flex" diff --git a/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb b/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb index 88e5c101d32..1ddb468f6b7 100644 --- a/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb +++ b/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20191017045817_schedule_fix_gitlab_com_pages_access_level.rb') diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 66b65d8b6d8..96d81f4cc49 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do @@ -63,7 +65,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :redis } before do - build_trace_chunk.send(:unsafe_set_data!, 'Sample data in redis') + build_trace_chunk.send(:unsafe_set_data!, +'Sample data in redis') end it { is_expected.to eq('Sample data in redis') } @@ -71,7 +73,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do context 'when data_store is database' do let(:data_store) { :database } - let(:raw_data) { 'Sample data in database' } + let(:raw_data) { +'Sample data in database' } it { is_expected.to eq('Sample data in database') } end @@ -80,7 +82,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :fog } before do - build_trace_chunk.send(:unsafe_set_data!, 'Sample data in fog') + build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog') end it { is_expected.to eq('Sample data in fog') } @@ -90,7 +92,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do describe '#append' do subject { build_trace_chunk.append(new_data, offset) } - let(:new_data) { 'Sample new data' } + let(:new_data) { +'Sample new data' } let(:offset) { 0 } let(:merged_data) { data + new_data.to_s } @@ -143,7 +145,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when new_data is empty' do - let(:new_data) { '' } + let(:new_data) { +'' } it 'does not append' do subject @@ -172,7 +174,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do shared_examples_for 'Scheduling sidekiq worker to flush data to persist store' do context 'when new data fulfilled chunk size' do - let(:new_data) { 'a' * described_class::CHUNK_SIZE } + let(:new_data) { +'a' * described_class::CHUNK_SIZE } it 'schedules trace chunk flush worker' do expect(Ci::BuildTraceChunkFlushWorker).to receive(:perform_async).once @@ -194,7 +196,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do shared_examples_for 'Scheduling no sidekiq worker' do context 'when new data fulfilled chunk size' do - let(:new_data) { 'a' * described_class::CHUNK_SIZE } + let(:new_data) { +'a' * described_class::CHUNK_SIZE } it 'does not schedule trace chunk flush worker' do expect(Ci::BuildTraceChunkFlushWorker).not_to receive(:perform_async) @@ -219,7 +221,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :redis } context 'when there are no data' do - let(:data) { '' } + let(:data) { +'' } it 'has no data' do expect(build_trace_chunk.data).to be_empty @@ -230,7 +232,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when there are some data' do - let(:data) { 'Sample data in redis' } + let(:data) { +'Sample data in redis' } before do build_trace_chunk.send(:unsafe_set_data!, data) @@ -249,7 +251,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :database } context 'when there are no data' do - let(:data) { '' } + let(:data) { +'' } it 'has no data' do expect(build_trace_chunk.data).to be_empty @@ -260,7 +262,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when there are some data' do - let(:raw_data) { 'Sample data in database' } + let(:raw_data) { +'Sample data in database' } let(:data) { raw_data } it 'has data' do @@ -276,7 +278,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :fog } context 'when there are no data' do - let(:data) { '' } + let(:data) { +'' } it 'has no data' do expect(build_trace_chunk.data).to be_empty @@ -287,7 +289,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when there are some data' do - let(:data) { 'Sample data in fog' } + let(:data) { +'Sample data in fog' } before do build_trace_chunk.send(:unsafe_set_data!, data) @@ -332,7 +334,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do context 'when data_store is redis' do let(:data_store) { :redis } - let(:data) { 'Sample data in redis' } + let(:data) { +'Sample data in redis' } before do build_trace_chunk.send(:unsafe_set_data!, data) @@ -343,7 +345,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do context 'when data_store is database' do let(:data_store) { :database } - let(:raw_data) { 'Sample data in database' } + let(:raw_data) { +'Sample data in database' } let(:data) { raw_data } it_behaves_like 'truncates' @@ -351,7 +353,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do context 'when data_store is fog' do let(:data_store) { :fog } - let(:data) { 'Sample data in fog' } + let(:data) { +'Sample data in fog' } before do build_trace_chunk.send(:unsafe_set_data!, data) @@ -368,7 +370,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :redis } context 'when data exists' do - let(:data) { 'Sample data in redis' } + let(:data) { +'Sample data in redis' } before do build_trace_chunk.send(:unsafe_set_data!, data) @@ -386,7 +388,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :database } context 'when data exists' do - let(:raw_data) { 'Sample data in database' } + let(:raw_data) { +'Sample data in database' } let(:data) { raw_data } it { is_expected.to eq(data.bytesize) } @@ -401,7 +403,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let(:data_store) { :fog } context 'when data exists' do - let(:data) { 'Sample data in fog' } + let(:data) { +'Sample data in fog' } let(:key) { "tmp/builds/#{build.id}/chunks/#{chunk_index}.log" } before do @@ -443,7 +445,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when data size reached CHUNK_SIZE' do - let(:data) { 'a' * described_class::CHUNK_SIZE } + let(:data) { +'a' * described_class::CHUNK_SIZE } it 'persists the data' do expect(build_trace_chunk.redis?).to be_truthy @@ -463,7 +465,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when data size has not reached CHUNK_SIZE' do - let(:data) { 'Sample data in redis' } + let(:data) { +'Sample data in redis' } it 'does not persist the data and the orignal data is intact' do expect { subject }.to raise_error(described_class::FailedToPersistDataError) @@ -492,7 +494,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when data size reached CHUNK_SIZE' do - let(:data) { 'a' * described_class::CHUNK_SIZE } + let(:data) { +'a' * described_class::CHUNK_SIZE } it 'persists the data' do expect(build_trace_chunk.database?).to be_truthy @@ -512,7 +514,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when data size has not reached CHUNK_SIZE' do - let(:data) { 'Sample data in database' } + let(:data) { +'Sample data in database' } it 'does not persist the data and the orignal data is intact' do expect { subject }.to raise_error(described_class::FailedToPersistDataError) @@ -561,7 +563,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end context 'when data size has not reached CHUNK_SIZE' do - let(:data) { 'Sample data in fog' } + let(:data) { +'Sample data in fog' } it 'does not raise error' do expect { subject }.not_to raise_error diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index c2c57379461..8a3a7eee25d 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -152,6 +152,16 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do end end + describe '.for_project_namespace' do + subject { described_class.for_project_namespace(namespace_id) } + + let!(:cluster) { create(:cluster, :project) } + let!(:another_cluster) { create(:cluster, :project) } + let(:namespace_id) { cluster.first_project.namespace_id } + + it { is_expected.to contain_exactly(cluster) } + end + describe 'validations' do subject { cluster.valid? } diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb index fc35b8257e9..1957e1fc5ee 100644 --- a/spec/models/clusters/clusters_hierarchy_spec.rb +++ b/spec/models/clusters/clusters_hierarchy_spec.rb @@ -42,6 +42,28 @@ describe Clusters::ClustersHierarchy do it 'returns clusters for project' do expect(base_and_ancestors(cluster.project)).to eq([cluster]) end + + context 'cluster has management project' do + let(:management_project) { create(:project, namespace: cluster.first_project.namespace) } + + before do + cluster.update!(management_project: management_project) + end + + context 'management_project is in same namespace as cluster' do + it 'returns cluster for management_project' do + expect(base_and_ancestors(management_project)).to eq([cluster]) + end + end + + context 'management_project is in a different namespace from cluster' do + let(:management_project) { create(:project) } + + it 'returns nothing' do + expect(base_and_ancestors(management_project)).to be_empty + end + end + end end context 'cluster has management project' do @@ -50,16 +72,12 @@ describe Clusters::ClustersHierarchy do let(:group) { create(:group) } let(:project) { create(:project, group: group) } - let(:management_project) { create(:project) } + let(:management_project) { create(:project, group: group) } it 'returns clusters for management_project' do expect(base_and_ancestors(management_project)).to eq([group_cluster]) end - it 'returns nothing if include_management_project is false' do - expect(base_and_ancestors(management_project, include_management_project: false)).to be_empty - end - it 'returns clusters for project' do expect(base_and_ancestors(project)).to eq([project_cluster, group_cluster]) end @@ -70,17 +88,21 @@ describe Clusters::ClustersHierarchy do end context 'project in nested group with clusters at some levels' do - let!(:child) { create(:cluster, :group, groups: [child_group], management_project: management_project) } - let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) } + let!(:child) { create(:cluster, :group, groups: [child_group]) } + let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group], management_project: management_project) } let(:ancestor_group) { create(:group) } let(:parent_group) { create(:group, parent: ancestor_group) } let(:child_group) { create(:group, parent: parent_group) } let(:project) { create(:project, group: child_group) } - let(:management_project) { create(:project) } + let(:management_project) { create(:project, group: child_group) } + + it 'returns clusters for management_project' do + expect(base_and_ancestors(management_project)).to eq([ancestor, child]) + end it 'returns clusters for management_project' do - expect(base_and_ancestors(management_project)).to eq([child]) + expect(base_and_ancestors(management_project, include_management_project: false)).to eq([child, ancestor]) end it 'returns clusters for project' do diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb index 2cac42f56ff..9164c3a75c5 100644 --- a/spec/models/concerns/deployment_platform_spec.rb +++ b/spec/models/concerns/deployment_platform_spec.rb @@ -13,7 +13,11 @@ describe DeploymentPlatform do end context 'when project is the cluster\'s management project ' do - let!(:cluster_with_management_project) { create(:cluster, :provided_by_user, management_project: project) } + let(:another_project) { create(:project, namespace: project.namespace) } + + let!(:cluster_with_management_project) do + create(:cluster, :provided_by_user, projects: [another_project], management_project: project) + end context 'cluster_management_project feature is enabled' do it 'returns the cluster with management project' do @@ -66,7 +70,11 @@ describe DeploymentPlatform do end context 'when project is the cluster\'s management project ' do - let!(:cluster_with_management_project) { create(:cluster, :provided_by_user, management_project: project) } + let(:another_project) { create(:project, namespace: project.namespace) } + + let!(:cluster_with_management_project) do + create(:cluster, :provided_by_user, projects: [another_project], management_project: project) + end context 'cluster_management_project feature is enabled' do it 'returns the cluster with management project' do diff --git a/spec/models/shard_spec.rb b/spec/models/shard_spec.rb index 83104711b55..4da86858b54 100644 --- a/spec/models/shard_spec.rb +++ b/spec/models/shard_spec.rb @@ -1,4 +1,5 @@ -# frozen_string_literals: true +# frozen_string_literal: true + require 'spec_helper' describe Shard do diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb index 46e3dd650cc..97465647a87 100644 --- a/spec/requests/api/group_clusters_spec.rb +++ b/spec/requests/api/group_clusters_spec.rb @@ -286,12 +286,15 @@ describe API::GroupClusters do let(:update_params) do { domain: domain, - platform_kubernetes_attributes: platform_kubernetes_attributes + platform_kubernetes_attributes: platform_kubernetes_attributes, + management_project_id: management_project_id } end let(:domain) { 'new-domain.com' } let(:platform_kubernetes_attributes) { {} } + let(:management_project) { create(:project, group: group) } + let(:management_project_id) { management_project.id } let(:cluster) do create(:cluster, :group, :provided_by_gcp, @@ -308,6 +311,8 @@ describe API::GroupClusters do context 'authorized user' do before do + management_project.add_maintainer(current_user) + put api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: update_params cluster.reload @@ -320,6 +325,7 @@ describe API::GroupClusters do it 'updates cluster attributes' do expect(cluster.domain).to eq('new-domain.com') + expect(cluster.management_project).to eq(management_project) end end @@ -332,6 +338,7 @@ describe API::GroupClusters do it 'does not update cluster attributes' do expect(cluster.domain).to eq('old-domain.com') + expect(cluster.management_project).to be_nil end it 'returns validation errors' do @@ -339,6 +346,18 @@ describe API::GroupClusters do end end + context 'current user does not have access to management_project_id' do + let(:management_project_id) { create(:project).id } + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'returns validation errors' do + expect(json_response['message']['management_project_id'].first).to match('don\'t have permission') + end + end + context 'with a GCP cluster' do context 'when user tries to change GCP specific fields' do let(:platform_kubernetes_attributes) do diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index 63b75b810dc..02d1e4bf2f1 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -316,6 +316,7 @@ describe API::Internal::Base do expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true') expect(user.reload.last_activity_on).to eql(Date.today) end end @@ -335,6 +336,7 @@ describe API::Internal::Base do expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true') expect(user.reload.last_activity_on).to be_nil end end @@ -576,6 +578,7 @@ describe API::Internal::Base do expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true') end end diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index a7b919de2ef..04e59238877 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -281,11 +281,14 @@ describe API::ProjectClusters do let(:api_url) { 'https://kubernetes.example.com' } let(:namespace) { 'new-namespace' } let(:platform_kubernetes_attributes) { { namespace: namespace } } + let(:management_project) { create(:project, namespace: project.namespace) } + let(:management_project_id) { management_project.id } let(:update_params) do { domain: 'new-domain.com', - platform_kubernetes_attributes: platform_kubernetes_attributes + platform_kubernetes_attributes: platform_kubernetes_attributes, + management_project_id: management_project_id } end @@ -310,6 +313,8 @@ describe API::ProjectClusters do context 'authorized user' do before do + management_project.add_maintainer(current_user) + put api("/projects/#{project.id}/clusters/#{cluster.id}", current_user), params: update_params cluster.reload @@ -323,6 +328,7 @@ describe API::ProjectClusters do it 'updates cluster attributes' do expect(cluster.domain).to eq('new-domain.com') expect(cluster.platform_kubernetes.namespace).to eq('new-namespace') + expect(cluster.management_project).to eq(management_project) end end @@ -336,6 +342,7 @@ describe API::ProjectClusters do it 'does not update cluster attributes' do expect(cluster.domain).not_to eq('new_domain.com') expect(cluster.platform_kubernetes.namespace).not_to eq('invalid_namespace') + expect(cluster.management_project).not_to eq(management_project) end it 'returns validation errors' do @@ -343,6 +350,18 @@ describe API::ProjectClusters do end end + context 'current user does not have access to management_project_id' do + let(:management_project_id) { create(:project).id } + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'returns validation errors' do + expect(json_response['message']['management_project_id'].first).to match('don\'t have permission') + end + end + context 'with a GCP cluster' do context 'when user tries to change GCP specific fields' do let(:platform_kubernetes_attributes) do diff --git a/spec/services/clusters/update_service_spec.rb b/spec/services/clusters/update_service_spec.rb index 3ee45375dca..8c2d8c9246e 100644 --- a/spec/services/clusters/update_service_spec.rb +++ b/spec/services/clusters/update_service_spec.rb @@ -90,5 +90,115 @@ describe Clusters::UpdateService do end end end + + context 'when params includes :management_project_id' do + context 'management_project is non-existent' do + let(:params) do + { management_project_id: 0 } + end + + it 'does not update management_project_id' do + is_expected.to eq(false) + + expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action') + + cluster.reload + expect(cluster.management_project_id).to be_nil + end + end + + shared_examples 'setting a management project' do + context 'user is authorized to adminster manangement_project' do + before do + management_project.add_maintainer(cluster.user) + end + + let(:params) do + { management_project_id: management_project.id } + end + + it 'updates management_project_id' do + is_expected.to eq(true) + + expect(cluster.management_project).to eq(management_project) + end + end + + context 'user is not authorized to adminster manangement_project' do + let(:params) do + { management_project_id: management_project.id } + end + + it 'does not update management_project_id' do + is_expected.to eq(false) + + expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action') + + cluster.reload + expect(cluster.management_project_id).to be_nil + end + end + end + + context 'project cluster' do + include_examples 'setting a management project' do + let(:management_project) { create(:project, namespace: cluster.first_project.namespace) } + end + + context 'manangement_project is outside of the namespace scope' do + before do + management_project.update(group: create(:group)) + end + + let(:params) do + { management_project_id: management_project.id } + end + + it 'does not update management_project_id' do + is_expected.to eq(false) + + expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action') + + cluster.reload + expect(cluster.management_project_id).to be_nil + end + end + end + + context 'group cluster' do + let(:cluster) { create(:cluster, :group) } + + include_examples 'setting a management project' do + let(:management_project) { create(:project, group: cluster.first_group) } + end + + context 'manangement_project is outside of the namespace scope' do + before do + management_project.update(group: create(:group)) + end + + let(:params) do + { management_project_id: management_project.id } + end + + it 'does not update management_project_id' do + is_expected.to eq(false) + + expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action') + + cluster.reload + expect(cluster.management_project_id).to be_nil + end + end + end + + context 'instance cluster' do + let(:cluster) { create(:cluster, :instance) } + + include_examples 'setting a management project' do + let(:management_project) { create(:project) } + end + end + end end end diff --git a/spec/sidekiq/cron/job_gem_dependency_spec.rb b/spec/sidekiq/cron/job_gem_dependency_spec.rb index 2e7de75fd08..20347b4d306 100644 --- a/spec/sidekiq/cron/job_gem_dependency_spec.rb +++ b/spec/sidekiq/cron/job_gem_dependency_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Sidekiq::Cron::Job do diff --git a/spec/support/helpers/smime_helper.rb b/spec/support/helpers/smime_helper.rb index 656b3e196ba..3ad19cd3da0 100644 --- a/spec/support/helpers/smime_helper.rb +++ b/spec/support/helpers/smime_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SmimeHelper include OpenSSL |