diff options
author | Shinya Maeda <shinya@gitlab.com> | 2017-11-08 01:22:56 +0900 |
---|---|---|
committer | Shinya Maeda <shinya@gitlab.com> | 2017-11-08 01:22:56 +0900 |
commit | 1f43d5632d4ceb2626893df14b857868ae8f7b5f (patch) | |
tree | 94f5853b0174fac86cc967bf22dd9e8f25b298b3 | |
parent | 93fe65702bfaaa12fd748c57fbbb819e3074ecef (diff) | |
parent | e3b5dfdf20b2617cc706d82e9d52d67ec92d26b6 (diff) | |
download | gitlab-ce-1f43d5632d4ceb2626893df14b857868ae8f7b5f.tar.gz |
Merge branch '38464-k8s-apps' of https://gitlab.com/gitlab-org/gitlab-ce into 38464-k8s-apps
-rw-r--r-- | app/models/clusters/applications/helm.rb | 4 | ||||
-rw-r--r-- | app/services/clusters/applications/base_helm_service.rb | 4 | ||||
-rw-r--r-- | app/services/clusters/applications/check_installation_progress_service.rb | 6 | ||||
-rw-r--r-- | app/services/clusters/applications/install_service.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/helm.rb | 77 | ||||
-rw-r--r-- | spec/factories/clusters/applications/helm.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes/helm_spec.rb | 100 | ||||
-rw-r--r-- | spec/models/clusters/applications/helm_spec.rb | 6 | ||||
-rw-r--r-- | spec/services/clusters/applications/install_service_spec.rb | 4 |
9 files changed, 163 insertions, 42 deletions
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb index 7ea84841118..c7949d11ef8 100644 --- a/app/models/clusters/applications/helm.rb +++ b/app/models/clusters/applications/helm.rb @@ -26,6 +26,10 @@ module Clusters def name self.class.application_name end + + def install_command + Gitlab::Kubernetes::Helm::InstallCommand.new(name, true) + end end end end diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb index 68320a3b267..9a4ce31cb39 100644 --- a/app/services/clusters/applications/base_helm_service.rb +++ b/app/services/clusters/applications/base_helm_service.rb @@ -20,6 +20,10 @@ module Clusters def helm_api @helm_api ||= Gitlab::Kubernetes::Helm.new(kubeclient) end + + def install_command + @install_command ||= app.install_command + end end end end diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb index 69bd3613cce..bde090eaeec 100644 --- a/app/services/clusters/applications/check_installation_progress_service.rb +++ b/app/services/clusters/applications/check_installation_progress_service.rb @@ -48,17 +48,17 @@ module Clusters end def remove_installation_pod - helm_api.delete_installation_pod!(app) + helm_api.delete_installation_pod!(install_command.pod_name) rescue # no-op end def installation_phase - helm_api.installation_status(app) + helm_api.installation_status(install_command.pod_name) end def installation_errors - helm_api.installation_log(app) + helm_api.installation_log(install_command.pod_name) end end end diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb index 4eba19a474e..8ceeec687cd 100644 --- a/app/services/clusters/applications/install_service.rb +++ b/app/services/clusters/applications/install_service.rb @@ -6,7 +6,7 @@ module Clusters begin app.make_installing! - helm_api.install(app) + helm_api.install(install_command) ClusterWaitForAppInstallationWorker.perform_in( ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id) diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb index 16b2abb7de2..7a50f07f3c5 100644 --- a/lib/gitlab/kubernetes/helm.rb +++ b/lib/gitlab/kubernetes/helm.rb @@ -3,27 +3,27 @@ module Gitlab class Helm HELM_VERSION = '2.7.0'.freeze NAMESPACE = 'gitlab-managed-apps'.freeze - COMMAND_SCRIPT = <<-EOS.freeze + INSTALL_DEPS = <<-EOS.freeze set -eo pipefail apk add -U ca-certificates openssl >/dev/null wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null mv /tmp/linux-amd64/helm /usr/bin/ - helm init ${HELM_INIT_OPTS} >/dev/null - [[ -z "${HELM_COMMAND+x}" ]] || helm ${HELM_COMMAND} >/dev/null EOS + InstallCommand = Struct.new(:name, :install_helm, :chart) do + def pod_name + "install-#{name}" + end + end + def initialize(kubeclient) @kubeclient = kubeclient @namespace = Namespace.new(NAMESPACE, kubeclient) end - def init! - install(OpenStruct.new(name: 'helm')) - end - - def install(app) + def install(command) @namespace.ensure_exists! - @kubeclient.create_pod(pod_resource(app)) + @kubeclient.create_pod(pod_resource(command)) end ## @@ -33,31 +33,27 @@ module Gitlab # # values: "Pending", "Running", "Succeeded", "Failed", "Unknown" # - def installation_status(app) - @kubeclient.get_pod(pod_name(app), @namespace.name).status.phase + def installation_status(pod_name) + @kubeclient.get_pod(pod_name, @namespace.name).status.phase end - def installation_log(app) - @kubeclient.get_pod_log(pod_name(app), @namespace.name).body + def installation_log(pod_name) + @kubeclient.get_pod_log(pod_name, @namespace.name).body end - def delete_installation_pod!(app) - @kubeclient.delete_pod(pod_name(app), @namespace.name) + def delete_installation_pod!(pod_name) + @kubeclient.delete_pod(pod_name, @namespace.name) end private - def pod_name(app) - "install-#{app.name}" - end - - def pod_resource(app) - labels = { 'gitlab.org/action': 'install', 'gitlab.org/application': app.name } - metadata = { name: pod_name(app), namespace: @namespace.name, labels: labels } + def pod_resource(command) + labels = { 'gitlab.org/action': 'install', 'gitlab.org/application': command.name } + metadata = { name: command.pod_name, namespace: @namespace.name, labels: labels } container = { name: 'helm', image: 'alpine:3.6', - env: generate_pod_env(app), + env: generate_pod_env(command), command: %w(/bin/sh), args: %w(-c $(COMMAND_SCRIPT)) } @@ -66,23 +62,34 @@ module Gitlab ::Kubeclient::Resource.new(metadata: metadata, spec: spec) end - def generate_pod_env(app) - env = { + def generate_pod_env(command) + { HELM_VERSION: HELM_VERSION, - TILLER_NAMESPACE: NAMESPACE, - COMMAND_SCRIPT: COMMAND_SCRIPT - } + TILLER_NAMESPACE: @namespace.name, + COMMAND_SCRIPT: generate_script(command) + }.map { |key, value| { name: key, value: value } } + end - if app.name != 'helm' - env[:HELM_INIT_OPTS] = '--client-only' - env[:HELM_COMMAND] = helm_install_comand(app) - end + def generate_script(command) + [ + INSTALL_DEPS, + helm_init_command(command), + helm_install_command(command) + ].join("\n") + end - env.map { |key, value| { name: key, value: value } } + def helm_init_command(command) + if command.install_helm + 'helm init >/dev/null' + else + 'helm init --client-only >/dev/null' + end end - def helm_install_comand(app) - "install #{app.chart} --name #{app.name} --namespace #{NAMESPACE}" + def helm_install_command(command) + return if command.chart.nil? + + "helm install #{command.chart} --name #{command.name} --namespace #{@namespace.name} >/dev/null" end end end diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index a0c874b103b..fab37195113 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -3,7 +3,7 @@ FactoryGirl.define do cluster factory: %i(cluster provided_by_gcp) trait :not_installable do - status -2 + status(-2) end trait :installable do diff --git a/spec/lib/gitlab/kubernetes/helm_spec.rb b/spec/lib/gitlab/kubernetes/helm_spec.rb new file mode 100644 index 00000000000..15f99b0401f --- /dev/null +++ b/spec/lib/gitlab/kubernetes/helm_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' + +describe Gitlab::Kubernetes::Helm do + let(:client) { double('kubernetes client') } + let(:helm) { described_class.new(client) } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(described_class::NAMESPACE, client) } + let(:install_helm) { true } + let(:chart) { 'stable/a_chart' } + let(:application_name) { 'app_name' } + let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm, chart) } + subject { helm } + + before do + allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client).and_return(namespace) + end + + describe '#initialize' do + it 'creates a namespace object' do + expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client) + + subject + end + end + + describe '#install' do + before do + allow(client).to receive(:create_pod).and_return(nil) + allow(namespace).to receive(:ensure_exists!).once + 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.install(command) + end + end + + describe '#installation_status' do + let(:phase) { Gitlab::Kubernetes::Pod::RUNNING } + let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation + + it 'fetches POD phase from kubernetes cluster' do + expect(client).to receive(:get_pod).with(command.pod_name, described_class::NAMESPACE).once.and_return(pod) + + expect(subject.installation_status(command.pod_name)).to eq(phase) + end + end + + describe '#installation_log' do + let(:log) { 'some output' } + let(:response) { RestClient::Response.new(log) } + + it 'fetches POD phase from kubernetes cluster' do + expect(client).to receive(:get_pod_log).with(command.pod_name, described_class::NAMESPACE).once.and_return(response) + + expect(subject.installation_log(command.pod_name)).to eq(log) + end + end + + describe '#delete_installation_pod!' do + it 'deletes the POD from kubernetes cluster' do + expect(client).to receive(:delete_pod).with(command.pod_name, described_class::NAMESPACE).once + + subject.delete_installation_pod!(command.pod_name) + end + end + + describe '#helm_init_command' do + subject { helm.send(:helm_init_command, command) } + + context 'when command.install_helm is true' do + let(:install_helm) { true } + + it { is_expected.to eq('helm init >/dev/null') } + end + + context 'when command.install_helm is false' do + let(:install_helm) { false } + + it { is_expected.to eq('helm init --client-only >/dev/null') } + end + end + + describe '#helm_install_command' do + subject { helm.send(:helm_install_command, command) } + + context 'when command.chart is nil' do + let(:chart) { nil } + + it { is_expected.to be_nil } + end + + context 'when command.chart is set' do + let(:chart) { 'stable/a_chart' } + + it { is_expected.to eq("helm install #{chart} --name #{application_name} --namespace #{namespace.name} >/dev/null")} + end + end +end diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb index beb10bf38c9..f8855079842 100644 --- a/spec/models/clusters/applications/helm_spec.rb +++ b/spec/models/clusters/applications/helm_spec.rb @@ -38,6 +38,12 @@ describe Clusters::Applications::Helm do end end + describe '#install_command' do + it 'has all the needed information' do + expect(subject.install_command).to have_attributes(name: subject.name, install_helm: true, chart: nil) + end + end + describe 'status state machine' do describe '#make_installing' do subject { create(:cluster_applications_helm, :scheduled) } diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb index 408f7e4354e..054a49ffedf 100644 --- a/spec/services/clusters/applications/install_service_spec.rb +++ b/spec/services/clusters/applications/install_service_spec.rb @@ -12,7 +12,7 @@ describe Clusters::Applications::InstallService do context 'when there are no errors' do before do - expect(helm_client).to receive(:install).with(application) + expect(helm_client).to receive(:install).with(application.install_command) allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil) end @@ -33,7 +33,7 @@ describe Clusters::Applications::InstallService do context 'when k8s cluster communication fails' do before do error = KubeException.new(500, 'system failure', nil) - expect(helm_client).to receive(:install).with(application).and_raise(error) + expect(helm_client).to receive(:install).with(application.install_command).and_raise(error) end it 'make the application errored' do |