summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil Trzciński <ayufan@ayufan.eu>2018-10-02 17:06:29 +0000
committerKamil Trzciński <ayufan@ayufan.eu>2018-10-02 17:06:29 +0000
commit607f263c4869d78b113c3610f11cfc3f909ec3b4 (patch)
tree8badae69eef5ac4770d462dc4be9d9d1b5c82ef1
parentcfc72c126e22df2485f08f11be500a183727c306 (diff)
parentf6ff32d9bd7a9817bb74379a1f28954aa378559c (diff)
downloadgitlab-ce-607f263c4869d78b113c3610f11cfc3f909ec3b4.tar.gz
Merge branch '49952-port-upgrade-command-to-ce' into 'master'
Port UpgradeCommand to CE See merge request gitlab-org/gitlab-ce!21949
-rw-r--r--app/models/clusters/concerns/application_status.rb24
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb18
-rw-r--r--lib/gitlab/kubernetes/helm/upgrade_command.rb71
-rw-r--r--spec/factories/clusters/applications/helm.rb13
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb58
-rw-r--r--spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb136
6 files changed, 320 insertions, 0 deletions
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index d4d3859dfd5..a9df59fc059 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -15,6 +15,9 @@ module Clusters
state :scheduled, value: 1
state :installing, value: 2
state :installed, value: 3
+ state :updating, value: 4
+ state :updated, value: 5
+ state :update_errored, value: 6
event :make_scheduled do
transition [:installable, :errored] => :scheduled
@@ -32,6 +35,18 @@ module Clusters
transition any => :errored
end
+ event :make_updating do
+ transition [:installed, :updated, :update_errored] => :updating
+ end
+
+ event :make_updated do
+ transition [:updating] => :updated
+ end
+
+ event :make_update_errored do
+ transition any => :update_errored
+ end
+
before_transition any => [:scheduled] do |app_status, _|
app_status.status_reason = nil
end
@@ -40,6 +55,15 @@ module Clusters
status_reason = transition.args.first
app_status.status_reason = status_reason if status_reason
end
+
+ before_transition any => [:updating] do |app_status, _|
+ app_status.status_reason = nil
+ end
+
+ before_transition any => [:update_errored] do |app_status, transition|
+ status_reason = transition.args.first
+ app_status.status_reason = status_reason if status_reason
+ end
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index 2dd74c68075..e21bc531444 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -17,6 +17,12 @@ module Gitlab
kubeclient.create_pod(command.pod_resource)
end
+ def update(command)
+ namespace.ensure_exists!
+ update_config_map(command)
+ kubeclient.create_pod(command.pod_resource)
+ end
+
##
# Returns Pod phase
#
@@ -36,6 +42,12 @@ module Gitlab
kubeclient.delete_pod(pod_name, namespace.name)
end
+ def get_config_map(config_map_name)
+ namespace.ensure_exists!
+
+ kubeclient.get_config_map(config_map_name, namespace.name)
+ end
+
private
attr_reader :kubeclient, :namespace
@@ -46,6 +58,12 @@ module Gitlab
end
end
+ def update_config_map(command)
+ command.config_map_resource.tap do |config_map_resource|
+ kubeclient.update_config_map(config_map_resource)
+ end
+ end
+
def create_service_account(command)
command.service_account_resource.tap do |service_account_resource|
break unless service_account_resource
diff --git a/lib/gitlab/kubernetes/helm/upgrade_command.rb b/lib/gitlab/kubernetes/helm/upgrade_command.rb
new file mode 100644
index 00000000000..74188046739
--- /dev/null
+++ b/lib/gitlab/kubernetes/helm/upgrade_command.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ module Helm
+ class UpgradeCommand
+ include BaseCommand
+
+ attr_reader :name, :chart, :version, :repository, :files
+
+ def initialize(name, chart:, files:, rbac:, version: nil, repository: nil)
+ @name = name
+ @chart = chart
+ @rbac = rbac
+ @version = version
+ @files = files
+ @repository = repository
+ end
+
+ def generate_script
+ super + [
+ init_command,
+ repository_command,
+ script_command
+ ].compact.join("\n")
+ end
+
+ def rbac?
+ @rbac
+ end
+
+ def pod_name
+ "upgrade-#{name}"
+ end
+
+ private
+
+ def init_command
+ 'helm init --client-only >/dev/null'
+ end
+
+ def repository_command
+ "helm repo add #{name} #{repository}" if repository
+ end
+
+ def script_command
+ upgrade_flags = "#{optional_version_flag}#{optional_tls_flags}" \
+ " --reset-values" \
+ " --install" \
+ " --namespace #{::Gitlab::Kubernetes::Helm::NAMESPACE}" \
+ " -f /data/helm/#{name}/config/values.yaml"
+
+ "helm upgrade #{name} #{chart}#{upgrade_flags} >/dev/null\n"
+ end
+
+ def optional_version_flag
+ " --version #{version}" if version
+ 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/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index c13b0249d94..5756486df27 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -22,11 +22,24 @@ FactoryBot.define do
status 3
end
+ trait :updating do
+ status 4
+ end
+
+ trait :updated do
+ status 5
+ end
+
trait :errored do
status(-1)
status_reason 'something went wrong'
end
+ trait :update_errored do
+ status(6)
+ status_reason 'something went wrong'
+ end
+
trait :timeouted do
installing
updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index 25c3b37753d..9200724ed23 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -150,6 +150,43 @@ describe Gitlab::Kubernetes::Helm::Api do
end
end
+ describe '#update' do
+ let(:rbac) { false }
+
+ let(:command) do
+ Gitlab::Kubernetes::Helm::UpgradeCommand.new(
+ application_name,
+ chart: 'chart-name',
+ files: files,
+ rbac: rbac
+ )
+ end
+
+ before do
+ allow(namespace).to receive(:ensure_exists!).once
+
+ allow(client).to receive(:update_config_map).and_return(nil)
+ allow(client).to receive(:create_pod).and_return(nil)
+ end
+
+ it 'ensures the namespace exists before creating the pod' do
+ expect(namespace).to receive(:ensure_exists!).once.ordered
+ expect(client).to receive(:create_pod).once.ordered
+
+ subject.update(command)
+ end
+
+ it 'updates the config map on kubeclient when one exists' do
+ resource = Gitlab::Kubernetes::ConfigMap.new(
+ application_name, files
+ ).generate
+
+ expect(client).to receive(:update_config_map).with(resource).once
+
+ subject.update(command)
+ end
+ end
+
describe '#status' do
let(:phase) { Gitlab::Kubernetes::Pod::RUNNING }
let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation
@@ -179,4 +216,25 @@ describe Gitlab::Kubernetes::Helm::Api do
subject.delete_pod!(command.pod_name)
end
end
+
+ describe '#get_config_map' do
+ before do
+ allow(namespace).to receive(:ensure_exists!).once
+ allow(client).to receive(:get_config_map).and_return(nil)
+ end
+
+ it 'ensures the namespace exists before retrieving the config map' do
+ expect(namespace).to receive(:ensure_exists!).once
+
+ subject.get_config_map('example-config-map-name')
+ end
+
+ it 'gets the config map on kubeclient' do
+ expect(client).to receive(:get_config_map)
+ .with('example-config-map-name', namespace.name)
+ .once
+
+ subject.get_config_map('example-config-map-name')
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb
new file mode 100644
index 00000000000..3dabf04413e
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/upgrade_command_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Gitlab::Kubernetes::Helm::UpgradeCommand do
+ let(:application) { build(:clusters_applications_prometheus) }
+ let(:files) { { 'ca.pem': 'some file content' } }
+ let(:namespace) { ::Gitlab::Kubernetes::Helm::NAMESPACE }
+ let(:rbac) { false }
+ let(:upgrade_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ files: files,
+ rbac: rbac
+ )
+ end
+
+ subject { upgrade_command }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm upgrade #{application.name} #{application.chart} --tls --tls-ca-cert /data/helm/#{application.name}/config/ca.pem --tls-cert /data/helm/#{application.name}/config/cert.pem --tls-key /data/helm/#{application.name}/config/key.pem --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
+ end
+ end
+
+ context 'rbac is true' do
+ let(:rbac) { true }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm upgrade #{application.name} #{application.chart} --tls --tls-ca-cert /data/helm/#{application.name}/config/ca.pem --tls-cert /data/helm/#{application.name}/config/cert.pem --tls-key /data/helm/#{application.name}/config/key.pem --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
+ end
+ end
+ end
+
+ context 'with an application with a repository' do
+ let(:ci_runner) { create(:ci_runner) }
+ let(:application) { build(:clusters_applications_runner, runner: ci_runner) }
+ let(:upgrade_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ files: files,
+ rbac: rbac,
+ repository: application.repository
+ )
+ end
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm repo add #{application.name} #{application.repository}
+ helm upgrade #{application.name} #{application.chart} --tls --tls-ca-cert /data/helm/#{application.name}/config/ca.pem --tls-cert /data/helm/#{application.name}/config/cert.pem --tls-key /data/helm/#{application.name}/config/key.pem --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
+ end
+ end
+ end
+
+ context 'when there is no ca.pem file' do
+ let(:files) { { 'file.txt': 'some content' } }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm upgrade #{application.name} #{application.chart} --reset-values --install --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
+ end
+ end
+ end
+
+ describe '#pod_resource' do
+ subject { upgrade_command.pod_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a pod that uses the tiller serviceAccountName' do
+ expect(subject.spec.serviceAccountName).to eq('tiller')
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates a pod that uses the default serviceAccountName' do
+ expect(subject.spec.serviceAcccountName).to be_nil
+ end
+ end
+ end
+
+ describe '#config_map_resource' do
+ let(:metadata) do
+ {
+ name: "values-content-configuration-#{application.name}",
+ namespace: namespace,
+ labels: { name: "values-content-configuration-#{application.name}" }
+ }
+ end
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
+
+ it 'returns a KubeClient resource with config map content for the application' do
+ expect(subject.config_map_resource).to eq(resource)
+ end
+ end
+
+ describe '#rbac?' do
+ subject { upgrade_command.rbac? }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#pod_name' do
+ it 'returns the pod name' do
+ expect(subject.pod_name).to eq("upgrade-#{application.name}")
+ end
+ end
+end