From b768811c54eb2c5f3c0a664395da5877676cd81d Mon Sep 17 00:00:00 2001 From: Florent Benoit Date: Mon, 25 Nov 2019 15:29:24 +0100 Subject: [PATCH] feat(helm): Add support for helm v3 fix https://github.com/eclipse/che/issues/15235 Change-Id: If48ecd53baaf4bdca70cfbbd3175c97f73592afd Signed-off-by: Florent Benoit --- src/tasks/installers/helm.ts | 66 ++++++++++++++++++++++++++++++ test/tasks/installers/helm.test.ts | 28 +++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 test/tasks/installers/helm.test.ts diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index c20df3e92..8ac496b2f 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -16,6 +16,7 @@ import { copy, mkdirp, remove } from 'fs-extra' import * as Listr from 'listr' import * as path from 'path' +import { CheHelper } from '../../api/che' import { KubeHelper } from '../../api/kube' export class HelmTasks { @@ -28,6 +29,33 @@ export class HelmTasks { title: 'Verify if helm is installed', task: () => { if (!commandExists.sync('helm')) { command.error('E_REQUISITE_NOT_FOUND') } } }, + { + title: 'Check Helm Version', + task: async (ctx: any, task: any) => { + try { + const version = await this.getVersion() + if (version.startsWith('v3.')) { + ctx.isHelmV3 = true + } + task.title = await `${task.title}: Found ${version}` + } catch (error) { + command.error(`Unable to get helm version. ${error.message}`) + } + } + }, + { + title: `Create Namespace (${flags.chenamespace})`, + task: async (_ctx: any, task: any) => { + const che = new CheHelper(flags) + const exist = await che.cheNamespaceExist(flags.chenamespace) + if (exist) { + task.title = `${task.title}...does already exist.` + } else { + await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true }) + task.title = `${task.title}...done.` + } + } + }, { title: 'Check for TLS secret prerequisites', // Check only if TLS is enabled @@ -72,6 +100,8 @@ export class HelmTasks { }, { title: 'Create Tiller Role Binding', + // Tiller is not used anymore in helm v3 + enabled: (ctx: any) => !ctx.isHelmV3, task: async (_ctx: any, task: any) => { const roleBindingExist = await this.tillerRoleBindingExist() if (roleBindingExist) { @@ -82,8 +112,24 @@ export class HelmTasks { } } }, + { + title: 'Check Cluster Role Binding', + // For helm v3 check for cluster role and delete it if exists + enabled: (ctx: any) => ctx.isHelmV3, + task: async (_ctx: any, task: any) => { + const roleBindingExist = await this.clusterRoleBindingExist(flags.chenamespace) + if (!roleBindingExist) { + task.title = `${task.title}...does not exists.` + } else { + await this.removeClusterRoleBinding(flags.chenamespace) + task.title = `${task.title}...done.` + } + } + }, { title: 'Create Tiller Service Account', + // Tiller is not used anymore in helm v3 + enabled: (ctx: any) => !ctx.isHelmV3, task: async (_ctx: any, task: any) => { const tillerServiceAccountExist = await this.tillerServiceAccountExist() if (tillerServiceAccountExist) { @@ -96,9 +142,13 @@ export class HelmTasks { }, { title: 'Create Tiller RBAC', + // Tiller is not used anymore in helm v3 + enabled: (ctx: any) => !ctx.isHelmV3, task: async () => this.createTillerRBAC(flags.templates) }, { + // Tiller is not used anymore in helm v3 + enabled: (ctx: any) => !ctx.isHelmV3, title: 'Create Tiller Service', task: async (_ctx: any, task: any) => { const tillerServiceExist = await this.tillerServiceExist() @@ -152,6 +202,16 @@ export class HelmTasks { }] } + async clusterRoleBindingExist(cheNamespace: string, execTimeout = 30000): Promise { + const { exitCode } = await execa('kubectl', ['get', 'clusterrolebinding', `${cheNamespace}-che-clusterrole-binding`], { timeout: execTimeout, reject: false }) + if (exitCode === 0) { return true } else { return false } + } + + async removeClusterRoleBinding(cheNamespace: string, execTimeout = 30000): Promise { + const { exitCode } = await execa('kubectl', ['delete', 'clusterrolebinding', `${cheNamespace}-che-clusterrole-binding`], { timeout: execTimeout, reject: false }) + if (exitCode === 0) { return true } else { return false } + } + async tillerRoleBindingExist(execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['get', 'clusterrolebinding', 'add-on-cluster-admin'], { timeout: execTimeout, reject: false }) if (exitCode === 0) { return true } else { return false } @@ -182,6 +242,12 @@ export class HelmTasks { if (exitCode === 0) { return true } else { return false } } + async getVersion(execTimeout = 10000): Promise { + const { stdout, exitCode } = await execa('helm', ['version', '--short'], { timeout: execTimeout, reject: false }) + if (exitCode === 0) { return stdout } + throw new Error('Unable to get version') + } + async createTillerService(execTimeout = 120000) { const { command, exitCode, stderr, stdout, timedOut } = await execa('helm', ['init', '--service-account', 'tiller', '--wait'], { timeout: execTimeout, reject: false }) diff --git a/test/tasks/installers/helm.test.ts b/test/tasks/installers/helm.test.ts new file mode 100644 index 000000000..6b939d1aa --- /dev/null +++ b/test/tasks/installers/helm.test.ts @@ -0,0 +1,28 @@ +/********************************************************************* + * Copyright (c) 2019 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ +// tslint:disable:object-curly-spacing +import { expect, fancy } from 'fancy-test' +import * as execa from 'execa' + +import { HelmTasks } from '../../../src/tasks/installers/helm' + +jest.mock('execa') + +let helmTasks = new HelmTasks() +describe('Helm helper', () => { + + fancy + .it('check get v3 version', async () => { + const helmVersionOutput = 'v3.0.0+ge29ce2a'; + (execa as any).mockResolvedValue({ exitCode: 0, stdout: helmVersionOutput }) + const version = await helmTasks.getVersion(); + expect(version).to.equal('v3.0.0+ge29ce2a') + }) +})