提交 1def0719 编写于 作者: G Grzegorz Bizon

Merge branch '55623-group-cluster-apis' into 'master'

Resolve "API support for group-level clusters"

Closes #55623

See merge request gitlab-org/gitlab-ce!30213
......@@ -2,11 +2,6 @@
module Clusters
class InstancePolicy < BasePolicy
include ClusterableActions
condition(:has_clusters, scope: :subject) { clusterable_has_clusters? }
condition(:can_have_multiple_clusters) { multiple_clusters_available? }
rule { admin }.policy do
enable :read_cluster
enable :add_cluster
......@@ -14,7 +9,5 @@ module Clusters
enable :update_cluster
enable :admin_cluster
end
rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster
end
end
# frozen_string_literal: true
module ClusterableActions
private
# Overridden on EE module
def multiple_clusters_available?
false
end
def clusterable_has_clusters?
!subject.clusters.empty?
end
end
# frozen_string_literal: true
class GroupPolicy < BasePolicy
include ClusterableActions
desc "Group is public"
with_options scope: :subject, score: 0
condition(:public_group) { @subject.public? }
......@@ -29,9 +27,6 @@ class GroupPolicy < BasePolicy
GroupProjectsFinder.new(group: @subject, current_user: @user, options: { include_subgroups: true, only_owned: true }).execute.any?
end
condition(:has_clusters, scope: :subject) { clusterable_has_clusters? }
condition(:can_have_multiple_clusters) { multiple_clusters_available? }
with_options scope: :subject, score: 0
condition(:request_access_enabled) { @subject.request_access_enabled }
......@@ -121,8 +116,6 @@ class GroupPolicy < BasePolicy
rule { owner & (~share_with_group_locked | ~has_parent | ~parent_share_with_group_locked | can_change_parent_share_with_group_lock) }.enable :change_share_with_group_lock
rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster
rule { developer & developer_maintainer_access }.enable :create_projects
rule { create_projects_disabled }.prevent :create_projects
......
......@@ -2,7 +2,6 @@
class ProjectPolicy < BasePolicy
extend ClassMethods
include ClusterableActions
READONLY_FEATURES_WHEN_ARCHIVED = %i[
issue
......@@ -114,9 +113,6 @@ class ProjectPolicy < BasePolicy
@subject.feature_available?(:merge_requests, @user)
end
condition(:has_clusters, scope: :subject) { clusterable_has_clusters? }
condition(:can_have_multiple_clusters) { multiple_clusters_available? }
condition(:internal_builds_disabled) do
!@subject.builds_enabled?
end
......@@ -430,8 +426,6 @@ class ProjectPolicy < BasePolicy
(~guest & can?(:read_project_for_iids) & merge_requests_visible_to_user) | can?(:read_merge_request)
end.enable :read_merge_request_iid
rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster
rule { ~can?(:read_cross_project) & ~classification_label_authorized }.policy do
# Preventing access here still allows the projects to be listed. Listing
# projects doesn't check the `:read_project` ability. But instead counts
......
......@@ -13,7 +13,8 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
end
def can_add_cluster?
can?(current_user, :add_cluster, clusterable)
can?(current_user, :add_cluster, clusterable) &&
(has_no_clusters? || multiple_clusters_available?)
end
def can_create_cluster?
......@@ -63,4 +64,15 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
def learn_more_link
raise NotImplementedError
end
private
# Overridden on EE module
def multiple_clusters_available?
false
end
def has_no_clusters?
clusterable.clusters.empty?
end
end
......@@ -10,24 +10,27 @@ module Clusters
def execute(access_token: nil)
raise ArgumentError, 'Unknown clusterable provided' unless clusterable
raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster?
cluster_params = params.merge(user: current_user).merge(clusterable_params)
cluster_params[:provider_gcp_attributes].try do |provider|
provider[:access_token] = access_token
end
create_cluster(cluster_params).tap do |cluster|
ClusterProvisionWorker.perform_async(cluster.id) if cluster.persisted?
cluster = Clusters::Cluster.new(cluster_params)
unless can_create_cluster?
cluster.errors.add(:base, _('Instance does not support multiple Kubernetes clusters'))
end
end
private
return cluster if cluster.errors.present?
def create_cluster(cluster_params)
Clusters::Cluster.create(cluster_params)
cluster.tap do |cluster|
cluster.save && ClusterProvisionWorker.perform_async(cluster.id)
end
end
private
def clusterable
@clusterable ||= params.delete(:clusterable)
end
......
---
title: Add API for CRUD group clusters
merge_request: 30213
author:
type: added
# Group clusters API
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/30213)
in GitLab 12.1.
NOTE: **Note:**
User will need at least maintainer access for the group to use these endpoints.
## List group clusters
Returns a list of group clusters.
```
GET /groups/:id/clusters
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
Example request:
```bash
curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters
```
Example response:
```json
[
{
"id":18,
"name":"cluster-1",
"domain":"example.com",
"created_at":"2019-01-02T20:18:12.563Z",
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
"cluster_type":"group_type",
"user":
{
"id":1,
"name":"Administrator",
"username":"root",
"state":"active",
"avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
"web_url":"https://gitlab.example.com/root"
},
"platform_kubernetes":
{
"api_url":"https://104.197.68.152",
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
}
},
{
"id":19,
"name":"cluster-2",
...
}
]
```
## Get a single group cluster
Gets a single group cluster.
```
GET /groups/:id/clusters/:cluster_id
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
```bash
curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/18
```
Example response:
```json
{
"id":18,
"name":"cluster-1",
"domain":"example.com",
"created_at":"2019-01-02T20:18:12.563Z",
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
"cluster_type":"group_type",
"user":
{
"id":1,
"name":"Administrator",
"username":"root",
"state":"active",
"avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
"web_url":"https://gitlab.example.com/root"
},
"platform_kubernetes":
{
"api_url":"https://104.197.68.152",
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
},
"group":
{
"id":26,
"name":"group-with-clusters-api",
"web_url":"https://gitlab.example.com/group-with-clusters-api"
}
}
```
## Add existing cluster to group
Adds an existing Kubernetes cluster to the group.
```
POST /groups/:id/clusters/user
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `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 |
| `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 |
| `platform_kubernetes_attributes[token]` | String | yes | 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 |
| `platform_kubernetes_attributes[authorization_type]` | String | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
| `environment_scope` | String | no | The associated environment to the cluster. Defaults to `*` **[PREMIUM]** |
Example request:
```bash
curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/user \
-H "Accept: application/json" \
-H "Content-Type:application/json" \
--request POST --data '{"name":"cluster-5", "platform_kubernetes_attributes":{"api_url":"https://35.111.51.20","token":"12345","ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"}}'
```
Example response:
```json
{
"id":24,
"name":"cluster-5",
"created_at":"2019-01-03T21:53:40.610Z",
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
"cluster_type":"group_type",
"user":
{
"id":1,
"name":"Administrator",
"username":"root",
"state":"active",
"avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
"web_url":"https://gitlab.example.com/root"
},
"platform_kubernetes":
{
"api_url":"https://35.111.51.20",
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
},
"group":
{
"id":26,
"name":"group-with-clusters-api",
"web_url":"https://gitlab.example.com/root/group-with-clusters-api"
}
}
```
## Edit group cluster
Updates an existing group cluster.
```
PUT /groups/:id/clusters/:cluster_id
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `cluster_id` | integer | yes | The ID of the cluster |
| `name` | String | no | The name of the cluster |
| `domain` | String | no | The [base domain](../user/group/clusters/index.md#base-domain) of 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 |
| `environment_scope` | String | no | The associated environment to the cluster **[PREMIUM]** |
NOTE: **Note:**
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
through the ["Add an existing Kubernetes Cluster"](../user/project/clusters/index.md#adding-an-existing-kubernetes-cluster) option or
through the ["Add existing cluster to group"](#add-existing-cluster-to-group) endpoint.
Example request:
```bash
curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/24 \
-H "Content-Type:application/json" \
--request PUT --data '{"name":"new-cluster-name","domain":"new-domain.com","api_url":"https://new-api-url.com"}'
```
Example response:
```json
{
"id":24,
"name":"new-cluster-name",
"domain":"new-domain.com",
"created_at":"2019-01-03T21:53:40.610Z",
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
"cluster_type":"group_type",
"user":
{
"id":1,
"name":"Administrator",
"username":"root",
"state":"active",
"avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
"web_url":"https://gitlab.example.com/root"
},
"platform_kubernetes":
{
"api_url":"https://new-api-url.com",
"authorization_type":"rbac",
"ca_cert":null
},
"group":
{
"id":26,
"name":"group-with-clusters-api",
"web_url":"https://gitlab.example.com/group-with-clusters-api"
}
}
```
## Delete group cluster
Deletes an existing group cluster.
```
DELETE /groups/:id/clusters/:cluster_id
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
```bash
curl --request DELETE --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/23'
```
......@@ -111,6 +111,7 @@ module API
mount ::API::Features
mount ::API::Files
mount ::API::GroupBoards
mount ::API::GroupClusters
mount ::API::GroupLabels
mount ::API::GroupMilestones
mount ::API::Groups
......
......@@ -1686,5 +1686,9 @@ module API
class ClusterProject < Cluster
expose :project, using: Entities::BasicProjectDetails
end
class ClusterGroup < Cluster
expose :group, using: Entities::BasicGroupDetails
end
end
end
# frozen_string_literal: true
module API
class GroupClusters < Grape::API
include PaginationParams
before { authenticate! }
# EE::API::GroupClusters will
# override these methods
helpers do
params :create_params_ee do
end
params :update_params_ee do
end
end
params do
requires :id, type: String, desc: 'The ID of the group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all clusters from the group' do
success Entities::Cluster
end
params do
use :pagination
end
get ':id/clusters' do
authorize! :read_cluster, user_group
present paginate(clusters_for_current_user), with: Entities::Cluster
end
desc 'Get specific cluster for the group' do
success Entities::ClusterGroup
end
params do
requires :cluster_id, type: Integer, desc: 'The cluster ID'
end
get ':id/clusters/:cluster_id' do
authorize! :read_cluster, cluster
present cluster, with: Entities::ClusterGroup
end
desc 'Adds an existing cluster' do
success Entities::ClusterGroup
end
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 :domain, type: String, desc: 'Cluster base domain'
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
requires :api_url, type: String, allow_blank: false, desc: 'URL to access the Kubernetes API'
requires :token, type: String, desc: 'Token to authenticate against Kubernetes'
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'
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
user_cluster = ::Clusters::CreateService
.new(current_user, create_cluster_user_params)
.execute
if user_cluster.persisted?
present user_cluster, with: Entities::ClusterGroup
else
render_validation_error!(user_cluster)
end
end
desc 'Update an existing cluster' do
success Entities::ClusterGroup
end
params do
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 :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'
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
update_service = Clusters::UpdateService.new(current_user, update_cluster_params)
if update_service.execute(cluster)
present cluster, with: Entities::ClusterGroup
else
render_validation_error!(cluster)
end
end
desc 'Remove a cluster' do
success Entities::ClusterGroup
end
params do
requires :cluster_id, type: Integer, desc: 'The Cluster ID'
end
delete ':id/clusters/:cluster_id' do
authorize! :admin_cluster, cluster
destroy_conditionally!(cluster)
end
end
helpers do
def clusters_for_current_user
@clusters_for_current_user ||= ClustersFinder.new(user_group, current_user, :all).execute
end
def cluster
@cluster ||= clusters_for_current_user.find(params[:cluster_id])
end
def create_cluster_user_params
declared_params.merge({
provider_type: :user,
platform_type: :kubernetes,
clusterable: user_group
})
end
def update_cluster_params
declared_params(include_missing: false).without(:cluster_id)
end
end
end
end
......@@ -65,7 +65,7 @@ module API
use :create_params_ee
end
post ':id/clusters/user' do
authorize! :add_cluster, user_project, 'Instance does not support multiple Kubernetes clusters'
authorize! :add_cluster, user_project
user_cluster = ::Clusters::CreateService
.new(current_user, create_cluster_user_params)
......
# frozen_string_literal: true
require 'spec_helper'
describe API::GroupClusters do
include KubernetesHelpers
let(:current_user) { create(:user) }
let(:developer_user) { create(:user) }
let(:group) { create(:group, :private) }
before do
group.add_developer(developer_user)
group.add_maintainer(current_user)
end
describe 'GET /groups/:id/clusters' do
let!(:extra_cluster) { create(:cluster, :provided_by_gcp, :group) }
let!(:clusters) do
create_list(:cluster, 5, :provided_by_gcp, :group, :production_environment,
groups: [group])
end
context 'non-authorized user' do
it 'responds with 403' do
get api("/groups/#{group.id}/clusters", developer_user)
expect(response).to have_gitlab_http_status(403)
end
end
context 'authorized user' do
before do
get api("/groups/#{group.id}/clusters", current_user)
end
it 'responds with 200' do
expect(response).to have_gitlab_http_status(200)
end
it 'includes pagination headers' do
expect(response).to include_pagination_headers
end
it 'only include authorized clusters' do
cluster_ids = json_response.map { |cluster| cluster['id'] }
expect(cluster_ids).to match_array(clusters.pluck(:id))
expect(cluster_ids).not_to include(extra_cluster.id)
end
end
end
describe 'GET /groups/:id/clusters/:cluster_id' do
let(:cluster_id) { cluster.id }
let(:platform_kubernetes) do
create(:cluster_platform_kubernetes, :configured)
end
let(:cluster) do
create(:cluster, :group, :provided_by_gcp, :with_domain,
platform_kubernetes: platform_kubernetes,
user: current_user,
groups: [group])
end
context 'non-authorized user' do
it 'responds with 403' do
get api("/groups/#{group.id}/clusters/#{cluster_id}", developer_user)
expect(response).to have_gitlab_http_status(403)
end
end
context 'authorized user' do
before do
get api("/groups/#{group.id}/clusters/#{cluster_id}", current_user)
end
it 'returns specific cluster' do
expect(json_response['id']).to eq(cluster.id)
end
it 'returns cluster information' do
expect(json_response['provider_type']).to eq('gcp')
expect(json_response['platform_type']).to eq('kubernetes')
expect(json_response['environment_scope']).to eq('*')
expect(json_response['cluster_type']).to eq('group_type')
expect(json_response['domain']).to eq('example.com')
end
it 'returns group information' do
cluster_group = json_response['group']
expect(cluster_group['id']).to eq(group.id)
expect(cluster_group['name']).to eq(group.name)
expect(cluster_group['web_url']).to eq(group.web_url)
end
it 'returns kubernetes platform information' do
platform = json_response['platform_kubernetes']
expect(platform['api_url']).to eq('https://kubernetes.example.com')
expect(platform['ca_cert']).to be_present
end
it 'returns user information' do
user = json_response['user']
expect(user['id']).to eq(current_user.id)
expect(user['username']).to eq(current_user.username)
end
it 'returns GCP provider information' do
gcp_provider = json_response['provider_gcp']
expect(gcp_provider['cluster_id']).to eq(cluster.id)
expect(gcp_provider['status_name']).to eq('created')
expect(gcp_provider['gcp_project_id']).to eq('test-gcp-project')
expect(gcp_provider['zone']).to eq('us-central1-a')
expect(gcp_provider['machine_type']).to eq('n1-standard-2')
expect(gcp_provider['num_nodes']).to eq(3)
expect(gcp_provider['endpoint']).to eq('111.111.111.111')
end
context 'when cluster has no provider' do
let(:cluster) do
create(:cluster, :group, :provided_by_user,
groups: [group])
end
it 'does not include GCP provider info' do
expect(json_response['provider_gcp']).not_to be_present
end
end
context 'with non-existing cluster' do
let(:cluster_id) { 123 }
it 'returns 404' do
expect(response).to have_gitlab_http_status(404)
end
end
end
end
shared_context 'kubernetes calls stubbed' do
before do
stub_kubeclient_discover(api_url)
end
end
describe 'POST /groups/:id/clusters/user' do
include_context 'kubernetes calls stubbed'
let(:api_url) { 'https://kubernetes.example.com' }
let(:authorization_type) { 'rbac' }
let(:platform_kubernetes_attributes) do
{
api_url: api_url,
token: 'sample-token',
authorization_type: authorization_type
}
end
let(:cluster_params) do
{
name: 'test-cluster',
domain: 'domain.example.com',
managed: false,
platform_kubernetes_attributes: platform_kubernetes_attributes
}
end
context 'non-authorized user' do
it 'responds with 403' do
post api("/groups/#{group.id}/clusters/user", developer_user), params: cluster_params
expect(response).to have_gitlab_http_status(403)
end
end
context 'authorized user' do
before do
post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params
end
context 'with valid params' do
it 'responds with 201' do
expect(response).to have_gitlab_http_status(201)
end
it 'creates a new Cluster::Cluster' do
cluster_result = Clusters::Cluster.find(json_response["id"])
platform_kubernetes = cluster_result.platform
expect(cluster_result).to be_user
expect(cluster_result).to be_kubernetes
expect(cluster_result.group).to eq(group)
expect(cluster_result.name).to eq('test-cluster')
expect(cluster_result.domain).to eq('domain.example.com')
expect(cluster_result.managed).to be_falsy
expect(platform_kubernetes.rbac?).to be_truthy
expect(platform_kubernetes.api_url).to eq(api_url)
expect(platform_kubernetes.token).to eq('sample-token')
end
end
context 'when user does not indicate authorization type' do
let(:platform_kubernetes_attributes) do
{
api_url: api_url,
token: 'sample-token'
}
end
it 'defaults to RBAC' do
cluster_result = Clusters::Cluster.find(json_response['id'])
expect(cluster_result.platform_kubernetes.rbac?).to be_truthy
end
end
context 'when user sets authorization type as ABAC' do
let(:authorization_type) { 'abac' }
it 'creates an ABAC cluster' do
cluster_result = Clusters::Cluster.find(json_response['id'])
expect(cluster_result.platform.abac?).to be_truthy
end
end
context 'with invalid params' do
let(:api_url) { 'invalid_api_url' }
it 'responds with 400' do
expect(response).to have_gitlab_http_status(400)
end
it 'does not create a new Clusters::Cluster' do
expect(group.reload.clusters).to be_empty
end
it 'returns validation errors' do
expect(json_response['message']['platform_kubernetes.api_url'].first).to be_present
end
end
end
context 'when user tries to add multiple clusters' do
before do
create(:cluster, :provided_by_gcp, :group,
groups: [group])
post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params
end
it 'responds with 400' do
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['base'].first).to include('Instance does not support multiple Kubernetes clusters')
end
end
context 'non-authorized user' do
before do
post api("/groups/#{group.id}/clusters/user", developer_user), params: cluster_params
end
it 'responds with 403' do
expect(response).to have_gitlab_http_status(403)
expect(json_response['message']).to eq('403 Forbidden')
end
end
end
describe 'PUT /groups/:id/clusters/:cluster_id' do
include_context 'kubernetes calls stubbed'
let(:api_url) { 'https://kubernetes.example.com' }
let(:update_params) do
{
domain: domain,
platform_kubernetes_attributes: platform_kubernetes_attributes
}
end
let(:domain) { 'new-domain.com' }
let(:platform_kubernetes_attributes) { {} }
let(:cluster) do
create(:cluster, :group, :provided_by_gcp,
groups: [group], domain: 'old-domain.com')
end
context 'non-authorized user' do
it 'responds with 403' do
put api("/groups/#{group.id}/clusters/#{cluster.id}", developer_user), params: update_params
expect(response).to have_gitlab_http_status(403)
end
end
context 'authorized user' do
before do
put api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: update_params
cluster.reload
end
context 'with valid params' do
it 'responds with 200' do
expect(response).to have_gitlab_http_status(200)
end
it 'updates cluster attributes' do
expect(cluster.domain).to eq('new-domain.com')
end
end
context 'with invalid params' do
let(:domain) { 'invalid domain' }
it 'responds with 400' do
expect(response).to have_gitlab_http_status(400)
end
it 'does not update cluster attributes' do
expect(cluster.domain).to eq('old-domain.com')
end
it 'returns validation errors' do
expect(json_response['message']['domain'].first).to match('contains invalid characters (valid characters: [a-z0-9\\-])')
end
end
context 'with a GCP cluster' do
context 'when user tries to change GCP specific fields' do
let(:platform_kubernetes_attributes) do
{
api_url: 'https://new-api-url.com',
token: 'new-sample-token'
}
end
it 'responds with 400' do
expect(response).to have_gitlab_http_status(400)
end
it 'returns validation error' do
expect(json_response['message']['platform_kubernetes.base'].first).to eq('Cannot modify managed Kubernetes cluster')
end
end
context 'when user tries to change domain' do
let(:domain) { 'new-domain.com' }
it 'responds with 200' do
expect(response).to have_gitlab_http_status(200)
end
end
end
context 'with an user cluster' do
let(:api_url) { 'https://new-api-url.com' }
let(:cluster) do
create(:cluster, :group, :provided_by_user,
groups: [group])
end
let(:platform_kubernetes_attributes) do
{
api_url: api_url,
token: 'new-sample-token'
}
end
let(:update_params) do
{
name: 'new-name',
platform_kubernetes_attributes: platform_kubernetes_attributes
}
end
it 'responds with 200' do
expect(response).to have_gitlab_http_status(200)
end
it 'updates platform kubernetes attributes' do
platform_kubernetes = cluster.platform_kubernetes
expect(cluster.name).to eq('new-name')
expect(platform_kubernetes.api_url).to eq('https://new-api-url.com')
expect(platform_kubernetes.token).to eq('new-sample-token')
end
end
context 'with a cluster that does not belong to user' do
let(:cluster) { create(:cluster, :group, :provided_by_user) }
it 'responds with 404' do
expect(response).to have_gitlab_http_status(404)
end
end
end
end
describe 'DELETE /groups/:id/clusters/:cluster_id' do
let(:cluster_params) { { cluster_id: cluster.id } }
let(:cluster) do
create(:cluster, :group, :provided_by_gcp,
groups: [group])
end
context 'non-authorized user' do
it 'responds with 403' do
delete api("/groups/#{group.id}/clusters/#{cluster.id}", developer_user), params: cluster_params
expect(response).to have_gitlab_http_status(403)
end
end
context 'authorized user' do
before do
delete api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: cluster_params
end
it 'responds with 204' do
expect(response).to have_gitlab_http_status(204)
end
it 'deletes the cluster' do
expect(Clusters::Cluster.exists?(id: cluster.id)).to be_falsy
end
context 'with a cluster that does not belong to user' do
let(:cluster) { create(:cluster, :group, :provided_by_user) }
it 'responds with 404' do
expect(response).to have_gitlab_http_status(404)
end
end
end
end
end
......@@ -257,12 +257,22 @@ describe API::ProjectClusters do
post api("/projects/#{project.id}/clusters/user", current_user), params: cluster_params
end
it 'responds with 400' do
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['base'].first).to eq('Instance does not support multiple Kubernetes clusters')
end
end
context 'non-authorized user' do
before do
post api("/projects/#{project.id}/clusters/user", developer_user), params: cluster_params
end
it 'responds with 403' do
expect(response).to have_gitlab_http_status(403)
end
it 'returns an appropriate message' do
expect(json_response['message']).to include('Instance does not support multiple Kubernetes clusters')
expect(json_response['message']).to eq('403 Forbidden')
end
end
end
......
......@@ -24,14 +24,6 @@ shared_examples 'clusterable policies' do
context 'with no clusters' do
it { expect_allowed(:add_cluster) }
end
context 'with an existing cluster' do
before do
cluster
end
it { expect_disallowed(:add_cluster) }
end
end
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册