diff options
Diffstat (limited to 'lib/gitlab/kubernetes')
22 files changed, 791 insertions, 490 deletions
diff --git a/lib/gitlab/kubernetes/helm/base_command.rb b/lib/gitlab/kubernetes/helm/base_command.rb deleted file mode 100644 index 49d2969f7f3..00000000000 --- a/lib/gitlab/kubernetes/helm/base_command.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Kubernetes - module Helm - class BaseCommand - attr_reader :name, :files - - def initialize(rbac:, name:, files:) - @rbac = rbac - @name = name - @files = files - end - - def rbac? - @rbac - end - - def pod_resource - pod_service_account_name = rbac? ? service_account_name : nil - - Gitlab::Kubernetes::Helm::Pod.new(self, namespace, service_account_name: pod_service_account_name).generate - end - - def generate_script - <<~HEREDOC - set -xeo pipefail - HEREDOC - end - - def pod_name - "install-#{name}" - end - - def config_map_resource - Gitlab::Kubernetes::ConfigMap.new(name, files).generate - end - - def service_account_resource - return unless rbac? - - Gitlab::Kubernetes::ServiceAccount.new(service_account_name, namespace).generate - end - - def cluster_role_binding_resource - return unless rbac? - - subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: namespace }] - - Gitlab::Kubernetes::ClusterRoleBinding.new( - cluster_role_binding_name, - cluster_role_name, - subjects - ).generate - end - - def file_names - files.keys - end - - private - - def files_dir - "/data/helm/#{name}/config" - end - - def namespace - Gitlab::Kubernetes::Helm::NAMESPACE - end - - def service_account_name - Gitlab::Kubernetes::Helm::SERVICE_ACCOUNT - end - - def cluster_role_binding_name - Gitlab::Kubernetes::Helm::CLUSTER_ROLE_BINDING - end - - def cluster_role_name - Gitlab::Kubernetes::Helm::CLUSTER_ROLE - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/certificate.rb b/lib/gitlab/kubernetes/helm/certificate.rb deleted file mode 100644 index 598714e0874..00000000000 --- a/lib/gitlab/kubernetes/helm/certificate.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true -module Gitlab - module Kubernetes - module Helm - class Certificate - INFINITE_EXPIRY = 1000.years - SHORT_EXPIRY = 30.minutes - - attr_reader :key, :cert - - def key_string - @key.to_s - end - - def cert_string - @cert.to_pem - end - - def self.from_strings(key_string, cert_string) - key = OpenSSL::PKey::RSA.new(key_string) - cert = OpenSSL::X509::Certificate.new(cert_string) - new(key, cert) - end - - def self.generate_root - _issue(signed_by: nil, expires_in: INFINITE_EXPIRY, certificate_authority: true) - end - - def issue(expires_in: SHORT_EXPIRY) - self.class._issue(signed_by: self, expires_in: expires_in, certificate_authority: false) - end - - private - - def self._issue(signed_by:, expires_in:, certificate_authority:) - key = OpenSSL::PKey::RSA.new(4096) - public_key = key.public_key - - subject = OpenSSL::X509::Name.parse("/C=US") - - cert = OpenSSL::X509::Certificate.new - cert.subject = subject - - cert.issuer = signed_by&.cert&.subject || subject - - cert.not_before = Time.now - cert.not_after = expires_in.from_now - cert.public_key = public_key - cert.serial = 0x0 - cert.version = 2 - - if certificate_authority - extension_factory = OpenSSL::X509::ExtensionFactory.new - extension_factory.subject_certificate = cert - extension_factory.issuer_certificate = cert - cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash')) - cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)) - cert.add_extension(extension_factory.create_extension('keyUsage', 'cRLSign,keyCertSign', true)) - end - - cert.sign(signed_by&.key || key, OpenSSL::Digest::SHA256.new) - - new(key, cert) - end - - def initialize(key, cert) - @key = key - @cert = cert - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/client_command.rb b/lib/gitlab/kubernetes/helm/client_command.rb deleted file mode 100644 index a9e93c0c90e..00000000000 --- a/lib/gitlab/kubernetes/helm/client_command.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Kubernetes - module Helm - module ClientCommand - def init_command - <<~SHELL.chomp - export HELM_HOST="localhost:44134" - tiller -listen ${HELM_HOST} -alsologtostderr & - helm init --client-only - SHELL - end - - def repository_command - ['helm', 'repo', 'add', name, repository].shelljoin if repository - end - - private - - def repository_update_command - 'helm repo update' - end - - def optional_tls_flags - return [] unless files.key?(:'ca.pem') - - [ - '--tls', - '--tls-ca-cert', "#{files_dir}/ca.pem", - '--tls-cert', "#{files_dir}/cert.pem", - '--tls-key', "#{files_dir}/key.pem" - ] - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/delete_command.rb b/lib/gitlab/kubernetes/helm/delete_command.rb deleted file mode 100644 index f8b9601bc98..00000000000 --- a/lib/gitlab/kubernetes/helm/delete_command.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Kubernetes - module Helm - class DeleteCommand < BaseCommand - include ClientCommand - - attr_reader :predelete, :postdelete - - def initialize(predelete: nil, postdelete: nil, **args) - super(**args) - @predelete = predelete - @postdelete = postdelete - end - - def generate_script - super + [ - init_command, - predelete, - delete_command, - postdelete - ].compact.join("\n") - end - - def pod_name - "uninstall-#{name}" - end - - def delete_command - ['helm', 'delete', '--purge', name].shelljoin - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/init_command.rb b/lib/gitlab/kubernetes/helm/init_command.rb deleted file mode 100644 index e4844e255c5..00000000000 --- a/lib/gitlab/kubernetes/helm/init_command.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Kubernetes - module Helm - class InitCommand < BaseCommand - def generate_script - super + [ - init_helm_command - ].join("\n") - end - - private - - def init_helm_command - command = %w[helm init] + init_command_flags - - command.shelljoin - end - - def init_command_flags - tls_flags + optional_service_account_flag - end - - def tls_flags - [ - '--tiller-tls', - '--tiller-tls-verify', - '--tls-ca-cert', "#{files_dir}/ca.pem", - '--tiller-tls-cert', "#{files_dir}/cert.pem", - '--tiller-tls-key', "#{files_dir}/key.pem" - ] - end - - def optional_service_account_flag - return [] unless rbac? - - ['--service-account', service_account_name] - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/install_command.rb b/lib/gitlab/kubernetes/helm/install_command.rb deleted file mode 100644 index d166842fce6..00000000000 --- a/lib/gitlab/kubernetes/helm/install_command.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Kubernetes - module Helm - class InstallCommand < BaseCommand - include ClientCommand - - attr_reader :chart, :repository, :preinstall, :postinstall - attr_accessor :version - - def initialize(chart:, version: nil, repository: nil, preinstall: nil, postinstall: nil, **args) - super(**args) - @chart = chart - @version = version - @repository = repository - @preinstall = preinstall - @postinstall = postinstall - end - - def generate_script - super + [ - init_command, - repository_command, - repository_update_command, - preinstall, - install_command, - postinstall - ].compact.join("\n") - end - - private - - # Uses `helm upgrade --install` which means we can use this for both - # installation and uprade of applications - def install_command - command = ['helm', 'upgrade', name, chart] + - install_flag + - rollback_support_flag + - reset_values_flag + - optional_version_flag + - rbac_create_flag + - namespace_flag + - value_flag - - command.shelljoin - end - - def install_flag - ['--install'] - end - - def reset_values_flag - ['--reset-values'] - end - - def value_flag - ['-f', "/data/helm/#{name}/config/values.yaml"] - end - - def namespace_flag - ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE] - end - - def rbac_create_flag - if rbac? - %w[--set rbac.create=true,rbac.enabled=true] - else - %w[--set rbac.create=false,rbac.enabled=false] - end - end - - def optional_version_flag - return [] unless version - - ['--version', version] - end - - def rollback_support_flag - ['--atomic', '--cleanup-on-fail'] - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/patch_command.rb b/lib/gitlab/kubernetes/helm/patch_command.rb deleted file mode 100644 index a33dbdac134..00000000000 --- a/lib/gitlab/kubernetes/helm/patch_command.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -# PatchCommand is for updating values in installed charts without overwriting -# existing values. -module Gitlab - module Kubernetes - module Helm - class PatchCommand < BaseCommand - include ClientCommand - - attr_reader :chart, :repository - attr_accessor :version - - def initialize(chart:, version:, repository: nil, **args) - super(**args) - - # version is mandatory to prevent chart mismatches - # we do not want our values interpreted in the context of the wrong version - raise ArgumentError, 'version is required' if version.blank? - - @chart = chart - @version = version - @repository = repository - end - - def generate_script - super + [ - init_command, - repository_command, - repository_update_command, - upgrade_command - ].compact.join("\n") - end - - private - - def upgrade_command - command = ['helm', 'upgrade', name, chart] + - reuse_values_flag + - version_flag + - namespace_flag + - value_flag - - command.shelljoin - end - - def reuse_values_flag - ['--reuse-values'] - end - - def value_flag - ['-f', "/data/helm/#{name}/config/values.yaml"] - end - - def namespace_flag - ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE] - end - - def version_flag - ['--version', version] - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/pod.rb b/lib/gitlab/kubernetes/helm/pod.rb index 75484f80070..9d0207e6b1f 100644 --- a/lib/gitlab/kubernetes/helm/pod.rb +++ b/lib/gitlab/kubernetes/helm/pod.rb @@ -27,7 +27,7 @@ module Gitlab def container_specification { name: 'helm', - image: "registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/#{Gitlab::Kubernetes::Helm::HELM_VERSION}-kube-#{Gitlab::Kubernetes::Helm::KUBECTL_VERSION}", + image: "registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/#{command.class::HELM_VERSION}-kube-#{Gitlab::Kubernetes::Helm::KUBECTL_VERSION}-alpine-3.12", env: generate_pod_env(command), command: %w(/bin/sh), args: %w(-c $(COMMAND_SCRIPT)) @@ -50,11 +50,10 @@ module Gitlab end def generate_pod_env(command) - { - HELM_VERSION: Gitlab::Kubernetes::Helm::HELM_VERSION, - TILLER_NAMESPACE: namespace_name, + command.env.merge( + HELM_VERSION: command.class::HELM_VERSION, COMMAND_SCRIPT: command.generate_script - }.map { |key, value| { name: key, value: value } } + ).map { |key, value| { name: key, value: value } } end def volumes_specification diff --git a/lib/gitlab/kubernetes/helm/reset_command.rb b/lib/gitlab/kubernetes/helm/reset_command.rb deleted file mode 100644 index f1f7938039c..00000000000 --- a/lib/gitlab/kubernetes/helm/reset_command.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Kubernetes - module Helm - class ResetCommand < BaseCommand - include ClientCommand - - def generate_script - super + [ - reset_helm_command, - delete_tiller_replicaset, - delete_tiller_clusterrolebinding - ].join("\n") - end - - def pod_name - "uninstall-#{name}" - end - - private - - # This method can be delete once we upgrade Helm to > 12.13.0 - # https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/27096#note_159695900 - # - # Tracking this method to be removed here: - # https://gitlab.com/gitlab-org/gitlab-foss/issues/52791#note_199374155 - def delete_tiller_replicaset - delete_args = %w[replicaset -n gitlab-managed-apps -l name=tiller] - - Gitlab::Kubernetes::KubectlCmd.delete(*delete_args) - end - - def delete_tiller_clusterrolebinding - delete_args = %w[clusterrolebinding tiller-admin] - - Gitlab::Kubernetes::KubectlCmd.delete(*delete_args) - end - - def reset_helm_command - command = %w[helm reset] + optional_tls_flags - - command.shelljoin - end - end - end - end -end diff --git a/lib/gitlab/kubernetes/helm/v2/base_command.rb b/lib/gitlab/kubernetes/helm/v2/base_command.rb new file mode 100644 index 00000000000..931c2248310 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/base_command.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V2 + class BaseCommand + attr_reader :name, :files + + HELM_VERSION = '2.16.9' + + def initialize(rbac:, name:, files:) + @rbac = rbac + @name = name + @files = files + end + + def env + { TILLER_NAMESPACE: namespace } + end + + def rbac? + @rbac + end + + def pod_resource + pod_service_account_name = rbac? ? service_account_name : nil + + Gitlab::Kubernetes::Helm::Pod.new(self, namespace, service_account_name: pod_service_account_name).generate + end + + def generate_script + <<~HEREDOC + set -xeo pipefail + HEREDOC + end + + def pod_name + "install-#{name}" + end + + def config_map_resource + Gitlab::Kubernetes::ConfigMap.new(name, files).generate + end + + def service_account_resource + return unless rbac? + + Gitlab::Kubernetes::ServiceAccount.new(service_account_name, namespace).generate + end + + def cluster_role_binding_resource + return unless rbac? + + subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: namespace }] + + Gitlab::Kubernetes::ClusterRoleBinding.new( + cluster_role_binding_name, + cluster_role_name, + subjects + ).generate + end + + def file_names + files.keys + end + + private + + def files_dir + "/data/helm/#{name}/config" + end + + def namespace + Gitlab::Kubernetes::Helm::NAMESPACE + end + + def service_account_name + Gitlab::Kubernetes::Helm::SERVICE_ACCOUNT + end + + def cluster_role_binding_name + Gitlab::Kubernetes::Helm::CLUSTER_ROLE_BINDING + end + + def cluster_role_name + Gitlab::Kubernetes::Helm::CLUSTER_ROLE + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/certificate.rb b/lib/gitlab/kubernetes/helm/v2/certificate.rb new file mode 100644 index 00000000000..f603ff44ef3 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/certificate.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true +module Gitlab + module Kubernetes + module Helm + module V2 + class Certificate + INFINITE_EXPIRY = 1000.years + SHORT_EXPIRY = 30.minutes + + attr_reader :key, :cert + + def key_string + @key.to_s + end + + def cert_string + @cert.to_pem + end + + def self.from_strings(key_string, cert_string) + key = OpenSSL::PKey::RSA.new(key_string) + cert = OpenSSL::X509::Certificate.new(cert_string) + new(key, cert) + end + + def self.generate_root + _issue(signed_by: nil, expires_in: INFINITE_EXPIRY, certificate_authority: true) + end + + def issue(expires_in: SHORT_EXPIRY) + self.class._issue(signed_by: self, expires_in: expires_in, certificate_authority: false) + end + + private + + def self._issue(signed_by:, expires_in:, certificate_authority:) + key = OpenSSL::PKey::RSA.new(4096) + public_key = key.public_key + + subject = OpenSSL::X509::Name.parse("/C=US") + + cert = OpenSSL::X509::Certificate.new + cert.subject = subject + + cert.issuer = signed_by&.cert&.subject || subject + + cert.not_before = Time.now.utc + cert.not_after = expires_in.from_now.utc + cert.public_key = public_key + cert.serial = 0x0 + cert.version = 2 + + if certificate_authority + extension_factory = OpenSSL::X509::ExtensionFactory.new + extension_factory.subject_certificate = cert + extension_factory.issuer_certificate = cert + cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash')) + cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)) + cert.add_extension(extension_factory.create_extension('keyUsage', 'cRLSign,keyCertSign', true)) + end + + cert.sign(signed_by&.key || key, OpenSSL::Digest::SHA256.new) + + new(key, cert) + end + + def initialize(key, cert) + @key = key + @cert = cert + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/client_command.rb b/lib/gitlab/kubernetes/helm/v2/client_command.rb new file mode 100644 index 00000000000..88693a28d6c --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/client_command.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V2 + module ClientCommand + def init_command + <<~SHELL.chomp + export HELM_HOST="localhost:44134" + tiller -listen ${HELM_HOST} -alsologtostderr & + helm init --client-only + SHELL + end + + def repository_command + ['helm', 'repo', 'add', name, repository].shelljoin if repository + end + + private + + def repository_update_command + 'helm repo update' + end + + def optional_tls_flags + return [] unless files.key?(:'ca.pem') + + [ + '--tls', + '--tls-ca-cert', "#{files_dir}/ca.pem", + '--tls-cert', "#{files_dir}/cert.pem", + '--tls-key', "#{files_dir}/key.pem" + ] + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/delete_command.rb b/lib/gitlab/kubernetes/helm/v2/delete_command.rb new file mode 100644 index 00000000000..4d52fc1398f --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/delete_command.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V2 + class DeleteCommand < BaseCommand + include ClientCommand + + attr_reader :predelete, :postdelete + + def initialize(predelete: nil, postdelete: nil, **args) + super(**args) + @predelete = predelete + @postdelete = postdelete + end + + def generate_script + super + [ + init_command, + predelete, + delete_command, + postdelete + ].compact.join("\n") + end + + def pod_name + "uninstall-#{name}" + end + + def delete_command + ['helm', 'delete', '--purge', name].shelljoin + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/init_command.rb b/lib/gitlab/kubernetes/helm/v2/init_command.rb new file mode 100644 index 00000000000..f8b52feb5b6 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/init_command.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V2 + class InitCommand < BaseCommand + def generate_script + super + [ + init_helm_command + ].join("\n") + end + + private + + def init_helm_command + command = %w[helm init] + init_command_flags + + command.shelljoin + end + + def init_command_flags + tls_flags + optional_service_account_flag + end + + def tls_flags + [ + '--tiller-tls', + '--tiller-tls-verify', + '--tls-ca-cert', "#{files_dir}/ca.pem", + '--tiller-tls-cert', "#{files_dir}/cert.pem", + '--tiller-tls-key', "#{files_dir}/key.pem" + ] + end + + def optional_service_account_flag + return [] unless rbac? + + ['--service-account', service_account_name] + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/install_command.rb b/lib/gitlab/kubernetes/helm/v2/install_command.rb new file mode 100644 index 00000000000..10e16723e45 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/install_command.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V2 + class InstallCommand < BaseCommand + include ClientCommand + + attr_reader :chart, :repository, :preinstall, :postinstall + attr_accessor :version + + def initialize(chart:, version: nil, repository: nil, preinstall: nil, postinstall: nil, **args) + super(**args) + @chart = chart + @version = version + @repository = repository + @preinstall = preinstall + @postinstall = postinstall + end + + def generate_script + super + [ + init_command, + repository_command, + repository_update_command, + preinstall, + install_command, + postinstall + ].compact.join("\n") + end + + private + + # Uses `helm upgrade --install` which means we can use this for both + # installation and uprade of applications + def install_command + command = ['helm', 'upgrade', name, chart] + + install_flag + + rollback_support_flag + + reset_values_flag + + optional_version_flag + + rbac_create_flag + + namespace_flag + + value_flag + + command.shelljoin + end + + def install_flag + ['--install'] + end + + def reset_values_flag + ['--reset-values'] + end + + def value_flag + ['-f', "/data/helm/#{name}/config/values.yaml"] + end + + def namespace_flag + ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE] + end + + def rbac_create_flag + if rbac? + %w[--set rbac.create=true,rbac.enabled=true] + else + %w[--set rbac.create=false,rbac.enabled=false] + end + end + + def optional_version_flag + return [] unless version + + ['--version', version] + end + + def rollback_support_flag + ['--atomic', '--cleanup-on-fail'] + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/patch_command.rb b/lib/gitlab/kubernetes/helm/v2/patch_command.rb new file mode 100644 index 00000000000..2855e6444b1 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/patch_command.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +# PatchCommand is for updating values in installed charts without overwriting +# existing values. +module Gitlab + module Kubernetes + module Helm + module V2 + class PatchCommand < BaseCommand + include ClientCommand + + attr_reader :chart, :repository + attr_accessor :version + + def initialize(chart:, version:, repository: nil, **args) + super(**args) + + # version is mandatory to prevent chart mismatches + # we do not want our values interpreted in the context of the wrong version + raise ArgumentError, 'version is required' if version.blank? + + @chart = chart + @version = version + @repository = repository + end + + def generate_script + super + [ + init_command, + repository_command, + repository_update_command, + upgrade_command + ].compact.join("\n") + end + + private + + def upgrade_command + command = ['helm', 'upgrade', name, chart] + + reuse_values_flag + + version_flag + + namespace_flag + + value_flag + + command.shelljoin + end + + def reuse_values_flag + ['--reuse-values'] + end + + def value_flag + ['-f', "/data/helm/#{name}/config/values.yaml"] + end + + def namespace_flag + ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE] + end + + def version_flag + ['--version', version] + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v2/reset_command.rb b/lib/gitlab/kubernetes/helm/v2/reset_command.rb new file mode 100644 index 00000000000..172a0884c49 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v2/reset_command.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V2 + class ResetCommand < BaseCommand + include ClientCommand + + def generate_script + super + [ + reset_helm_command, + delete_tiller_replicaset, + delete_tiller_clusterrolebinding + ].join("\n") + end + + def pod_name + "uninstall-#{name}" + end + + private + + # This method can be delete once we upgrade Helm to > 12.13.0 + # https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/27096#note_159695900 + # + # Tracking this method to be removed here: + # https://gitlab.com/gitlab-org/gitlab-foss/issues/52791#note_199374155 + def delete_tiller_replicaset + delete_args = %w[replicaset -n gitlab-managed-apps -l name=tiller] + + Gitlab::Kubernetes::KubectlCmd.delete(*delete_args) + end + + def delete_tiller_clusterrolebinding + delete_args = %w[clusterrolebinding tiller-admin] + + Gitlab::Kubernetes::KubectlCmd.delete(*delete_args) + end + + def reset_helm_command + command = %w[helm reset] + optional_tls_flags + + command.shelljoin + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v3/base_command.rb b/lib/gitlab/kubernetes/helm/v3/base_command.rb new file mode 100644 index 00000000000..ca1bf5462f0 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v3/base_command.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V3 + class BaseCommand + attr_reader :name, :files + + HELM_VERSION = '3.2.4' + + def initialize(rbac:, name:, files:) + @rbac = rbac + @name = name + @files = files + end + + def env + {} + end + + def rbac? + @rbac + end + + def pod_resource + pod_service_account_name = rbac? ? service_account_name : nil + + Gitlab::Kubernetes::Helm::Pod.new(self, namespace, service_account_name: pod_service_account_name).generate + end + + def generate_script + <<~HEREDOC + set -xeo pipefail + HEREDOC + end + + def pod_name + "install-#{name}" + end + + def config_map_resource + Gitlab::Kubernetes::ConfigMap.new(name, files).generate + end + + def service_account_resource + return unless rbac? + + Gitlab::Kubernetes::ServiceAccount.new(service_account_name, namespace).generate + end + + def cluster_role_binding_resource + return unless rbac? + + subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: namespace }] + + Gitlab::Kubernetes::ClusterRoleBinding.new( + cluster_role_binding_name, + cluster_role_name, + subjects + ).generate + end + + def file_names + files.keys + end + + def repository_command + ['helm', 'repo', 'add', name, repository].shelljoin if repository + end + + private + + def repository_update_command + 'helm repo update' + end + + def namespace_flag + ['--namespace', Gitlab::Kubernetes::Helm::NAMESPACE] + end + + def namespace + Gitlab::Kubernetes::Helm::NAMESPACE + end + + def service_account_name + Gitlab::Kubernetes::Helm::SERVICE_ACCOUNT + end + + def cluster_role_binding_name + Gitlab::Kubernetes::Helm::CLUSTER_ROLE_BINDING + end + + def cluster_role_name + Gitlab::Kubernetes::Helm::CLUSTER_ROLE + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v3/delete_command.rb b/lib/gitlab/kubernetes/helm/v3/delete_command.rb new file mode 100644 index 00000000000..f628e852f54 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v3/delete_command.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V3 + class DeleteCommand < BaseCommand + attr_reader :predelete, :postdelete + + def initialize(predelete: nil, postdelete: nil, **args) + super(**args) + @predelete = predelete + @postdelete = postdelete + end + + def generate_script + super + [ + predelete, + delete_command, + postdelete + ].compact.join("\n") + end + + def pod_name + "uninstall-#{name}" + end + + def delete_command + ['helm', 'uninstall', name, *namespace_flag].shelljoin + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v3/install_command.rb b/lib/gitlab/kubernetes/helm/v3/install_command.rb new file mode 100644 index 00000000000..20d17f49115 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v3/install_command.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + module Helm + module V3 + class InstallCommand < BaseCommand + attr_reader :chart, :repository, :preinstall, :postinstall + attr_accessor :version + + def initialize(chart:, version: nil, repository: nil, preinstall: nil, postinstall: nil, **args) + super(**args) + @chart = chart + @version = version + @repository = repository + @preinstall = preinstall + @postinstall = postinstall + end + + def generate_script + super + [ + repository_command, + repository_update_command, + preinstall, + install_command, + postinstall + ].compact.join("\n") + end + + private + + # Uses `helm upgrade --install` which means we can use this for both + # installation and uprade of applications + def install_command + command = ['helm', 'upgrade', name, chart] + + install_flag + + rollback_support_flag + + reset_values_flag + + optional_version_flag + + rbac_create_flag + + namespace_flag + + value_flag + + command.shelljoin + end + + def install_flag + ['--install'] + end + + def reset_values_flag + ['--reset-values'] + end + + def value_flag + ['-f', "/data/helm/#{name}/config/values.yaml"] + end + + def rbac_create_flag + if rbac? + %w[--set rbac.create=true,rbac.enabled=true] + else + %w[--set rbac.create=false,rbac.enabled=false] + end + end + + def optional_version_flag + return [] unless version + + ['--version', version] + end + + def rollback_support_flag + ['--atomic', '--cleanup-on-fail'] + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/v3/patch_command.rb b/lib/gitlab/kubernetes/helm/v3/patch_command.rb new file mode 100644 index 00000000000..00f340591e7 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/v3/patch_command.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# PatchCommand is for updating values in installed charts without overwriting +# existing values. +module Gitlab + module Kubernetes + module Helm + module V3 + class PatchCommand < BaseCommand + attr_reader :chart, :repository + attr_accessor :version + + def initialize(chart:, version:, repository: nil, **args) + super(**args) + + # version is mandatory to prevent chart mismatches + # we do not want our values interpreted in the context of the wrong version + raise ArgumentError, 'version is required' if version.blank? + + @chart = chart + @version = version + @repository = repository + end + + def generate_script + super + [ + repository_command, + repository_update_command, + upgrade_command + ].compact.join("\n") + end + + private + + def upgrade_command + command = ['helm', 'upgrade', name, chart] + + reuse_values_flag + + version_flag + + namespace_flag + + value_flag + + command.shelljoin + end + + def reuse_values_flag + ['--reuse-values'] + end + + def value_flag + ['-f', "/data/helm/#{name}/config/values.yaml"] + end + + def version_flag + ['--version', version] + end + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb index 13cd6dcad3f..a25f005d81e 100644 --- a/lib/gitlab/kubernetes/kube_client.rb +++ b/lib/gitlab/kubernetes/kube_client.rb @@ -61,18 +61,11 @@ module Gitlab # RBAC methods delegates to the apis/rbac.authorization.k8s.io api # group client delegate :update_cluster_role_binding, - to: :rbac_client - - # RBAC methods delegates to the apis/rbac.authorization.k8s.io api - # group client - delegate :create_role, - :get_role, - :update_role, - to: :rbac_client - - # RBAC methods delegates to the apis/rbac.authorization.k8s.io api - # group client - delegate :update_role_binding, + :create_role, + :get_role, + :update_role, + :delete_role_binding, + :update_role_binding, to: :rbac_client # non-entity methods that can only work with the core client @@ -182,10 +175,21 @@ module Gitlab end end + def patch_ingress(*args) + extensions_client.discover unless extensions_client.discovered + + if extensions_client.respond_to?(:patch_ingress) + extensions_client.patch_ingress(*args) + else + networking_client.patch_ingress(*args) + end + end + def create_or_update_cluster_role_binding(resource) update_cluster_role_binding(resource) end + # Note that we cannot update roleRef as that is immutable def create_or_update_role_binding(resource) update_role_binding(resource) end |