From 1f71bc143b23fc8defb195f0712743e73628713b Mon Sep 17 00:00:00 2001 From: Hordur Freyr Yngvason Date: Fri, 28 Jun 2019 14:02:35 +0200 Subject: Create Knative cluster role with service account GitLab managed clusters use a service account to perform deployments. For serverless deployments to work as expected, this account requires additional permissions in the serving.knative.dev API group. --- app/services/clusters/gcp/kubernetes.rb | 1 + .../create_or_update_service_account_service.rb | 22 +++++++++++++++- ...ve-cluster-role-on-service-account-creation.yml | 5 ++++ lib/gitlab/kubernetes/cluster_role.rb | 22 ++++++++++++++++ lib/gitlab/kubernetes/kube_client.rb | 7 +++++ spec/lib/gitlab/kubernetes/cluster_role_spec.rb | 30 ++++++++++++++++++++++ spec/lib/gitlab/kubernetes/kube_client_spec.rb | 3 +++ .../clusters/gcp/finalize_creation_service_spec.rb | 1 + .../create_or_update_namespace_service_spec.rb | 1 + ...reate_or_update_service_account_service_spec.rb | 26 +++++++++++++++++++ spec/support/helpers/kubernetes_helpers.rb | 10 ++++++++ 11 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml create mode 100644 lib/gitlab/kubernetes/cluster_role.rb create mode 100644 spec/lib/gitlab/kubernetes/cluster_role_spec.rb diff --git a/app/services/clusters/gcp/kubernetes.rb b/app/services/clusters/gcp/kubernetes.rb index 90ed529670c..102784c4c58 100644 --- a/app/services/clusters/gcp/kubernetes.rb +++ b/app/services/clusters/gcp/kubernetes.rb @@ -9,6 +9,7 @@ module Clusters GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin' GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin' PROJECT_CLUSTER_ROLE_NAME = 'edit' + GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME = 'gitlab-knative-serving-only-role' end end end diff --git a/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb b/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb index 49e766cbf13..df4458382d3 100644 --- a/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb +++ b/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb @@ -41,7 +41,11 @@ module Clusters kubeclient.create_or_update_service_account(service_account_resource) kubeclient.create_or_update_secret(service_account_token_resource) - create_role_or_cluster_role_binding if rbac + + if rbac + create_role_or_cluster_role_binding + create_knative_serving_only_cluster_role + end end private @@ -63,6 +67,10 @@ module Clusters end end + def create_knative_serving_only_cluster_role + kubeclient.update_cluster_role(knative_serving_only_cluster_role_resource) + end + def service_account_resource Gitlab::Kubernetes::ServiceAccount.new( service_account_name, @@ -96,6 +104,18 @@ module Clusters service_account_name: service_account_name ).generate end + + def knative_serving_only_cluster_role_resource + Gitlab::Kubernetes::ClusterRole.new( + name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME, + labels: { 'rbac.authorization.k8s.io/aggregate-to-edit' => 'true' }, + 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 end end end diff --git a/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml b/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml new file mode 100644 index 00000000000..96a90caf02f --- /dev/null +++ b/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml @@ -0,0 +1,5 @@ +--- +title: Create Knative cluster role on service account creation +merge_request: 30235 +author: +type: changed diff --git a/lib/gitlab/kubernetes/cluster_role.rb b/lib/gitlab/kubernetes/cluster_role.rb new file mode 100644 index 00000000000..1f3e962ae13 --- /dev/null +++ b/lib/gitlab/kubernetes/cluster_role.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + class ClusterRole + attr_reader :name, :labels, :rules + + def initialize(name:, labels:, rules:) + @name = name + @labels = labels + @rules = rules + end + + def generate + ::Kubeclient::Resource.new( + metadata: { name: name, labels: labels }, + rules: rules + ) + end + end + end +end diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb index de14df56555..199612b22fb 100644 --- a/lib/gitlab/kubernetes/kube_client.rb +++ b/lib/gitlab/kubernetes/kube_client.rb @@ -50,6 +50,13 @@ module Gitlab :update_service_account, to: :core_client + # RBAC methods delegates to the apis/rbac.authorization.k8s.io api + # group client + delegate :create_cluster_role, + :get_cluster_role, + :update_cluster_role, + to: :rbac_client + # RBAC methods delegates to the apis/rbac.authorization.k8s.io api # group client delegate :create_cluster_role_binding, diff --git a/spec/lib/gitlab/kubernetes/cluster_role_spec.rb b/spec/lib/gitlab/kubernetes/cluster_role_spec.rb new file mode 100644 index 00000000000..620180d6c58 --- /dev/null +++ b/spec/lib/gitlab/kubernetes/cluster_role_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Kubernetes::ClusterRole do + let(:cluster_role) { described_class.new(name: name, labels: labels, rules: rules) } + + let(:name) { 'example-cluster-role' } + let(:labels) { { 'rbac.authorization.k8s.io/aggregate-to-edit' => 'true' } } + let(:rules) do + [{ + apiGroups: %w(serving.knative.dev), + resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services), + verbs: %w(get list create update delete patch watch) + }] + end + + describe '#generate' do + let(:resource) do + ::Kubeclient::Resource.new( + metadata: { name: name, labels: labels }, + rules: rules + ) + end + + subject { cluster_role.generate } + + it { is_expected.to eq(resource) } + end +end diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb index 978e64c4407..ecdfd337c50 100644 --- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb +++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb @@ -176,6 +176,9 @@ describe Gitlab::Kubernetes::KubeClient do let(:rbac_client) { client.rbac_client } [ + :create_cluster_role, + :get_cluster_role, + :update_cluster_role, :create_cluster_role_binding, :get_cluster_role_binding, :update_cluster_role_binding diff --git a/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb index 5f91acb8e84..7d5231e979f 100644 --- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb +++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb @@ -136,6 +136,7 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do stub_kubeclient_get_cluster_role_binding_error(api_url, 'gitlab-admin') stub_kubeclient_create_cluster_role_binding(api_url) + stub_kubeclient_update_cluster_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME) end include_context 'kubernetes information successfully fetched' diff --git a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb index be052a07da7..f187c1c9c07 100644 --- a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb +++ b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb @@ -34,6 +34,7 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' d stub_kubeclient_create_service_account(api_url, namespace: namespace) stub_kubeclient_create_secret(api_url, namespace: namespace) stub_kubeclient_put_secret(api_url, "#{namespace}-token", namespace: namespace) + stub_kubeclient_update_cluster_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME) stub_kubeclient_get_secret( api_url, diff --git a/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb index 382b9043566..f56af409eaa 100644 --- a/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb +++ b/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb @@ -52,6 +52,26 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do end end + shared_examples 'creates a knative serving role' do + it 'creates a cluster role aggregating knative serving permissions to the edit role' do + subject + + expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterroles/#{Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME}").with( + body: hash_including( + metadata: { + name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME, + labels: { 'rbac.authorization.k8s.io/aggregate-to-edit' => 'true' } + }, + rules: [{ + apiGroups: %w(serving.knative.dev), + resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services), + verbs: %w(get list create update delete patch watch) + }] + ) + ) + end + end + before do stub_kubeclient_discover(api_url) stub_kubeclient_get_namespace(api_url, namespace: namespace) @@ -85,6 +105,7 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do stub_kubeclient_get_cluster_role_binding_error(api_url, cluster_role_binding_name) stub_kubeclient_create_cluster_role_binding(api_url) + stub_kubeclient_update_cluster_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME) end it_behaves_like 'creates service account and token' @@ -111,6 +132,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do ) ) end + + it_behaves_like 'creates a knative serving role' end end @@ -143,6 +166,7 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do stub_kubeclient_get_role_binding_error(api_url, role_binding_name, namespace: namespace) stub_kubeclient_create_role_binding(api_url, namespace: namespace) + stub_kubeclient_update_cluster_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ONLY_CLUSTER_ROLE_NAME) end it_behaves_like 'creates service account and token' @@ -169,6 +193,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do ) ) end + + it_behaves_like 'creates a knative serving role' end end end diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index 3c7bcba2b42..21fa1baa844 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -199,6 +199,16 @@ module KubernetesHelpers .to_return(kube_response({})) end + def stub_kubeclient_create_cluster_role(api_url, name) + WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterroles/#{name}") + .to_return(kube_response({})) + end + + def stub_kubeclient_update_cluster_role(api_url, name) + WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterroles/#{name}") + .to_return(kube_response({})) + end + def kube_v1_secret_body(**options) { "kind" => "SecretList", -- cgit v1.2.1