diff options
author | Thong Kuah <tkuah@gitlab.com> | 2018-09-06 13:03:38 +0300 |
---|---|---|
committer | Kamil TrzciĆski <ayufan@ayufan.eu> | 2018-09-06 13:03:38 +0300 |
commit | 6f2ad2b6041b8a007df7eb8c4f477c24cc153ac3 (patch) | |
tree | 7b190f17b6da295cf3599174f48c0fbc060ddbb1 /spec/lib/gitlab/kubernetes/helm | |
parent | a2ea32dd44cc4a104e404325c73a77151913a946 (diff) |
Enable Kubernetes RBAC for GitLab Managed Apps for existing clusters
Diffstat (limited to 'spec/lib/gitlab/kubernetes/helm')
-rw-r--r-- | spec/lib/gitlab/kubernetes/helm/api_spec.rb | 109 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes/helm/base_command_spec.rb | 20 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes/helm/init_command_spec.rb | 130 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes/helm/install_command_spec.rb | 155 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes/helm/pod_spec.rb | 17 |
5 files changed, 413 insertions, 18 deletions
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb index 341f71a3e49..25c3b37753d 100644 --- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb @@ -5,9 +5,18 @@ describe Gitlab::Kubernetes::Helm::Api do let(:helm) { described_class.new(client) } let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) } - let(:application) { create(:clusters_applications_prometheus) } - - let(:command) { application.install_command } + let(:application_name) { 'app-name' } + let(:rbac) { false } + let(:files) { {} } + + let(:command) do + Gitlab::Kubernetes::Helm::InstallCommand.new( + name: application_name, + chart: 'chart-name', + rbac: rbac, + files: files + ) + end subject { helm } @@ -28,6 +37,8 @@ describe Gitlab::Kubernetes::Helm::Api do before do allow(client).to receive(:create_pod).and_return(nil) allow(client).to receive(:create_config_map).and_return(nil) + allow(client).to receive(:create_service_account).and_return(nil) + allow(client).to receive(:create_cluster_role_binding).and_return(nil) allow(namespace).to receive(:ensure_exists!).once end @@ -39,7 +50,7 @@ describe Gitlab::Kubernetes::Helm::Api do end context 'with a ConfigMap' do - let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.files).generate } + let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application_name, files).generate } it 'creates a ConfigMap on kubeclient' do expect(client).to receive(:create_config_map).with(resource).once @@ -47,6 +58,96 @@ describe Gitlab::Kubernetes::Helm::Api do subject.install(command) end end + + context 'without a service account' do + it 'does not create a service account on kubeclient' do + expect(client).not_to receive(:create_service_account) + expect(client).not_to receive(:create_cluster_role_binding) + + subject.install(command) + end + end + + context 'with a service account' do + let(:command) { Gitlab::Kubernetes::Helm::InitCommand.new(name: application_name, files: files, rbac: rbac) } + + context 'rbac-enabled cluster' do + let(:rbac) { true } + + let(:service_account_resource) do + Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' }) + end + + let(:cluster_role_binding_resource) do + Kubeclient::Resource.new( + metadata: { name: 'tiller-admin' }, + roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' }, + subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }] + ) + end + + context 'service account and cluster role binding does not exist' do + before do + expect(client).to receive('get_service_account').with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::HttpError.new(404, 'Not found', nil)) + expect(client).to receive('get_cluster_role_binding').with('tiller-admin').and_raise(Kubeclient::HttpError.new(404, 'Not found', nil)) + end + + it 'creates a service account, followed the cluster role binding on kubeclient' do + expect(client).to receive(:create_service_account).with(service_account_resource).once.ordered + expect(client).to receive(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered + + subject.install(command) + end + end + + context 'service account already exists' do + before do + expect(client).to receive('get_service_account').with('tiller', 'gitlab-managed-apps').and_return(service_account_resource) + expect(client).to receive('get_cluster_role_binding').with('tiller-admin').and_raise(Kubeclient::HttpError.new(404, 'Not found', nil)) + end + + it 'updates the service account, followed by creating the cluster role binding' do + expect(client).to receive(:update_service_account).with(service_account_resource).once.ordered + expect(client).to receive(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered + + subject.install(command) + end + end + + context 'service account and cluster role binding already exists' do + before do + expect(client).to receive('get_service_account').with('tiller', 'gitlab-managed-apps').and_return(service_account_resource) + expect(client).to receive('get_cluster_role_binding').with('tiller-admin').and_return(cluster_role_binding_resource) + end + + it 'updates the service account, followed by creating the cluster role binding' do + expect(client).to receive(:update_service_account).with(service_account_resource).once.ordered + expect(client).to receive(:update_cluster_role_binding).with(cluster_role_binding_resource).once.ordered + + subject.install(command) + end + end + + context 'a non-404 error is thrown' do + before do + expect(client).to receive('get_service_account').with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) + end + + it 'raises an error' do + expect { subject.install(command) }.to raise_error(Kubeclient::HttpError) + end + end + end + + context 'legacy abac cluster' do + it 'does not create a service account on kubeclient' do + expect(client).not_to receive(:create_service_account) + expect(client).not_to receive(:create_cluster_role_binding) + + subject.install(command) + end + end + end end describe '#status' do diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb index d50616e95e8..aacae78be43 100644 --- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb @@ -2,14 +2,24 @@ require 'spec_helper' describe Gitlab::Kubernetes::Helm::BaseCommand do let(:application) { create(:clusters_applications_helm) } + let(:rbac) { false } + let(:test_class) do Class.new do include Gitlab::Kubernetes::Helm::BaseCommand + def initialize(rbac) + @rbac = rbac + end + def name "test-class-name" end + def rbac? + @rbac + end + def files { some: 'value' @@ -19,7 +29,7 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do end let(:base_command) do - test_class.new + test_class.new(rbac) end subject { base_command } @@ -34,6 +44,14 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do it 'should returns a kubeclient resoure with pod content for application' do is_expected.to be_an_instance_of ::Kubeclient::Resource end + + context 'when rbac is true' do + let(:rbac) { true } + + it 'also returns a kubeclient resource' do + is_expected.to be_an_instance_of ::Kubeclient::Resource + end + end end describe '#pod_name' do diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb index dcbc046cf00..72dc1817936 100644 --- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb @@ -2,9 +2,135 @@ require 'spec_helper' describe Gitlab::Kubernetes::Helm::InitCommand do let(:application) { create(:clusters_applications_helm) } - let(:commands) { 'helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null' } + let(:rbac) { false } + let(:files) { {} } + let(:init_command) { described_class.new(name: application.name, files: files, rbac: rbac) } - subject { described_class.new(name: application.name, files: {}) } + let(:commands) do + <<~EOS + helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null + EOS + end + + subject { init_command } it_behaves_like 'helm commands' + + context 'on a rbac-enabled cluster' do + let(:rbac) { true } + + it_behaves_like 'helm commands' do + let(:commands) do + <<~EOS + helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem --service-account tiller >/dev/null + EOS + end + end + end + + describe '#rbac?' do + subject { init_command.rbac? } + + context 'rbac is enabled' do + let(:rbac) { true } + + it { is_expected.to be_truthy } + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it { is_expected.to be_falsey } + end + end + + describe '#config_map_resource' do + let(:metadata) do + { + name: 'values-content-configuration-helm', + namespace: 'gitlab-managed-apps', + labels: { name: 'values-content-configuration-helm' } + } + end + + let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) } + + subject { init_command.config_map_resource } + + it 'returns a KubeClient resource with config map content for the application' do + is_expected.to eq(resource) + end + end + + describe '#pod_resource' do + subject { init_command.pod_resource } + + context 'rbac is enabled' do + let(:rbac) { true } + + it 'generates a pod that uses the tiller serviceAccountName' do + expect(subject.spec.serviceAccountName).to eq('tiller') + end + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it 'generates a pod that uses the default serviceAccountName' do + expect(subject.spec.serviceAcccountName).to be_nil + end + end + end + + describe '#service_account_resource' do + let(:resource) do + Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' }) + end + + subject { init_command.service_account_resource } + + context 'rbac is enabled' do + let(:rbac) { true } + + it 'generates a Kubeclient resource for the tiller ServiceAccount' do + is_expected.to eq(resource) + end + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it 'generates nothing' do + is_expected.to be_nil + end + end + end + + describe '#cluster_role_binding_resource' do + let(:resource) do + Kubeclient::Resource.new( + metadata: { name: 'tiller-admin' }, + roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' }, + subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }] + ) + end + + subject { init_command.cluster_role_binding_resource } + + context 'rbac is enabled' do + let(:rbac) { true } + + it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do + is_expected.to eq(resource) + end + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it 'generates nothing' do + is_expected.to be_nil + end + end + end end diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb index 982e2f41043..f28941ce58f 100644 --- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb @@ -3,14 +3,17 @@ require 'rails_helper' describe Gitlab::Kubernetes::Helm::InstallCommand do let(:files) { { 'ca.pem': 'some file content' } } let(:repository) { 'https://repository.example.com' } + let(:rbac) { false } let(:version) { '1.2.3' } let(:install_command) do described_class.new( name: 'app-name', chart: 'chart-name', + rbac: rbac, files: files, - version: version, repository: repository + version: version, + repository: repository ) end @@ -21,19 +24,76 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do <<~EOS helm init --client-only >/dev/null helm repo add app-name https://repository.example.com - helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null + #{helm_install_comand} + EOS + end + + let(:helm_install_comand) do + <<~EOS.squish + helm install chart-name + --name app-name + --tls + --tls-ca-cert /data/helm/app-name/config/ca.pem + --tls-cert /data/helm/app-name/config/cert.pem + --tls-key /data/helm/app-name/config/key.pem + --version 1.2.3 + --namespace gitlab-managed-apps + -f /data/helm/app-name/config/values.yaml >/dev/null EOS end end + context 'when rbac is true' do + let(:rbac) { true } + + it_behaves_like 'helm commands' do + let(:commands) do + <<~EOS + helm init --client-only >/dev/null + helm repo add app-name https://repository.example.com + #{helm_install_command} + EOS + end + + let(:helm_install_command) do + <<~EOS.squish + helm install chart-name + --name app-name + --tls + --tls-ca-cert /data/helm/app-name/config/ca.pem + --tls-cert /data/helm/app-name/config/cert.pem + --tls-key /data/helm/app-name/config/key.pem + --version 1.2.3 + --set rbac.create\\=true,rbac.enabled\\=true + --namespace gitlab-managed-apps + -f /data/helm/app-name/config/values.yaml >/dev/null + EOS + end + end + end + context 'when there is no repository' do let(:repository) { nil } it_behaves_like 'helm commands' do let(:commands) do <<~EOS - helm init --client-only >/dev/null - helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null + helm init --client-only >/dev/null + #{helm_install_command} + EOS + end + + let(:helm_install_command) do + <<~EOS.squish + helm install chart-name + --name app-name + --tls + --tls-ca-cert /data/helm/app-name/config/ca.pem + --tls-cert /data/helm/app-name/config/cert.pem + --tls-key /data/helm/app-name/config/key.pem + --version 1.2.3 + --namespace gitlab-managed-apps + -f /data/helm/app-name/config/values.yaml >/dev/null EOS end end @@ -45,9 +105,19 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do it_behaves_like 'helm commands' do let(:commands) do <<~EOS - helm init --client-only >/dev/null - helm repo add app-name https://repository.example.com - helm install chart-name --name app-name --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null + helm init --client-only >/dev/null + helm repo add app-name https://repository.example.com + #{helm_install_command} + EOS + end + + let(:helm_install_command) do + <<~EOS.squish + helm install chart-name + --name app-name + --version 1.2.3 + --namespace gitlab-managed-apps + -f /data/helm/app-name/config/values.yaml >/dev/null EOS end end @@ -59,14 +129,63 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do it_behaves_like 'helm commands' do let(:commands) do <<~EOS - helm init --client-only >/dev/null - helm repo add app-name https://repository.example.com - helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null + helm init --client-only >/dev/null + helm repo add app-name https://repository.example.com + #{helm_install_command} + EOS + end + + let(:helm_install_command) do + <<~EOS.squish + helm install chart-name + --name app-name + --tls + --tls-ca-cert /data/helm/app-name/config/ca.pem + --tls-cert /data/helm/app-name/config/cert.pem + --tls-key /data/helm/app-name/config/key.pem + --namespace gitlab-managed-apps + -f /data/helm/app-name/config/values.yaml >/dev/null EOS end end end + describe '#rbac?' do + subject { install_command.rbac? } + + context 'rbac is enabled' do + let(:rbac) { true } + + it { is_expected.to be_truthy } + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it { is_expected.to be_falsey } + end + end + + describe '#pod_resource' do + subject { install_command.pod_resource } + + context 'rbac is enabled' do + let(:rbac) { true } + + it 'generates a pod that uses the tiller serviceAccountName' do + expect(subject.spec.serviceAccountName).to eq('tiller') + end + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it 'generates a pod that uses the default serviceAccountName' do + expect(subject.spec.serviceAcccountName).to be_nil + end + end + end + describe '#config_map_resource' do let(:metadata) do { @@ -84,4 +203,20 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do is_expected.to eq(resource) end end + + describe '#service_account_resource' do + subject { install_command.service_account_resource } + + it 'returns nothing' do + is_expected.to be_nil + end + end + + describe '#cluster_role_binding_resource' do + subject { install_command.cluster_role_binding_resource } + + it 'returns nothing' do + is_expected.to be_nil + end + end end diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb index ec64193c0b2..b333b334f36 100644 --- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb @@ -5,8 +5,9 @@ describe Gitlab::Kubernetes::Helm::Pod do let(:app) { create(:clusters_applications_prometheus) } let(:command) { app.install_command } let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } + let(:service_account_name) { nil } - subject { described_class.new(command, namespace) } + subject { described_class.new(command, namespace, service_account_name: service_account_name) } context 'with a command' do it 'should generate a Kubeclient::Resource' do @@ -58,6 +59,20 @@ describe Gitlab::Kubernetes::Helm::Pod do expect(volume.configMap['items'].first['key']).to eq(:'values.yaml') expect(volume.configMap['items'].first['path']).to eq(:'values.yaml') end + + it 'should have no serviceAccountName' do + spec = subject.generate.spec + expect(spec.serviceAccountName).to be_nil + end + + context 'with a service_account_name' do + let(:service_account_name) { 'sa' } + + it 'should use the serviceAccountName provided' do + spec = subject.generate.spec + expect(spec.serviceAccountName).to eq(service_account_name) + end + end end end end |