summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMayra Cabrera <mcabrera@gitlab.com>2018-09-21 17:23:33 -0500
committerMayra Cabrera <mcabrera@gitlab.com>2018-09-26 21:47:29 -0300
commite5a512628b7889fad30242751f982251dffdc463 (patch)
tree7561cd92417c54e38628d67ca4ef7f0b2eefa0d5
parente255b88e51b956d92afb5e9b90a2749a60e63459 (diff)
downloadgitlab-ce-51716-automatically-create-service-account-to-project-namespace.tar.gz
Limit GCP Kubernetes service to project namespace51716-automatically-create-service-account-to-project-namespace
This is needed to support RBAC on AutoDevOps, basically we: - Creates a service account under project's namespace and assign it a different token - If RBAC is enabled we create a RoleBinding for this new service account with edit access - Service account name is exposed through environment variables on Platform::Kubernetes - KUBE_TOKEN and KUBECONFIG are replaced with new credentials Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/51716
-rw-r--r--app/models/clusters/platforms/kubernetes.rb7
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb23
-rw-r--r--app/services/clusters/gcp/kubernetes.rb1
-rw-r--r--app/services/clusters/gcp/kubernetes/create_service_account_service.rb34
-rw-r--r--app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb11
-rw-r--r--app/services/clusters/gcp/services_account_service.rb43
-rw-r--r--changelogs/unreleased/51716-automatically-create-service-account-to-project-namespace.yml5
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb5
-rw-r--r--lib/gitlab/kubernetes/role_binding.rb47
-rw-r--r--spec/lib/gitlab/kubernetes/role_binding_spec.rb47
-rw-r--r--spec/services/clusters/gcp/finalize_creation_service_spec.rb270
-rw-r--r--spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb41
-rw-r--r--spec/services/clusters/gcp/services_account_service_spec.rb73
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb23
14 files changed, 455 insertions, 175 deletions
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 3a335909101..1e097953dd5 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -71,6 +71,7 @@ module Clusters
.append(key: 'KUBE_TOKEN', value: token, public: false)
.append(key: 'KUBE_NAMESPACE', value: actual_namespace)
.append(key: 'KUBECONFIG', value: config, public: false, file: true)
+ .append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name)
if ca_pem.present?
variables
@@ -105,6 +106,12 @@ module Clusters
@kubeclient ||= build_kube_client!(api_groups: ['api', 'apis/rbac.authorization.k8s.io'])
end
+ def service_account_name
+ default_name = Clusters::Gcp::Kubernetes::SERVICE_ACCOUNT_NAME
+
+ rbac? ? "#{default_name}-#{actual_namespace}" : default_name
+ end
+
private
def kubeconfig
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index 3ae0a4a19d0..5893f27a5cf 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -9,8 +9,9 @@ module Clusters
@provider = provider
configure_provider
- create_gitlab_service_account!
configure_kubernetes
+ create_gitlab_services_account!
+ configure_kubernetes_token
cluster.save!
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
@@ -23,8 +24,8 @@ module Clusters
private
- def create_gitlab_service_account!
- Clusters::Gcp::Kubernetes::CreateServiceAccountService.new(kube_client, rbac: create_rbac_cluster?).execute
+ def create_gitlab_services_account!
+ Clusters::Gcp::ServicesAccountService.new(kube_client, cluster).execute
end
def configure_provider
@@ -39,19 +40,25 @@ module Clusters
ca_cert: Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
username: gke_cluster.master_auth.username,
password: gke_cluster.master_auth.password,
- authorization_type: authorization_type,
- token: request_kubernetes_token)
+ authorization_type: authorization_type
+ )
+ end
+
+ def configure_kubernetes_token
+ cluster.platform_kubernetes.token = request_kubernetes_token
end
def request_kubernetes_token
- Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(kube_client).execute
+ namespace = rbac_cluster? ? cluster.platform_kubernetes.actual_namespace : Clusters::Gcp::Kubernetes::SERVICE_ACCOUNT_NAMESPACE
+
+ Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(kube_client, namespace).execute
end
def authorization_type
- create_rbac_cluster? ? 'rbac' : 'abac'
+ rbac_cluster? ? 'rbac' : 'abac'
end
- def create_rbac_cluster?
+ def rbac_cluster?
!provider.legacy_abac?
end
diff --git a/app/services/clusters/gcp/kubernetes.rb b/app/services/clusters/gcp/kubernetes.rb
index d014d73b3e8..483f5a63c48 100644
--- a/app/services/clusters/gcp/kubernetes.rb
+++ b/app/services/clusters/gcp/kubernetes.rb
@@ -8,6 +8,7 @@ module Clusters
SERVICE_ACCOUNT_TOKEN_NAME = 'gitlab-token'
CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin'
CLUSTER_ROLE_NAME = 'cluster-admin'
+ EDIT_ROLE_NAME = 'edit'
end
end
end
diff --git a/app/services/clusters/gcp/kubernetes/create_service_account_service.rb b/app/services/clusters/gcp/kubernetes/create_service_account_service.rb
index d17744591e6..be2740d0c4e 100644
--- a/app/services/clusters/gcp/kubernetes/create_service_account_service.rb
+++ b/app/services/clusters/gcp/kubernetes/create_service_account_service.rb
@@ -4,46 +4,46 @@ module Clusters
module Gcp
module Kubernetes
class CreateServiceAccountService
- attr_reader :kubeclient, :rbac
+ attr_reader :kubeclient, :name, :namespace, :rbac
- def initialize(kubeclient, rbac:)
+ def initialize(kubeclient, name:, namespace:, rbac:)
@kubeclient = kubeclient
+ @name = name
+ @namespace = namespace
@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
+ kubeclient.create_role_binding(role_binding_resource) if rbac
end
private
def service_account_resource
- Gitlab::Kubernetes::ServiceAccount.new(service_account_name, service_account_namespace).generate
+ Gitlab::Kubernetes::ServiceAccount.new(name, namespace).generate
end
def service_account_token_resource
Gitlab::Kubernetes::ServiceAccountToken.new(
- SERVICE_ACCOUNT_TOKEN_NAME, service_account_name, service_account_namespace).generate
+ service_account_token_name, name, 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
+ def service_account_token_name
+ SERVICE_ACCOUNT_TOKEN_NAME
end
- def service_account_name
- SERVICE_ACCOUNT_NAME
+ def edit_role_name
+ EDIT_ROLE_NAME
end
- def service_account_namespace
- SERVICE_ACCOUNT_NAMESPACE
+ def role_binding_resource
+ Gitlab::Kubernetes::RoleBinding.new(
+ role_name: edit_role_name,
+ namespace: namespace,
+ service_account_name: name
+ ).generate
end
end
end
diff --git a/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
index 9e09345c8dc..89209ed8bfa 100644
--- a/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
+++ b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb
@@ -4,10 +4,11 @@ module Clusters
module Gcp
module Kubernetes
class FetchKubernetesTokenService
- attr_reader :kubeclient
+ attr_reader :kubeclient, :namespace
- def initialize(kubeclient)
+ def initialize(kubeclient, namespace)
@kubeclient = kubeclient
+ @namespace = namespace
end
def execute
@@ -18,12 +19,16 @@ module Clusters
private
def get_secret
- kubeclient.get_secret(SERVICE_ACCOUNT_TOKEN_NAME, SERVICE_ACCOUNT_NAMESPACE).as_json
+ kubeclient.get_secret(service_account_token_name, namespace).as_json
rescue Kubeclient::HttpError => err
raise err unless err.error_code == 404
nil
end
+
+ def service_account_token_name
+ SERVICE_ACCOUNT_TOKEN_NAME
+ end
end
end
end
diff --git a/app/services/clusters/gcp/services_account_service.rb b/app/services/clusters/gcp/services_account_service.rb
new file mode 100644
index 00000000000..064a00d4c2e
--- /dev/null
+++ b/app/services/clusters/gcp/services_account_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Gcp
+ class ServicesAccountService
+ attr_reader :kube_client, :cluster
+
+ def initialize(kube_client, cluster)
+ @kube_client = kube_client
+ @cluster = cluster
+ end
+
+ def execute
+ create_service_account
+ create_namespaced_service_account
+ end
+
+ private
+
+ def create_namespaced_service_account
+ return unless cluster.platform_kubernetes_rbac?
+
+ namespace_name = cluster.platform_kubernetes.actual_namespace
+
+ ensure_namespace_exists(namespace_name)
+ create_service_account(namespace: namespace_name, rbac: true)
+ end
+
+ def ensure_namespace_exists(namespace_name)
+ Gitlab::Kubernetes::Namespace.new(namespace_name, kube_client).ensure_exists!
+ end
+
+ def create_service_account(namespace: 'default', rbac: false)
+ Clusters::Gcp::Kubernetes::CreateServiceAccountService.new(
+ kube_client,
+ name: cluster.platform_kubernetes.service_account_name,
+ namespace: namespace,
+ rbac: rbac
+ ).execute
+ end
+ end
+ end
+end
diff --git a/changelogs/unreleased/51716-automatically-create-service-account-to-project-namespace.yml b/changelogs/unreleased/51716-automatically-create-service-account-to-project-namespace.yml
new file mode 100644
index 00000000000..be9a09b2013
--- /dev/null
+++ b/changelogs/unreleased/51716-automatically-create-service-account-to-project-namespace.yml
@@ -0,0 +1,5 @@
+---
+title: Limits GCP Kubernetes service to use project's namespace
+merge_request: 21867
+author:
+type: added
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
index 588238de608..81332da09ef 100644
--- a/lib/gitlab/kubernetes/kube_client.rb
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -45,6 +45,11 @@ module Gitlab
:update_cluster_role_binding,
to: :rbac_client
+ delegate :create_role_binding,
+ :get_role_binding,
+ :update_role_binding,
+ to: :rbac_client
+
# Deployments resource is currently on the apis/extensions api group
delegate :get_deployments,
to: :extensions_client
diff --git a/lib/gitlab/kubernetes/role_binding.rb b/lib/gitlab/kubernetes/role_binding.rb
new file mode 100644
index 00000000000..92c7f4dad87
--- /dev/null
+++ b/lib/gitlab/kubernetes/role_binding.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class RoleBinding
+ attr_reader :role_name, :namespace, :service_account_name
+
+ def initialize(role_name:, namespace:, service_account_name:)
+ @role_name = role_name
+ @namespace = namespace
+ @service_account_name = service_account_name
+ end
+
+ def generate
+ ::Kubeclient::Resource.new.tap do |resource|
+ resource.metadata = metadata
+ resource.roleRef = role_ref
+ resource.subjects = subjects
+ end
+ end
+
+ private
+
+ def metadata
+ { name: "gitlab-#{role_name}", namespace: namespace }
+ end
+
+ def role_ref
+ {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'Role',
+ name: role_name
+ }
+ end
+
+ def subjects
+ [
+ {
+ kind: 'ServiceAccount',
+ name: service_account_name,
+ namespace: namespace
+ }
+ ]
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/role_binding_spec.rb b/spec/lib/gitlab/kubernetes/role_binding_spec.rb
new file mode 100644
index 00000000000..aab1ca4087c
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/role_binding_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::RoleBinding, '#generate' do
+ let(:role_name) { 'edit' }
+ let(:namespace) { 'my-namespace' }
+ let(:service_account_name) { 'my-service-account' }
+
+ let(:subjects) do
+ [
+ {
+ kind: 'ServiceAccount',
+ name: service_account_name,
+ namespace: namespace
+ }
+ ]
+ end
+
+ let(:role_ref) do
+ {
+ apiGroup: 'rbac.authorization.k8s.io',
+ kind: 'RoleBinding',
+ name: role_name
+ }
+ end
+
+ let(:resource) do
+ ::Kubeclient::Resource.new(
+ metadata: { name: 'gitlab-edit' },
+ roleRef: role_ref,
+ subjects: subjects
+ )
+ end
+
+ subject do
+ described_class.new(
+ role_name: role_name,
+ namespace: namespace,
+ service_account_name: service_account_name
+ ).generate
+ end
+
+ it 'should build a Kubeclient Resource' do
+ is_expected.to eq(resource)
+ end
+end
diff --git a/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
index 0f484222228..75d5e57fd83 100644
--- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb
+++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
@@ -1,156 +1,168 @@
+# frozen_string_literal: true
+
require 'spec_helper'
-describe Clusters::Gcp::FinalizeCreationService do
+describe Clusters::Gcp::FinalizeCreationService, '#execute' do
include GoogleApi::CloudPlatformHelpers
include KubernetesHelpers
- describe '#execute' do
- let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
- let(:provider) { cluster.provider }
- let(:platform) { cluster.platform }
- let(:gcp_project_id) { provider.gcp_project_id }
- let(:zone) { provider.zone }
- let(:cluster_name) { cluster.name }
+ let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
+ let(:provider) { cluster.provider }
+ let(:platform) { cluster.platform }
+ let(:gcp_project_id) { provider.gcp_project_id }
+ let(:zone) { provider.zone }
+ let(:cluster_name) { cluster.name }
+ let(:endpoint) { '111.111.111.111' }
+ let(:api_url) { 'https://' + endpoint }
+ let(:username) { 'sample-username' }
+ let(:password) { 'sample-password' }
+ let(:secret_name) { 'gitlab-token' }
+
+ subject { described_class.new.execute(provider) }
+
+ shared_examples 'success' do
+ it 'configures provider and kubernetes' do
+ subject
+
+ expect(provider).to be_created
+ end
- subject { described_class.new.execute(provider) }
+ it 'properly configures database models' do
+ subject
- shared_examples 'success' do
- it 'configures provider and kubernetes' do
- subject
+ cluster.reload
- expect(provider).to be_created
- end
+ 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.token).to eq(token)
end
+ end
+
+ shared_examples 'error' do
+ it 'sets an error to provider object' do
+ subject
- shared_examples 'error' do
- it 'sets an error to provider object' do
- subject
+ expect(provider.reload).to be_errored
+ end
+ end
- expect(provider.reload).to be_errored
+ shared_examples 'kubernetes information not successfully fetched' do
+ context 'when failed to fetch gke cluster info' do
+ before do
+ stub_cloud_platform_get_zone_cluster_error(gcp_project_id, zone, cluster_name)
end
+
+ it_behaves_like 'error'
end
- context 'when suceeded to fetch gke cluster info' do
- let(:endpoint) { '111.111.111.111' }
- let(:api_url) { 'https://' + endpoint }
- let(:username) { 'sample-username' }
- let(:password) { 'sample-password' }
- let(:secret_name) { 'gitlab-token' }
+ context 'when token is empty' do
+ let(:token) { '' }
+ it_behaves_like 'error'
+ end
+
+ context 'when failed to fetch kubernetes token' do
before do
- stub_cloud_platform_get_zone_cluster(
- gcp_project_id, zone, cluster_name,
- {
- endpoint: endpoint,
- username: username,
- password: password
- }
- )
+ stub_kubeclient_get_secret_error(api_url, secret_name, namespace: namespace)
end
- 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' }
-
- before do
- stub_kubeclient_get_secret(
- api_url,
- {
- metadata_name: secret_name,
- token: Base64.encode64(token)
- } )
- end
- end
-
- context 'provider legacy_abac is enabled' do
- include_context 'kubernetes token successfully fetched'
-
- 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_abac
- expect(platform.authorization_type).to eq('abac')
- expect(platform.token).to eq(token)
- end
- end
-
- context 'provider legacy_abac is disabled' do
- before do
- provider.legacy_abac = false
- end
-
- 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
-
- context 'when token is empty' do
- before do
- stub_kubeclient_get_secret(api_url, token: '', metadata_name: secret_name)
- end
-
- it_behaves_like 'error'
- end
-
- context 'when failed to fetch kubernetes token' do
- before do
- stub_kubeclient_get_secret_error(api_url, secret_name)
- end
-
- it_behaves_like 'error'
- 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
+ it_behaves_like 'error'
end
- context 'when failed to fetch gke cluster info' do
+ context 'when service account fails to create' do
before do
- stub_cloud_platform_get_zone_cluster_error(gcp_project_id, zone, cluster_name)
+ stub_kubeclient_create_service_account_error(api_url)
end
it_behaves_like 'error'
end
end
+
+ shared_context 'kubernetes information successfully fetched' do
+ before do
+ stub_cloud_platform_get_zone_cluster(
+ gcp_project_id, zone, cluster_name,
+ {
+ endpoint: endpoint,
+ username: username,
+ password: password
+ }
+ )
+
+ stub_kubeclient_discover(api_url)
+ stub_kubeclient_create_service_account(api_url)
+ stub_kubeclient_create_secret(api_url)
+
+ stub_kubeclient_get_secret(
+ api_url,
+ {
+ metadata_name: secret_name,
+ token: Base64.encode64(token)
+ }
+ )
+ end
+ end
+
+ context 'With a legacy ABAC cluster' do
+ let(:token) { 'sample-token' }
+ let(:namespace) { 'default' }
+
+ before do
+ provider.legacy_abac = true
+ end
+
+ include_context 'kubernetes information successfully fetched'
+
+ it_behaves_like 'success'
+
+ it 'uses ABAC authorization type' do
+ subject
+ cluster.reload
+
+ expect(platform).to be_abac
+ expect(platform.authorization_type).to eq('abac')
+ end
+
+ it_behaves_like 'kubernetes information not successfully fetched'
+ end
+
+ context 'With an RBAC cluster' do
+ let(:token) { 'sample-token' }
+ let(:namespace) { "#{cluster.project.path}-#{cluster.project.id}" }
+
+ include_context 'kubernetes information successfully fetched'
+
+ before do
+ provider.legacy_abac = false
+
+ stub_kubeclient_create_service_account(api_url, namespace: namespace)
+ stub_kubeclient_create_secret(api_url, namespace: namespace)
+ stub_kubeclient_create_role_binding(api_url, namespace: namespace)
+ stub_kubeclient_create_namespace(api_url)
+ stub_kubeclient_get_namespace(api_url, namespace: namespace)
+
+ options = {
+ metadata_name: secret_name,
+ token: Base64.encode64(token),
+ namespace: namespace
+ }
+
+ stub_kubeclient_get_secret(api_url, options)
+ end
+
+ it_behaves_like 'success'
+
+ it 'uses RBAC authorization type' do
+ subject
+ cluster.reload
+
+ expect(platform).to be_rbac
+ expect(platform.authorization_type).to eq('rbac')
+ end
+
+ it_behaves_like 'kubernetes information not successfully fetched'
+ end
end
diff --git a/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
index 065d021db5e..1b26735df16 100644
--- a/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
+++ b/spec/services/clusters/gcp/kubernetes/create_service_account_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
include KubernetesHelpers
- let(:service) { described_class.new(kubeclient, rbac: rbac) }
+ let(:service) { described_class.new(kubeclient, name: name, namespace: namespace, rbac: rbac) }
describe '#execute' do
let(:rbac) { false }
@@ -13,6 +13,17 @@ describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
let(:username) { 'admin' }
let(:password) { 'xxx' }
+ let(:cluster) do
+ create(:cluster,
+ :project, :provided_by_gcp,
+ platform_kubernetes: create(:cluster_platform_kubernetes, :configured)
+ )
+ end
+
+ let(:platform_kubernetes) { cluster.platform_kubernetes }
+ let(:namespace) { platform_kubernetes.actual_namespace }
+ let(:name) { platform_kubernetes.service_account_name }
+
let(:kubeclient) do
Gitlab::Kubernetes::KubeClient.new(
api_url,
@@ -26,18 +37,18 @@ describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
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)
+ stub_kubeclient_create_service_account(api_url, namespace: namespace)
+ stub_kubeclient_create_secret(api_url, namespace: namespace)
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(
+ expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts").with(
body: hash_including(
kind: 'ServiceAccount',
- metadata: { name: 'gitlab', namespace: 'default' }
+ metadata: { name: name, namespace: namespace }
)
)
end
@@ -45,12 +56,12 @@ describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
it 'creates a kubernetes secret of type ServiceAccountToken' do
subject
- expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/secrets').with(
+ expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets").with(
body: hash_including(
kind: 'Secret',
metadata: {
name: 'gitlab-token',
- namespace: 'default',
+ namespace: namespace,
annotations: {
'kubernetes.io/service-account.name': 'gitlab'
}
@@ -69,24 +80,24 @@ describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do
let(:rbac) { true }
before do
- stub_kubeclient_create_cluster_role_binding(api_url)
+ stub_kubeclient_create_role_binding(api_url, namespace: namespace)
end
it_behaves_like 'creates service account and token'
- it 'creates a kubernetes cluster role binding' do
+ it 'creates a kubernetes role binding with edit access' do
subject
- expect(WebMock).to have_requested(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings').with(
+ expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings").with(
body: hash_including(
- kind: 'ClusterRoleBinding',
- metadata: { name: 'gitlab-admin' },
+ kind: 'RoleBinding',
+ metadata: { name: 'gitlab-edit', namespace: namespace },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
- kind: 'ClusterRole',
- name: 'cluster-admin'
+ kind: 'Role',
+ name: 'edit'
},
- subjects: [{ kind: 'ServiceAccount', namespace: 'default', name: 'gitlab' }]
+ subjects: [{ kind: 'ServiceAccount', name: 'gitlab', namespace: namespace }]
)
)
end
diff --git a/spec/services/clusters/gcp/services_account_service_spec.rb b/spec/services/clusters/gcp/services_account_service_spec.rb
new file mode 100644
index 00000000000..f6f08eae666
--- /dev/null
+++ b/spec/services/clusters/gcp/services_account_service_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Gcp::ServicesAccountService, '#execute' do
+ include GoogleApi::CloudPlatformHelpers
+ include KubernetesHelpers
+
+ let(:endpoint) { '111.111.111.111' }
+ let(:api_url) { 'https://' + endpoint }
+ let(:cluster) { create(:cluster, :project, :providing_by_gcp, platform_kubernetes: create(:cluster_platform_kubernetes)) }
+ let(:username) { 'sample-username' }
+ let(:password) { 'sample-password' }
+
+ 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, cluster).execute }
+
+ context 'With an ABAC cluster' do
+ before do
+ stub_kubeclient_discover(api_url)
+ stub_kubeclient_create_service_account(api_url)
+ stub_kubeclient_create_secret(api_url)
+ end
+
+ it 'creates default 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
+ end
+
+ context 'With an RBAC cluster' do
+ let(:namespace) { "#{cluster.project.path}-#{cluster.project.id}" }
+
+ before do
+ cluster.platform_kubernetes.rbac!
+
+ stub_kubeclient_discover(api_url)
+ stub_kubeclient_create_service_account(api_url)
+ stub_kubeclient_create_secret(api_url)
+
+ stub_kubeclient_create_namespace(api_url)
+ stub_kubeclient_get_namespace(api_url, namespace: namespace)
+
+ stub_kubeclient_create_service_account(api_url, namespace: namespace)
+ stub_kubeclient_create_secret(api_url, namespace: namespace)
+ stub_kubeclient_create_role_binding(api_url, namespace: namespace)
+ end
+
+ it 'creates namespaced service account' do
+ subject
+
+ expect(WebMock).to have_requested(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts").with(
+ body: hash_including(
+ kind: 'ServiceAccount',
+ metadata: { name: "gitlab-#{namespace}", namespace: namespace }
+ )
+ )
+ end
+ end
+end
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index c077ca9f15b..a03d9c4045f 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -33,10 +33,11 @@ module KubernetesHelpers
WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
end
- def stub_kubeclient_get_secret(api_url, namespace: 'default', **options)
+ def stub_kubeclient_get_secret(api_url, **options)
options[:metadata_name] ||= "default-token-1"
+ options[:namespace] ||= "default"
- WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{options[:metadata_name]}")
+ WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
.to_return(kube_response(kube_v1_secret_body(options)))
end
@@ -65,6 +66,21 @@ module KubernetesHelpers
.to_return(kube_response({}))
end
+ def stub_kubeclient_create_role_binding(api_url, namespace: 'default')
+ WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings")
+ .to_return(kube_response({}))
+ end
+
+ def stub_kubeclient_create_namespace(api_url)
+ WebMock.stub_request(:post, api_url + "/api/v1/namespaces")
+ .to_return(kube_response({}))
+ end
+
+ def stub_kubeclient_get_namespace(api_url, namespace: 'default')
+ WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}")
+ .to_return(kube_response({}))
+ end
+
def kube_v1_secret_body(**options)
{
"kind" => "SecretList",
@@ -87,7 +103,8 @@ module KubernetesHelpers
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
{ "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
- { "name" => "services", "namespaced" => true, "kind" => "Service" }
+ { "name" => "services", "namespaced" => true, "kind" => "Service" },
+ { "name" => "namespaces", "namespaced" => true, "kind" => "Namespace" }
]
}
end