Commit 375e1a52 authored by ap4y's avatar ap4y

Assign labels to the GMA and project k8s namespaces

Namespace labels are essential for predictable network policy
targeting. Labels will be assigned to the gitlab-managed-apps
namespace and to the project namespaces created by the CI.
parent ecfa56a4
...@@ -21,10 +21,15 @@ module Clusters ...@@ -21,10 +21,15 @@ module Clusters
attr_reader :cluster, :kubernetes_namespace, :platform attr_reader :cluster, :kubernetes_namespace, :platform
def create_project_service_account def create_project_service_account
environment_slug = kubernetes_namespace.environment&.slug
namespace_labels = { 'app.gitlab.com/app' => kubernetes_namespace.project.full_path_slug }
namespace_labels['app.gitlab.com/env'] = environment_slug if environment_slug
Clusters::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator( Clusters::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator(
platform.kubeclient, platform.kubeclient,
service_account_name: kubernetes_namespace.service_account_name, service_account_name: kubernetes_namespace.service_account_name,
service_account_namespace: kubernetes_namespace.namespace, service_account_namespace: kubernetes_namespace.namespace,
service_account_namespace_labels: namespace_labels,
rbac: platform.rbac? rbac: platform.rbac?
).execute ).execute
end end
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
module Clusters module Clusters
module Kubernetes module Kubernetes
class CreateOrUpdateServiceAccountService class CreateOrUpdateServiceAccountService
def initialize(kubeclient, service_account_name:, service_account_namespace:, token_name:, rbac:, namespace_creator: false, role_binding_name: nil) def initialize(kubeclient, service_account_name:, service_account_namespace:, service_account_namespace_labels: nil, token_name:, rbac:, namespace_creator: false, role_binding_name: nil)
@kubeclient = kubeclient @kubeclient = kubeclient
@service_account_name = service_account_name @service_account_name = service_account_name
@service_account_namespace = service_account_namespace @service_account_namespace = service_account_namespace
@service_account_namespace_labels = service_account_namespace_labels
@token_name = token_name @token_name = token_name
@rbac = rbac @rbac = rbac
@namespace_creator = namespace_creator @namespace_creator = namespace_creator
...@@ -23,11 +24,12 @@ module Clusters ...@@ -23,11 +24,12 @@ module Clusters
) )
end end
def self.namespace_creator(kubeclient, service_account_name:, service_account_namespace:, rbac:) def self.namespace_creator(kubeclient, service_account_name:, service_account_namespace:, service_account_namespace_labels:, rbac:)
self.new( self.new(
kubeclient, kubeclient,
service_account_name: service_account_name, service_account_name: service_account_name,
service_account_namespace: service_account_namespace, service_account_namespace: service_account_namespace,
service_account_namespace_labels: service_account_namespace_labels,
token_name: "#{service_account_namespace}-token", token_name: "#{service_account_namespace}-token",
rbac: rbac, rbac: rbac,
namespace_creator: true, namespace_creator: true,
...@@ -55,12 +57,13 @@ module Clusters ...@@ -55,12 +57,13 @@ module Clusters
private private
attr_reader :kubeclient, :service_account_name, :service_account_namespace, :token_name, :rbac, :namespace_creator, :role_binding_name attr_reader :kubeclient, :service_account_name, :service_account_namespace, :service_account_namespace_labels, :token_name, :rbac, :namespace_creator, :role_binding_name
def ensure_project_namespace_exists def ensure_project_namespace_exists
Gitlab::Kubernetes::Namespace.new( Gitlab::Kubernetes::Namespace.new(
service_account_namespace, service_account_namespace,
kubeclient kubeclient,
labels: service_account_namespace_labels
).ensure_exists! ).ensure_exists!
end end
......
---
title: Assign labels to the GMA and project k8s namespaces
merge_request: 23027
author:
type: added
...@@ -6,6 +6,7 @@ module Gitlab ...@@ -6,6 +6,7 @@ module Gitlab
HELM_VERSION = '2.16.1' HELM_VERSION = '2.16.1'
KUBECTL_VERSION = '1.13.12' KUBECTL_VERSION = '1.13.12'
NAMESPACE = 'gitlab-managed-apps' NAMESPACE = 'gitlab-managed-apps'
NAMESPACE_LABELS = { 'app.gitlab.com/managed_by' => :gitlab }.freeze
SERVICE_ACCOUNT = 'tiller' SERVICE_ACCOUNT = 'tiller'
CLUSTER_ROLE_BINDING = 'tiller-admin' CLUSTER_ROLE_BINDING = 'tiller-admin'
CLUSTER_ROLE = 'cluster-admin' CLUSTER_ROLE = 'cluster-admin'
......
...@@ -6,7 +6,11 @@ module Gitlab ...@@ -6,7 +6,11 @@ module Gitlab
class Api class Api
def initialize(kubeclient) def initialize(kubeclient)
@kubeclient = kubeclient @kubeclient = kubeclient
@namespace = Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, kubeclient) @namespace = Gitlab::Kubernetes::Namespace.new(
Gitlab::Kubernetes::Helm::NAMESPACE,
kubeclient,
labels: Gitlab::Kubernetes::Helm::NAMESPACE_LABELS
)
end end
def install(command) def install(command)
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
module Gitlab module Gitlab
module Kubernetes module Kubernetes
class Namespace class Namespace
attr_accessor :name attr_accessor :name, :labels
def initialize(name, client) def initialize(name, client, labels: nil)
@name = name @name = name
@client = client @client = client
@labels = labels
end end
def exists? def exists?
...@@ -17,7 +18,7 @@ module Gitlab ...@@ -17,7 +18,7 @@ module Gitlab
end end
def create! def create!
resource = ::Kubeclient::Resource.new(metadata: { name: name }) resource = ::Kubeclient::Resource.new(metadata: { name: name, labels: labels })
log_event(:begin_create) log_event(:begin_create)
@client.create_namespace(resource) @client.create_namespace(resource)
......
...@@ -6,7 +6,8 @@ describe Gitlab::Kubernetes::Helm::Api do ...@@ -6,7 +6,8 @@ describe Gitlab::Kubernetes::Helm::Api do
let(:client) { double('kubernetes client') } let(:client) { double('kubernetes client') }
let(:helm) { described_class.new(client) } let(:helm) { described_class.new(client) }
let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) } let(:gitlab_namespace_labels) { Gitlab::Kubernetes::Helm::NAMESPACE_LABELS }
let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client, labels: gitlab_namespace_labels) }
let(:application_name) { 'app-name' } let(:application_name) { 'app-name' }
let(:rbac) { false } let(:rbac) { false }
let(:files) { {} } let(:files) { {} }
...@@ -23,13 +24,17 @@ describe Gitlab::Kubernetes::Helm::Api do ...@@ -23,13 +24,17 @@ describe Gitlab::Kubernetes::Helm::Api do
subject { helm } subject { helm }
before do before do
allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client).and_return(namespace) allow(Gitlab::Kubernetes::Namespace).to(
receive(:new).with(gitlab_namespace, client, labels: gitlab_namespace_labels).and_return(namespace)
)
allow(client).to receive(:create_config_map) allow(client).to receive(:create_config_map)
end end
describe '#initialize' do describe '#initialize' do
it 'creates a namespace object' do it 'creates a namespace object' do
expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client) expect(Gitlab::Kubernetes::Namespace).to(
receive(:new).with(gitlab_namespace, client, labels: gitlab_namespace_labels)
)
subject subject
end end
......
...@@ -5,8 +5,9 @@ require 'spec_helper' ...@@ -5,8 +5,9 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Namespace do describe Gitlab::Kubernetes::Namespace do
let(:name) { 'a_namespace' } let(:name) { 'a_namespace' }
let(:client) { double('kubernetes client') } let(:client) { double('kubernetes client') }
let(:labels) { nil }
subject { described_class.new(name, client) } subject { described_class.new(name, client, labels: labels) }
it { expect(subject.name).to eq(name) } it { expect(subject.name).to eq(name) }
...@@ -49,6 +50,17 @@ describe Gitlab::Kubernetes::Namespace do ...@@ -49,6 +50,17 @@ describe Gitlab::Kubernetes::Namespace do
expect { subject.create! }.not_to raise_error expect { subject.create! }.not_to raise_error
end end
context 'with labels' do
let(:labels) { { foo: :bar } }
it 'creates a namespace with labels' do
matcher = have_attributes(metadata: have_attributes(name: name, labels: have_attributes(foo: :bar)))
expect(client).to receive(:create_namespace).with(matcher).once
expect { subject.create! }.not_to raise_error
end
end
end end
describe '#ensure_exists!' do describe '#ensure_exists!' do
......
...@@ -57,11 +57,21 @@ describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do ...@@ -57,11 +57,21 @@ describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
end.to change(Clusters::KubernetesNamespace, :count).by(1) end.to change(Clusters::KubernetesNamespace, :count).by(1)
end end
it 'creates project service account' do it 'creates project service account and namespace' do
expect_next_instance_of(Clusters::Kubernetes::CreateOrUpdateServiceAccountService) do |instance| account_service = double(Clusters::Kubernetes::CreateOrUpdateServiceAccountService)
expect(instance).to receive(:execute).once expect(Clusters::Kubernetes::CreateOrUpdateServiceAccountService).to(
end receive(:namespace_creator).with(
cluster.platform.kubeclient,
service_account_name: "#{namespace}-service-account",
service_account_namespace: namespace,
service_account_namespace_labels: {
'app.gitlab.com/app' => project.full_path_slug,
'app.gitlab.com/env' => environment.slug
},
rbac: true
).and_return(account_service)
)
expect(account_service).to receive(:execute).once
subject subject
end end
...@@ -73,6 +83,29 @@ describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do ...@@ -73,6 +83,29 @@ describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account") expect(kubernetes_namespace.service_account_name).to eq("#{namespace}-service-account")
expect(kubernetes_namespace.encrypted_service_account_token).to be_present expect(kubernetes_namespace.encrypted_service_account_token).to be_present
end end
context 'without environment' do
before do
kubernetes_namespace.environment = nil
end
it 'creates project service account and namespace' do
account_service = double(Clusters::Kubernetes::CreateOrUpdateServiceAccountService)
expect(Clusters::Kubernetes::CreateOrUpdateServiceAccountService).to(
receive(:namespace_creator).with(
cluster.platform.kubeclient,
service_account_name: "#{namespace}-service-account",
service_account_namespace: namespace,
service_account_namespace_labels: {
'app.gitlab.com/app' => project.full_path_slug
},
rbac: true
).and_return(account_service)
)
expect(account_service).to receive(:execute).once
subject
end
end
end end
context 'group clusters' do context 'group clusters' do
......
...@@ -116,6 +116,7 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do ...@@ -116,6 +116,7 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
describe '.namespace_creator' do describe '.namespace_creator' do
let(:namespace) { "#{project.path}-#{project.id}" } let(:namespace) { "#{project.path}-#{project.id}" }
let(:namespace_labels) { { app: project.full_path_slug, env: "staging" } }
let(:service_account_name) { "#{namespace}-service-account" } let(:service_account_name) { "#{namespace}-service-account" }
let(:token_name) { "#{namespace}-token" } let(:token_name) { "#{namespace}-token" }
...@@ -124,6 +125,7 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do ...@@ -124,6 +125,7 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
kubeclient, kubeclient,
service_account_name: service_account_name, service_account_name: service_account_name,
service_account_namespace: namespace, service_account_namespace: namespace,
service_account_namespace_labels: namespace_labels,
rbac: rbac rbac: rbac
).execute ).execute
end end
...@@ -149,6 +151,16 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do ...@@ -149,6 +151,16 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace) stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
end end
it 'creates a namespace object' do
kubernetes_namespace = double(Gitlab::Kubernetes::Namespace)
expect(Gitlab::Kubernetes::Namespace).to(
receive(:new).with(namespace, kubeclient, labels: namespace_labels).and_return(kubernetes_namespace)
)
expect(kubernetes_namespace).to receive(:ensure_exists!)
subject
end
it_behaves_like 'creates service account and token' it_behaves_like 'creates service account and token'
it 'creates a namespaced role binding with edit access' do it 'creates a namespaced role binding with edit access' do
......
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