Skip to content

Commit

Permalink
feat: Add a chectl preflight check step that verifies that the platfo…
Browse files Browse the repository at this point in the history
…rm version is supported (#500)

* Add versions checks tasks

Signed-off-by: Anatoliy Bazko <abazko@redhat.com>
  • Loading branch information
tolusha authored Feb 12, 2020
1 parent 1744186 commit 7240203
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 15 deletions.
30 changes: 30 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,36 @@
"skipFiles": [
"<node_internals>/**"
]
},
{
"type": "node",
"request": "launch",
"name": "Jest All",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"--runInBand"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
}
},
{
"type": "node",
"request": "launch",
"name": "Jest Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"${fileBasenameNoExtension}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
}
}
]
}
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ USAGE
OPTIONS
-h, --help show CLI help
-n, --chenamespace=chenamespace [default: che] Kubernetes namespace where Che server is supposed to be
deployed
-n, --chenamespace=chenamespace [default: che] Kubernetes namespace where Eclipse Che server is supposed to
be deployed
--debug-port=debug-port [default: 8000] Eclipse Che Server debug port
Expand Down Expand Up @@ -316,6 +316,9 @@ OPTIONS
Authorize usage of self signed certificates for encryption. Note that `self-signed-cert` secret with CA certificate
must be created in the configured namespace.
--skip-version-check
Skip minimal versions check.
--workspace-pvc-storage-class-name=workspace-pvc-storage-class-name
persistent volume(s) storage class name to use to store Eclipse Che workspaces data
```
Expand Down
133 changes: 133 additions & 0 deletions src/api/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*********************************************************************
* Copyright (c) 2020 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
**********************************************************************/

import execa = require('execa')
import Listr = require('listr')

export namespace VersionHelper {
export const MINIMAL_OPENSHIFT_VERSION = '3.11'
export const MINIMAL_K8S_VERSION = '1.9'
export const MINIMAL_HELM_VERSION = '2.15'

export function getOpenShiftCheckVersionTask(flags: any): Listr.ListrTask {
return {
title: 'Check OpenShift version',
task: async (_ctx: any, task: any) => {
const actualVersion = await getOpenShiftVersion()
if (actualVersion) {
task.title = `${task.title}: Found ${actualVersion}.`
} else {
task.title = `${task.title}: Unknown.`
}

if (!flags['skip-version-check'] && actualVersion) {
const checkPassed = checkMinimalVersion(actualVersion, MINIMAL_OPENSHIFT_VERSION)
if (!checkPassed) {
throw getError('OpenShift', actualVersion, MINIMAL_OPENSHIFT_VERSION)
}
}
}
}
}
export function getK8sCheckVersionTask(flags: any): Listr.ListrTask {
return {
title: 'Check Kubernetes version',
task: async (_ctx: any, task: any) => {
let actualVersion
switch (flags.platform) {
case 'minishift':
case 'openshift':
case 'crc':
actualVersion = await getK8sVersionWithOC()
break
default:
actualVersion = await getK8sVersionWithKubectl()
}

if (actualVersion) {
task.title = `${task.title}: Found ${actualVersion}.`
} else {
task.title = `${task.title}: Unknown.`
}

if (!flags['skip-version-check'] && actualVersion) {
const checkPassed = checkMinimalVersion(actualVersion, MINIMAL_K8S_VERSION)
if (!checkPassed) {
throw getError('Kubernetes', actualVersion, MINIMAL_K8S_VERSION)
}
}
}
}
}

export async function getOpenShiftVersion(): Promise<string | undefined> {
return getVersionWithOC('openshift ')
}

export async function getK8sVersionWithOC(): Promise<string | undefined> {
return getVersionWithOC('kubernetes ')
}

export async function getK8sVersionWithKubectl(): Promise<string | undefined> {
return getVersionWithKubectl('Server Version: ')
}

export function checkMinimalK8sVersion(actualVersion: string): boolean {
return checkMinimalVersion(actualVersion, MINIMAL_K8S_VERSION)
}

export function checkMinimalOpenShiftVersion(actualVersion: string): boolean {
return checkMinimalVersion(actualVersion, MINIMAL_OPENSHIFT_VERSION)
}

export function checkMinimalHelmVersion(actualVersion: string): boolean {
return checkMinimalVersion(actualVersion, MINIMAL_HELM_VERSION)
}

/**
* Compare versions and return true if actual version is greater or equal to minimal.
* The comparison will be done by major and minor versions.
*/
export function checkMinimalVersion(actual: string, minimal: string): boolean {
actual = removeVPrefix(actual)
let vers = actual.split('.')
const actualMajor = parseInt(vers[0], 10)
const actualMinor = parseInt(vers[1], 10)

minimal = removeVPrefix(minimal)
vers = minimal.split('.')
const minimalMajor = parseInt(vers[0], 10)
const minimalMinor = parseInt(vers[1], 10)

return (actualMajor > minimalMajor || (actualMajor === minimalMajor && actualMinor >= minimalMinor))
}

export function getError(actualVersion: string, minimalVersion: string, component: string): Error {
return new Error(`The minimal supported version of ${component} is '${minimalVersion} but found '${actualVersion}'. To bypass version check use '--skip-version-check' flag.`)
}

async function getVersionWithOC(versionPrefix: string): Promise<string | undefined> {
const command = 'oc'
const args = ['version']
const { stdout } = await execa(command, args, { timeout: 60000 })
return stdout.split('\n').filter(value => value.startsWith(versionPrefix)).map(value => value.substring(versionPrefix.length))[0]
}

async function getVersionWithKubectl(versionPrefix: string): Promise<string | undefined> {
const command = 'kubectl'
const args = ['version', '--short']
const { stdout } = await execa(command, args, { timeout: 60000 })
return stdout.split('\n').filter(value => value.startsWith(versionPrefix)).map(value => value.substring(versionPrefix.length))[0]
}

function removeVPrefix(version: string): string {
return version.startsWith('v') ? version.substring(1) : version
}
}
8 changes: 6 additions & 2 deletions src/commands/server/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ export default class Start extends Command {
'postgres-pvc-storage-class-name': string({
description: 'persistent volume storage class name to use to store Eclipse Che Postgres database',
default: ''
})
}),
'skip-version-check': flags.boolean({
description: 'Skip minimal versions check.',
default: false
}),
}

static getTemplatesDir(): string {
Expand Down Expand Up @@ -270,7 +274,7 @@ export default class Start extends Command {
await postInstallTasks.run(ctx)
this.log('Command server:start has completed successfully.')
} catch (err) {
this.error(err)
this.error(`${err}\nInstallation failed, check logs in '${ctx.directory}'`)
}

notifier.notify({
Expand Down
13 changes: 10 additions & 3 deletions src/tasks/installers/helm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as path from 'path'

import { CheHelper } from '../../api/che'
import { KubeHelper } from '../../api/kube'
import { VersionHelper } from '../../api/version'

export class HelmTasks {
/**
Expand All @@ -34,10 +35,16 @@ export class HelmTasks {
task: async (ctx: any, task: any) => {
try {
const version = await this.getVersion()
if (version.startsWith('v3.')) {
ctx.isHelmV3 = true
ctx.isHelmV3 = version.startsWith('v3.')

if (!flags['skip-version-check']) {
const checkPassed = VersionHelper.checkMinimalHelmVersion(version)
if (!checkPassed) {
throw VersionHelper.getError(version, VersionHelper.MINIMAL_HELM_VERSION, 'helm')
}
}
task.title = await `${task.title}: Found ${version}`

task.title = `${task.title}: Found ${version}`
} catch (error) {
command.error(`Unable to get helm version. ${error.message}`)
}
Expand Down
4 changes: 4 additions & 0 deletions src/tasks/platforms/crc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import * as commandExists from 'command-exists'
import * as execa from 'execa'
import * as Listr from 'listr'

import { VersionHelper } from '../../api/version'

/**
* Helper for Code Ready Container
*/
Expand Down Expand Up @@ -50,6 +52,8 @@ export class CRCHelper {
}
}
},
VersionHelper.getOpenShiftCheckVersionTask(flags),
VersionHelper.getK8sCheckVersionTask(flags),
{
title: 'Retrieving CodeReady Containers IP and domain for routes URLs',
enabled: () => flags.domain !== undefined,
Expand Down
2 changes: 2 additions & 0 deletions src/tasks/platforms/docker-desktop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as Listr from 'listr'
import * as os from 'os'

import { KubeHelper } from '../../api/kube'
import { VersionHelper } from '../../api/version'

export class DockerDesktopTasks {
private readonly kh: KubeHelper
Expand Down Expand Up @@ -58,6 +59,7 @@ export class DockerDesktopTasks {
}
}
},
VersionHelper.getK8sCheckVersionTask(flags),
{
title: 'Verify if nginx ingress is installed',
task: async (ctx: any) => {
Expand Down
2 changes: 2 additions & 0 deletions src/tasks/platforms/k8s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as commandExists from 'command-exists'
import * as Listr from 'listr'

import { KubeHelper } from '../../api/kube'
import { VersionHelper } from '../../api/version'

export class K8sTasks {
/**
Expand Down Expand Up @@ -64,6 +65,7 @@ export class K8sTasks {
}
}
},
VersionHelper.getK8sCheckVersionTask(flags),
// Should automatically compute route if missing
{
title: 'Verify domain is set',
Expand Down
5 changes: 3 additions & 2 deletions src/tasks/platforms/microk8s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import * as commandExists from 'command-exists'
import * as execa from 'execa'
import * as Listr from 'listr'

import { VersionHelper } from '../../api/version'

export class MicroK8sTasks {
/**
* Returns tasks list which perform preflight platform checks.
Expand Down Expand Up @@ -54,8 +56,7 @@ export class MicroK8sTasks {
command.error('MicroK8s is not running.', { code: 'E_REQUISITE_NOT_RUNNING' })
}
},
// { title: 'Verify microk8s memory configuration', skip: () => 'Not implemented yet', task: () => {}},
// { title: 'Verify kubernetes version', skip: () => 'Not implemented yet', task: () => {}},
VersionHelper.getK8sCheckVersionTask(flags),
{
title: 'Verify if microk8s ingress and storage addons is enabled',
task: async (ctx: any) => {
Expand Down
5 changes: 3 additions & 2 deletions src/tasks/platforms/minikube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import * as commandExists from 'command-exists'
import * as execa from 'execa'
import * as Listr from 'listr'

import { VersionHelper } from '../../api/version'

export class MinikubeTasks {
/**
* Returns tasks list which perform preflight platform checks.
Expand Down Expand Up @@ -50,8 +52,7 @@ export class MinikubeTasks {
},
task: () => this.startMinikube()
},
// { title: 'Verify minikube memory configuration', skip: () => 'Not implemented yet', task: () => {}},
// { title: 'Verify kubernetes version', skip: () => 'Not implemented yet', task: () => {}},
VersionHelper.getK8sCheckVersionTask(flags),
{
title: 'Verify if minikube ingress addon is enabled',
task: async (ctx: any) => {
Expand Down
6 changes: 4 additions & 2 deletions src/tasks/platforms/minishift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import * as commandExists from 'command-exists'
import * as execa from 'execa'
import * as Listr from 'listr'

import { VersionHelper } from '../../api/version'

export class MinishiftTasks {
/**
* Returns tasks list which perform preflight platform checks.
Expand Down Expand Up @@ -50,8 +52,8 @@ export class MinishiftTasks {
}
}
},
// { title: 'Verify minishift memory configuration', skip: () => 'Not implemented yet', task: () => {}},
// { title: 'Verify kubernetes version', skip: () => 'Not implemented yet', task: () => {}},
VersionHelper.getOpenShiftCheckVersionTask(flags),
VersionHelper.getK8sCheckVersionTask(flags)
], { renderer: flags['listr-renderer'] as any })
}

Expand Down
4 changes: 4 additions & 0 deletions src/tasks/platforms/openshift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import * as commandExists from 'command-exists'
import * as execa from 'execa'
import * as Listr from 'listr'

import { VersionHelper } from '../../api/version'

export class OpenshiftTasks {
/**
* Returns tasks list which perform preflight platform checks.
Expand Down Expand Up @@ -40,6 +42,8 @@ export class OpenshiftTasks {
}
}
},
VersionHelper.getOpenShiftCheckVersionTask(flags),
VersionHelper.getK8sCheckVersionTask(flags)
], { renderer: flags['listr-renderer'] as any })
}

Expand Down
Loading

0 comments on commit 7240203

Please sign in to comment.