Don't use Kubernetes namespaces with no token

Whenever we are selecting a namespace to use for a
deployment or to query a cluster we want to exclude
Kubernetes namespace records that don't have a token
set as they will not have the required permissions.
However when configuring clusters, we want to
use the original namespace record even if it has no
token, as a namespace has to be unique on a cluster.
This commit is contained in:
Tiger 2019-06-14 10:18:50 +10:00
parent b05de5a583
commit ddd271b602
5 changed files with 101 additions and 5 deletions

View file

@ -193,15 +193,34 @@ module Clusters
platform_kubernetes.kubeclient if kubernetes?
end
##
# This is subtly different to #find_or_initialize_kubernetes_namespace_for_project
# below because it will ignore any namespaces that have not got a service account
# token. This provides a guarantee that any namespace selected here can be used
# for cluster operations - a namespace needs to have a service account configured
# before it it can be used.
#
# This is used for selecting a namespace to use when querying a cluster, or
# generating variables to pass to CI.
def kubernetes_namespace_for(project)
find_or_initialize_kubernetes_namespace_for_project(project).namespace
find_or_initialize_kubernetes_namespace_for_project(
project, scope: kubernetes_namespaces.has_service_account_token
).namespace
end
def find_or_initialize_kubernetes_namespace_for_project(project)
##
# This is subtly different to #kubernetes_namespace_for because it will include
# namespaces that have yet to receive a service account token. This allows
# the namespace configuration process to be repeatable - if a namespace has
# already been created without a token we don't need to create another
# record entirely, just set the token on the pre-existing namespace.
#
# This is used for configuring cluster namespaces.
def find_or_initialize_kubernetes_namespace_for_project(project, scope: kubernetes_namespaces)
attributes = { project: project }
attributes[:cluster_project] = cluster_project if project_type?
kubernetes_namespaces.find_or_initialize_by(attributes).tap do |namespace|
scope.find_or_initialize_by(attributes).tap do |namespace|
namespace.set_defaults
end
end

View file

@ -0,0 +1,6 @@
---
title: Ensure a Kubernetes namespace is not used for deployments if there is no service
account token associated with it
merge_request: 29643
author:
type: fixed

View file

@ -35,7 +35,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
end
context 'and a namespace is already created for this project' do
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, project: build.project) }
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster, project: build.project) }
it { is_expected.to be_falsey }
end

View file

@ -555,6 +555,63 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
describe '#find_or_initialize_kubernetes_namespace_for_project' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.projects.first }
subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project) }
context 'kubernetes namespace exists' do
context 'with no service account token' do
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) }
it { is_expected.to eq kubernetes_namespace }
end
context 'with a service account token' do
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) }
it { is_expected.to eq kubernetes_namespace }
end
end
context 'kubernetes namespace does not exist' do
it 'initializes a new namespace and sets default values' do
expect(subject).to be_new_record
expect(subject.project).to eq project
expect(subject.cluster).to eq cluster
expect(subject.namespace).to be_present
expect(subject.service_account_name).to be_present
end
end
context 'a custom scope is provided' do
let(:scope) { cluster.kubernetes_namespaces.has_service_account_token }
subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project, scope: scope) }
context 'kubernetes namespace exists' do
context 'with no service account token' do
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) }
it 'initializes a new namespace and sets default values' do
expect(subject).to be_new_record
expect(subject.project).to eq project
expect(subject.cluster).to eq cluster
expect(subject.namespace).to be_present
expect(subject.service_account_name).to be_present
end
end
context 'with a service account token' do
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) }
it { is_expected.to eq kubernetes_namespace }
end
end
end
end
describe '#predefined_variables' do
subject { cluster.predefined_variables }

View file

@ -223,19 +223,33 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
let(:namespace) { 'namespace-123' }
it { is_expected.to eq(namespace) }
context 'kubernetes namespace is present but has no service account token' do
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
it { is_expected.to eq(namespace) }
end
end
context 'with no namespace assigned' do
let(:namespace) { nil }
context 'when kubernetes namespace is present' do
let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster) }
before do
kubernetes_namespace
end
it { is_expected.to eq(kubernetes_namespace.namespace) }
context 'kubernetes namespace has no service account token' do
before do
kubernetes_namespace.update!(namespace: 'old-namespace', service_account_token: nil)
end
it { is_expected.to eq("#{project.path}-#{project.id}") }
end
end
context 'when kubernetes namespace is not present' do