Skip to content
Snippets Groups Projects
Commit 8ec618a6 authored by Alessio Caiazza's avatar Alessio Caiazza
Browse files

Add Helm InstallCommand

parent 760a154a
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -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
Loading
Loading
@@ -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
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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)
Loading
Loading
Loading
Loading
@@ -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
 
##
Loading
Loading
@@ -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))
}
Loading
Loading
@@ -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
Loading
Loading
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
Loading
Loading
@@ -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) }
Loading
Loading
Loading
Loading
@@ -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
 
Loading
Loading
@@ -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
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment