diff options
Diffstat (limited to 'lib/gitlab/kubernetes/helm/v2')
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/base_command.rb | 93 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/certificate.rb | 75 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/client_command.rb | 40 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/delete_command.rb | 38 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/init_command.rb | 45 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/install_command.rb | 87 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/patch_command.rb | 67 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm/v2/reset_command.rb | 50 |
8 files changed, 495 insertions, 0 deletions
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 |