diff options
-rw-r--r-- | app/models/project_services/kubernetes_service.rb | 14 | ||||
-rw-r--r-- | lib/gitlab/kubernetes.rb | 39 | ||||
-rw-r--r-- | spec/fixtures/config/kubeconfig-without-ca.yml | 18 | ||||
-rw-r--r-- | spec/fixtures/config/kubeconfig.yml | 19 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes_spec.rb | 24 | ||||
-rw-r--r-- | spec/models/project_services/kubernetes_service_spec.rb | 40 |
6 files changed, 138 insertions, 16 deletions
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 48e7802c557..f1b321139d3 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -96,10 +96,14 @@ class KubernetesService < DeploymentService end def predefined_variables + config = YAML.dump(kubeconfig) + variables = [ { key: 'KUBE_URL', value: api_url, public: true }, { key: 'KUBE_TOKEN', value: token, public: false }, - { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true } + { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true }, + { key: 'KUBECONFIG', value: config, public: false }, + { key: 'KUBECONFIG_FILE', value: config, public: false, file: true }, ] if ca_pem.present? @@ -135,6 +139,14 @@ class KubernetesService < DeploymentService private + def kubeconfig + to_kubeconfig( + url: api_url, + namespace: actual_namespace, + token: token, + ca_pem: ca_pem) + end + def namespace_placeholder default_namespace || TEMPLATE_PLACEHOLDER end diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb index c56c1a4322f..cedef9b65ba 100644 --- a/lib/gitlab/kubernetes.rb +++ b/lib/gitlab/kubernetes.rb @@ -76,5 +76,44 @@ module Gitlab url.to_s end + + def to_kubeconfig(url:, namespace:, token:, ca_pem: nil) + config = { + apiVersion: 'v1', + clusters: [ + name: 'gitlab-deploy', + cluster: { + server: url + }, + ], + contexts: [ + name: 'gitlab-deploy', + context: { + cluster: 'gitlab-deploy', + namespace: namespace, + user: 'gitlab-deploy' + }, + ], + :'current-context' => 'gitlab-deploy', + kind: 'Config', + users: [ + { + name: 'gitlab-deploy', + user: {token: token} + } + ] + } + + kubeconfig_embed_ca_pem(config, ca_pem) if ca_pem + + config.deep_stringify_keys + end + + private + + def kubeconfig_embed_ca_pem(config, ca_pem) + cluster = config.dig(:clusters, 0, :cluster) + cluster[:'certificate-authority-data'] = ca_pem + end end end diff --git a/spec/fixtures/config/kubeconfig-without-ca.yml b/spec/fixtures/config/kubeconfig-without-ca.yml new file mode 100644 index 00000000000..b2cb989d548 --- /dev/null +++ b/spec/fixtures/config/kubeconfig-without-ca.yml @@ -0,0 +1,18 @@ +--- +apiVersion: v1 +clusters: +- name: gitlab-deploy + cluster: + server: https://kube.domain.com +contexts: +- name: gitlab-deploy + context: + cluster: gitlab-deploy + namespace: NAMESPACE + user: gitlab-deploy +current-context: gitlab-deploy +kind: Config +users: +- name: gitlab-deploy + user: + token: TOKEN diff --git a/spec/fixtures/config/kubeconfig.yml b/spec/fixtures/config/kubeconfig.yml new file mode 100644 index 00000000000..4fa52818fee --- /dev/null +++ b/spec/fixtures/config/kubeconfig.yml @@ -0,0 +1,19 @@ +--- +apiVersion: v1 +clusters: +- name: gitlab-deploy + cluster: + server: https://kube.domain.com + certificate-authority-data: PEM +contexts: +- name: gitlab-deploy + context: + cluster: gitlab-deploy + namespace: NAMESPACE + user: gitlab-deploy +current-context: gitlab-deploy +kind: Config +users: +- name: gitlab-deploy + user: + token: TOKEN diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb index e8c599a95ee..34b33772578 100644 --- a/spec/lib/gitlab/kubernetes_spec.rb +++ b/spec/lib/gitlab/kubernetes_spec.rb @@ -46,4 +46,28 @@ describe Gitlab::Kubernetes do expect(filter_by_label(items, app: 'foo')).to eq(matching_items) end end + + describe '#to_kubeconfig' do + subject do + to_kubeconfig( + url: 'https://kube.domain.com', + namespace: 'NAMESPACE', + token: 'TOKEN', + ca_pem: ca_pem) + end + + context 'when CA PEM is provided' do + let(:ca_pem) { 'PEM' } + let(:path) { expand_fixture_path('config/kubeconfig.yml') } + + it { is_expected.to eq(YAML.load_file(path)) } + end + + context 'when CA PEM is not provided' do + let(:ca_pem) { nil } + let(:path) { expand_fixture_path('config/kubeconfig-without-ca.yml') } + + it { is_expected.to eq(YAML.load_file(path)) } + end + end end diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 858ad595dbf..f69e273cd7c 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -129,7 +129,7 @@ describe KubernetesService, models: true, caching: true do it "returns the default namespace" do is_expected.to eq(service.send(:default_namespace)) end - + context 'when namespace is specified' do before do service.namespace = 'my-namespace' @@ -201,6 +201,13 @@ describe KubernetesService, models: true, caching: true do end describe '#predefined_variables' do + let(:kubeconfig) do + File.read(expand_fixture_path('config/kubeconfig.yml')) + .gsub('TOKEN', 'token') + .gsub('PEM', 'CA PEM DATA') + .gsub('NAMESPACE', namespace) + end + before do subject.api_url = 'https://kube.domain.com' subject.token = 'token' @@ -208,32 +215,35 @@ describe KubernetesService, models: true, caching: true do subject.project = project end - context 'namespace is provided' do - before do - subject.namespace = 'my-project' - end - + shared_examples 'setting variables' do it 'sets the variables' do expect(subject.predefined_variables).to include( { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }, { key: 'KUBE_TOKEN', value: 'token', public: false }, - { key: 'KUBE_NAMESPACE', value: 'my-project', public: true }, + { key: 'KUBE_NAMESPACE', value: namespace, public: true }, + { key: 'KUBECONFIG', value: kubeconfig, public: false }, + { key: 'KUBECONFIG_FILE', value: kubeconfig, public: false, file: true }, { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true }, { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true } ) end end - context 'no namespace provided' do - it 'sets the variables' do - expect(subject.predefined_variables).to include( - { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }, - { key: 'KUBE_TOKEN', value: 'token', public: false }, - { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true }, - { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true } - ) + context 'namespace is provided' do + let(:namespace) { 'my-project' } + + before do + subject.namespace = namespace end + it_behaves_like 'setting variables' + end + + context 'no namespace provided' do + let(:namespace) { subject.actual_namespace } + + it_behaves_like 'setting variables' + it 'sets the KUBE_NAMESPACE' do kube_namespace = subject.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' } |