diff options
author | Thong Kuah <tkuah@gitlab.com> | 2018-09-28 04:29:25 +0300 |
---|---|---|
committer | Thong Kuah <tkuah@gitlab.com> | 2018-09-28 04:48:10 +0300 |
commit | 86d374399933e2dfc7f1acfb0f6c47dd27ebdf3f (patch) | |
tree | f138ecfcc05b29d2e403892285f6aaf253e68901 | |
parent | ec4eda2686bb05b8625d0ff5e5e2052d07246b67 (diff) |
Restore Gcp:: services to master
We need the admin `gitlab` SA to remain as-is,
with the `cluster-admin` privillege.
8 files changed, 172 insertions, 324 deletions
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb index 5893f27a5cf..3ae0a4a19d0 100644 --- a/app/services/clusters/gcp/finalize_creation_service.rb +++ b/app/services/clusters/gcp/finalize_creation_service.rb @@ -9,9 +9,8 @@ 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 @@ -24,8 +23,8 @@ module Clusters private - def create_gitlab_services_account! - Clusters::Gcp::ServicesAccountService.new(kube_client, cluster).execute + def create_gitlab_service_account! + Clusters::Gcp::Kubernetes::CreateServiceAccountService.new(kube_client, rbac: create_rbac_cluster?).execute end def configure_provider @@ -40,25 +39,19 @@ 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 - ) - end - - def configure_kubernetes_token - cluster.platform_kubernetes.token = request_kubernetes_token + authorization_type: authorization_type, + token: request_kubernetes_token) end def request_kubernetes_token - namespace = rbac_cluster? ? cluster.platform_kubernetes.actual_namespace : Clusters::Gcp::Kubernetes::SERVICE_ACCOUNT_NAMESPACE - - Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(kube_client, namespace).execute + Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(kube_client).execute end def authorization_type - rbac_cluster? ? 'rbac' : 'abac' + create_rbac_cluster? ? 'rbac' : 'abac' end - def rbac_cluster? + def create_rbac_cluster? !provider.legacy_abac? end diff --git a/app/services/clusters/gcp/kubernetes.rb b/app/services/clusters/gcp/kubernetes.rb index 483f5a63c48..d014d73b3e8 100644 --- a/app/services/clusters/gcp/kubernetes.rb +++ b/app/services/clusters/gcp/kubernetes.rb @@ -8,7 +8,6 @@ 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 be2740d0c4e..d17744591e6 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, :name, :namespace, :rbac + attr_reader :kubeclient, :rbac - def initialize(kubeclient, name:, namespace:, rbac:) + def initialize(kubeclient, 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_role_binding(role_binding_resource) if rbac + kubeclient.create_cluster_role_binding(cluster_role_binding_resource) if rbac end private def service_account_resource - Gitlab::Kubernetes::ServiceAccount.new(name, namespace).generate + 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, name, namespace).generate + SERVICE_ACCOUNT_TOKEN_NAME, service_account_name, service_account_namespace).generate end - def service_account_token_name - SERVICE_ACCOUNT_TOKEN_NAME + 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 edit_role_name - EDIT_ROLE_NAME + def service_account_name + SERVICE_ACCOUNT_NAME end - def role_binding_resource - Gitlab::Kubernetes::RoleBinding.new( - role_name: edit_role_name, - namespace: namespace, - service_account_name: name - ).generate + def service_account_namespace + SERVICE_ACCOUNT_NAMESPACE 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 89209ed8bfa..9e09345c8dc 100644 --- a/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb +++ b/app/services/clusters/gcp/kubernetes/fetch_kubernetes_token_service.rb @@ -4,11 +4,10 @@ module Clusters module Gcp module Kubernetes class FetchKubernetesTokenService - attr_reader :kubeclient, :namespace + attr_reader :kubeclient - def initialize(kubeclient, namespace) + def initialize(kubeclient) @kubeclient = kubeclient - @namespace = namespace end def execute @@ -19,16 +18,12 @@ module Clusters private def get_secret - kubeclient.get_secret(service_account_token_name, namespace).as_json + 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 - - 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 deleted file mode 100644 index 064a00d4c2e..00000000000 --- a/app/services/clusters/gcp/services_account_service.rb +++ /dev/null @@ -1,43 +0,0 @@ -# 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/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb index 75d5e57fd83..0f484222228 100644 --- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb +++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb @@ -1,168 +1,156 @@ -# frozen_string_literal: true - require 'spec_helper' -describe Clusters::Gcp::FinalizeCreationService, '#execute' do +describe Clusters::Gcp::FinalizeCreationService do include GoogleApi::CloudPlatformHelpers include KubernetesHelpers - 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 + 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 } - it 'properly configures database models' do - subject + subject { described_class.new.execute(provider) } - cluster.reload + shared_examples 'success' do + it 'configures provider and kubernetes' do + subject - 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) + expect(provider).to be_created + end end - end - - shared_examples 'error' do - it 'sets an error to provider object' do - subject - expect(provider.reload).to be_errored - end - end + shared_examples 'error' do + it 'sets an error to provider object' do + subject - 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) + expect(provider.reload).to be_errored end - - it_behaves_like 'error' end - context 'when token is empty' do - let(:token) { '' } + 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' } - it_behaves_like 'error' - end - - context 'when failed to fetch kubernetes token' do before do - stub_kubeclient_get_secret_error(api_url, secret_name, namespace: namespace) + stub_cloud_platform_get_zone_cluster( + gcp_project_id, zone, cluster_name, + { + endpoint: endpoint, + username: username, + password: password + } + ) end - it_behaves_like 'error' + 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 end - context 'when service account fails to create' do + context 'when failed to fetch gke cluster info' do before do - stub_kubeclient_create_service_account_error(api_url) + stub_cloud_platform_get_zone_cluster_error(gcp_project_id, zone, cluster_name) 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 1b26735df16..065d021db5e 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, name: name, namespace: namespace, rbac: rbac) } + let(:service) { described_class.new(kubeclient, rbac: rbac) } describe '#execute' do let(:rbac) { false } @@ -13,17 +13,6 @@ 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, @@ -37,18 +26,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, namespace: namespace) - stub_kubeclient_create_secret(api_url, namespace: namespace) + 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/#{namespace}/serviceaccounts").with( + expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/serviceaccounts').with( body: hash_including( kind: 'ServiceAccount', - metadata: { name: name, namespace: namespace } + metadata: { name: 'gitlab', namespace: 'default' } ) ) end @@ -56,12 +45,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/#{namespace}/secrets").with( + expect(WebMock).to have_requested(:post, api_url + '/api/v1/namespaces/default/secrets').with( body: hash_including( kind: 'Secret', metadata: { name: 'gitlab-token', - namespace: namespace, + namespace: 'default', annotations: { 'kubernetes.io/service-account.name': 'gitlab' } @@ -80,24 +69,24 @@ describe Clusters::Gcp::Kubernetes::CreateServiceAccountService do let(:rbac) { true } before do - stub_kubeclient_create_role_binding(api_url, namespace: namespace) + stub_kubeclient_create_cluster_role_binding(api_url) end it_behaves_like 'creates service account and token' - it 'creates a kubernetes role binding with edit access' do + it 'creates a kubernetes cluster role binding' do subject - expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings").with( + expect(WebMock).to have_requested(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings').with( body: hash_including( - kind: 'RoleBinding', - metadata: { name: 'gitlab-edit', namespace: namespace }, + kind: 'ClusterRoleBinding', + metadata: { name: 'gitlab-admin' }, roleRef: { apiGroup: 'rbac.authorization.k8s.io', - kind: 'Role', - name: 'edit' + kind: 'ClusterRole', + name: 'cluster-admin' }, - subjects: [{ kind: 'ServiceAccount', name: 'gitlab', namespace: namespace }] + subjects: [{ kind: 'ServiceAccount', namespace: 'default', name: 'gitlab' }] ) ) end diff --git a/spec/services/clusters/gcp/services_account_service_spec.rb b/spec/services/clusters/gcp/services_account_service_spec.rb deleted file mode 100644 index f6f08eae666..00000000000 --- a/spec/services/clusters/gcp/services_account_service_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -# 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 |