summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-24 12:06:03 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-24 12:06:03 +0000
commit33813f993b49da58426d33a148ee70952e6835bb (patch)
treea8310742d6eb7e1dc83f72ceba1fefb3d5b8a030
parentdc0622dbe3cd552abca4107557c6c09edb23625c (diff)
downloadgitlab-ce-33813f993b49da58426d33a148ee70952e6835bb.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue28
-rw-r--r--app/assets/stylesheets/framework/memory_graph.scss6
-rw-r--r--app/controllers/clusters/clusters_controller.rb2
-rw-r--r--app/models/clusters/cluster.rb2
-rw-r--r--app/models/clusters/clusters_hierarchy.rb41
-rw-r--r--app/models/concerns/deployment_platform.rb2
-rw-r--r--app/services/clusters/update_service.rb50
-rw-r--r--changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml5
-rw-r--r--changelogs/unreleased/cluster_management_projects_updating.yml5
-rw-r--r--changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml5
-rw-r--r--doc/api/group_clusters.md32
-rw-r--r--doc/api/project_clusters.md32
-rw-r--r--doc/user/clusters/management_project.md14
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/group_clusters.rb1
-rw-r--r--lib/api/helpers/internal_helpers.rb3
-rw-r--r--lib/api/project_clusters.rb1
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap56
-rw-r--r--spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb50
-rw-r--r--spec/models/clusters/cluster_spec.rb10
-rw-r--r--spec/models/clusters/clusters_hierarchy_spec.rb40
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb12
-rw-r--r--spec/models/shard_spec.rb3
-rw-r--r--spec/requests/api/group_clusters_spec.rb21
-rw-r--r--spec/requests/api/internal/base_spec.rb3
-rw-r--r--spec/requests/api/project_clusters_spec.rb21
-rw-r--r--spec/services/clusters/update_service_spec.rb110
-rw-r--r--spec/sidekiq/cron/job_gem_dependency_spec.rb2
-rw-r--r--spec/support/helpers/smime_helper.rb2
32 files changed, 474 insertions, 93 deletions
diff --git a/VERSION b/VERSION
index 80212c6e1f0..4dd2ed8f250 100644
--- a/VERSION
+++ b/VERSION
@@ -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