Commit e0b3257b authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '33360-generate-kubeconfig' into 'master'

Generate KUBECONFIG in KubernetesService#predefined_variables

Closes #33360

See merge request !12223
parents a33fb213 dbb313c2
...@@ -96,10 +96,13 @@ class KubernetesService < DeploymentService ...@@ -96,10 +96,13 @@ class KubernetesService < DeploymentService
end end
def predefined_variables def predefined_variables
config = YAML.dump(kubeconfig)
variables = [ variables = [
{ key: 'KUBE_URL', value: api_url, public: true }, { key: 'KUBE_URL', value: api_url, public: true },
{ key: 'KUBE_TOKEN', value: token, public: false }, { 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, file: true }
] ]
if ca_pem.present? if ca_pem.present?
...@@ -135,6 +138,14 @@ class KubernetesService < DeploymentService ...@@ -135,6 +138,14 @@ class KubernetesService < DeploymentService
private private
def kubeconfig
to_kubeconfig(
url: api_url,
namespace: actual_namespace,
token: token,
ca_pem: ca_pem)
end
def namespace_placeholder def namespace_placeholder
default_namespace || TEMPLATE_PLACEHOLDER default_namespace || TEMPLATE_PLACEHOLDER
end end
......
---
title: Provide KUBECONFIG from KubernetesService for runners
merge_request: 12223
author:
...@@ -55,6 +55,7 @@ GitLab CI build environment: ...@@ -55,6 +55,7 @@ GitLab CI build environment:
- `KUBE_CA_PEM_FILE` - only present if a custom CA bundle was specified. Path - `KUBE_CA_PEM_FILE` - only present if a custom CA bundle was specified. Path
to a file containing PEM data. to a file containing PEM data.
- `KUBE_CA_PEM` (deprecated)- only if a custom CA bundle was specified. Raw PEM data. - `KUBE_CA_PEM` (deprecated)- only if a custom CA bundle was specified. Raw PEM data.
- `KUBECONFIG` - Path to a file containing kubeconfig for this deployment. CA bundle would be embedded if specified.
## Web terminals ## Web terminals
......
...@@ -76,5 +76,44 @@ module Gitlab ...@@ -76,5 +76,44 @@ module Gitlab
url.to_s url.to_s
end 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'] = Base64.encode64(ca_pem)
end
end end
end end
---
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
---
apiVersion: v1
clusters:
- name: gitlab-deploy
cluster:
server: https://kube.domain.com
certificate-authority-data: "UEVN\n"
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
...@@ -46,4 +46,28 @@ describe Gitlab::Kubernetes do ...@@ -46,4 +46,28 @@ describe Gitlab::Kubernetes do
expect(filter_by_label(items, app: 'foo')).to eq(matching_items) expect(filter_by_label(items, app: 'foo')).to eq(matching_items)
end end
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 end
...@@ -129,7 +129,7 @@ describe KubernetesService, models: true, caching: true do ...@@ -129,7 +129,7 @@ describe KubernetesService, models: true, caching: true do
it "returns the default namespace" do it "returns the default namespace" do
is_expected.to eq(service.send(:default_namespace)) is_expected.to eq(service.send(:default_namespace))
end end
context 'when namespace is specified' do context 'when namespace is specified' do
before do before do
service.namespace = 'my-namespace' service.namespace = 'my-namespace'
...@@ -201,6 +201,22 @@ describe KubernetesService, models: true, caching: true do ...@@ -201,6 +201,22 @@ describe KubernetesService, models: true, caching: true do
end end
describe '#predefined_variables' do describe '#predefined_variables' do
let(:kubeconfig) do
config =
YAML.load(File.read(expand_fixture_path('config/kubeconfig.yml')))
config.dig('users', 0, 'user')['token'] =
'token'
config.dig('clusters', 0, 'cluster')['certificate-authority-data'] =
Base64.encode64('CA PEM DATA')
config.dig('contexts', 0, 'context')['namespace'] =
namespace
YAML.dump(config)
end
before do before do
subject.api_url = 'https://kube.domain.com' subject.api_url = 'https://kube.domain.com'
subject.token = 'token' subject.token = 'token'
...@@ -208,32 +224,34 @@ describe KubernetesService, models: true, caching: true do ...@@ -208,32 +224,34 @@ describe KubernetesService, models: true, caching: true do
subject.project = project subject.project = project
end end
context 'namespace is provided' do shared_examples 'setting variables' do
before do
subject.namespace = 'my-project'
end
it 'sets the variables' do it 'sets the variables' do
expect(subject.predefined_variables).to include( expect(subject.predefined_variables).to include(
{ key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }, { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true },
{ key: 'KUBE_TOKEN', value: 'token', public: false }, { 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, file: true },
{ key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true }, { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true },
{ key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true } { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true }
) )
end end
end end
context 'no namespace provided' do context 'namespace is provided' do
it 'sets the variables' do let(:namespace) { 'my-project' }
expect(subject.predefined_variables).to include(
{ key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }, before do
{ key: 'KUBE_TOKEN', value: 'token', public: false }, subject.namespace = namespace
{ 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
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 it 'sets the KUBE_NAMESPACE' do
kube_namespace = subject.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' } kube_namespace = subject.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment