summaryrefslogtreecommitdiff
path: root/lib/gitlab/kubernetes/helm
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 08:27:35 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 08:27:35 +0000
commit7e9c479f7de77702622631cff2628a9c8dcbc627 (patch)
treec8f718a08e110ad7e1894510980d2155a6549197 /lib/gitlab/kubernetes/helm
parente852b0ae16db4052c1c567d9efa4facc81146e88 (diff)
downloadgitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'lib/gitlab/kubernetes/helm')
-rw-r--r--lib/gitlab/kubernetes/helm/base_command.rb85
-rw-r--r--lib/gitlab/kubernetes/helm/certificate.rb73
-rw-r--r--lib/gitlab/kubernetes/helm/client_command.rb38
-rw-r--r--lib/gitlab/kubernetes/helm/delete_command.rb36
-rw-r--r--lib/gitlab/kubernetes/helm/init_command.rb43
-rw-r--r--lib/gitlab/kubernetes/helm/install_command.rb85
-rw-r--r--lib/gitlab/kubernetes/helm/patch_command.rb65
-rw-r--r--lib/gitlab/kubernetes/helm/pod.rb9
-rw-r--r--lib/gitlab/kubernetes/helm/reset_command.rb48
-rw-r--r--lib/gitlab/kubernetes/helm/v2/base_command.rb93
-rw-r--r--lib/gitlab/kubernetes/helm/v2/certificate.rb75
-rw-r--r--lib/gitlab/kubernetes/helm/v2/client_command.rb40
-rw-r--r--lib/gitlab/kubernetes/helm/v2/delete_command.rb38
-rw-r--r--lib/gitlab/kubernetes/helm/v2/init_command.rb45
-rw-r--r--lib/gitlab/kubernetes/helm/v2/install_command.rb87
-rw-r--r--lib/gitlab/kubernetes/helm/v2/patch_command.rb67
-rw-r--r--lib/gitlab/kubernetes/helm/v2/reset_command.rb50
-rw-r--r--lib/gitlab/kubernetes/helm/v3/base_command.rb101
-rw-r--r--lib/gitlab/kubernetes/helm/v3/delete_command.rb35
-rw-r--r--lib/gitlab/kubernetes/helm/v3/install_command.rb80
-rw-r--r--lib/gitlab/kubernetes/helm/v3/patch_command.rb60
21 files changed, 775 insertions, 478 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