This instance will be upgraded to Heptapod 0.31.0 (final) on 2022-05-24 at 14:00 UTC+2 (a few minutes of down time)

Commit 27f3bead authored by Tiger's avatar Tiger
Browse files

Move generic k8s services out of GCP namespace

These services aren't specific to GCP, and will be used for AWS
as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/46686
parent fa7f8a464dc3
......@@ -26,7 +26,7 @@ def execute(provider)
private
def create_gitlab_service_account!
Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService.gitlab_creator(
Clusters::Kubernetes::CreateOrUpdateServiceAccountService.gitlab_creator(
kube_client,
rbac: create_rbac_cluster?
).execute
......@@ -49,10 +49,10 @@ def configure_kubernetes
end
def request_kubernetes_token
Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(
Clusters::Kubernetes::FetchKubernetesTokenService.new(
kube_client,
Clusters::Gcp::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
Clusters::Gcp::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE
Clusters::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
Clusters::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE
).execute
end
......
# frozen_string_literal: true
module Clusters
module Gcp
module Kubernetes
GITLAB_SERVICE_ACCOUNT_NAME = 'gitlab'
GITLAB_SERVICE_ACCOUNT_NAMESPACE = 'default'
GITLAB_ADMIN_TOKEN_NAME = 'gitlab-token'
GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin'
GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin'
PROJECT_CLUSTER_ROLE_NAME = 'edit'
GITLAB_KNATIVE_SERVING_ROLE_NAME = 'gitlab-knative-serving-role'
GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME = 'gitlab-knative-serving-rolebinding'
end
end
end
# frozen_string_literal: true
module Clusters
module Gcp
module Kubernetes
class CreateOrUpdateNamespaceService
def initialize(cluster:, kubernetes_namespace:)
@cluster = cluster
@kubernetes_namespace = kubernetes_namespace
@platform = cluster.platform
end
def execute
create_project_service_account
configure_kubernetes_token
kubernetes_namespace.save!
end
private
attr_reader :cluster, :kubernetes_namespace, :platform
def create_project_service_account
Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator(
platform.kubeclient,
service_account_name: kubernetes_namespace.service_account_name,
service_account_namespace: kubernetes_namespace.namespace,
rbac: platform.rbac?
).execute
end
def configure_kubernetes_token
kubernetes_namespace.service_account_token = fetch_service_account_token
end
def fetch_service_account_token
Clusters::Gcp::Kubernetes::FetchKubernetesTokenService.new(
platform.kubeclient,
kubernetes_namespace.token_name,
kubernetes_namespace.namespace
).execute
end
end
end
end
end
# frozen_string_literal: true
module Clusters
module Gcp
module Kubernetes
class CreateOrUpdateServiceAccountService
def initialize(kubeclient, service_account_name:, service_account_namespace:, token_name:, rbac:, namespace_creator: false, role_binding_name: nil)
@kubeclient = kubeclient
@service_account_name = service_account_name
@service_account_namespace = service_account_namespace
@token_name = token_name
@rbac = rbac
@namespace_creator = namespace_creator
@role_binding_name = role_binding_name
end
def self.gitlab_creator(kubeclient, rbac:)
self.new(
kubeclient,
service_account_name: Clusters::Gcp::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAME,
service_account_namespace: Clusters::Gcp::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE,
token_name: Clusters::Gcp::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
rbac: rbac
)
end
def self.namespace_creator(kubeclient, service_account_name:, service_account_namespace:, rbac:)
self.new(
kubeclient,
service_account_name: service_account_name,
service_account_namespace: service_account_namespace,
token_name: "#{service_account_namespace}-token",
rbac: rbac,
namespace_creator: true,
role_binding_name: "gitlab-#{service_account_namespace}"
)
end
def execute
ensure_project_namespace_exists if namespace_creator
kubeclient.create_or_update_service_account(service_account_resource)
kubeclient.create_or_update_secret(service_account_token_resource)
return unless rbac
create_role_or_cluster_role_binding
return unless namespace_creator
create_or_update_knative_serving_role
create_or_update_knative_serving_role_binding
end
private
attr_reader :kubeclient, :service_account_name, :service_account_namespace, :token_name, :rbac, :namespace_creator, :role_binding_name
def ensure_project_namespace_exists
Gitlab::Kubernetes::Namespace.new(
service_account_namespace,
kubeclient
).ensure_exists!
end
def create_role_or_cluster_role_binding
if namespace_creator
kubeclient.create_or_update_role_binding(role_binding_resource)
else
kubeclient.create_or_update_cluster_role_binding(cluster_role_binding_resource)
end
end
def create_or_update_knative_serving_role
kubeclient.update_role(knative_serving_role_resource)
end
def create_or_update_knative_serving_role_binding
kubeclient.update_role_binding(knative_serving_role_binding_resource)
end
def service_account_resource
Gitlab::Kubernetes::ServiceAccount.new(
service_account_name,
service_account_namespace
).generate
end
def service_account_token_resource
Gitlab::Kubernetes::ServiceAccountToken.new(
token_name,
service_account_name,
service_account_namespace
).generate
end
def cluster_role_binding_resource
subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: service_account_namespace }]
Gitlab::Kubernetes::ClusterRoleBinding.new(
Clusters::Gcp::Kubernetes::GITLAB_CLUSTER_ROLE_BINDING_NAME,
Clusters::Gcp::Kubernetes::GITLAB_CLUSTER_ROLE_NAME,
subjects
).generate
end
def role_binding_resource
Gitlab::Kubernetes::RoleBinding.new(
name: role_binding_name,
role_name: Clusters::Gcp::Kubernetes::PROJECT_CLUSTER_ROLE_NAME,
role_kind: :ClusterRole,
namespace: service_account_namespace,
service_account_name: service_account_name
).generate
end
def knative_serving_role_resource
Gitlab::Kubernetes::Role.new(
name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
namespace: service_account_namespace,
rules: [{
apiGroups: %w(serving.knative.dev),
resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services),
verbs: %w(get list create update delete patch watch)
}]
).generate
end
def knative_serving_role_binding_resource
Gitlab::Kubernetes::RoleBinding.new(
name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME,
role_name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
role_kind: :Role,
namespace: service_account_namespace,
service_account_name: service_account_name
).generate
end
end
end
end
end
# frozen_string_literal: true
module Clusters
module Gcp
module Kubernetes
class FetchKubernetesTokenService
DEFAULT_TOKEN_RETRY_DELAY = 5.seconds
TOKEN_RETRY_LIMIT = 5
attr_reader :kubeclient, :service_account_token_name, :namespace
def initialize(kubeclient, service_account_token_name, namespace, token_retry_delay: DEFAULT_TOKEN_RETRY_DELAY)
@kubeclient = kubeclient
@service_account_token_name = service_account_token_name
@namespace = namespace
@token_retry_delay = token_retry_delay
end
def execute
# Kubernetes will create the Secret and set the token asynchronously
# so it is necessary to retry
# https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#token-controller
TOKEN_RETRY_LIMIT.times do
token_base64 = get_secret&.dig('data', 'token')
return Base64.decode64(token_base64) if token_base64
sleep @token_retry_delay
end
nil
end
private
def get_secret
kubeclient.get_secret(service_account_token_name, namespace).as_json
rescue Kubeclient::ResourceNotFoundError
end
end
end
end
end
# frozen_string_literal: true
module Clusters
module Kubernetes
class CreateOrUpdateNamespaceService
def initialize(cluster:, kubernetes_namespace:)
@cluster = cluster
@kubernetes_namespace = kubernetes_namespace
@platform = cluster.platform
end
def execute
create_project_service_account
configure_kubernetes_token
kubernetes_namespace.save!
end
private
attr_reader :cluster, :kubernetes_namespace, :platform
def create_project_service_account
Clusters::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator(
platform.kubeclient,
service_account_name: kubernetes_namespace.service_account_name,
service_account_namespace: kubernetes_namespace.namespace,
rbac: platform.rbac?
).execute
end
def configure_kubernetes_token
kubernetes_namespace.service_account_token = fetch_service_account_token
end
def fetch_service_account_token
Clusters::Kubernetes::FetchKubernetesTokenService.new(
platform.kubeclient,
kubernetes_namespace.token_name,
kubernetes_namespace.namespace
).execute
end
end
end
end
# frozen_string_literal: true
module Clusters
module Kubernetes
class CreateOrUpdateServiceAccountService
def initialize(kubeclient, service_account_name:, service_account_namespace:, token_name:, rbac:, namespace_creator: false, role_binding_name: nil)
@kubeclient = kubeclient
@service_account_name = service_account_name
@service_account_namespace = service_account_namespace
@token_name = token_name
@rbac = rbac
@namespace_creator = namespace_creator
@role_binding_name = role_binding_name
end
def self.gitlab_creator(kubeclient, rbac:)
self.new(
kubeclient,
service_account_name: Clusters::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAME,
service_account_namespace: Clusters::Kubernetes::GITLAB_SERVICE_ACCOUNT_NAMESPACE,
token_name: Clusters::Kubernetes::GITLAB_ADMIN_TOKEN_NAME,
rbac: rbac
)
end
def self.namespace_creator(kubeclient, service_account_name:, service_account_namespace:, rbac:)
self.new(
kubeclient,
service_account_name: service_account_name,
service_account_namespace: service_account_namespace,
token_name: "#{service_account_namespace}-token",
rbac: rbac,
namespace_creator: true,
role_binding_name: "gitlab-#{service_account_namespace}"
)
end
def execute
ensure_project_namespace_exists if namespace_creator
kubeclient.create_or_update_service_account(service_account_resource)
kubeclient.create_or_update_secret(service_account_token_resource)
return unless rbac
create_role_or_cluster_role_binding
return unless namespace_creator
create_or_update_knative_serving_role
create_or_update_knative_serving_role_binding
end
private
attr_reader :kubeclient, :service_account_name, :service_account_namespace, :token_name, :rbac, :namespace_creator, :role_binding_name
def ensure_project_namespace_exists
Gitlab::Kubernetes::Namespace.new(
service_account_namespace,
kubeclient
).ensure_exists!
end
def create_role_or_cluster_role_binding
if namespace_creator
kubeclient.create_or_update_role_binding(role_binding_resource)
else
kubeclient.create_or_update_cluster_role_binding(cluster_role_binding_resource)
end
end
def create_or_update_knative_serving_role
kubeclient.update_role(knative_serving_role_resource)
end
def create_or_update_knative_serving_role_binding
kubeclient.update_role_binding(knative_serving_role_binding_resource)
end
def service_account_resource
Gitlab::Kubernetes::ServiceAccount.new(
service_account_name,
service_account_namespace
).generate
end
def service_account_token_resource
Gitlab::Kubernetes::ServiceAccountToken.new(
token_name,
service_account_name,
service_account_namespace
).generate
end
def cluster_role_binding_resource
subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: service_account_namespace }]
Gitlab::Kubernetes::ClusterRoleBinding.new(
Clusters::Kubernetes::GITLAB_CLUSTER_ROLE_BINDING_NAME,
Clusters::Kubernetes::GITLAB_CLUSTER_ROLE_NAME,
subjects
).generate
end
def role_binding_resource
Gitlab::Kubernetes::RoleBinding.new(
name: role_binding_name,
role_name: Clusters::Kubernetes::PROJECT_CLUSTER_ROLE_NAME,
role_kind: :ClusterRole,
namespace: service_account_namespace,
service_account_name: service_account_name
).generate
end
def knative_serving_role_resource
Gitlab::Kubernetes::Role.new(
name: Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
namespace: service_account_namespace,
rules: [{
apiGroups: %w(serving.knative.dev),
resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services),
verbs: %w(get list create update delete patch watch)
}]
).generate
end
def knative_serving_role_binding_resource
Gitlab::Kubernetes::RoleBinding.new(
name: Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME,
role_name: Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
role_kind: :Role,
namespace: service_account_namespace,
service_account_name: service_account_name
).generate
end
end
end
end
# frozen_string_literal: true
module Clusters
module Kubernetes
class FetchKubernetesTokenService
DEFAULT_TOKEN_RETRY_DELAY = 5.seconds
TOKEN_RETRY_LIMIT = 5
attr_reader :kubeclient, :service_account_token_name, :namespace
def initialize(kubeclient, service_account_token_name, namespace, token_retry_delay: DEFAULT_TOKEN_RETRY_DELAY)
@kubeclient = kubeclient
@service_account_token_name = service_account_token_name
@namespace = namespace
@token_retry_delay = token_retry_delay
end
def execute
# Kubernetes will create the Secret and set the token asynchronously
# so it is necessary to retry
# https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#token-controller
TOKEN_RETRY_LIMIT.times do
token_base64 = get_secret&.dig('data', 'token')
return Base64.decode64(token_base64) if token_base64
sleep @token_retry_delay
end
nil
end
private
def get_secret
kubeclient.get_secret(service_account_token_name, namespace).as_json
rescue Kubeclient::ResourceNotFoundError
end
end
end
end
# frozen_string_literal: true
module Clusters
module Kubernetes
GITLAB_SERVICE_ACCOUNT_NAME = 'gitlab'
GITLAB_SERVICE_ACCOUNT_NAMESPACE = 'default'
GITLAB_ADMIN_TOKEN_NAME = 'gitlab-token'
GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin'
GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin'
PROJECT_CLUSTER_ROLE_NAME = 'edit'
GITLAB_KNATIVE_SERVING_ROLE_NAME = 'gitlab-knative-serving-role'
GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME = 'gitlab-knative-serving-rolebinding'
end
end
......@@ -43,7 +43,7 @@ def kubernetes_namespace
end
def create_namespace
Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
Clusters::Kubernetes::CreateOrUpdateNamespaceService.new(
cluster: deployment_cluster,
kubernetes_namespace: kubernetes_namespace || build_namespace_record
).execute
......
......@@ -13,7 +13,7 @@
gitlab_sign_in(user)
allow(Groups::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
allow_any_instance_of(Clusters::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
end
......
......@@ -13,7 +13,7 @@
gitlab_sign_in(user)
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
allow_any_instance_of(Clusters::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
end
......
......@@ -87,7 +87,7 @@
.with(cluster, environment: deployment.environment)
.and_return(namespace_builder)
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService)
expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
.to receive(:new)
.with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
.and_return(service)
......@@ -107,7 +107,7 @@
it 'creates a namespace using the tokenless record' do
expect(Clusters::BuildKubernetesNamespaceService).not_to receive(:new)
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService)
expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
.to receive(:new)
.with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
.and_return(service)
......@@ -123,7 +123,7 @@
end
it 'does not create a namespace' do
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new)
expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new)
subject
end
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
include KubernetesHelpers
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
......@@ -35,8 +35,8 @@
stub_kubeclient_create_service_account(api_url, namespace: namespace)
stub_kubeclient_create_secret(api_url, namespace: namespace)