Merge branch '29398-support-rbac-for-gitlab-provisioned-clusters' into 'master'
Support Kubernetes RBAC for GitLab Managed Apps for creating new clusters Closes #29398 See merge request gitlab-org/gitlab-ce!21401
This commit is contained in:
commit
5a8908bf58
30 changed files with 770 additions and 270 deletions
|
@ -141,7 +141,8 @@ class Projects::ClustersController < Projects::ApplicationController
|
||||||
:gcp_project_id,
|
:gcp_project_id,
|
||||||
:zone,
|
:zone,
|
||||||
:num_nodes,
|
:num_nodes,
|
||||||
:machine_type
|
:machine_type,
|
||||||
|
:legacy_abac
|
||||||
]).merge(
|
]).merge(
|
||||||
provider_type: :gcp,
|
provider_type: :gcp,
|
||||||
platform_type: :kubernetes
|
platform_type: :kubernetes
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
##
|
|
||||||
# TODO:
|
|
||||||
# Almost components in this class were copied from app/models/project_services/kubernetes_service.rb
|
|
||||||
# We should dry up those classes not to repeat the same code.
|
|
||||||
# Maybe we should have a special facility (e.g. lib/kubernetes_api) to maintain all Kubernetes API caller.
|
|
||||||
module Ci
|
|
||||||
class FetchKubernetesTokenService
|
|
||||||
attr_reader :api_url, :ca_pem, :username, :password
|
|
||||||
|
|
||||||
def initialize(api_url, ca_pem, username, password)
|
|
||||||
@api_url = api_url
|
|
||||||
@ca_pem = ca_pem
|
|
||||||
@username = username
|
|
||||||
@password = password
|
|
||||||
end
|
|
||||||
|
|
||||||
def execute
|
|
||||||
read_secrets.each do |secret|
|
|
||||||
name = secret.dig('metadata', 'name')
|
|
||||||
if /default-token/ =~ name
|
|
||||||
token_base64 = secret.dig('data', 'token')
|
|
||||||
return Base64.decode64(token_base64) if token_base64
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def read_secrets
|
|
||||||
kubeclient = build_kubeclient!
|
|
||||||
|
|
||||||
kubeclient.get_secrets.as_json
|
|
||||||
rescue Kubeclient::HttpError => err
|
|
||||||
raise err unless err.error_code == 404
|
|
||||||
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_kubeclient!(api_path: 'api', api_version: 'v1')
|
|
||||||
raise "Incomplete settings" unless api_url && username && password
|
|
||||||
|
|
||||||
::Kubeclient::Client.new(
|
|
||||||
join_api_url(api_path),
|
|
||||||
api_version,
|
|
||||||
auth_options: { username: username, password: password },
|
|
||||||
ssl_options: kubeclient_ssl_options,
|
|
||||||
http_proxy_uri: ENV['http_proxy']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def join_api_url(api_path)
|
|
||||||
url = URI.parse(api_url)
|
|
||||||
prefix = url.path.sub(%r{/+\z}, '')
|
|
||||||
|
|
||||||
url.path = [prefix, api_path].join("/")
|
|
||||||
|
|
||||||
url.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def kubeclient_ssl_options
|
|
||||||
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
|
|
||||||
|
|
||||||
if ca_pem.present?
|
|
||||||
opts[:cert_store] = OpenSSL::X509::Store.new
|
|
||||||
opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
|
|
||||||
end
|
|
||||||
|
|
||||||
opts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -9,17 +9,24 @@ module Clusters
|
||||||
@provider = provider
|
@provider = provider
|
||||||
|
|
||||||
configure_provider
|
configure_provider
|
||||||
|
create_gitlab_service_account!
|
||||||
configure_kubernetes
|
configure_kubernetes
|
||||||
|
|
||||||
cluster.save!
|
cluster.save!
|
||||||
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
|
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
|
||||||
provider.make_errored!("Failed to request to CloudPlatform; #{e.message}")
|
provider.make_errored!("Failed to request to CloudPlatform; #{e.message}")
|
||||||
|
rescue Kubeclient::HttpError => e
|
||||||
|
provider.make_errored!("Failed to run Kubeclient: #{e.message}")
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
provider.make_errored!("Failed to configure Google Kubernetes Engine Cluster: #{e.message}")
|
provider.make_errored!("Failed to configure Google Kubernetes Engine Cluster: #{e.message}")
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def create_gitlab_service_account!
|
||||||
|
Clusters::Gcp::Kubernetes::CreateServiceAccountService.new(kube_client, rbac: create_rbac_cluster?).execute
|
||||||
|
end
|
||||||
|
|
||||||
def configure_provider
|
def configure_provider
|
||||||
provider.endpoint = gke_cluster.endpoint
|
provider.endpoint = gke_cluster.endpoint
|
||||||
provider.status_event = :make_created
|
provider.status_event = :make_created
|
||||||
|
@ -32,15 +39,54 @@ module Clusters
|
||||||
ca_cert: Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
|
ca_cert: Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
|
||||||
username: gke_cluster.master_auth.username,
|
username: gke_cluster.master_auth.username,
|
||||||
password: gke_cluster.master_auth.password,
|
password: gke_cluster.master_auth.password,
|
||||||
|
authorization_type: authorization_type,
|
||||||
token: request_kubernetes_token)
|
token: request_kubernetes_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_kubernetes_token
|
def request_kubernetes_token
|
||||||
Ci::FetchKubernetesTokenService.new(
|
Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(kube_client).execute
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorization_type
|
||||||
|
create_rbac_cluster? ? 'rbac' : 'abac'
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_rbac_cluster?
|
||||||
|
!provider.legacy_abac?
|
||||||
|
end
|
||||||
|
|
||||||
|
def kube_client
|
||||||
|
@kube_client ||= build_kube_client!(
|
||||||
'https://' + gke_cluster.endpoint,
|
'https://' + gke_cluster.endpoint,
|
||||||
Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
|
Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
|
||||||
gke_cluster.master_auth.username,
|
gke_cluster.master_auth.username,
|
||||||
gke_cluster.master_auth.password).execute
|
gke_cluster.master_auth.password,
|
||||||
|
api_groups: ['api', 'apis/rbac.authorization.k8s.io']
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_kube_client!(api_url, ca_pem, username, password, api_groups: ['api'], api_version: 'v1')
|
||||||
|
raise "Incomplete settings" unless api_url && username && password
|
||||||
|
|
||||||
|
Gitlab::Kubernetes::KubeClient.new(
|
||||||
|
api_url,
|
||||||
|
api_groups,
|
||||||
|
api_version,
|
||||||
|
auth_options: { username: username, password: password },
|
||||||
|
ssl_options: kubeclient_ssl_options(ca_pem),
|
||||||
|
http_proxy_uri: ENV['http_proxy']
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def kubeclient_ssl_options(ca_pem)
|
||||||
|
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
|
||||||
|
|
||||||
|
if ca_pem.present?
|
||||||
|
opts[:cert_store] = OpenSSL::X509::Store.new
|
||||||
|
opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
|
||||||
|
end
|
||||||
|
|
||||||
|
opts
|
||||||
end
|
end
|
||||||
|
|
||||||
def gke_cluster
|
def gke_cluster
|
||||||
|
|
13
app/services/clusters/gcp/kubernetes.rb
Normal file
13
app/services/clusters/gcp/kubernetes.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Clusters
|
||||||
|
module Gcp
|
||||||
|
module Kubernetes
|
||||||
|
SERVICE_ACCOUNT_NAME = 'gitlab'
|
||||||
|
SERVICE_ACCOUNT_NAMESPACE = 'default'
|
||||||
|
SERVICE_ACCOUNT_TOKEN_NAME = 'gitlab-token'
|
||||||
|
CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin'
|
||||||
|
CLUSTER_ROLE_NAME = 'cluster-admin'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,51 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Clusters
|
||||||
|
module Gcp
|
||||||
|
module Kubernetes
|
||||||
|
class CreateServiceAccountService
|
||||||
|
attr_reader :kubeclient, :rbac
|
||||||
|
|
||||||
|
def initialize(kubeclient, rbac:)
|
||||||
|
@kubeclient = kubeclient
|
||||||
|
@rbac = rbac
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
kubeclient.create_service_account(service_account_resource)
|
||||||
|
kubeclient.create_secret(service_account_token_resource)
|
||||||
|
kubeclient.create_cluster_role_binding(cluster_role_binding_resource) if rbac
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def service_account_resource
|
||||||
|
Gitlab::Kubernetes::ServiceAccount.new(service_account_name, service_account_namespace).generate
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_account_token_resource
|
||||||
|
Gitlab::Kubernetes::ServiceAccountToken.new(
|
||||||
|
SERVICE_ACCOUNT_TOKEN_NAME, service_account_name, service_account_namespace).generate
|
||||||
|
end
|
||||||
|
|
||||||
|
def cluster_role_binding_resource
|
||||||
|
subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: service_account_namespace }]
|
||||||
|
|
||||||
|
Gitlab::Kubernetes::ClusterRoleBinding.new(
|
||||||
|
CLUSTER_ROLE_BINDING_NAME,
|
||||||
|
CLUSTER_ROLE_NAME,
|
||||||
|
subjects
|
||||||
|
).generate
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_account_name
|
||||||
|
SERVICE_ACCOUNT_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_account_namespace
|
||||||
|
SERVICE_ACCOUNT_NAMESPACE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Clusters
|
||||||
|
module Gcp
|
||||||
|
module Kubernetes
|
||||||
|
class FetchKubernetesTokenService
|
||||||
|
attr_reader :kubeclient
|
||||||
|
|
||||||
|
def initialize(kubeclient)
|
||||||
|
@kubeclient = kubeclient
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
token_base64 = get_secret&.dig('data', 'token')
|
||||||
|
Base64.decode64(token_base64) if token_base64
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def get_secret
|
||||||
|
kubeclient.get_secret(SERVICE_ACCOUNT_TOKEN_NAME, SERVICE_ACCOUNT_NAMESPACE).as_json
|
||||||
|
rescue Kubeclient::HttpError => err
|
||||||
|
raise err unless err.error_code == 404
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,7 +27,9 @@ module Clusters
|
||||||
provider.zone,
|
provider.zone,
|
||||||
provider.cluster.name,
|
provider.cluster.name,
|
||||||
provider.num_nodes,
|
provider.num_nodes,
|
||||||
machine_type: provider.machine_type)
|
machine_type: provider.machine_type,
|
||||||
|
legacy_abac: provider.legacy_abac
|
||||||
|
)
|
||||||
|
|
||||||
unless operation.status == 'PENDING' || operation.status == 'RUNNING'
|
unless operation.status == 'PENDING' || operation.status == 'RUNNING'
|
||||||
return provider.make_errored!("Operation status is unexpected; #{operation.status_message}")
|
return provider.make_errored!("Operation status is unexpected; #{operation.status_message}")
|
||||||
|
|
|
@ -61,5 +61,15 @@
|
||||||
%p.form-text.text-muted
|
%p.form-text.text-muted
|
||||||
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
|
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
|
||||||
|
|
||||||
|
- if rbac_clusters_feature_enabled?
|
||||||
|
.form-group
|
||||||
|
.form-check
|
||||||
|
= provider_gcp_field.check_box :legacy_abac, { class: 'form-check-input' }, false, true
|
||||||
|
= provider_gcp_field.label :legacy_abac, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
|
||||||
|
.form-text.text-muted
|
||||||
|
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
|
||||||
|
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
|
||||||
|
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-experimental-support'), target: '_blank'
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
|
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
|
||||||
|
|
|
@ -37,5 +37,14 @@
|
||||||
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
|
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
|
||||||
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
||||||
|
|
||||||
|
- if rbac_clusters_feature_enabled?
|
||||||
|
.form-group
|
||||||
|
.form-check
|
||||||
|
= platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
|
||||||
|
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
|
||||||
|
.form-text.text-muted
|
||||||
|
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
|
||||||
|
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
|
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
.form-text.text-muted
|
.form-text.text-muted
|
||||||
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
|
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
|
||||||
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
|
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
|
||||||
|
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-experimental-support'), target: '_blank'
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
|
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Support Kubernetes RBAC for GitLab Managed Apps when creating new clusters
|
||||||
|
merge_request: 21401
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddLegacyAbacToClusterProvidersGcp < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column_with_default(:cluster_providers_gcp, :legacy_abac, :boolean, default: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column(:cluster_providers_gcp, :legacy_abac)
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20180906101639) do
|
ActiveRecord::Schema.define(version: 20180907015926) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -620,6 +620,7 @@ ActiveRecord::Schema.define(version: 20180906101639) do
|
||||||
t.string "endpoint"
|
t.string "endpoint"
|
||||||
t.text "encrypted_access_token"
|
t.text "encrypted_access_token"
|
||||||
t.string "encrypted_access_token_iv"
|
t.string "encrypted_access_token_iv"
|
||||||
|
t.boolean "legacy_abac", default: true, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "cluster_providers_gcp", ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true, using: :btree
|
add_index "cluster_providers_gcp", ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true, using: :btree
|
||||||
|
|
|
@ -127,8 +127,81 @@ applications running on the cluster.
|
||||||
When GitLab creates the cluster, it enables and uses the legacy
|
When GitLab creates the cluster, it enables and uses the legacy
|
||||||
[Attribute-based access control (ABAC)](https://kubernetes.io/docs/admin/authorization/abac/).
|
[Attribute-based access control (ABAC)](https://kubernetes.io/docs/admin/authorization/abac/).
|
||||||
The newer [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/)
|
The newer [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/)
|
||||||
authorization will be supported in a
|
authorization is [experimental](#role-based-access-control-rbac).
|
||||||
[future release](https://gitlab.com/gitlab-org/gitlab-ce/issues/29398).
|
|
||||||
|
### Role-based access control (RBAC) **[CORE ONLY]**
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21401) in GitLab 11.4.
|
||||||
|
|
||||||
|
CAUTION: **Warning:**
|
||||||
|
The RBAC authorization is experimental. To enable it you need access to the
|
||||||
|
server where GitLab is installed.
|
||||||
|
|
||||||
|
The support for RBAC-enabled clusters is hidden behind a feature flag. Once
|
||||||
|
the feature flag is enabled, GitLab will create the necessary service accounts
|
||||||
|
and privileges in order to install and run [GitLab managed applications](#installing-applications).
|
||||||
|
|
||||||
|
To enable the feature flag:
|
||||||
|
|
||||||
|
1. SSH into the server where GitLab is installed.
|
||||||
|
1. Enter the Rails console:
|
||||||
|
|
||||||
|
**For Omnibus GitLab**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo gitlab-rails console
|
||||||
|
```
|
||||||
|
|
||||||
|
**For installations from source**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo -u git -H bundle exec rails console
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Enable the RBAC authorization:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Feature.enable('rbac_clusters')
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are creating a [new GKE cluster via
|
||||||
|
GitLab](#adding-and-creating-a-new-gke-cluster-via-gitlab), you will be
|
||||||
|
asked if you would like to create an RBAC-enabled cluster. Enabling this
|
||||||
|
setting will create a `gitlab` service account which will be used by
|
||||||
|
GitLab to manage the newly created cluster. To enable this, this service
|
||||||
|
account will have the `cluster-admin` privilege.
|
||||||
|
|
||||||
|
If you are [adding an existing Kubernetes
|
||||||
|
cluster](#adding-an-existing-kubernetes-cluster), you will be asked if
|
||||||
|
the cluster you are adding is a RBAC-enabled cluster. Ensure the
|
||||||
|
token of the account has administrator privileges for the cluster.
|
||||||
|
|
||||||
|
In both cases above, when you install Helm Tiller into your cluster, an
|
||||||
|
RBAC-enabled cluster will create a `tiller` service account, with `cluster-admin`
|
||||||
|
privileges in the `gitlab-managed-apps` namespace. This service account will be
|
||||||
|
added to the installed Helm Tiller and will be used by Helm to install and run
|
||||||
|
[GitLab managed applications](#installing-applications).
|
||||||
|
|
||||||
|
The table below summarizes which resources will be created in a
|
||||||
|
RBAC-enabled cluster :
|
||||||
|
|
||||||
|
| Name | Kind | Details | Created when |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster |
|
||||||
|
| `gitlab-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Creating a new GKE Cluster |
|
||||||
|
| `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster |
|
||||||
|
| `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller |
|
||||||
|
| `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller |
|
||||||
|
|
||||||
|
|
||||||
|
Helm Tiller will also create additional service accounts and other RBAC
|
||||||
|
resources for each installed application. Consult the documentation for the
|
||||||
|
Helm charts for each application for details.
|
||||||
|
|
||||||
|
NOTE: **Note:**
|
||||||
|
Auto DevOps will not successfully complete in a cluster that only has RBAC
|
||||||
|
authorization enabled. RBAC support for Auto DevOps is planned in a
|
||||||
|
[future release](https://gitlab.com/gitlab-org/gitlab-ce/issues/44597).
|
||||||
|
|
||||||
### Security of GitLab Runners
|
### Security of GitLab Runners
|
||||||
|
|
||||||
|
@ -161,13 +234,13 @@ with Tiller already installed, you should be careful as GitLab cannot
|
||||||
detect it. By installing it via the applications will result into having it
|
detect it. By installing it via the applications will result into having it
|
||||||
twice, which can lead to confusion during deployments.
|
twice, which can lead to confusion during deployments.
|
||||||
|
|
||||||
| Application | GitLab version | Description |
|
| Application | GitLab version | Description | Helm Chart |
|
||||||
| ----------- | :------------: | ----------- |
|
| ----------- | :------------: | ----------- | --------------- |
|
||||||
| [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. |
|
| [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. | n/a |
|
||||||
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. |
|
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
|
||||||
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. |
|
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
|
||||||
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. |
|
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
|
||||||
| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. |
|
| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
|
||||||
|
|
||||||
## Getting the external IP address
|
## Getting the external IP address
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,14 @@ module Gitlab
|
||||||
:get_config_map,
|
:get_config_map,
|
||||||
:get_namespace,
|
:get_namespace,
|
||||||
:get_pod,
|
:get_pod,
|
||||||
|
:get_secret,
|
||||||
:get_service,
|
:get_service,
|
||||||
:get_service_account,
|
:get_service_account,
|
||||||
:delete_pod,
|
:delete_pod,
|
||||||
:create_config_map,
|
:create_config_map,
|
||||||
:create_namespace,
|
:create_namespace,
|
||||||
:create_pod,
|
:create_pod,
|
||||||
|
:create_secret,
|
||||||
:create_service_account,
|
:create_service_account,
|
||||||
:update_config_map,
|
:update_config_map,
|
||||||
:update_service_account,
|
:update_service_account,
|
||||||
|
|
36
lib/gitlab/kubernetes/service_account_token.rb
Normal file
36
lib/gitlab/kubernetes/service_account_token.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
module Kubernetes
|
||||||
|
class ServiceAccountToken
|
||||||
|
attr_reader :name, :service_account_name, :namespace_name
|
||||||
|
|
||||||
|
def initialize(name, service_account_name, namespace_name)
|
||||||
|
@name = name
|
||||||
|
@service_account_name = service_account_name
|
||||||
|
@namespace_name = namespace_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate
|
||||||
|
::Kubeclient::Resource.new(metadata: metadata, type: service_acount_token_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# as per https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#to-create-additional-api-tokens
|
||||||
|
def service_acount_token_type
|
||||||
|
'kubernetes.io/service-account-token'
|
||||||
|
end
|
||||||
|
|
||||||
|
def metadata
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
namespace: namespace_name,
|
||||||
|
annotations: {
|
||||||
|
"kubernetes.io/service-account.name": service_account_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -50,7 +50,7 @@ module GoogleApi
|
||||||
service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header)
|
service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header)
|
||||||
end
|
end
|
||||||
|
|
||||||
def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:)
|
def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:, legacy_abac:)
|
||||||
service = Google::Apis::ContainerV1::ContainerService.new
|
service = Google::Apis::ContainerV1::ContainerService.new
|
||||||
service.authorization = access_token
|
service.authorization = access_token
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ module GoogleApi
|
||||||
"machine_type": machine_type
|
"machine_type": machine_type
|
||||||
},
|
},
|
||||||
"legacy_abac": {
|
"legacy_abac": {
|
||||||
"enabled": true
|
"enabled": legacy_abac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,12 +170,14 @@ describe Projects::ClustersController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST create for new cluster' do
|
describe 'POST create for new cluster' do
|
||||||
|
let(:legacy_abac_param) { 'true' }
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
cluster: {
|
cluster: {
|
||||||
name: 'new-cluster',
|
name: 'new-cluster',
|
||||||
provider_gcp_attributes: {
|
provider_gcp_attributes: {
|
||||||
gcp_project_id: 'gcp-project-12345'
|
gcp_project_id: 'gcp-project-12345',
|
||||||
|
legacy_abac: legacy_abac_param
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,6 +203,18 @@ describe Projects::ClustersController do
|
||||||
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
|
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
|
||||||
expect(project.clusters.first).to be_gcp
|
expect(project.clusters.first).to be_gcp
|
||||||
expect(project.clusters.first).to be_kubernetes
|
expect(project.clusters.first).to be_kubernetes
|
||||||
|
expect(project.clusters.first.provider_gcp).to be_legacy_abac
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when legacy_abac param is false' do
|
||||||
|
let(:legacy_abac_param) { 'false' }
|
||||||
|
|
||||||
|
it 'creates a new cluster with legacy_abac_disabled' do
|
||||||
|
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||||
|
expect { go }.to change { Clusters::Cluster.count }
|
||||||
|
.and change { Clusters::Providers::Gcp.count }
|
||||||
|
expect(project.clusters.first.provider_gcp).not_to be_legacy_abac
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,32 @@ describe 'Gcp Cluster', :js do
|
||||||
context 'when user filled form with valid parameters' do
|
context 'when user filled form with valid parameters' do
|
||||||
subject { click_button 'Create Kubernetes cluster' }
|
subject { click_button 'Create Kubernetes cluster' }
|
||||||
|
|
||||||
|
shared_examples 'valid cluster gcp form' do
|
||||||
|
it 'users sees a form with the GCP token' do
|
||||||
|
expect(page).to have_selector(:css, 'form[data-token="token"]')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'user sees a cluster details page and creation status' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
|
||||||
|
|
||||||
|
Clusters::Cluster.last.provider.make_created!
|
||||||
|
|
||||||
|
expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'user sees a error if something wrong during creation' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
|
||||||
|
|
||||||
|
Clusters::Cluster.last.provider.make_errored!('Something wrong!')
|
||||||
|
|
||||||
|
expect(page).to have_content('Something wrong!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
||||||
.to receive(:projects_zones_clusters_create) do
|
.to receive(:projects_zones_clusters_create) do
|
||||||
|
@ -56,28 +82,16 @@ describe 'Gcp Cluster', :js do
|
||||||
fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
|
fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'users sees a form with the GCP token' do
|
it_behaves_like 'valid cluster gcp form'
|
||||||
expect(page).to have_selector(:css, 'form[data-token="token"]')
|
|
||||||
|
context 'rbac_clusters feature flag is enabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(rbac_clusters: true)
|
||||||
|
|
||||||
|
check 'cluster_provider_gcp_attributes_legacy_abac'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'user sees a cluster details page and creation status' do
|
it_behaves_like 'valid cluster gcp form'
|
||||||
subject
|
|
||||||
|
|
||||||
expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
|
|
||||||
|
|
||||||
Clusters::Cluster.last.provider.make_created!
|
|
||||||
|
|
||||||
expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'user sees a error if something wrong during creation' do
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
|
|
||||||
|
|
||||||
Clusters::Cluster.last.provider.make_errored!('Something wrong!')
|
|
||||||
|
|
||||||
expect(page).to have_content('Something wrong!')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,14 +21,10 @@ describe 'User Cluster', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user filled form with valid parameters' do
|
context 'when user filled form with valid parameters' do
|
||||||
before do
|
shared_examples 'valid cluster user form' do
|
||||||
fill_in 'cluster_name', with: 'dev-cluster'
|
|
||||||
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
|
|
||||||
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token'
|
|
||||||
click_button 'Add Kubernetes cluster'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'user sees a cluster details page' do
|
it 'user sees a cluster details page' do
|
||||||
|
subject
|
||||||
|
|
||||||
expect(page).to have_content('Kubernetes cluster integration')
|
expect(page).to have_content('Kubernetes cluster integration')
|
||||||
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
|
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
|
||||||
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
|
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
|
||||||
|
@ -38,27 +34,32 @@ describe 'User Cluster', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
fill_in 'cluster_name', with: 'dev-cluster'
|
||||||
|
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
|
||||||
|
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token'
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { click_button 'Add Kubernetes cluster' }
|
||||||
|
|
||||||
|
it_behaves_like 'valid cluster user form'
|
||||||
|
|
||||||
context 'rbac_clusters feature flag is enabled' do
|
context 'rbac_clusters feature flag is enabled' do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(rbac_clusters: true)
|
stub_feature_flags(rbac_clusters: true)
|
||||||
|
|
||||||
fill_in 'cluster_name', with: 'dev-cluster'
|
|
||||||
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
|
|
||||||
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token'
|
|
||||||
check 'cluster_platform_kubernetes_attributes_authorization_type'
|
check 'cluster_platform_kubernetes_attributes_authorization_type'
|
||||||
click_button 'Add Kubernetes cluster'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'user sees a cluster details page' do
|
it_behaves_like 'valid cluster user form'
|
||||||
expect(page).to have_content('Kubernetes cluster integration')
|
|
||||||
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
|
it 'user sees a cluster details page with RBAC enabled' do
|
||||||
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
|
subject
|
||||||
.to have_content('http://example.com')
|
|
||||||
expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
|
|
||||||
.to have_content('my-token')
|
|
||||||
expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked
|
expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when user filled form with invalid parameters' do
|
context 'when user filled form with invalid parameters' do
|
||||||
before do
|
before do
|
||||||
|
|
|
@ -116,12 +116,14 @@ describe Gitlab::Kubernetes::KubeClient do
|
||||||
:get_config_map,
|
:get_config_map,
|
||||||
:get_pod,
|
:get_pod,
|
||||||
:get_namespace,
|
:get_namespace,
|
||||||
|
:get_secret,
|
||||||
:get_service,
|
:get_service,
|
||||||
:get_service_account,
|
:get_service_account,
|
||||||
:delete_pod,
|
:delete_pod,
|
||||||
:create_config_map,
|
:create_config_map,
|
||||||
:create_namespace,
|
:create_namespace,
|
||||||
:create_pod,
|
:create_pod,
|
||||||
|
:create_secret,
|
||||||
:create_service_account,
|
:create_service_account,
|
||||||
:update_config_map,
|
:update_config_map,
|
||||||
:update_service_account
|
:update_service_account
|
||||||
|
|
35
spec/lib/gitlab/kubernetes/service_account_token_spec.rb
Normal file
35
spec/lib/gitlab/kubernetes/service_account_token_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::Kubernetes::ServiceAccountToken do
|
||||||
|
let(:name) { 'token-name' }
|
||||||
|
let(:service_account_name) { 'a_service_account' }
|
||||||
|
let(:namespace_name) { 'a_namespace' }
|
||||||
|
let(:service_account_token) { described_class.new(name, service_account_name, namespace_name) }
|
||||||
|
|
||||||
|
it { expect(service_account_token.name).to eq(name) }
|
||||||
|
it { expect(service_account_token.service_account_name).to eq(service_account_name) }
|
||||||
|
it { expect(service_account_token.namespace_name).to eq(namespace_name) }
|
||||||
|
|
||||||
|
describe '#generate' do
|
||||||
|
let(:resource) do
|
||||||
|
::Kubeclient::Resource.new(
|
||||||
|
metadata: {
|
||||||
|
name: name,
|
||||||
|
namespace: namespace_name,
|
||||||
|
annotations: {
|
||||||
|
'kubernetes.io/service-account.name': service_account_name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'kubernetes.io/service-account-token'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { service_account_token.generate }
|
||||||
|
|
||||||
|
it 'should build a Kubeclient Resource' do
|
||||||
|
is_expected.to eq(resource)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -66,25 +66,30 @@ describe GoogleApi::CloudPlatform::Client do
|
||||||
describe '#projects_zones_clusters_create' do
|
describe '#projects_zones_clusters_create' do
|
||||||
subject do
|
subject do
|
||||||
client.projects_zones_clusters_create(
|
client.projects_zones_clusters_create(
|
||||||
spy, spy, cluster_name, cluster_size, machine_type: machine_type)
|
project_id, zone, cluster_name, cluster_size, machine_type: machine_type, legacy_abac: legacy_abac)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:project_id) { 'project-123' }
|
||||||
|
let(:zone) { 'us-central1-a' }
|
||||||
let(:cluster_name) { 'test-cluster' }
|
let(:cluster_name) { 'test-cluster' }
|
||||||
let(:cluster_size) { 1 }
|
let(:cluster_size) { 1 }
|
||||||
let(:machine_type) { 'n1-standard-2' }
|
let(:machine_type) { 'n1-standard-2' }
|
||||||
|
let(:legacy_abac) { true }
|
||||||
|
let(:create_cluster_request_body) { double('Google::Apis::ContainerV1::CreateClusterRequest') }
|
||||||
let(:operation) { double }
|
let(:operation) { double }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
||||||
.to receive(:create_cluster).with(any_args, options: user_agent_options)
|
.to receive(:create_cluster).with(any_args)
|
||||||
.and_return(operation)
|
.and_return(operation)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(operation) }
|
|
||||||
|
|
||||||
it 'sets corresponded parameters' do
|
it 'sets corresponded parameters' do
|
||||||
expect_any_instance_of(Google::Apis::ContainerV1::CreateClusterRequest)
|
expect_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
||||||
.to receive(:initialize).with(
|
.to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
|
||||||
|
|
||||||
|
expect(Google::Apis::ContainerV1::CreateClusterRequest)
|
||||||
|
.to receive(:new).with(
|
||||||
{
|
{
|
||||||
"cluster": {
|
"cluster": {
|
||||||
"name": cluster_name,
|
"name": cluster_name,
|
||||||
|
@ -96,9 +101,35 @@ describe GoogleApi::CloudPlatform::Client do
|
||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} )
|
} ).and_return(create_cluster_request_body)
|
||||||
|
|
||||||
subject
|
expect(subject).to eq operation
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'create without legacy_abac' do
|
||||||
|
let(:legacy_abac) { false }
|
||||||
|
|
||||||
|
it 'sets corresponded parameters' do
|
||||||
|
expect_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
||||||
|
.to receive(:create_cluster).with(project_id, zone, create_cluster_request_body, options: user_agent_options)
|
||||||
|
|
||||||
|
expect(Google::Apis::ContainerV1::CreateClusterRequest)
|
||||||
|
.to receive(:new).with(
|
||||||
|
{
|
||||||
|
"cluster": {
|
||||||
|
"name": cluster_name,
|
||||||
|
"initial_node_count": cluster_size,
|
||||||
|
"node_config": {
|
||||||
|
"machine_type": machine_type
|
||||||
|
},
|
||||||
|
"legacy_abac": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ).and_return(create_cluster_request_body)
|
||||||
|
|
||||||
|
expect(subject).to eq operation
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,24 @@ describe Clusters::Providers::Gcp do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#legacy_abac?' do
|
||||||
|
let(:gcp) { build(:cluster_provider_gcp) }
|
||||||
|
|
||||||
|
subject { gcp }
|
||||||
|
|
||||||
|
it 'should default to true' do
|
||||||
|
is_expected.to be_legacy_abac
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'legacy_abac is set to false' do
|
||||||
|
let(:gcp) { build(:cluster_provider_gcp, legacy_abac: false) }
|
||||||
|
|
||||||
|
it 'is false' do
|
||||||
|
is_expected.not_to be_legacy_abac
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#state_machine' do
|
describe '#state_machine' do
|
||||||
context 'when any => [:created]' do
|
context 'when any => [:created]' do
|
||||||
let(:gcp) { build(:cluster_provider_gcp, :creating) }
|
let(:gcp) { build(:cluster_provider_gcp, :creating) }
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Ci::FetchKubernetesTokenService do
|
|
||||||
describe '#execute' do
|
|
||||||
subject { described_class.new(api_url, ca_pem, username, password).execute }
|
|
||||||
|
|
||||||
let(:api_url) { 'http://111.111.111.111' }
|
|
||||||
let(:ca_pem) { '' }
|
|
||||||
let(:username) { 'admin' }
|
|
||||||
let(:password) { 'xxx' }
|
|
||||||
|
|
||||||
context 'when params correct' do
|
|
||||||
let(:token) { 'xxx.token.xxx' }
|
|
||||||
|
|
||||||
let(:secrets_json) do
|
|
||||||
[
|
|
||||||
{
|
|
||||||
'metadata': {
|
|
||||||
name: metadata_name
|
|
||||||
},
|
|
||||||
'data': {
|
|
||||||
'token': Base64.encode64(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow_any_instance_of(Kubeclient::Client)
|
|
||||||
.to receive(:get_secrets).and_return(secrets_json)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when default-token exists' do
|
|
||||||
let(:metadata_name) { 'default-token-123' }
|
|
||||||
|
|
||||||
it { is_expected.to eq(token) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when default-token does not exist' do
|
|
||||||
let(:metadata_name) { 'another-token-123' }
|
|
||||||
|
|
||||||
it { is_expected.to be_nil }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when api_url is nil' do
|
|
||||||
let(:api_url) { nil }
|
|
||||||
|
|
||||||
it { expect { subject }.to raise_error("Incomplete settings") }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when username is nil' do
|
|
||||||
let(:username) { nil }
|
|
||||||
|
|
||||||
it { expect { subject }.to raise_error("Incomplete settings") }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when password is nil' do
|
|
||||||
let(:password) { nil }
|
|
||||||
|
|
||||||
it { expect { subject }.to raise_error("Incomplete settings") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -12,9 +12,11 @@ describe Clusters::Gcp::FinalizeCreationService do
|
||||||
let(:zone) { provider.zone }
|
let(:zone) { provider.zone }
|
||||||
let(:cluster_name) { cluster.name }
|
let(:cluster_name) { cluster.name }
|
||||||
|
|
||||||
|
subject { described_class.new.execute(provider) }
|
||||||
|
|
||||||
shared_examples 'success' do
|
shared_examples 'success' do
|
||||||
it 'configures provider and kubernetes' do
|
it 'configures provider and kubernetes' do
|
||||||
described_class.new.execute(provider)
|
subject
|
||||||
|
|
||||||
expect(provider).to be_created
|
expect(provider).to be_created
|
||||||
end
|
end
|
||||||
|
@ -22,7 +24,7 @@ describe Clusters::Gcp::FinalizeCreationService do
|
||||||
|
|
||||||
shared_examples 'error' do
|
shared_examples 'error' do
|
||||||
it 'sets an error to provider object' do
|
it 'sets an error to provider object' do
|
||||||
described_class.new.execute(provider)
|
subject
|
||||||
|
|
||||||
expect(provider.reload).to be_errored
|
expect(provider.reload).to be_errored
|
||||||
end
|
end
|
||||||
|
@ -33,6 +35,7 @@ describe Clusters::Gcp::FinalizeCreationService do
|
||||||
let(:api_url) { 'https://' + endpoint }
|
let(:api_url) { 'https://' + endpoint }
|
||||||
let(:username) { 'sample-username' }
|
let(:username) { 'sample-username' }
|
||||||
let(:password) { 'sample-password' }
|
let(:password) { 'sample-password' }
|
||||||
|
let(:secret_name) { 'gitlab-token' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_cloud_platform_get_zone_cluster(
|
stub_cloud_platform_get_zone_cluster(
|
||||||
|
@ -43,61 +46,103 @@ describe Clusters::Gcp::FinalizeCreationService do
|
||||||
password: password
|
password: password
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
stub_kubeclient_discover(api_url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when suceeded to fetch kuberenetes token' do
|
context 'service account and token created' do
|
||||||
|
before do
|
||||||
|
stub_kubeclient_discover(api_url)
|
||||||
|
stub_kubeclient_create_service_account(api_url)
|
||||||
|
stub_kubeclient_create_secret(api_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context 'kubernetes token successfully fetched' do
|
||||||
let(:token) { 'sample-token' }
|
let(:token) { 'sample-token' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_kubeclient_get_secrets(
|
stub_kubeclient_get_secret(
|
||||||
api_url,
|
api_url,
|
||||||
{
|
{
|
||||||
|
metadata_name: secret_name,
|
||||||
token: Base64.encode64(token)
|
token: Base64.encode64(token)
|
||||||
} )
|
} )
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'provider legacy_abac is enabled' do
|
||||||
|
include_context 'kubernetes token successfully fetched'
|
||||||
|
|
||||||
it_behaves_like 'success'
|
it_behaves_like 'success'
|
||||||
|
|
||||||
it 'has corresponded data' do
|
it 'properly configures database models' do
|
||||||
described_class.new.execute(provider)
|
subject
|
||||||
|
|
||||||
cluster.reload
|
cluster.reload
|
||||||
provider.reload
|
|
||||||
platform.reload
|
|
||||||
|
|
||||||
expect(provider.endpoint).to eq(endpoint)
|
expect(provider.endpoint).to eq(endpoint)
|
||||||
expect(platform.api_url).to eq(api_url)
|
expect(platform.api_url).to eq(api_url)
|
||||||
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
|
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
|
||||||
expect(platform.username).to eq(username)
|
expect(platform.username).to eq(username)
|
||||||
expect(platform.password).to eq(password)
|
expect(platform.password).to eq(password)
|
||||||
|
expect(platform).to be_abac
|
||||||
|
expect(platform.authorization_type).to eq('abac')
|
||||||
expect(platform.token).to eq(token)
|
expect(platform.token).to eq(token)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when default-token is not found' do
|
context 'provider legacy_abac is disabled' do
|
||||||
before do
|
before do
|
||||||
stub_kubeclient_get_secrets(api_url, metadata_name: 'aaaa')
|
provider.legacy_abac = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'error'
|
include_context 'kubernetes token successfully fetched'
|
||||||
|
|
||||||
|
context 'cluster role binding created' do
|
||||||
|
before do
|
||||||
|
stub_kubeclient_create_cluster_role_binding(api_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'success'
|
||||||
|
|
||||||
|
it 'properly configures database models' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
cluster.reload
|
||||||
|
|
||||||
|
expect(provider.endpoint).to eq(endpoint)
|
||||||
|
expect(platform.api_url).to eq(api_url)
|
||||||
|
expect(platform.ca_cert).to eq(Base64.decode64(load_sample_cert))
|
||||||
|
expect(platform.username).to eq(username)
|
||||||
|
expect(platform.password).to eq(password)
|
||||||
|
expect(platform).to be_rbac
|
||||||
|
expect(platform.token).to eq(token)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when token is empty' do
|
context 'when token is empty' do
|
||||||
before do
|
before do
|
||||||
stub_kubeclient_get_secrets(api_url, token: '')
|
stub_kubeclient_get_secret(api_url, token: '', metadata_name: secret_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'error'
|
it_behaves_like 'error'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when failed to fetch kuberenetes token' do
|
context 'when failed to fetch kubernetes token' do
|
||||||
before do
|
before do
|
||||||
stub_kubeclient_get_secrets_error(api_url)
|
stub_kubeclient_get_secret_error(api_url, secret_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'error'
|
it_behaves_like 'error'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when service account fails to create' do
|
||||||
|
before do
|
||||||
|
stub_kubeclient_create_service_account_error(api_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'error'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when failed to fetch gke cluster info' do
|
context 'when failed to fetch gke cluster info' do
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
|
||||||
|
include KubernetesHelpers
|
||||||
|
|
||||||
|
let(:service) { described_class.new(kubeclient, rbac: rbac) }
|
||||||
|
|
||||||
|
describe '#execute' do
|
||||||
|
let(:rbac) { false }
|
||||||
|
let(:api_url) { 'http://111.111.111.111' }
|
||||||
|
let(:username) { 'admin' }
|
||||||
|
let(:password) { 'xxx' }
|
||||||
|
|
||||||
|
let(:kubeclient) do
|
||||||
|
Gitlab::Kubernetes::KubeClient.new(
|
||||||
|
api_url,
|
||||||
|
['api', 'apis/rbac.authorization.k8s.io'],
|
||||||
|
auth_options: { username: username, password: password }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { service.execute }
|
||||||
|
|
||||||
|
context 'when params are correct' do
|
||||||
|
before do
|
||||||
|
stub_kubeclient_discover(api_url)
|
||||||
|
stub_kubeclient_create_service_account(api_url)
|
||||||
|
stub_kubeclient_create_secret(api_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'creates service account and token' do
|
||||||
|
it 'creates a kubernetes service account' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/serviceaccounts').with(
|
||||||
|
body: hash_including(
|
||||||
|
kind: 'ServiceAccount',
|
||||||
|
metadata: { name: 'gitlab', namespace: 'default' }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a kubernetes secret of type ServiceAccountToken' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/secrets').with(
|
||||||
|
body: hash_including(
|
||||||
|
kind: 'Secret',
|
||||||
|
metadata: {
|
||||||
|
name: 'gitlab-token',
|
||||||
|
namespace: 'default',
|
||||||
|
annotations: {
|
||||||
|
'kubernetes.io/service-account.name': 'gitlab'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'kubernetes.io/service-account-token'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'abac enabled cluster' do
|
||||||
|
it_behaves_like 'creates service account and token'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'rbac enabled cluster' do
|
||||||
|
let(:rbac) { true }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_kubeclient_create_cluster_role_binding(api_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'creates service account and token'
|
||||||
|
|
||||||
|
it 'creates a kubernetes cluster role binding' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(WebMock).to have_requested(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings').with(
|
||||||
|
body: hash_including(
|
||||||
|
kind: 'ClusterRoleBinding',
|
||||||
|
metadata: { name: 'gitlab-admin' },
|
||||||
|
roleRef: {
|
||||||
|
apiGroup: 'rbac.authorization.k8s.io',
|
||||||
|
kind: 'ClusterRole',
|
||||||
|
name: 'cluster-admin'
|
||||||
|
},
|
||||||
|
subjects: [{ kind: 'ServiceAccount', namespace: 'default', name: 'gitlab' }]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,60 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
|
describe Clusters::Gcp::Kubernetes::FetchKubernetesTokenService do
|
||||||
|
describe '#execute' do
|
||||||
|
let(:api_url) { 'http://111.111.111.111' }
|
||||||
|
let(:username) { 'admin' }
|
||||||
|
let(:password) { 'xxx' }
|
||||||
|
|
||||||
|
let(:kubeclient) do
|
||||||
|
Gitlab::Kubernetes::KubeClient.new(
|
||||||
|
api_url,
|
||||||
|
['api', 'apis/rbac.authorization.k8s.io'],
|
||||||
|
auth_options: { username: username, password: password }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new(kubeclient).execute }
|
||||||
|
|
||||||
|
context 'when params correct' do
|
||||||
|
let(:decoded_token) { 'xxx.token.xxx' }
|
||||||
|
let(:token) { Base64.encode64(decoded_token) }
|
||||||
|
|
||||||
|
let(:secret_json) do
|
||||||
|
{
|
||||||
|
'metadata': {
|
||||||
|
name: 'gitlab-token'
|
||||||
|
},
|
||||||
|
'data': {
|
||||||
|
'token': token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(Kubeclient::Client)
|
||||||
|
.to receive(:get_secret).and_return(secret_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when gitlab-token exists' do
|
||||||
|
let(:metadata_name) { 'gitlab-token' }
|
||||||
|
|
||||||
|
it { is_expected.to eq(decoded_token) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when gitlab-token does not exist' do
|
||||||
|
let(:secret_json) { {} }
|
||||||
|
|
||||||
|
it { is_expected.to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when token is nil' do
|
||||||
|
let(:token) { nil }
|
||||||
|
|
||||||
|
it { is_expected.to be_nil }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -33,22 +33,42 @@ module KubernetesHelpers
|
||||||
WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
|
WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_kubeclient_get_secrets(api_url, **options)
|
def stub_kubeclient_get_secret(api_url, namespace: 'default', **options)
|
||||||
WebMock.stub_request(:get, api_url + '/api/v1/secrets')
|
options[:metadata_name] ||= "default-token-1"
|
||||||
.to_return(kube_response(kube_v1_secrets_body(options)))
|
|
||||||
|
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{options[:metadata_name]}")
|
||||||
|
.to_return(kube_response(kube_v1_secret_body(options)))
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_kubeclient_get_secrets_error(api_url)
|
def stub_kubeclient_get_secret_error(api_url, name, namespace: 'default')
|
||||||
WebMock.stub_request(:get, api_url + '/api/v1/secrets')
|
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}")
|
||||||
.to_return(status: [404, "Internal Server Error"])
|
.to_return(status: [404, "Internal Server Error"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def kube_v1_secrets_body(**options)
|
def stub_kubeclient_create_service_account(api_url, namespace: 'default')
|
||||||
|
WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts")
|
||||||
|
.to_return(kube_response({}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def stub_kubeclient_create_service_account_error(api_url, namespace: 'default')
|
||||||
|
WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts")
|
||||||
|
.to_return(status: [500, "Internal Server Error"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def stub_kubeclient_create_secret(api_url, namespace: 'default')
|
||||||
|
WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets")
|
||||||
|
.to_return(kube_response({}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def stub_kubeclient_create_cluster_role_binding(api_url)
|
||||||
|
WebMock.stub_request(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings')
|
||||||
|
.to_return(kube_response({}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def kube_v1_secret_body(**options)
|
||||||
{
|
{
|
||||||
"kind" => "SecretList",
|
"kind" => "SecretList",
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"items" => [
|
|
||||||
{
|
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": options[:metadata_name] || "default-token-1",
|
"name": options[:metadata_name] || "default-token-1",
|
||||||
"namespace": "kube-system"
|
"namespace": "kube-system"
|
||||||
|
@ -57,8 +77,6 @@ module KubernetesHelpers
|
||||||
"token": options[:token] || Base64.encode64('token-sample-123')
|
"token": options[:token] || Base64.encode64('token-sample-123')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def kube_v1_discovery_body
|
def kube_v1_discovery_body
|
||||||
|
@ -68,6 +86,7 @@ module KubernetesHelpers
|
||||||
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
|
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
|
||||||
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
|
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
|
||||||
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
|
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
|
||||||
|
{ "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
|
||||||
{ "name" => "services", "namespaced" => true, "kind" => "Service" }
|
{ "name" => "services", "namespaced" => true, "kind" => "Service" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -80,6 +99,7 @@ module KubernetesHelpers
|
||||||
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
|
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
|
||||||
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
|
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
|
||||||
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
|
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
|
||||||
|
{ "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
|
||||||
{ "name" => "services", "namespaced" => true, "kind" => "Service" }
|
{ "name" => "services", "namespaced" => true, "kind" => "Service" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ shared_context 'valid cluster create params' do
|
||||||
gcp_project_id: 'gcp-project',
|
gcp_project_id: 'gcp-project',
|
||||||
zone: 'us-central1-a',
|
zone: 'us-central1-a',
|
||||||
num_nodes: 1,
|
num_nodes: 1,
|
||||||
machine_type: 'machine_type-a'
|
machine_type: 'machine_type-a',
|
||||||
|
legacy_abac: 'true'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -29,6 +30,10 @@ shared_context 'invalid cluster create params' do
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'create cluster service success' do
|
shared_examples 'create cluster service success' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(rbac_clusters: false)
|
||||||
|
end
|
||||||
|
|
||||||
it 'creates a cluster object and performs a worker' do
|
it 'creates a cluster object and performs a worker' do
|
||||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||||
|
|
||||||
|
@ -44,6 +49,7 @@ shared_examples 'create cluster service success' do
|
||||||
expect(subject.provider.num_nodes).to eq(1)
|
expect(subject.provider.num_nodes).to eq(1)
|
||||||
expect(subject.provider.machine_type).to eq('machine_type-a')
|
expect(subject.provider.machine_type).to eq('machine_type-a')
|
||||||
expect(subject.provider.access_token).to eq(access_token)
|
expect(subject.provider.access_token).to eq(access_token)
|
||||||
|
expect(subject.provider).to be_legacy_abac
|
||||||
expect(subject.platform).to be_nil
|
expect(subject.platform).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue