Commit 075a4ae4 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch '27630-deploy-to-ci-specified-namespace' into 'master'

Deploy to Kubernetes namespace specified in CI template

See merge request gitlab-org/gitlab!20686
parents c6465409 12dc59fc
...@@ -425,6 +425,18 @@ module Ci ...@@ -425,6 +425,18 @@ module Ci
end end
end end
def expanded_kubernetes_namespace
return unless has_environment?
namespace = options.dig(:environment, :kubernetes, :namespace)
if namespace.present?
strong_memoize(:expanded_kubernetes_namespace) do
ExpandVariables.expand(namespace, -> { simple_variables })
end
end
end
def has_environment? def has_environment?
environment.present? environment.present?
end end
......
...@@ -63,7 +63,7 @@ module Clusters ...@@ -63,7 +63,7 @@ module Clusters
default_value_for :authorization_type, :rbac default_value_for :authorization_type, :rbac
def predefined_variables(project:, environment_name:) def predefined_variables(project:, environment_name:, kubernetes_namespace: nil)
Gitlab::Ci::Variables::Collection.new.tap do |variables| Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'KUBE_URL', value: api_url) variables.append(key: 'KUBE_URL', value: api_url)
...@@ -74,15 +74,15 @@ module Clusters ...@@ -74,15 +74,15 @@ module Clusters
end end
if !cluster.managed? || cluster.management_project == project if !cluster.managed? || cluster.management_project == project
namespace = Gitlab::Kubernetes::DefaultNamespace.new(cluster, project: project).from_environment_name(environment_name) namespace = kubernetes_namespace || default_namespace(project, environment_name: environment_name)
variables variables
.append(key: 'KUBE_TOKEN', value: token, public: false, masked: true) .append(key: 'KUBE_TOKEN', value: token, public: false, masked: true)
.append(key: 'KUBE_NAMESPACE', value: namespace) .append(key: 'KUBE_NAMESPACE', value: namespace)
.append(key: 'KUBECONFIG', value: kubeconfig(namespace), public: false, file: true) .append(key: 'KUBECONFIG', value: kubeconfig(namespace), public: false, file: true)
elsif kubernetes_namespace = find_persisted_namespace(project, environment_name: environment_name) elsif persisted_namespace = find_persisted_namespace(project, environment_name: environment_name)
variables.concat(kubernetes_namespace.predefined_variables) variables.concat(persisted_namespace.predefined_variables)
end end
variables.concat(cluster.predefined_variables) variables.concat(cluster.predefined_variables)
...@@ -107,6 +107,13 @@ module Clusters ...@@ -107,6 +107,13 @@ module Clusters
private private
def default_namespace(project, environment_name:)
Gitlab::Kubernetes::DefaultNamespace.new(
cluster,
project: project
).from_environment_name(environment_name)
end
def find_persisted_namespace(project, environment_name:) def find_persisted_namespace(project, environment_name:)
Clusters::KubernetesNamespaceFinder.new( Clusters::KubernetesNamespaceFinder.new(
cluster, cluster,
......
...@@ -15,7 +15,7 @@ module Ci ...@@ -15,7 +15,7 @@ module Ci
variables.concat(project.predefined_variables) variables.concat(project.predefined_variables)
variables.concat(pipeline.predefined_variables) variables.concat(pipeline.predefined_variables)
variables.concat(runner.predefined_variables) if runnable? && runner variables.concat(runner.predefined_variables) if runnable? && runner
variables.concat(project.deployment_variables(environment: environment)) if environment variables.concat(deployment_variables(environment: environment))
variables.concat(yaml_variables) variables.concat(yaml_variables)
variables.concat(user_variables) variables.concat(user_variables)
variables.concat(secret_group_variables) variables.concat(secret_group_variables)
...@@ -72,6 +72,15 @@ module Ci ...@@ -72,6 +72,15 @@ module Ci
end end
end end
def deployment_variables(environment:)
return [] unless environment
project.deployment_variables(
environment: environment,
kubernetes_namespace: expanded_kubernetes_namespace
)
end
def secret_group_variables def secret_group_variables
return [] unless project.group return [] unless project.group
......
...@@ -1986,12 +1986,16 @@ class Project < ApplicationRecord ...@@ -1986,12 +1986,16 @@ class Project < ApplicationRecord
end end
end end
def deployment_variables(environment:) def deployment_variables(environment:, kubernetes_namespace: nil)
platform = deployment_platform(environment: environment) platform = deployment_platform(environment: environment)
return [] unless platform.present? return [] unless platform.present?
platform.predefined_variables(project: self, environment_name: environment) platform.predefined_variables(
project: self,
environment_name: environment,
kubernetes_namespace: kubernetes_namespace
)
end end
def auto_devops_variables def auto_devops_variables
......
---
title: Use CI configured namespace for deployments to unmanaged clusters
merge_request: 20686
author:
type: added
...@@ -1197,6 +1197,54 @@ describe Ci::Build do ...@@ -1197,6 +1197,54 @@ describe Ci::Build do
end end
end end
describe '#expanded_kubernetes_namespace' do
let(:build) { create(:ci_build, environment: environment, options: options) }
subject { build.expanded_kubernetes_namespace }
context 'environment and namespace are not set' do
let(:environment) { nil }
let(:options) { nil }
it { is_expected.to be_nil }
end
context 'environment is specified' do
let(:environment) { 'production' }
context 'namespace is not set' do
let(:options) { nil }
it { is_expected.to be_nil }
end
context 'namespace is provided' do
let(:options) do
{
environment: {
name: environment,
kubernetes: {
namespace: namespace
}
}
}
end
context 'with a static value' do
let(:namespace) { 'production' }
it { is_expected.to eq namespace }
end
context 'with a dynamic value' do
let(:namespace) { 'deploy-$CI_COMMIT_REF_NAME'}
it { is_expected.to eq 'deploy-master' }
end
end
end
end
describe '#starts_environment?' do describe '#starts_environment?' do
subject { build.starts_environment? } subject { build.starts_environment? }
...@@ -2987,6 +3035,32 @@ describe Ci::Build do ...@@ -2987,6 +3035,32 @@ describe Ci::Build do
end end
end end
describe '#deployment_variables' do
let(:build) { create(:ci_build, environment: environment) }
let(:environment) { 'production' }
let(:kubernetes_namespace) { 'namespace' }
let(:project_variables) { double }
subject { build.deployment_variables(environment: environment) }
before do
allow(build).to receive(:expanded_kubernetes_namespace)
.and_return(kubernetes_namespace)
allow(build.project).to receive(:deployment_variables)
.with(environment: environment, kubernetes_namespace: kubernetes_namespace)
.and_return(project_variables)
end
it { is_expected.to eq(project_variables) }
context 'environment is nil' do
let(:environment) { nil }
it { is_expected.to be_empty }
end
end
describe '#scoped_variables_hash' do describe '#scoped_variables_hash' do
context 'when overriding CI variables' do context 'when overriding CI variables' do
before do before do
......
...@@ -290,6 +290,26 @@ describe Clusters::Platforms::Kubernetes do ...@@ -290,6 +290,26 @@ describe Clusters::Platforms::Kubernetes do
it { is_expected.to include(key: 'KUBE_TOKEN', value: platform.token, public: false, masked: true) } it { is_expected.to include(key: 'KUBE_TOKEN', value: platform.token, public: false, masked: true) }
it { is_expected.to include(key: 'KUBE_NAMESPACE', value: namespace) } it { is_expected.to include(key: 'KUBE_NAMESPACE', value: namespace) }
it { is_expected.to include(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) } it { is_expected.to include(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) }
context 'custom namespace is provided' do
let(:custom_namespace) { 'custom-namespace' }
subject do
platform.predefined_variables(
project: project,
environment_name: environment_name,
kubernetes_namespace: custom_namespace
)
end
before do
allow(platform).to receive(:kubeconfig).with(custom_namespace).and_return(kubeconfig)
end
it { is_expected.to include(key: 'KUBE_TOKEN', value: platform.token, public: false, masked: true) }
it { is_expected.to include(key: 'KUBE_NAMESPACE', value: custom_namespace) }
it { is_expected.to include(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) }
end
end end
end end
......
...@@ -2765,8 +2765,9 @@ describe Project do ...@@ -2765,8 +2765,9 @@ describe Project do
describe '#deployment_variables' do describe '#deployment_variables' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:environment) { 'production' } let(:environment) { 'production' }
let(:namespace) { 'namespace' }
subject { project.deployment_variables(environment: environment) } subject { project.deployment_variables(environment: environment, kubernetes_namespace: namespace) }
before do before do
expect(project).to receive(:deployment_platform).with(environment: environment) expect(project).to receive(:deployment_platform).with(environment: environment)
...@@ -2785,7 +2786,7 @@ describe Project do ...@@ -2785,7 +2786,7 @@ describe Project do
before do before do
expect(deployment_platform).to receive(:predefined_variables) expect(deployment_platform).to receive(:predefined_variables)
.with(project: project, environment_name: environment) .with(project: project, environment_name: environment, kubernetes_namespace: namespace)
.and_return(platform_variables) .and_return(platform_variables)
end end
......
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