Skip to content
Snippets Groups Projects
Commit 36a01a88 authored by Tiger Watson's avatar Tiger Watson Committed by Thong Kuah
Browse files

Use separate Kubernetes namespaces per environment

Kubernetes deployments on new clusters will now have
a separate namespace per project environment, instead
of sharing a single namespace for the project.

Behaviour of existing clusters is unchanged.

All new functionality is controlled by the
:kubernetes_namespace_per_environment feature flag,
which is safe to enable/disable at any time.
parent 54377159
No related branches found
No related tags found
No related merge requests found
Showing
with 283 additions and 167 deletions
Loading
@@ -13,11 +13,11 @@ module Clusters
Loading
@@ -13,11 +13,11 @@ module Clusters
self.reactive_cache_key = ->(finder) { finder.model_name } self.reactive_cache_key = ->(finder) { finder.model_name }
self.reactive_cache_worker_finder = ->(_id, *cache_args) { from_cache(*cache_args) } self.reactive_cache_worker_finder = ->(_id, *cache_args) { from_cache(*cache_args) }
   
attr_reader :cluster, :project attr_reader :cluster, :environment
   
def initialize(cluster, project) def initialize(cluster, environment)
@cluster = cluster @cluster = cluster
@project = project @environment = environment
end end
   
def with_reactive_cache_memoized(*cache_args, &block) def with_reactive_cache_memoized(*cache_args, &block)
Loading
@@ -30,11 +30,11 @@ module Clusters
Loading
@@ -30,11 +30,11 @@ module Clusters
clear_reactive_cache!(*cache_args) clear_reactive_cache!(*cache_args)
end end
   
def self.from_cache(cluster_id, project_id) def self.from_cache(cluster_id, environment_id)
cluster = Clusters::Cluster.find(cluster_id) cluster = Clusters::Cluster.find(cluster_id)
project = ::Project.find(project_id) environment = Environment.find(environment_id)
   
new(cluster, project) new(cluster, environment)
end end
   
def calculate_reactive_cache(*) def calculate_reactive_cache(*)
Loading
@@ -56,7 +56,7 @@ module Clusters
Loading
@@ -56,7 +56,7 @@ module Clusters
end end
   
def cache_args def cache_args
[cluster.id, project.id] [cluster.id, environment.id]
end end
   
def service_pod_details(service) def service_pod_details(service)
Loading
@@ -84,7 +84,7 @@ module Clusters
Loading
@@ -84,7 +84,7 @@ module Clusters
private private
   
def search_namespace def search_namespace
@search_namespace ||= cluster.kubernetes_namespace_for(project) @search_namespace ||= cluster.kubernetes_namespace_for(environment)
end end
   
def knative_client def knative_client
Loading
Loading
# frozen_string_literal: true
module Clusters
class KubernetesNamespaceFinder
attr_reader :cluster, :project, :environment_slug
def initialize(cluster, project:, environment_slug:, allow_blank_token: false)
@cluster = cluster
@project = project
@environment_slug = environment_slug
@allow_blank_token = allow_blank_token
end
def execute
find_namespace(with_environment: cluster.namespace_per_environment?)
end
private
attr_reader :allow_blank_token
def find_namespace(with_environment:)
relation = with_environment ? namespaces.with_environment_slug(environment_slug) : namespaces
relation.find_by_project_id(project.id)
end
def namespaces
if allow_blank_token
cluster.kubernetes_namespaces
else
cluster.kubernetes_namespaces.has_service_account_token
end
end
end
end
Loading
@@ -3,10 +3,11 @@
Loading
@@ -3,10 +3,11 @@
module Projects module Projects
module Serverless module Serverless
class FunctionsFinder class FunctionsFinder
include Gitlab::Utils::StrongMemoize
attr_reader :project attr_reader :project
   
def initialize(project) def initialize(project)
@clusters = project.clusters
@project = project @project = project
end end
   
Loading
@@ -16,9 +17,8 @@ module Projects
Loading
@@ -16,9 +17,8 @@ module Projects
   
# Possible return values: Clusters::KnativeServicesFinder::KNATIVE_STATE # Possible return values: Clusters::KnativeServicesFinder::KNATIVE_STATE
def knative_installed def knative_installed
states = @clusters.map do |cluster| states = services_finders.map do |finder|
cluster.application_knative finder.knative_detected.tap do |state|
cluster.knative_services_finder(project).knative_detected.tap do |state|
return state if state == ::Clusters::KnativeServicesFinder::KNATIVE_STATES['checking'] # rubocop:disable Cop/AvoidReturnFromBlocks return state if state == ::Clusters::KnativeServicesFinder::KNATIVE_STATES['checking'] # rubocop:disable Cop/AvoidReturnFromBlocks
end end
end end
Loading
@@ -31,66 +31,70 @@ module Projects
Loading
@@ -31,66 +31,70 @@ module Projects
end end
   
def invocation_metrics(environment_scope, name) def invocation_metrics(environment_scope, name)
return unless prometheus_adapter&.can_query? environment = finders_for_scope(environment_scope).first&.environment
   
cluster = @clusters.find do |c| if environment.present? && environment.prometheus_adapter&.can_query?
environment_scope == c.environment_scope func = ::Serverless::Function.new(project, name, environment.deployment_namespace)
environment.prometheus_adapter.query(:knative_invocation, func)
end end
func = ::Serverless::Function.new(project, name, cluster.kubernetes_namespace_for(project))
prometheus_adapter.query(:knative_invocation, func)
end end
   
def has_prometheus?(environment_scope) def has_prometheus?(environment_scope)
@clusters.any? do |cluster| finders_for_scope(environment_scope).any? do |finder|
environment_scope == cluster.environment_scope && cluster.application_prometheus_available? finder.cluster.application_prometheus_available?
end end
end end
   
private private
   
def knative_service(environment_scope, name) def knative_service(environment_scope, name)
@clusters.map do |cluster| finders_for_scope(environment_scope).map do |finder|
next if environment_scope != cluster.environment_scope services = finder
services = cluster
.knative_services_finder(project)
.services .services
.select { |svc| svc["metadata"]["name"] == name } .select { |svc| svc["metadata"]["name"] == name }
   
add_metadata(cluster, services).first unless services.nil? add_metadata(finder, services).first unless services.nil?
end end
end end
   
def knative_services def knative_services
@clusters.map do |cluster| services_finders.map do |finder|
services = cluster services = finder.services
.knative_services_finder(project)
.services
   
add_metadata(cluster, services) unless services.nil? add_metadata(finder, services) unless services.nil?
end end
end end
   
def add_metadata(cluster, services) def add_metadata(finder, services)
add_pod_count = services.one?
services.each do |s| services.each do |s|
s["environment_scope"] = cluster.environment_scope s["environment_scope"] = finder.cluster.environment_scope
s["cluster_id"] = cluster.id s["cluster_id"] = finder.cluster.id
   
if services.length == 1 if add_pod_count
s["podcount"] = cluster s["podcount"] = finder
.knative_services_finder(project)
.service_pod_details(s["metadata"]["name"]) .service_pod_details(s["metadata"]["name"])
.length .length
end end
end end
end end
   
# rubocop: disable CodeReuse/ServiceClass def services_finders
def prometheus_adapter strong_memoize(:services_finders) do
@prometheus_adapter ||= ::Prometheus::AdapterService.new(project).prometheus_adapter available_environments.map(&:knative_services_finder).compact
end
end
def available_environments
@project.environments.available.preload_cluster
end
def finders_for_scope(environment_scope)
services_finders.select do |finder|
environment_scope == finder.cluster.environment_scope
end
end end
# rubocop: enable CodeReuse/ServiceClass
end end
end end
end end
Loading
@@ -53,6 +53,7 @@ module Clusters
Loading
@@ -53,6 +53,7 @@ module Clusters
validates :name, cluster_name: true validates :name, cluster_name: true
validates :cluster_type, presence: true validates :cluster_type, presence: true
validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true } validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
validates :namespace_per_environment, inclusion: { in: [true, false] }
   
validate :restrict_modification, on: :update validate :restrict_modification, on: :update
validate :no_groups, unless: :group_type? validate :no_groups, unless: :group_type?
Loading
@@ -100,16 +101,6 @@ module Clusters
Loading
@@ -100,16 +101,6 @@ module Clusters
   
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) } scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
   
scope :with_knative_installed, -> { joins(:application_knative).merge(Clusters::Applications::Knative.available) }
scope :preload_knative, -> {
preload(
:kubernetes_namespaces,
:platform_kubernetes,
:application_knative
)
}
def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc) def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc)
return [] if clusterable.is_a?(Instance) return [] if clusterable.is_a?(Instance)
   
Loading
@@ -177,36 +168,15 @@ module Clusters
Loading
@@ -177,36 +168,15 @@ module Clusters
platform_kubernetes.kubeclient if kubernetes? platform_kubernetes.kubeclient if kubernetes?
end end
   
## def kubernetes_namespace_for(environment)
# This is subtly different to #find_or_initialize_kubernetes_namespace_for_project project = environment.project
# below because it will ignore any namespaces that have not got a service account persisted_namespace = Clusters::KubernetesNamespaceFinder.new(
# token. This provides a guarantee that any namespace selected here can be used self,
# for cluster operations - a namespace needs to have a service account configured project: project,
# before it it can be used. environment_slug: environment.slug
# ).execute
# 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, scope: kubernetes_namespaces.has_service_account_token
).namespace
end
##
# 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?
   
scope.find_or_initialize_by(attributes).tap do |namespace| persisted_namespace&.namespace || Gitlab::Kubernetes::DefaultNamespace.new(self, project: project).from_environment_slug(environment.slug)
namespace.set_defaults
end
end end
   
def allow_user_defined_namespace? def allow_user_defined_namespace?
Loading
@@ -225,10 +195,6 @@ module Clusters
Loading
@@ -225,10 +195,6 @@ module Clusters
end end
end end
   
def knative_services_finder(project)
@knative_services_finder ||= KnativeServicesFinder.new(self, project)
end
private private
   
def instance_domain def instance_domain
Loading
Loading
Loading
@@ -9,12 +9,12 @@ module Clusters
Loading
@@ -9,12 +9,12 @@ module Clusters
belongs_to :cluster_project, class_name: 'Clusters::Project' belongs_to :cluster_project, class_name: 'Clusters::Project'
belongs_to :cluster, class_name: 'Clusters::Cluster' belongs_to :cluster, class_name: 'Clusters::Cluster'
belongs_to :project, class_name: '::Project' belongs_to :project, class_name: '::Project'
belongs_to :environment, optional: true
has_one :platform_kubernetes, through: :cluster has_one :platform_kubernetes, through: :cluster
   
before_validation :set_defaults
validates :namespace, presence: true validates :namespace, presence: true
validates :namespace, uniqueness: { scope: :cluster_id } validates :namespace, uniqueness: { scope: :cluster_id }
validates :environment_id, uniqueness: { scope: [:cluster_id, :project_id] }, allow_nil: true
   
validates :service_account_name, presence: true validates :service_account_name, presence: true
   
Loading
@@ -27,6 +27,7 @@ module Clusters
Loading
@@ -27,6 +27,7 @@ module Clusters
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
   
scope :has_service_account_token, -> { where.not(encrypted_service_account_token: nil) } scope :has_service_account_token, -> { where.not(encrypted_service_account_token: nil) }
scope :with_environment_slug, -> (slug) { joins(:environment).where(environments: { slug: slug }) }
   
def token_name def token_name
"#{namespace}-token" "#{namespace}-token"
Loading
@@ -42,34 +43,8 @@ module Clusters
Loading
@@ -42,34 +43,8 @@ module Clusters
end end
end end
   
def set_defaults
self.namespace ||= default_platform_kubernetes_namespace
self.namespace ||= default_project_namespace
self.service_account_name ||= default_service_account_name
end
private private
   
def default_service_account_name
return unless namespace
"#{namespace}-service-account"
end
def default_platform_kubernetes_namespace
platform_kubernetes&.namespace.presence
end
def default_project_namespace
Gitlab::NamespaceSanitizer.sanitize(project_slug) if project_slug
end
def project_slug
return unless project
"#{project.path}-#{project.id}".downcase
end
def kubeconfig def kubeconfig
to_kubeconfig( to_kubeconfig(
url: api_url, url: api_url,
Loading
Loading
Loading
@@ -51,11 +51,6 @@ module Clusters
Loading
@@ -51,11 +51,6 @@ module Clusters
delegate :provided_by_user?, to: :cluster, allow_nil: true delegate :provided_by_user?, to: :cluster, allow_nil: true
delegate :allow_user_defined_namespace?, to: :cluster, allow_nil: true delegate :allow_user_defined_namespace?, to: :cluster, allow_nil: true
   
# This is just to maintain compatibility with KubernetesService, which
# will be removed in https://gitlab.com/gitlab-org/gitlab-ce/issues/39217.
# It can be removed once KubernetesService is gone.
delegate :kubernetes_namespace_for, to: :cluster, allow_nil: true
alias_method :active?, :enabled? alias_method :active?, :enabled?
   
enum_with_nil authorization_type: { enum_with_nil authorization_type: {
Loading
@@ -66,7 +61,7 @@ module Clusters
Loading
@@ -66,7 +61,7 @@ module Clusters
   
default_value_for :authorization_type, :rbac default_value_for :authorization_type, :rbac
   
def predefined_variables(project:) def predefined_variables(project:, environment_name:)
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)
   
Loading
@@ -77,15 +72,14 @@ module Clusters
Loading
@@ -77,15 +72,14 @@ module Clusters
end end
   
if !cluster.managed? if !cluster.managed?
project_namespace = namespace.presence || "#{project.path}-#{project.id}".downcase namespace = Gitlab::Kubernetes::DefaultNamespace.new(cluster, project: project).from_environment_name(environment_name)
   
variables variables
.append(key: 'KUBE_URL', value: api_url)
.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: project_namespace) .append(key: 'KUBE_NAMESPACE', value: namespace)
.append(key: 'KUBECONFIG', value: kubeconfig(project_namespace), public: false, file: true) .append(key: 'KUBECONFIG', value: kubeconfig(namespace), public: false, file: true)
   
elsif kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project) elsif kubernetes_namespace = find_persisted_namespace(project, environment_name: environment_name)
variables.concat(kubernetes_namespace.predefined_variables) variables.concat(kubernetes_namespace.predefined_variables)
end end
   
Loading
@@ -111,6 +105,22 @@ module Clusters
Loading
@@ -111,6 +105,22 @@ module Clusters
   
private private
   
##
# Environment slug can be predicted given an environment
# name, so even if the environment isn't persisted yet we
# still know what to look for.
def environment_slug(name)
Gitlab::Slug::Environment.new(name).generate
end
def find_persisted_namespace(project, environment_name:)
Clusters::KubernetesNamespaceFinder.new(
cluster,
project: project,
environment_slug: environment_slug(environment_name)
).execute
end
def kubeconfig(namespace) def kubeconfig(namespace)
to_kubeconfig( to_kubeconfig(
url: api_url, url: api_url,
Loading
Loading
Loading
@@ -48,6 +48,7 @@ class Environment < ApplicationRecord
Loading
@@ -48,6 +48,7 @@ class Environment < ApplicationRecord
end end
scope :in_review_folder, -> { where(environment_type: "review") } scope :in_review_folder, -> { where(environment_type: "review") }
scope :for_name, -> (name) { where(name: name) } scope :for_name, -> (name) { where(name: name) }
scope :preload_cluster, -> { preload(last_deployment: :cluster) }
   
## ##
# Search environments which have names like the given query. # Search environments which have names like the given query.
Loading
@@ -170,7 +171,7 @@ class Environment < ApplicationRecord
Loading
@@ -170,7 +171,7 @@ class Environment < ApplicationRecord
   
def deployment_namespace def deployment_namespace
strong_memoize(:kubernetes_namespace) do strong_memoize(:kubernetes_namespace) do
deployment_platform&.kubernetes_namespace_for(project) deployment_platform.cluster.kubernetes_namespace_for(self) if deployment_platform
end end
end end
   
Loading
@@ -233,6 +234,12 @@ class Environment < ApplicationRecord
Loading
@@ -233,6 +234,12 @@ class Environment < ApplicationRecord
end end
end end
   
def knative_services_finder
if last_deployment&.cluster
Clusters::KnativeServicesFinder.new(last_deployment.cluster, self)
end
end
private private
   
def generate_slug def generate_slug
Loading
Loading
Loading
@@ -1855,8 +1855,12 @@ class Project < ApplicationRecord
Loading
@@ -1855,8 +1855,12 @@ class Project < ApplicationRecord
end end
end end
   
def deployment_variables(environment: nil) def deployment_variables(environment:)
deployment_platform(environment: environment)&.predefined_variables(project: self) || [] platform = deployment_platform(environment: environment)
return [] unless platform.present?
platform.predefined_variables(project: self, environment_name: environment)
end end
   
def auto_devops_variables def auto_devops_variables
Loading
Loading
Loading
@@ -24,7 +24,7 @@ class MockDeploymentService < Service
Loading
@@ -24,7 +24,7 @@ class MockDeploymentService < Service
%w() %w()
end end
   
def predefined_variables(project:) def predefined_variables(project:, environment_name:)
[] []
end end
   
Loading
Loading
# frozen_string_literal: true
module Clusters
class BuildKubernetesNamespaceService
attr_reader :cluster, :environment
def initialize(cluster, environment:)
@cluster = cluster
@environment = environment
end
def execute
cluster.kubernetes_namespaces.build(attributes)
end
private
def attributes
attributes = {
project: environment.project,
namespace: namespace,
service_account_name: "#{namespace}-service-account"
}
attributes[:cluster_project] = cluster.cluster_project if cluster.project_type?
attributes[:environment] = environment if cluster.namespace_per_environment?
attributes
end
def namespace
Gitlab::Kubernetes::DefaultNamespace.new(cluster, project: environment.project).from_environment_slug(environment.slug)
end
end
end
Loading
@@ -11,7 +11,8 @@ module Clusters
Loading
@@ -11,7 +11,8 @@ module Clusters
def execute(access_token: nil) def execute(access_token: nil)
raise ArgumentError, 'Unknown clusterable provided' unless clusterable raise ArgumentError, 'Unknown clusterable provided' unless clusterable
   
cluster_params = params.merge(user: current_user).merge(clusterable_params) cluster_params = params.merge(global_params).merge(clusterable_params)
cluster_params[:provider_gcp_attributes].try do |provider| cluster_params[:provider_gcp_attributes].try do |provider|
provider[:access_token] = access_token provider[:access_token] = access_token
end end
Loading
@@ -35,6 +36,10 @@ module Clusters
Loading
@@ -35,6 +36,10 @@ module Clusters
@clusterable ||= params.delete(:clusterable) @clusterable ||= params.delete(:clusterable)
end end
   
def global_params
{ user: current_user, namespace_per_environment: Feature.enabled?(:kubernetes_namespace_per_environment, default_enabled: true) }
end
def clusterable_params def clusterable_params
case clusterable case clusterable
when ::Project when ::Project
Loading
Loading
Loading
@@ -11,7 +11,6 @@ module Clusters
Loading
@@ -11,7 +11,6 @@ module Clusters
end end
   
def execute def execute
configure_kubernetes_namespace
create_project_service_account create_project_service_account
configure_kubernetes_token configure_kubernetes_token
   
Loading
@@ -22,10 +21,6 @@ module Clusters
Loading
@@ -22,10 +21,6 @@ module Clusters
   
attr_reader :cluster, :kubernetes_namespace, :platform attr_reader :cluster, :kubernetes_namespace, :platform
   
def configure_kubernetes_namespace
kubernetes_namespace.set_defaults
end
def create_project_service_account def create_project_service_account
Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator( Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator(
platform.kubeclient, platform.kubeclient,
Loading
Loading
---
title: Use separate Kubernetes namespaces per environment
merge_request: 30711
author:
type: added
# frozen_string_literal: true
class AddEnvironmentIdToClustersKubernetesNamespaces < ActiveRecord::Migration[5.1]
DOWNTIME = false
def change
add_reference :clusters_kubernetes_namespaces, :environment,
index: true, type: :bigint, foreign_key: { on_delete: :nullify }
end
end
# frozen_string_literal: true
class IndexClustersKubernetesNamespacesOnEnvironmentId < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_kubernetes_namespaces_on_cluster_project_environment_id'
disable_ddl_transaction!
def up
add_concurrent_index :clusters_kubernetes_namespaces, [:cluster_id, :project_id, :environment_id], unique: true, name: INDEX_NAME
end
def down
remove_concurrent_index :clusters_kubernetes_namespaces, name: INDEX_NAME
end
end
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddNamespacePerEnvironmentFlagToClusters < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default :clusters, :namespace_per_environment, :boolean, default: false
end
def down
remove_column :clusters, :namespace_per_environment
end
end
Loading
@@ -880,6 +880,7 @@ ActiveRecord::Schema.define(version: 2019_08_02_235445) do
Loading
@@ -880,6 +880,7 @@ ActiveRecord::Schema.define(version: 2019_08_02_235445) do
t.integer "cluster_type", limit: 2, default: 3, null: false t.integer "cluster_type", limit: 2, default: 3, null: false
t.string "domain" t.string "domain"
t.boolean "managed", default: true, null: false t.boolean "managed", default: true, null: false
t.boolean "namespace_per_environment", default: false, null: false
t.index ["enabled"], name: "index_clusters_on_enabled" t.index ["enabled"], name: "index_clusters_on_enabled"
t.index ["user_id"], name: "index_clusters_on_user_id" t.index ["user_id"], name: "index_clusters_on_user_id"
end end
Loading
@@ -984,9 +985,12 @@ ActiveRecord::Schema.define(version: 2019_08_02_235445) do
Loading
@@ -984,9 +985,12 @@ ActiveRecord::Schema.define(version: 2019_08_02_235445) do
t.string "encrypted_service_account_token_iv" t.string "encrypted_service_account_token_iv"
t.string "namespace", null: false t.string "namespace", null: false
t.string "service_account_name" t.string "service_account_name"
t.bigint "environment_id"
t.index ["cluster_id", "namespace"], name: "kubernetes_namespaces_cluster_and_namespace", unique: true t.index ["cluster_id", "namespace"], name: "kubernetes_namespaces_cluster_and_namespace", unique: true
t.index ["cluster_id", "project_id", "environment_id"], name: "index_kubernetes_namespaces_on_cluster_project_environment_id", unique: true
t.index ["cluster_id"], name: "index_clusters_kubernetes_namespaces_on_cluster_id" t.index ["cluster_id"], name: "index_clusters_kubernetes_namespaces_on_cluster_id"
t.index ["cluster_project_id"], name: "index_clusters_kubernetes_namespaces_on_cluster_project_id" t.index ["cluster_project_id"], name: "index_clusters_kubernetes_namespaces_on_cluster_project_id"
t.index ["environment_id"], name: "index_clusters_kubernetes_namespaces_on_environment_id"
t.index ["project_id"], name: "index_clusters_kubernetes_namespaces_on_project_id" t.index ["project_id"], name: "index_clusters_kubernetes_namespaces_on_project_id"
end end
   
Loading
@@ -3711,6 +3715,7 @@ ActiveRecord::Schema.define(version: 2019_08_02_235445) do
Loading
@@ -3711,6 +3715,7 @@ ActiveRecord::Schema.define(version: 2019_08_02_235445) do
add_foreign_key "clusters_applications_runners", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_runners", "clusters", on_delete: :cascade
add_foreign_key "clusters_kubernetes_namespaces", "cluster_projects", on_delete: :nullify add_foreign_key "clusters_kubernetes_namespaces", "cluster_projects", on_delete: :nullify
add_foreign_key "clusters_kubernetes_namespaces", "clusters", on_delete: :cascade add_foreign_key "clusters_kubernetes_namespaces", "clusters", on_delete: :cascade
add_foreign_key "clusters_kubernetes_namespaces", "environments", on_delete: :nullify
add_foreign_key "clusters_kubernetes_namespaces", "projects", on_delete: :nullify add_foreign_key "clusters_kubernetes_namespaces", "projects", on_delete: :nullify
add_foreign_key "container_repositories", "projects" add_foreign_key "container_repositories", "projects"
add_foreign_key "dependency_proxy_blobs", "namespaces", column: "group_id", name: "fk_db58bbc5d7", on_delete: :cascade add_foreign_key "dependency_proxy_blobs", "namespaces", column: "group_id", name: "fk_db58bbc5d7", on_delete: :cascade
Loading
Loading
Loading
@@ -384,13 +384,9 @@ NOTE: **Note:**
Loading
@@ -384,13 +384,9 @@ NOTE: **Note:**
[RBAC](#rbac-cluster-resources) is recommended and the GitLab default. [RBAC](#rbac-cluster-resources) is recommended and the GitLab default.
   
GitLab creates the necessary service accounts and privileges to install and run GitLab creates the necessary service accounts and privileges to install and run
[GitLab managed applications](#installing-applications). When GitLab creates the cluster: [GitLab managed applications](#installing-applications). When GitLab creates the cluster,
a `gitlab` service account with `cluster-admin` privileges is created in the `default` namespace
- A `gitlab` service account with `cluster-admin` privileges is created in the `default` namespace to manage the newly created cluster.
to manage the newly created cluster.
- A project service account with [`edit`
privileges](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
is created in the GitLab-created project namespace for [deployment jobs](#deployment-variables).
   
NOTE: **Note:** NOTE: **Note:**
Restricted service account for deployment was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/51716) in GitLab 11.5. Restricted service account for deployment was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/51716) in GitLab 11.5.
Loading
@@ -412,32 +408,37 @@ The resources created by GitLab differ depending on the type of cluster.
Loading
@@ -412,32 +408,37 @@ The resources created by GitLab differ depending on the type of cluster.
   
GitLab creates the following resources for ABAC clusters. GitLab creates the following resources for ABAC clusters.
   
| Name | Type | Details | Created when | | Name | Type | Details | Created when |
|:------------------|:---------------------|:----------------------------------|:---------------------------| |:----------------------|:---------------------|:-------------------------------------|:---------------------------|
| `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster | | `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster |
| `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster | | `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster |
| `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller | | `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller |
| `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller | | `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller |
| Project namespace | `ServiceAccount` | Uses namespace of Project | Deploying to a cluster | | Environment namespace | `Namespace` | Contains all environment-specific resources | Deploying to a cluster |
| Project namespace | `Secret` | Token for project ServiceAccount | Deploying to a cluster | | Environment namespace | `ServiceAccount` | Uses namespace of environment | Deploying to a cluster |
| Environment namespace | `Secret` | Token for environment ServiceAccount | Deploying to a cluster |
   
#### RBAC cluster resources #### RBAC cluster resources
   
GitLab creates the following resources for RBAC clusters. GitLab creates the following resources for RBAC clusters.
   
| Name | Type | Details | Created when | | Name | Type | Details | Created when |
|:------------------|:---------------------|:-----------------------------------------------------------------------------------------------------------|:---------------------------| |:----------------------|:---------------------|:-----------------------------------------------------------------------------------------------------------|:---------------------------|
| `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster | | `gitlab` | `ServiceAccount` | `default` namespace | Creating a new GKE Cluster |
| `gitlab-admin` | `ClusterRoleBinding` | [`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Creating a new GKE Cluster | | `gitlab-admin` | `ClusterRoleBinding` | [`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Creating a new GKE Cluster |
| `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster | | `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new GKE Cluster |
| `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller | | `tiller` | `ServiceAccount` | `gitlab-managed-apps` namespace | Installing Helm Tiller |
| `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller | | `tiller-admin` | `ClusterRoleBinding` | `cluster-admin` roleRef | Installing Helm Tiller |
| Project namespace | `ServiceAccount` | Uses namespace of Project | Deploying to a cluster | | Environment namespace | `Namespace` | Contains all environment-specific resources | Deploying to a cluster |
| Project namespace | `Secret` | Token for project ServiceAccount | Deploying to a cluster | | Environment namespace | `ServiceAccount` | Uses namespace of environment | Deploying to a cluster |
| Project namespace | `RoleBinding` | [`edit`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Deploying to a cluster | | Environment namespace | `Secret` | Token for environment ServiceAccount | Deploying to a cluster |
| Environment namespace | `RoleBinding` | [`edit`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Deploying to a cluster |
NOTE: **Note:**
Environment-specific resources are only created if your cluster is [managed by GitLab](#gitlab-managed-clusters).
   
NOTE: **Note:** NOTE: **Note:**
Project-specific resources are only created if your cluster is [managed by GitLab](#gitlab-managed-clusters). If your project was created before GitLab 12.2 it will use a single namespace for all project environments.
   
#### Security of GitLab Runners #### Security of GitLab Runners
   
Loading
@@ -640,8 +641,8 @@ GitLab CI/CD build environment.
Loading
@@ -640,8 +641,8 @@ GitLab CI/CD build environment.
| Variable | Description | | Variable | Description |
| -------- | ----------- | | -------- | ----------- |
| `KUBE_URL` | Equal to the API URL. | | `KUBE_URL` | Equal to the API URL. |
| `KUBE_TOKEN` | The Kubernetes token of the [project service account](#access-controls). | | `KUBE_TOKEN` | The Kubernetes token of the [environment service account](#access-controls). |
| `KUBE_NAMESPACE` | The Kubernetes namespace is auto-generated if not specified. The default value is `<project_name>-<project_id>`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. | | `KUBE_NAMESPACE` | The Kubernetes namespace is auto-generated if not specified. The default value is `<project_name>-<project_id>-<environment>`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. |
| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. | | `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. | | `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. | | `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
Loading
Loading
Loading
@@ -434,7 +434,7 @@ The instructions below relate to installing and running Certbot on a Linux serve
Loading
@@ -434,7 +434,7 @@ The instructions below relate to installing and running Certbot on a Linux serve
./certbot-auto certonly --manual --preferred-challenges dns -d '*.<namespace>.example.com' ./certbot-auto certonly --manual --preferred-challenges dns -d '*.<namespace>.example.com'
``` ```
   
Where `<namespace>` is the namespace created by GitLab for your serverless project (composed of `<projectname+id>`) and Where `<namespace>` is the namespace created by GitLab for your serverless project (composed of `<project_name>-<project_id>-<environment>`) and
`example.com` is the domain being used for your project. If you are unsure what the namespace of your project is, navigate `example.com` is the domain being used for your project. If you are unsure what the namespace of your project is, navigate
to the **Operations > Serverless** page of your project and inspect to the **Operations > Serverless** page of your project and inspect
the endpoint provided for your function/app. the endpoint provided for your function/app.
Loading
Loading
Loading
@@ -8,31 +8,51 @@ module Gitlab
Loading
@@ -8,31 +8,51 @@ module Gitlab
def unmet? def unmet?
deployment_cluster.present? && deployment_cluster.present? &&
deployment_cluster.managed? && deployment_cluster.managed? &&
(kubernetes_namespace.new_record? || kubernetes_namespace.service_account_token.blank?) missing_namespace?
end end
   
def complete! def complete!
return unless unmet? return unless unmet?
   
create_or_update_namespace create_namespace
end end
   
private private
   
def missing_namespace?
kubernetes_namespace.nil? || kubernetes_namespace.service_account_token.blank?
end
def deployment_cluster def deployment_cluster
build.deployment&.cluster build.deployment&.cluster
end end
   
def environment
build.deployment.environment
end
def kubernetes_namespace def kubernetes_namespace
strong_memoize(:kubernetes_namespace) do strong_memoize(:kubernetes_namespace) do
deployment_cluster.find_or_initialize_kubernetes_namespace_for_project(build.project) Clusters::KubernetesNamespaceFinder.new(
deployment_cluster,
project: environment.project,
environment_slug: environment.slug,
allow_blank_token: true
).execute
end end
end end
   
def create_or_update_namespace def create_namespace
Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new( Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
cluster: deployment_cluster, cluster: deployment_cluster,
kubernetes_namespace: kubernetes_namespace kubernetes_namespace: kubernetes_namespace || build_namespace_record
).execute
end
def build_namespace_record
Clusters::BuildKubernetesNamespaceService.new(
deployment_cluster,
environment: environment
).execute ).execute
end end
end end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment