From 93454d975f621df44b559269e1da53090591e37d Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 30 Mar 2020 11:57:11 +0300 Subject: [PATCH 01/40] Implement olm installer and tasks Signed-off-by: Oleksandr Andriienko --- README.md | 2 +- src/api/kube.ts | 215 ++++++++++++++++++++++++++- src/api/typings/olm.d.ts | 121 +++++++++++++++ src/commands/server/delete.ts | 3 + src/commands/server/start.ts | 6 +- src/constants.ts | 14 ++ src/tasks/installers/common-tasks.ts | 94 ++++++++++++ src/tasks/installers/installer.ts | 5 + src/tasks/installers/olm.ts | 156 +++++++++++++++++++ src/tasks/installers/operator.ts | 150 +++++-------------- 10 files changed, 645 insertions(+), 121 deletions(-) create mode 100644 src/api/typings/olm.d.ts create mode 100644 src/tasks/installers/common-tasks.ts create mode 100644 src/tasks/installers/olm.ts diff --git a/README.md b/README.md index a9b631c4e..333553d1b 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ USAGE $ chectl server:start OPTIONS - -a, --installer=helm|operator|minishift-addon + -a, --installer=helm|operator|olm|minishift-addon Installer type -b, --domain=domain diff --git a/src/api/kube.ts b/src/api/kube.ts index d7c6f3328..ba93652c1 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -20,10 +20,11 @@ import { merge } from 'lodash' import * as net from 'net' import { Writable } from 'stream' -import { DEFAULT_CHE_IMAGE } from '../constants' +import { DEFAULT_CHE_IMAGE, defaultOlmRegistry } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' +import { OperatorSource, OperatorGroup, Subscription, InstallPlan } from 'olm' const AWAIT_TIMEOUT_S = 30 @@ -1211,6 +1212,215 @@ export class KubeHelper { } } + async operatorSourceExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', name) + return this.compare(body, name) + } catch { + return false + } + } + + async createOperatorSource(name: string, registryNamespace: string, namespace: string) { + const operatorSource: OperatorSource = { + apiVersion: 'operators.coreos.com/v1', + kind: 'OperatorSource', + metadata: { + name, + namespace, + }, + spec: { + endpoint: defaultOlmRegistry, + registryNamespace, + type: "appregistry" + } + } + + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', operatorSource) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteOperatorSource(name: string, namespace: string) { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const options = new V1DeleteOptions() + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', name, options) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async operatorGroupExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', name) + return this.compare(body, name) + } catch { + return false + } + } + + async createOperatorGroup(operatorGroupName: string, namespace: string) { + const operatorGroup: OperatorGroup = { + apiVersion: 'operators.coreos.com/v1', + kind: 'OperatorGroup', + metadata: { + name: operatorGroupName, + namespace, + }, + spec: { + targetNamespaces: [ namespace ] + } + } + + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', operatorGroup) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteOperatorGroup(operatorGroupName: string, namespace: string) { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const options = new V1DeleteOptions() + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', operatorGroupName, options) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createOperatorSubscription(packageName: string, namespace: string, marketplaceNamespace: string, channel: string, csv?: string) { + const subscription: Subscription = { + apiVersion: "operators.coreos.com/v1alpha1", + kind: 'Subscription', + metadata: { + name: packageName, + namespace + }, + spec: { + channel, + installPlanApproval: 'Manual', + name: packageName, + // we use package name the same like subscription name + source: packageName, + // Todo let's use source namespace the same with Che installation + sourceNamespace: marketplaceNamespace, + // startingCSV: csv + } + } + + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', subscription) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async operatorSubscriptionExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', name) + return this.compare(body, name) + } catch { + return false + } + } + + async deleteOperatorSubscription(operatorSubscriptionName: string, namespace: string) { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const options = new V1DeleteOptions() + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', operatorSubscriptionName, options) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async waitOperatorSubscriptionReadyForApproval(namespace: string, subscriptionName: string, timeout = AWAIT_TIMEOUT_S): Promise { + return new Promise(async (resolve, reject) => { + const watcher = new Watch(this.kc) + let request: any + request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/subscriptions`, + { fieldSelector: `metadata.name=${subscriptionName}` }, + (_phase: string, obj: any) => { + const subscription = obj as Subscription + if (subscription.status && subscription.status.conditions) { + for (const condition of subscription.status.conditions) { + if (condition.type === 'InstallPlanPending' && condition.status === 'True') { + resolve(subscription.status.installplan) + } + } + } + }, + error => { + if (error) { + reject(error) + } + }) + + setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${subscriptionName}" subscription is ready.`) + }, timeout * 1000) + }) + } + + // await k8sApi.getAPIResources() // todo check if operator api exists... + + async aproveOperatorInstallationPlan(name = '', namespace = '') { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const patch: InstallPlan = { + spec: { + approved: true + } + } + await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'installplans', name, patch, {headers: {'Content-Type': 'application/merge-patch+json'}}) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async waitWhileOperatorInstalled(installPlanName: string, namespace: string, timeout = 30) { + return new Promise(async (resolve, reject) => { + const watcher = new Watch(this.kc) + let request: any + request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/installplans`, + { fieldSelector: `metadata.name=${installPlanName}` }, + (_phase: string, obj: any) => { + const installPlan = obj as InstallPlan + if (installPlan.status && installPlan.status.conditions) { + for (const condition of installPlan.status.conditions) { + if (condition.type === 'Installed' && condition.status === 'True') { + resolve() + } + } + } + }, + error => { + if (error) { + reject(error) + } + }) + + setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${installPlanName}" has go status 'Installed'.`) + }, timeout * 1000) + }) + } + async deleteNamespace(namespace: string): Promise { const k8sCoreApi = this.kc.makeApiClient(CoreV1Api) try { @@ -1472,7 +1682,8 @@ export class KubeHelper { // Set up watcher const watcher = new Watch(this.kc) request = watcher - .watch(`/api/v1/namespaces/${namespace}/secrets/`, { fieldSelector: `metadata.name=${secretName}` }, + .watch(`/api/v1/namespaces/${namespace}/secrets/`, + { fieldSelector: `metadata.name=${secretName}` }, (_phase: string, obj: any) => { const secret = obj as V1Secret diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts new file mode 100644 index 000000000..417f9e193 --- /dev/null +++ b/src/api/typings/olm.d.ts @@ -0,0 +1,121 @@ +/********************************************************************* + * 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 + **********************************************************************/ + +declare module 'olm' { + + export interface OperatorSource { + /** + * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources + */ + apiVersion: string; + /** + * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + */ + kind: string; + /** + * Standard object's metadata. + */ + metadata: V1ObjectMeta; + + spec: OperatorSourceSpec; + } + + export interface OperatorSourceSpec { + endpoint: string; + registryNamespace: string + type: string; + } + + export interface OperatorGroup { + /** + * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources + */ + apiVersion: string; + /** + * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + */ + kind: string; + /** + * Standard object's metadata. + */ + metadata: V1ObjectMeta; + + spec: OperatorGroupSpec; + } + + export interface OperatorGroupSpec { + targetNamespaces: string[]; + } + + export interface Subscription { + /** + * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources + */ + apiVersion: string; + /** + * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + */ + kind: string; + /** + * Standard object's metadata. + */ + metadata: V1ObjectMeta; + + spec: SubscriptionSpec + status?: SubscriptionStatus + } + + export interface SubscriptionSpec { + channel: string + installPlanApproval: string + name: string + source: string + sourceNamespace: string + startingCSV?: string + } + + export interface SubscriptionStatus { + conditions: SubscriptionStatusCondition[] + currentCSV: string + installplan: InstallPlan + state: string + } + + export interface SubscriptionStatusCondition { + message: string + reason: string + status: string + type: string + } + + export interface InstallPlan { + apiVersion?: string + kind?: string + name?: string + namespace?: string + uuid?: string + spec?: InstallPlanSpec + status?: InstallPlanStatus + } + + export interface InstallPlanSpec { + approved?: boolean + } + + export interface InstallPlanStatus { + conditions: InstallPlanCondition[] + } + + export interface InstallPlanCondition { + type: string + status: string + } +} + diff --git a/src/commands/server/delete.ts b/src/commands/server/delete.ts index 8fadb2b05..648f1b9d5 100644 --- a/src/commands/server/delete.ts +++ b/src/commands/server/delete.ts @@ -19,6 +19,7 @@ import { CheTasks } from '../../tasks/che' import { HelmTasks } from '../../tasks/installers/helm' import { MinishiftAddonTasks } from '../../tasks/installers/minishift-addon' import { OperatorTasks } from '../../tasks/installers/operator' +import { OLMTasks } from '../../tasks/installers/olm' import { ApiTasks } from '../../tasks/platforms/api' export default class Delete extends Command { @@ -43,6 +44,7 @@ export default class Delete extends Command { const helmTasks = new HelmTasks(flags) const minishiftAddonTasks = new MinishiftAddonTasks() const operatorTasks = new OperatorTasks() + const olmTasks = new OLMTasks() const cheTasks = new CheTasks(flags) let tasks = new Listrq(undefined, @@ -51,6 +53,7 @@ export default class Delete extends Command { tasks.add(apiTasks.testApiTasks(flags, this)) tasks.add(operatorTasks.deleteTasks(flags)) + tasks.add(olmTasks.deleteTasks(flags)) tasks.add(cheTasks.deleteTasks(flags)) tasks.add(helmTasks.deleteTasks(flags)) tasks.add(minishiftAddonTasks.deleteTasks(flags)) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index d2e77f34a..8b063d576 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -95,7 +95,7 @@ export default class Start extends Command { installer: string({ char: 'a', description: 'Installer type', - options: ['helm', 'operator', 'minishift-addon'], + options: ['helm', 'operator', 'olm', 'minishift-addon'], default: '' }), domain: string({ @@ -184,12 +184,12 @@ export default class Start extends Command { } } else if (flags.platform === 'crc') { if (flags.installer === '') { - flags.installer = 'operator' + flags.installer = 'olm' } } // TODO when tls by default is implemented for all platforms, make `tls` flag turned on by default. - if (flags.installer === 'helm' && (flags.platform === 'k8s' || flags.platform === 'minikube' || flags.platform === 'microk8s')) { + if (flags.installer === 'helm' && (flags.platform === 'k8s' || flags.platform === 'minikube' || flags.platform === 'microk8s')) { // todo olm flags.tls = true } } diff --git a/src/constants.ts b/src/constants.ts index 929b18c30..0c9fc0813 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -17,3 +17,17 @@ export const CA_CERT_GENERATION_JOB_IMAGE = 'quay.io/eclipse/che-cert-manager-ca export const CERT_MANAGER_NAMESPACE_NAME = 'cert-manager' export const CHE_TLS_SECRET_NAME = 'che-tls' + +export const operatorCheCluster = 'eclipse-che' + +export const defaultOlmOpenshiftRegistryNamespace = 'eclipse-che-operator-openshift' +export const defaultOmlKubernetesRegistryNamespace = 'eclipse-che-operator-kubernetes' +export const defaultOlmOpenshiftOperatorSourceNamespace = 'openshift-marketplace' +export const defaultOlmKubernetesOperatorSourceNamespace = 'marketplace' +export const defaultOlmRegistry = 'https://quay.io/cnr' + +// TODO figure out and handle, Do we have the same channels for CRW? +export enum CheOLMChannel { + NIGHTLY = 'nightly', + STABLE = 'stable' +} diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts new file mode 100644 index 000000000..be582ae78 --- /dev/null +++ b/src/tasks/installers/common-tasks.ts @@ -0,0 +1,94 @@ +/********************************************************************* + * 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 { ListrTask, ListrContext} from 'listr' +import { CheHelper } from '../../api/che' +import * as execa from 'execa' +import { copy, mkdirp, remove } from 'fs-extra' +import * as path from 'path' +import { KubeHelper } from '../../api/kube' +import { operatorCheCluster } from '../../constants' +import { isOpenshiftPlatformFamily, isKubernetesPlatformFamily } from '../../util' + +export function createNamespaceTask(flags: any): ListrTask { + return { + 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}...It already exists.` + } else if (isKubernetesPlatformFamily(flags.platform)) { + await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true }) + task.title = `${task.title}...done.` + } else if (isOpenshiftPlatformFamily(flags.platform)) { + await execa(`oc new-project ${flags.chenamespace}`, { shell: true }) + task.title = `${task.title}...done.` + } + } + } +} + +export function copyOperatorResources(flags: any, cacheDir: string): ListrTask { + return { + title: 'Copying operator resources', + task: async (ctx: any, task: any) => { + ctx.resourcesPath = await copyCheOperatorResources(flags.templates, cacheDir) + task.title = `${task.title}...done.` + } + } +} + +async function copyCheOperatorResources(templatesDir: string, cacheDir: string): Promise { + const srcDir = path.join(templatesDir, '/che-operator/') + const destDir = path.join(cacheDir, '/templates/che-operator/') + + await remove(destDir) + await mkdirp(destDir) + await copy(srcDir, destDir) + + return destDir +} + +export function createEclipeCheCluster(flags: any): ListrTask { + return { + title: `Create Eclipse Che cluster ${operatorCheCluster} in namespace ${flags.chenamespace}`, + task: async (ctx: any, task: any) => { + const kube = new KubeHelper(flags) + const exist = await kube.cheClusterExist(operatorCheCluster, flags.chenamespace) + if (exist) { + task.title = `${task.title}...It already exists.` + } else { + // Eclipse Che operator supports only Multi-User Che + ctx.isCheDeployed = true + ctx.isPostgresDeployed = true + ctx.isKeycloakDeployed = true + + // plugin and devfile registry will be deployed only when external ones are not configured + ctx.isPluginRegistryDeployed = !(flags['plugin-registry-url'] as boolean) + ctx.isDevfileRegistryDeployed = !(flags['devfile-registry-url'] as boolean) + + const yamlFilePath = flags['che-operator-cr-yaml'] === '' ? ctx.resourcesPath + 'crds/org_v1_che_cr.yaml' : flags['che-operator-cr-yaml'] + const cr = await kube.createCheClusterFromFile(yamlFilePath, flags, flags['che-operator-cr-yaml'] === '') + ctx.isKeycloakReady = ctx.isKeycloakReady || cr.spec.auth.externalIdentityProvider + ctx.isPostgresReady = ctx.isPostgresReady || cr.spec.database.externalDb + ctx.isDevfileRegistryReady = ctx.isDevfileRegistryReady || cr.spec.server.externalDevfileRegistry + ctx.isPluginRegistryReady = ctx.isPluginRegistryReady || cr.spec.server.externalPluginRegistry + + if (cr.spec.server.customCheProperties && cr.spec.server.customCheProperties.CHE_MULTIUSER === 'false') { + flags.multiuser = false + } + + task.title = `${task.title}...done.` + } + } + } +} + diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index 996521205..ff7afb9b8 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -14,6 +14,7 @@ import * as Listr from 'listr' import { HelmTasks } from './helm' import { MinishiftAddonTasks } from './minishift-addon' import { OperatorTasks } from './operator' +import { OLMTasks } from './olm' /** * Tasks related to installation way. @@ -68,6 +69,7 @@ export class InstallerTasks { installTasks(flags: any, command: Command): ReadonlyArray { const helmTasks = new HelmTasks(flags) const operatorTasks = new OperatorTasks() + const olmTasks = new OLMTasks() const minishiftAddonTasks = new MinishiftAddonTasks() let title: string @@ -86,6 +88,9 @@ export class InstallerTasks { return operatorTasks.startTasks(flags, command) } // installer.ts BEGIN CHE ONLY + } else if (flags.installer == 'olm') { + title = '🏃‍ Running Olm installaion Eclipse Che' + task = () => olmTasks.startTasks(flags, command) } else if (flags.installer === 'helm') { title = '🏃‍ Running Helm to install Eclipse Che' task = () => helmTasks.startTasks(flags, command) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts new file mode 100644 index 000000000..4af8567a2 --- /dev/null +++ b/src/tasks/installers/olm.ts @@ -0,0 +1,156 @@ +/********************************************************************* + * 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 Command from '@oclif/command'; +import Listr = require('listr'); +import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultOlmOpenshiftRegistryNamespace, defaultOmlKubernetesRegistryNamespace, defaultOlmOpenshiftOperatorSourceNamespace, defaultOlmKubernetesOperatorSourceNamespace } from '../../constants'; + +import { KubeHelper } from '../../api/kube'; +import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources } from './common-tasks'; + +export class OLMTasks { + + sourceName = 'eclipse-che-preview' + operatorGroupName = 'cheoperatorgroup' + packageNamePrefix = 'eclipse-che-preview-' + channel = this.getDefaultChannel() + + /** + * Returns list of tasks which perform preflight platform checks. + */ + startTasks(flags: any, command: Command): Listr { + const kube = new KubeHelper(flags) + return new Listr([ + createNamespaceTask(flags), + copyOperatorResources(flags, command.config.cacheDir), + { + title: "Create operator source", + task: async (ctx: any, task: any) => { + ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOlmOpenshiftRegistryNamespace : defaultOmlKubernetesRegistryNamespace + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + + if (await kube.operatorSourceExists(this.sourceName, ctx.marketplaceNamespace)) { + task.title += '...It already exists.' + } else { + await kube.createOperatorSource(this.sourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) + } + } + }, + { + title: 'Create operator group', + task: async (ctx: any, task: any) => { + if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { + task.title += '...It already exists.' + } else { + task.title += '...Create operator group' + await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) + } + } + }, + { + title: 'Create operator subscription', + task: async (ctx: any, task: any) => { + ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { + task.title += '...It already exists.' + } else { + await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel) + } + } + }, + { + title: 'Wait while subscription is ready', + task: async (ctx: any, task: any) => { + // Todo set time out ... + const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName, 60) + ctx.installPlanName = installPlan.name + } + }, + { + title: 'Approve installation', + task: async (ctx: any, task: any) => { + await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + } + }, + { + title: 'Wait while opertor installed', + task: async (ctx: any, task: any) => { + await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 30) + } + }, + createEclipeCheCluster(flags) + ], { renderer: flags['listr-renderer'] as any }) + } + + updateTasks(flags: any, command: Command): Listr { + const kube = new KubeHelper(flags) + return new Listr([ + { + title: 'Approve installation', + task: async (ctx: any, task: any) => { + await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + } + }, + { + title: 'Wait while newer operator installed', + task: async (ctx: any, task: any) => { + await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 30) + } + }, + ], { renderer: flags['listr-renderer'] as any }) + } + + deleteTasks(flags: any, command?: Command): ReadonlyArray { + const kube = new KubeHelper(flags) + return [ + { + title: `Delete(OLM) operator source ${this.sourceName}`, + task: async (ctx: any, task: any) => { + if (await kube.operatorSourceExists(this.sourceName, flags.chenamespace)) { + await kube.deleteOperatorSource(this.sourceName, flags.chenamespace) + } + task.title += '...OK' + } + }, + { + title: `Delete(OLM) operator group ${this.operatorGroupName}`, + task: async (ctx: any, task: any) => { + if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { + await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) + } + task.title += '...OK' + } + }, + { + title: `Delete(OLM) operator subscription 'Todo package name...'`, + task: async (ctx: any, task: any) => { + // todo why do we need the same subscription name like package name. Are you sure? or move it upper. + const packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + + if (await kube.operatorSubscriptionExists(packageName, flags.chenamespace)) { + await kube.deleteOperatorSubscription(packageName, flags.chenamespace) + } + task.title += '...OK' + } + }, + ] + } + + // To update chectl stable channel we are patching src/constants.ts from nightly to release version. + // Let's use it to determine which olm channel should we use by default. + // TODO: Fixme: take a look, maybe it is better to have chectl like field in the package.json... + getDefaultChannel(): CheOLMChannel { + if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { + return CheOLMChannel.NIGHTLY + } + return CheOLMChannel.STABLE + } +} + diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 309887468..8dc2ee9df 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -10,16 +10,13 @@ import { V1Deployment } from '@kubernetes/client-node' import { Command } from '@oclif/command' import { cli } from 'cli-ux' -import * as execa from 'execa' import * as fs from 'fs' -import { copy, mkdirp, remove } from 'fs-extra' import * as yaml from 'js-yaml' import * as Listr from 'listr' -import * as path from 'path' - -import { CheHelper } from '../../api/che' import { KubeHelper } from '../../api/kube' import { isKubernetesPlatformFamily } from '../../util' +import { createEclipeCheCluster, copyOperatorResources, createNamespaceTask } from './common-tasks' +import { operatorCheCluster } from '../../constants' export class OperatorTasks { operatorServiceAccount = 'che-operator' @@ -29,38 +26,15 @@ export class OperatorTasks { operatorClusterRoleBinding = 'che-operator' cheClusterCrd = 'checlusters.org.eclipse.che' operatorName = 'che-operator' - operatorCheCluster = 'eclipse-che' - resourcesPath = '' /** * Returns tasks list which perform preflight platform checks. */ startTasks(flags: any, command: Command): Listr { - const che = new CheHelper(flags) const kube = new KubeHelper(flags) return new Listr([ - { - title: 'Copying operator resources', - task: async (_ctx: any, task: any) => { - this.resourcesPath = await this.copyCheOperatorResources(flags.templates, command.config.cacheDir) - task.title = `${task.title}...done.` - } - }, - { - title: `Create Namespace (${flags.chenamespace})`, - task: async (_ctx: any, task: any) => { - const exist = await che.cheNamespaceExist(flags.chenamespace) - if (exist) { - task.title = `${task.title}...It already exists.` - } else if (flags.platform === 'minikube' || flags.platform === 'k8s' || flags.platform === 'microk8s') { - await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true }) - task.title = `${task.title}...done.` - } else if (flags.platform === 'minishift' || flags.platform === 'openshift' || flags.platform === 'crc') { - await execa(`oc new-project ${flags.chenamespace}`, { shell: true }) - task.title = `${task.title}...done.` - } - } - }, + copyOperatorResources(flags, command.config.cacheDir), + createNamespaceTask(flags), { title: 'Checking for pre-created TLS secret', // In case of Openshift infrastructure the certificate from cluster router will be used, so no need in the `che-tls` secret. @@ -125,12 +99,12 @@ export class OperatorTasks { }, { title: `Create ServiceAccount ${this.operatorServiceAccount} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.serviceAccountExist(this.operatorServiceAccount, flags.chenamespace) if (exist) { task.title = `${task.title}...It already exists.` } else { - const yamlFilePath = this.resourcesPath + 'service_account.yaml' + const yamlFilePath = ctx.resourcesPath + 'service_account.yaml' await kube.createServiceAccountFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...done.` } @@ -138,12 +112,12 @@ export class OperatorTasks { }, { title: `Create Role ${this.operatorRole} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.roleExist(this.operatorRole, flags.chenamespace) if (exist) { task.title = `${task.title}...It already exists.` } else { - const yamlFilePath = this.resourcesPath + 'role.yaml' + const yamlFilePath = ctx.resourcesPath + 'role.yaml' const statusCode = await kube.createRoleFromFile(yamlFilePath, flags.chenamespace) if (statusCode === 403) { command.error('ERROR: It looks like you don\'t have enough privileges. You need to grant more privileges to current user or use a different user. If you are using minishift you can "oc login -u system:admin"') @@ -154,12 +128,12 @@ export class OperatorTasks { }, { title: `Create ClusterRole ${this.operatorClusterRole}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.clusterRoleExist(this.operatorClusterRole) if (exist) { task.title = `${task.title}...It already exists.` } else { - const yamlFilePath = this.resourcesPath + 'cluster_role.yaml' + const yamlFilePath = ctx.resourcesPath + 'cluster_role.yaml' const statusCode = await kube.createClusterRoleFromFile(yamlFilePath) if (statusCode === 403) { command.error('ERROR: It looks like you don\'t have enough privileges. You need to grant more privileges to current user or use a different user. If you are using minishift you can "oc login -u system:admin"') @@ -170,12 +144,12 @@ export class OperatorTasks { }, { title: `Create RoleBinding ${this.operatorRoleBinding} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.roleBindingExist(this.operatorRoleBinding, flags.chenamespace) if (exist) { task.title = `${task.title}...It already exists.` } else { - const yamlFilePath = this.resourcesPath + 'role_binding.yaml' + const yamlFilePath = ctx.resourcesPath + 'role_binding.yaml' await kube.createRoleBindingFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...done.` } @@ -183,7 +157,7 @@ export class OperatorTasks { }, { title: `Create ClusterRoleBinding ${this.operatorClusterRoleBinding}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.clusterRoleBindingExist(this.operatorRoleBinding) if (exist) { task.title = `${task.title}...It already exists.` @@ -195,12 +169,12 @@ export class OperatorTasks { }, { title: `Create CRD ${this.cheClusterCrd}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.crdExist(this.cheClusterCrd) if (exist) { task.title = `${task.title}...It already exists.` } else { - const yamlFilePath = this.resourcesPath + 'crds/org_v1_che_crd.yaml' + const yamlFilePath = ctx.resourcesPath + 'crds/org_v1_che_crd.yaml' await kube.createCrdFromFile(yamlFilePath) task.title = `${task.title}...done.` } @@ -215,47 +189,17 @@ export class OperatorTasks { }, { title: `Create deployment ${this.operatorName} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.deploymentExist(this.operatorName, flags.chenamespace) if (exist) { task.title = `${task.title}...It already exists.` } else { - await kube.createDeploymentFromFile(this.resourcesPath + 'operator.yaml', flags.chenamespace, flags['che-operator-image']) + await kube.createDeploymentFromFile(ctx.resourcesPath + 'operator.yaml', flags.chenamespace, flags['che-operator-image']) task.title = `${task.title}...done.` } } }, - { - title: `Create Eclipse Che cluster ${this.operatorCheCluster} in namespace ${flags.chenamespace}`, - task: async (ctx: any, task: any) => { - const exist = await kube.cheClusterExist(this.operatorCheCluster, flags.chenamespace) - if (exist) { - task.title = `${task.title}...It already exists.` - } else { - // Eclipse Che operator supports only Multi-User Che - ctx.isCheDeployed = true - ctx.isPostgresDeployed = true - ctx.isKeycloakDeployed = true - - // plugin and devfile registry will be deployed only when external ones are not configured - ctx.isPluginRegistryDeployed = !(flags['plugin-registry-url'] as boolean) - ctx.isDevfileRegistryDeployed = !(flags['devfile-registry-url'] as boolean) - - const yamlFilePath = flags['che-operator-cr-yaml'] === '' ? this.resourcesPath + 'crds/org_v1_che_cr.yaml' : flags['che-operator-cr-yaml'] - const cr = await kube.createCheClusterFromFile(yamlFilePath, flags, flags['che-operator-cr-yaml'] === '') - ctx.isKeycloakReady = ctx.isKeycloakReady || cr.spec.auth.externalIdentityProvider - ctx.isPostgresReady = ctx.isPostgresReady || cr.spec.database.externalDb - ctx.isDevfileRegistryReady = ctx.isDevfileRegistryReady || cr.spec.server.externalDevfileRegistry - ctx.isPluginRegistryReady = ctx.isPluginRegistryReady || cr.spec.server.externalPluginRegistry - - if (cr.spec.server.customCheProperties && cr.spec.server.customCheProperties.CHE_MULTIUSER === 'false') { - flags.multiuser = false - } - - task.title = `${task.title}...done.` - } - } - } + createEclipeCheCluster(flags) ], { renderer: flags['listr-renderer'] as any }) } @@ -285,18 +229,12 @@ export class OperatorTasks { updateTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ - { - title: 'Copying operator resources', - task: async (_ctx: any, task: any) => { - this.resourcesPath = await this.copyCheOperatorResources(flags.templates, command.config.cacheDir) - task.title = `${task.title}...done.` - } - }, + copyOperatorResources(flags, command.config.cacheDir), { title: `Updating ServiceAccount ${this.operatorServiceAccount} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.serviceAccountExist(this.operatorServiceAccount, flags.chenamespace) - const yamlFilePath = this.resourcesPath + 'service_account.yaml' + const yamlFilePath = ctx.resourcesPath + 'service_account.yaml' if (exist) { await kube.replaceServiceAccountFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...updated.` @@ -308,9 +246,9 @@ export class OperatorTasks { }, { title: `Updating Role ${this.operatorRole} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.roleExist(this.operatorRole, flags.chenamespace) - const yamlFilePath = this.resourcesPath + 'role.yaml' + const yamlFilePath = ctx.resourcesPath + 'role.yaml' if (exist) { const statusCode = await kube.replaceRoleFromFile(yamlFilePath, flags.chenamespace) if (statusCode === 403) { @@ -328,9 +266,9 @@ export class OperatorTasks { }, { title: `Updating ClusterRole ${this.operatorClusterRole}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.clusterRoleExist(this.operatorClusterRole) - const yamlFilePath = this.resourcesPath + 'cluster_role.yaml' + const yamlFilePath = ctx.resourcesPath + 'cluster_role.yaml' if (exist) { const statusCode = await kube.replaceClusterRoleFromFile(yamlFilePath) if (statusCode === 403) { @@ -348,9 +286,9 @@ export class OperatorTasks { }, { title: `Updating RoleBinding ${this.operatorRoleBinding} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.roleBindingExist(this.operatorRoleBinding, flags.chenamespace) - const yamlFilePath = this.resourcesPath + 'role_binding.yaml' + const yamlFilePath = ctx.resourcesPath + 'role_binding.yaml' if (exist) { await kube.replaceRoleBindingFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...updated.` @@ -375,9 +313,9 @@ export class OperatorTasks { }, { title: `Updating Eclipse Che cluster CRD ${this.cheClusterCrd}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const crd = await kube.getCrd(this.cheClusterCrd) - const yamlFilePath = this.resourcesPath + 'crds/org_v1_che_crd.yaml' + const yamlFilePath = ctx.resourcesPath + 'crds/org_v1_che_crd.yaml' if (crd) { if (!crd.metadata || !crd.metadata.resourceVersion) { throw new Error(`Fetched CRD ${this.cheClusterCrd} without resource version`) @@ -400,13 +338,13 @@ export class OperatorTasks { }, { title: `Updating deployment ${this.operatorName} in namespace ${flags.chenamespace}`, - task: async (_ctx: any, task: any) => { + task: async (ctx: any, task: any) => { const exist = await kube.deploymentExist(this.operatorName, flags.chenamespace) if (exist) { - await kube.replaceDeploymentFromFile(this.resourcesPath + 'operator.yaml', flags.chenamespace, flags['che-operator-image']) + await kube.replaceDeploymentFromFile(ctx.resourcesPath + 'operator.yaml', flags.chenamespace, flags['che-operator-image']) task.title = `${task.title}...updated.` } else { - await kube.createDeploymentFromFile(this.resourcesPath + 'operator.yaml', flags.chenamespace, flags['che-operator-image']) + await kube.createDeploymentFromFile(ctx.resourcesPath + 'operator.yaml', flags.chenamespace, flags['che-operator-image']) task.title = `${task.title}...created new one.` } } @@ -427,11 +365,11 @@ export class OperatorTasks { deleteTasks(flags: any): ReadonlyArray { let kh = new KubeHelper(flags) return [{ - title: `Delete the CR ${this.operatorCheCluster} of type ${this.cheClusterCrd}`, + title: `Delete the CR ${operatorCheCluster} of type ${this.cheClusterCrd}`, task: async (_ctx: any, task: any) => { if (await kh.crdExist(this.cheClusterCrd) && - await kh.cheClusterExist(this.operatorCheCluster, flags.chenamespace)) { - await kh.deleteCheCluster(this.operatorCheCluster, flags.chenamespace) + await kh.cheClusterExist(operatorCheCluster, flags.chenamespace)) { + await kh.deleteCheCluster(operatorCheCluster, flags.chenamespace) await cli.wait(2000) //wait a couple of secs for the finalizers to be executed task.title = await `${task.title}...OK` } else { @@ -439,15 +377,6 @@ export class OperatorTasks { } } }, - { - title: `Delete CRD ${this.cheClusterCrd}`, - task: async (_ctx: any, task: any) => { - if (await kh.crdExist(this.cheClusterCrd)) { - await kh.deleteCrd(this.cheClusterCrd) - } - task.title = await `${task.title}...OK` - } - }, { title: `Delete role binding ${this.operatorRoleBinding}`, task: async (_ctx: any, task: any) => { @@ -520,15 +449,6 @@ export class OperatorTasks { ] } - async copyCheOperatorResources(templatesDir: string, cacheDir: string): Promise { - const srcDir = path.join(templatesDir, '/che-operator/') - const destDir = path.join(cacheDir, '/templates/che-operator/') - await remove(destDir) - await mkdirp(destDir) - await copy(srcDir, destDir) - return destDir - } - async evaluateTemplateOperatorImage(flags: any): Promise { if (flags['che-operator-image']) { return flags['che-operator-image'] From cb2eab1f7c4615d6b4355a730bac0e7b61c6bdf6 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 30 Mar 2020 17:37:15 +0300 Subject: [PATCH 02/40] Add update tasks. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 17 ++++- src/commands/server/update.ts | 4 +- src/tasks/installers/installer.ts | 14 ++++- src/tasks/installers/olm.ts | 101 +++++++++++++++++++++++------- 4 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index ba93652c1..5dcb59bbc 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1298,7 +1298,7 @@ export class KubeHelper { } } - async createOperatorSubscription(packageName: string, namespace: string, marketplaceNamespace: string, channel: string, csv?: string) { + async createOperatorSubscription(packageName: string, namespace: string, marketplaceNamespace: string, channel: string, sourceName: string, csv?: string) { const subscription: Subscription = { apiVersion: "operators.coreos.com/v1alpha1", kind: 'Subscription', @@ -1311,10 +1311,11 @@ export class KubeHelper { installPlanApproval: 'Manual', name: packageName, // we use package name the same like subscription name - source: packageName, + source: sourceName, // Todo let's use source namespace the same with Che installation sourceNamespace: marketplaceNamespace, - // startingCSV: csv + // todo remove it... + startingCSV: 'eclipse-che-preview-openshift.v7.9.0' } } @@ -1327,6 +1328,16 @@ export class KubeHelper { } } + async getOperatorSubscription(name: string, namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', name) + return body as Subscription + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + async operatorSubscriptionExists(name: string, namespace: string): Promise { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index 3d518e635..ece1c00ae 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -30,7 +30,7 @@ export default class Update extends Command { installer: string({ char: 'a', description: 'Installer type', - options: ['helm', 'operator', 'minishift-addon'], + options: ['helm', 'operator', 'minishift-addon', 'olm'], default: '' }), platform: string({ @@ -76,7 +76,7 @@ export default class Update extends Command { this.error('🛑 --installer parameter must be specified.') } - if (flags.installer === 'operator') { + if (flags.installer === 'operator' || flags.installer === 'olm') { // operator already supports updating return } diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index ff7afb9b8..d27da0ed1 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -22,6 +22,7 @@ import { OLMTasks } from './olm' export class InstallerTasks { updateTasks(flags: any, command: Command): ReadonlyArray { const operatorTasks = new OperatorTasks() + const olmTasks = new OLMTasks() let title: string let task: any @@ -32,8 +33,13 @@ export class InstallerTasks { task = () => { return operatorTasks.updateTasks(flags, command) } + } else if (flags.installer === 'olm') { + title = '🏃‍ Running the Eclipse Che operator Update using OLM' + task = () => { + return olmTasks.updateTasks(flags, command) + } } else { - title = '🏃‍ Installer preflight check' + title = '🏃‍ Installer preflight check' task = () => { command.error(`Installer ${flags.installer} does not support update ¯\\_(ツ)_/¯`) } } @@ -45,6 +51,7 @@ export class InstallerTasks { preUpdateTasks(flags: any, command: Command): ReadonlyArray { const operatorTasks = new OperatorTasks() + const olmTasks = new OLMTasks() let title: string let task: any @@ -55,6 +62,11 @@ export class InstallerTasks { task = () => { return operatorTasks.preUpdateTasks(flags, command) } + } else if (flags.installer === 'olm') { + title = '🏃‍ Running the Eclipse Che operator Update using OLM' + task = () => { + return olmTasks.preUpdateTasks(flags, command) + } } else { title = '🏃‍ Installer preflight check' task = () => { command.error(`Installer ${flags.installer} does not support update ¯\\_(ツ)_/¯`) } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 4af8567a2..809aa0fb5 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -14,10 +14,11 @@ import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultOlmOpenshiftRegistryNamespace, import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources } from './common-tasks'; +import { SubscriptionStatusCondition, Subscription } from 'olm'; export class OLMTasks { - sourceName = 'eclipse-che-preview' + OperatorSourceNamePrefix = 'eclipse-che-preview-' operatorGroupName = 'cheoperatorgroup' packageNamePrefix = 'eclipse-che-preview-' channel = this.getDefaultChannel() @@ -35,11 +36,13 @@ export class OLMTasks { task: async (ctx: any, task: any) => { ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOlmOpenshiftRegistryNamespace : defaultOmlKubernetesRegistryNamespace ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace - if (await kube.operatorSourceExists(this.sourceName, ctx.marketplaceNamespace)) { - task.title += '...It already exists.' + if (await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { + task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSource(this.sourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) + await kube.createOperatorSource(ctx.operatorSourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) + task.title = `${task.title}...OK` } } }, @@ -47,51 +50,104 @@ export class OLMTasks { title: 'Create operator group', task: async (ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { - task.title += '...It already exists.' - } else { - task.title += '...Create operator group' + task.title = `${task.title}...It already exists.` + } else { await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) + task.title = `${task.title}...OK` } } }, { title: 'Create operator subscription', task: async (ctx: any, task: any) => { - ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { - task.title += '...It already exists.' + ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + if (await kube.operatorSubscriptionExists) { + task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel) + await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel, ctx.operatorSourceName) + task.title = `${task.title}...OK` } } }, { title: 'Wait while subscription is ready', task: async (ctx: any, task: any) => { - // Todo set time out ... - const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName, 60) + const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName) ctx.installPlanName = installPlan.name + task.title = `${task.title}...OK` } }, { title: 'Approve installation', task: async (ctx: any, task: any) => { await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + task.title = `${task.title}...OK` } }, { title: 'Wait while opertor installed', task: async (ctx: any, task: any) => { - await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 30) + await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace) + task.title = `${task.title}...OK` } }, createEclipeCheCluster(flags) ], { renderer: flags['listr-renderer'] as any }) } + preUpdateTasks(flags: any, command: Command): Listr { + const kube = new KubeHelper(flags) + return new Listr([ + { + title: 'Check if operator source exists', + task: async (ctx: any, task: any) => { + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace + if (!await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { + command.error(`Unable to find operator source ${ctx.operatorSourceName}`) + } + } + }, + { + title: 'Check if operator group exists', + task: async (ctx: any, task: any) => { + if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)){ + command.error(`Unable to find operator group ${this.operatorGroupName}`) + } + } + }, + { + title: 'Check if operator subscription exists', + task: async (ctx: any, task: any) => { + ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + if (!await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { + command.error(`Unable to find operator subscription ${ctx.packageName}`) + } + } + }, + ], { renderer: flags['listr-renderer'] as any }) + } + updateTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ + { + title: 'Get operator installation plan', + task: async (ctx: any, task: any) => { + ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + const subscription: Subscription = await kube.getOperatorSubscription(ctx.packageName, flags.chenamespace) + if (subscription.status && subscription.status!.conditions) { + const installationCondition = subscription.status.conditions.find((condition: SubscriptionStatusCondition) => { + return condition.type === 'InstallPlanPending' && condition.status === 'True' + }) + if (installationCondition) { + ctx.installPlanName = subscription.status.installplan.name + return + } + } + command.error("Unable to find installation plan to update.") + } + }, { title: 'Approve installation', task: async (ctx: any, task: any) => { @@ -101,7 +157,7 @@ export class OLMTasks { { title: 'Wait while newer operator installed', task: async (ctx: any, task: any) => { - await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 30) + await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace) } }, ], { renderer: flags['listr-renderer'] as any }) @@ -111,12 +167,15 @@ export class OLMTasks { const kube = new KubeHelper(flags) return [ { - title: `Delete(OLM) operator source ${this.sourceName}`, + title: `Delete(OLM) operator source ${this.OperatorSourceNamePrefix}`, // todo use name instead of prefix task: async (ctx: any, task: any) => { - if (await kube.operatorSourceExists(this.sourceName, flags.chenamespace)) { - await kube.deleteOperatorSource(this.sourceName, flags.chenamespace) + // todo, maybe we should deploy source to the the same namespace with Che? + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + const operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace + if (await kube.operatorSourceExists(operatorSourceName, ctx.marketplaceNamespace)) { + await kube.deleteOperatorSource(operatorSourceName, ctx.marketplaceNamespace) } - task.title += '...OK' + task.title = `${task.title}...OK` } }, { @@ -125,7 +184,7 @@ export class OLMTasks { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) } - task.title += '...OK' + task.title = `${task.title}...OK` } }, { @@ -137,7 +196,7 @@ export class OLMTasks { if (await kube.operatorSubscriptionExists(packageName, flags.chenamespace)) { await kube.deleteOperatorSubscription(packageName, flags.chenamespace) } - task.title += '...OK' + task.title = `${task.title}...OK` } }, ] From f54c691f38b44a4b75027579e2541ccb041d4bd0 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 30 Mar 2020 22:39:18 +0300 Subject: [PATCH 03/40] Improve delete tasks and reuse tls tasks. Signed-off-by: Oleksandr Andriienko --- src/tasks/installers/common-tasks.ts | 104 ++++++++++++++++ src/tasks/installers/olm.ts | 173 ++++++++++++++------------- src/tasks/installers/operator.ts | 101 +--------------- 3 files changed, 194 insertions(+), 184 deletions(-) diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index be582ae78..28e19b7c5 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -16,6 +16,9 @@ import * as path from 'path' import { KubeHelper } from '../../api/kube' import { operatorCheCluster } from '../../constants' import { isOpenshiftPlatformFamily, isKubernetesPlatformFamily } from '../../util' +import * as fs from 'fs' +import * as yaml from 'js-yaml' +import { cli } from 'cli-ux' export function createNamespaceTask(flags: any): ListrTask { return { @@ -92,3 +95,104 @@ export function createEclipeCheCluster(flags: any): ListrTask { } } +export function checkPreCreatedTls(flags: any, kube: KubeHelper): ListrTask { + return { + title: 'Checking for pre-created TLS secret', + // In case of Openshift infrastructure the certificate from cluster router will be used, so no need in the `che-tls` secret. + skip: () => !isKubernetesPlatformFamily(flags.platform), + task: async (_: any, task: any) => { + // Che is being deployed on Kubernetes infrastructure + + if (! await checkTlsMode(flags)) { + // No TLS mode, skip this check + return + } + + const cheSecretName = 'che-tls' + const cheSecret = await kube.getSecret(cheSecretName, flags.chenamespace) + if (cheSecret) { + task.title = `${task.title}... "${cheSecretName}" secret found` + return + } + + // The secret is required but doesn't exist, show error message. + const errorMessage = + `Che TLS mode is turned on, but required "${cheSecretName}" secret is not pre-created in "${flags.chenamespace}" namespace, so Eclipse Che cannot be started. \n` + + 'This is not bug in Eclipse Che and such behavior is expected. \n' + + 'Please refer to Che documentation for more informations: ' + + 'https://www.eclipse.org/che/docs/che-7/setup-che-in-tls-mode-with-self-signed-certificate/' + throw new Error(errorMessage) + } + } +} + +export function checkTlsSertificate(flags: any) { + return { + title: 'Checking certificate', + // If the flag is set no need to check if it is required + skip: () => flags['self-signed-cert'], + task: async (_: any, task: any) => { + if (! await checkTlsMode(flags)) { + // No TLS mode, skip this check + return + } + + const warningMessage = 'Self-signed certificate is used, so "--self-signed-cert" option is required. Added automatically.' + + const platform = flags.platform + if (platform === 'minikube' || platform === 'crc' || platform === 'minishift') { + // There is no way to use real certificate on listed above platforms + cli.warn(warningMessage) + flags['self-signed-cert'] = true + task.title = `${task.title}... self-signed` + return + } + + if (flags.domain && (flags.domain.endsWith('nip.io') || flags.domain.endsWith('xip.io'))) { + // It is not possible to use real certificate with *.nip.io and similar services + cli.warn(warningMessage) + flags['self-signed-cert'] = true + task.title = `${task.title}... self-signed` + return + } + + // TODO check the secret certificate if it is commonly trusted. + cli.info('TLS mode is turned on, however we failed to determine whether self-signed certificate is used. \n\ + Please rerun chectl with "--self-signed-cert" option if it is the case, otherwise Eclipse Che will fail to start.') + } + } +} + +/** + * Checks if TLS is disabled via operator custom resource. + * Returns true if TLS is enabled (or omitted) and false if it is explicitly disabled. + */ +async function checkTlsMode(flags: any): Promise { + if (flags['che-operator-cr-yaml']) { + const cheOperatorCrYamlPath = flags['che-operator-cr-yaml'] + if (fs.existsSync(cheOperatorCrYamlPath)) { + const cr = yaml.safeLoad(fs.readFileSync(cheOperatorCrYamlPath).toString()) + if (cr && cr.spec && cr.spec.server && cr.spec.server.tlsSupport === false) { + return false + } + } + } + + if (flags['che-operator-cr-patch-yaml']) { + const cheOperatorCrPatchYamlPath = flags['che-operator-cr-patch-yaml'] + if (fs.existsSync(cheOperatorCrPatchYamlPath)) { + const crPatch = yaml.safeLoad(fs.readFileSync(cheOperatorCrPatchYamlPath).toString()) + if (crPatch && crPatch.spec && crPatch.spec.server && crPatch.spec.server.tlsSupport === false) { + return false + } + } + } + + // If tls flag is undefined we suppose that tls is turned on + if (flags.tls === false) { + return false + } + + // TLS is on + return true + } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 809aa0fb5..b254887a4 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -13,7 +13,7 @@ import Listr = require('listr'); import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultOlmOpenshiftRegistryNamespace, defaultOmlKubernetesRegistryNamespace, defaultOlmOpenshiftOperatorSourceNamespace, defaultOlmKubernetesOperatorSourceNamespace } from '../../constants'; import { KubeHelper } from '../../api/kube'; -import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources } from './common-tasks'; +import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; import { SubscriptionStatusCondition, Subscription } from 'olm'; export class OLMTasks { @@ -29,69 +29,71 @@ export class OLMTasks { startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ - createNamespaceTask(flags), - copyOperatorResources(flags, command.config.cacheDir), - { - title: "Create operator source", - task: async (ctx: any, task: any) => { - ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOlmOpenshiftRegistryNamespace : defaultOmlKubernetesRegistryNamespace - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace - ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace + copyOperatorResources(flags, command.config.cacheDir), + createNamespaceTask(flags), + checkPreCreatedTls(flags, kube), + checkTlsSertificate(flags), + { + title: "Create operator source", + task: async (ctx: any, task: any) => { + ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOlmOpenshiftRegistryNamespace : defaultOmlKubernetesRegistryNamespace + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace - if (await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { - task.title = `${task.title}...It already exists.` - } else { - await kube.createOperatorSource(ctx.operatorSourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) - task.title = `${task.title}...OK` - } - } - }, - { - title: 'Create operator group', - task: async (ctx: any, task: any) => { - if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { - task.title = `${task.title}...It already exists.` - } else { - await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) - task.title = `${task.title}...OK` - } - } - }, - { - title: 'Create operator subscription', - task: async (ctx: any, task: any) => { - ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - if (await kube.operatorSubscriptionExists) { + if (await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel, ctx.operatorSourceName) + await kube.createOperatorSource(ctx.operatorSourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) task.title = `${task.title}...OK` } - } - }, - { - title: 'Wait while subscription is ready', - task: async (ctx: any, task: any) => { - const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName) - ctx.installPlanName = installPlan.name - task.title = `${task.title}...OK` - } - }, - { - title: 'Approve installation', - task: async (ctx: any, task: any) => { - await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + } + }, + { + title: 'Create operator group', + task: async (ctx: any, task: any) => { + if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { + task.title = `${task.title}...It already exists.` + } else { + await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) task.title = `${task.title}...OK` } - }, - { - title: 'Wait while opertor installed', - task: async (ctx: any, task: any) => { - await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace) + } + }, + { + title: 'Create operator subscription', + task: async (ctx: any, task: any) => { + ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { + task.title = `${task.title}...It already exists.` + } else { + await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel, ctx.operatorSourceName) task.title = `${task.title}...OK` } - }, - createEclipeCheCluster(flags) + } + }, + { + title: 'Wait while subscription is ready', + task: async (ctx: any, task: any) => { + const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName) + ctx.installPlanName = installPlan.name + task.title = `${task.title}...OK` + } + }, + { + title: 'Approve installation', + task: async (ctx: any, task: any) => { + await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + task.title = `${task.title}...OK` + } + }, + { + title: 'Wait while operator installed', + task: async (ctx: any, task: any) => { + await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace) + task.title = `${task.title}...OK` + } + }, + createEclipeCheCluster(flags) ], { renderer: flags['listr-renderer'] as any }) } @@ -166,45 +168,44 @@ export class OLMTasks { deleteTasks(flags: any, command?: Command): ReadonlyArray { const kube = new KubeHelper(flags) return [ - { - title: `Delete(OLM) operator source ${this.OperatorSourceNamePrefix}`, // todo use name instead of prefix - task: async (ctx: any, task: any) => { - // todo, maybe we should deploy source to the the same namespace with Che? - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace - const operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace - if (await kube.operatorSourceExists(operatorSourceName, ctx.marketplaceNamespace)) { - await kube.deleteOperatorSource(operatorSourceName, ctx.marketplaceNamespace) - } - task.title = `${task.title}...OK` + { + title: `Delete(OLM) operator subscription 'Todo package name...'`, + task: async (ctx: any, task: any) => { + // todo why do we need the same subscription name like package name. Are you sure? or move it upper. + const packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + + if (await kube.operatorSubscriptionExists(packageName, flags.chenamespace)) { + await kube.deleteOperatorSubscription(packageName, flags.chenamespace) } - }, - { - title: `Delete(OLM) operator group ${this.operatorGroupName}`, - task: async (ctx: any, task: any) => { - if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { - await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) - } - task.title = `${task.title}...OK` + task.title = `${task.title}...OK` + } + }, + { + title: `Delete(OLM) operator group ${this.operatorGroupName}`, + task: async (ctx: any, task: any) => { + if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { + await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) } - }, - { - title: `Delete(OLM) operator subscription 'Todo package name...'`, - task: async (ctx: any, task: any) => { - // todo why do we need the same subscription name like package name. Are you sure? or move it upper. - const packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - - if (await kube.operatorSubscriptionExists(packageName, flags.chenamespace)) { - await kube.deleteOperatorSubscription(packageName, flags.chenamespace) - } - task.title = `${task.title}...OK` + task.title = `${task.title}...OK` + } + }, + { + title: `Delete(OLM) operator source ${this.OperatorSourceNamePrefix}`, // todo use name instead of prefix + task: async (ctx: any, task: any) => { + // todo, maybe we should deploy source to the the same namespace with Che? + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + const operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace + if (await kube.operatorSourceExists(operatorSourceName, ctx.marketplaceNamespace)) { + await kube.deleteOperatorSource(operatorSourceName, ctx.marketplaceNamespace) } - }, + task.title = `${task.title}...OK` + } + } ] } // To update chectl stable channel we are patching src/constants.ts from nightly to release version. // Let's use it to determine which olm channel should we use by default. - // TODO: Fixme: take a look, maybe it is better to have chectl like field in the package.json... getDefaultChannel(): CheOLMChannel { if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { return CheOLMChannel.NIGHTLY diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 8dc2ee9df..ed58752c7 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -14,8 +14,7 @@ import * as fs from 'fs' import * as yaml from 'js-yaml' import * as Listr from 'listr' import { KubeHelper } from '../../api/kube' -import { isKubernetesPlatformFamily } from '../../util' -import { createEclipeCheCluster, copyOperatorResources, createNamespaceTask } from './common-tasks' +import { createEclipeCheCluster, copyOperatorResources, createNamespaceTask, checkPreCreatedTls, checkTlsSertificate } from './common-tasks' import { operatorCheCluster } from '../../constants' export class OperatorTasks { @@ -35,68 +34,8 @@ export class OperatorTasks { return new Listr([ copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), - { - title: 'Checking for pre-created TLS secret', - // In case of Openshift infrastructure the certificate from cluster router will be used, so no need in the `che-tls` secret. - skip: () => !isKubernetesPlatformFamily(flags.platform), - task: async (_: any, task: any) => { - // Che is being deployed on Kubernetes infrastructure - - if (! await this.checkTlsMode(flags)) { - // No TLS mode, skip this check - return - } - - const cheSecretName = 'che-tls' - const cheSecret = await kube.getSecret(cheSecretName, flags.chenamespace) - if (cheSecret) { - task.title = `${task.title}... "${cheSecretName}" secret found` - return - } - - // The secret is required but doesn't exist, show error message. - const errorMessage = - `Che TLS mode is turned on, but required "${cheSecretName}" secret is not pre-created in "${flags.chenamespace}" namespace, so Eclipse Che cannot be started. \n` + - 'This is not bug in Eclipse Che and such behavior is expected. \n' + - 'Please refer to Che documentation for more informations: ' + - 'https://www.eclipse.org/che/docs/che-7/setup-che-in-tls-mode-with-self-signed-certificate/' - throw new Error(errorMessage) - } - }, - { - title: 'Checking certificate', - // If the flag is set no need to check if it is required - skip: () => flags['self-signed-cert'], - task: async (_: any, task: any) => { - if (! await this.checkTlsMode(flags)) { - // No TLS mode, skip this check - return - } - - const warningMessage = 'Self-signed certificate is used, so "--self-signed-cert" option is required. Added automatically.' - - const platform = flags.platform - if (platform === 'minikube' || platform === 'crc' || platform === 'minishift') { - // There is no way to use real certificate on listed above platforms - cli.warn(warningMessage) - flags['self-signed-cert'] = true - task.title = `${task.title}... self-signed` - return - } - - if (flags.domain && (flags.domain.endsWith('nip.io') || flags.domain.endsWith('xip.io'))) { - // It is not possible to use real certificate with *.nip.io and similar services - cli.warn(warningMessage) - flags['self-signed-cert'] = true - task.title = `${task.title}... self-signed` - return - } - - // TODO check the secret certificate if it is commonly trusted. - cli.info('TLS mode is turned on, however we failed to determine whether self-signed certificate is used. \n\ - Please rerun chectl with "--self-signed-cert" option if it is the case, otherwise Eclipse Che will fail to start.') - } - }, + checkPreCreatedTls(flags, kube), + checkTlsSertificate(flags), { title: `Create ServiceAccount ${this.operatorServiceAccount} in namespace ${flags.chenamespace}`, task: async (ctx: any, task: any) => { @@ -480,38 +419,4 @@ export class OperatorTasks { return container.image } - - /** - * Checks if TLS is disabled via operator custom resource. - * Returns true if TLS is enabled (or omitted) and false if it is explicitly disabled. - */ - private async checkTlsMode(flags: any): Promise { - if (flags['che-operator-cr-yaml']) { - const cheOperatorCrYamlPath = flags['che-operator-cr-yaml'] - if (fs.existsSync(cheOperatorCrYamlPath)) { - const cr = yaml.safeLoad(fs.readFileSync(cheOperatorCrYamlPath).toString()) - if (cr && cr.spec && cr.spec.server && cr.spec.server.tlsSupport === false) { - return false - } - } - } - - if (flags['che-operator-cr-patch-yaml']) { - const cheOperatorCrPatchYamlPath = flags['che-operator-cr-patch-yaml'] - if (fs.existsSync(cheOperatorCrPatchYamlPath)) { - const crPatch = yaml.safeLoad(fs.readFileSync(cheOperatorCrPatchYamlPath).toString()) - if (crPatch && crPatch.spec && crPatch.spec.server && crPatch.spec.server.tlsSupport === false) { - return false - } - } - } - - // If tls flag is undefined we suppose that tls is turned on - if (flags.tls === false) { - return false - } - - // TLS is on - return true - } } From 0edc6fe16848cee6c60f97a33da9d4dd7f1cb4e7 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 31 Mar 2020 09:44:24 +0300 Subject: [PATCH 04/40] Add api OLM api group check. Signed-off-by: Oleksandr Andriienko --- README.md | 2 +- src/api/kube.ts | 17 ++++++++++++++--- src/tasks/installers/olm.ts | 9 ++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 333553d1b..32106e37b 100644 --- a/README.md +++ b/README.md @@ -376,7 +376,7 @@ USAGE $ chectl server:update OPTIONS - -a, --installer=helm|operator|minishift-addon Installer type + -a, --installer=helm|operator|minishift-addon|olm Installer type -h, --help show CLI help -n, --chenamespace=chenamespace [default: che] Kubernetes namespace where diff --git a/src/api/kube.ts b/src/api/kube.ts index 5dcb59bbc..b949da2b5 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1314,8 +1314,8 @@ export class KubeHelper { source: sourceName, // Todo let's use source namespace the same with Che installation sourceNamespace: marketplaceNamespace, - // todo remove it... - startingCSV: 'eclipse-che-preview-openshift.v7.9.0' + // Todo: remove it. Applied to test update olm after installation older Che and stable channel. + // startingCSV: 'eclipse-che-preview-openshift.v7.9.0' } } @@ -1387,7 +1387,18 @@ export class KubeHelper { }) } - // await k8sApi.getAPIResources() // todo check if operator api exists... + async isPreInstalledOLM() { + const apiApi = this.kc.makeApiClient(ApisApi) + try { + + const { body } = await apiApi.getAPIVersions() + const olmGroup = body.groups.find((apiGroup) => apiGroup.name === 'operators.coreos.com') + if (olmGroup) { + return true + } + } catch {} + return false +} async aproveOperatorInstallationPlan(name = '', namespace = '') { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index b254887a4..b585c5ebb 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -29,6 +29,14 @@ export class OLMTasks { startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ + { + title: "Check if OLM is pre-installed on the platform", + task: async (ctx: any, task: any) => { + if (!await kube.isPreInstalledOLM()) { + command.error("OLM isn't installed on your platfrom. If your platform hasn't got emdedded OML, you need install it manually.") + } + } + }, copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), checkPreCreatedTls(flags, kube), @@ -173,7 +181,6 @@ export class OLMTasks { task: async (ctx: any, task: any) => { // todo why do we need the same subscription name like package name. Are you sure? or move it upper. const packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - if (await kube.operatorSubscriptionExists(packageName, flags.chenamespace)) { await kube.deleteOperatorSubscription(packageName, flags.chenamespace) } From 117b372b12f4107e0dcf90b8b714d1936dfaebd9 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 31 Mar 2020 10:14:24 +0300 Subject: [PATCH 05/40] Fix up. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 4 ++-- src/tasks/installers/olm.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index b949da2b5..d71745d36 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -20,7 +20,7 @@ import { merge } from 'lodash' import * as net from 'net' import { Writable } from 'stream' -import { DEFAULT_CHE_IMAGE, defaultOlmRegistry } from '../constants' +import { DEFAULT_CHE_IMAGE, defaultOLMRegistry } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' @@ -1231,7 +1231,7 @@ export class KubeHelper { namespace, }, spec: { - endpoint: defaultOlmRegistry, + endpoint: defaultOLMRegistry, registryNamespace, type: "appregistry" } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index b585c5ebb..464fa1094 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -10,7 +10,7 @@ import Command from '@oclif/command'; import Listr = require('listr'); -import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultOlmOpenshiftRegistryNamespace, defaultOmlKubernetesRegistryNamespace, defaultOlmOpenshiftOperatorSourceNamespace, defaultOlmKubernetesOperatorSourceNamespace } from '../../constants'; +import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultOLMOpenshiftRegistryNamespace, defaultOLMKubernetesRegistryNamespace, defaultOLMOpenshiftOperatorSourceNamespace, defaultOLMKubernetesOperatorSourceNamespace } from '../../constants'; import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; @@ -44,8 +44,8 @@ export class OLMTasks { { title: "Create operator source", task: async (ctx: any, task: any) => { - ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOlmOpenshiftRegistryNamespace : defaultOmlKubernetesRegistryNamespace - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOLMOpenshiftRegistryNamespace : defaultOLMKubernetesRegistryNamespace + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace if (await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { @@ -111,7 +111,7 @@ export class OLMTasks { { title: 'Check if operator source exists', task: async (ctx: any, task: any) => { - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace if (!await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { command.error(`Unable to find operator source ${ctx.operatorSourceName}`) @@ -200,7 +200,7 @@ export class OLMTasks { title: `Delete(OLM) operator source ${this.OperatorSourceNamePrefix}`, // todo use name instead of prefix task: async (ctx: any, task: any) => { // todo, maybe we should deploy source to the the same namespace with Che? - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOlmOpenshiftOperatorSourceNamespace : defaultOlmKubernetesOperatorSourceNamespace + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace const operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace if (await kube.operatorSourceExists(operatorSourceName, ctx.marketplaceNamespace)) { await kube.deleteOperatorSource(operatorSourceName, ctx.marketplaceNamespace) From 7d04cd6a879c2ae763cba080e87dedb5fe382d52 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 31 Mar 2020 10:31:45 +0300 Subject: [PATCH 06/40] Clean up. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 29 ++++++++++++++--------------- src/api/typings/olm.d.ts | 29 ----------------------------- src/commands/server/start.ts | 2 +- src/constants.ts | 10 +++++----- src/tasks/installers/installer.ts | 2 +- 5 files changed, 21 insertions(+), 51 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index d71745d36..d2c468bc4 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1212,6 +1212,19 @@ export class KubeHelper { } } + async isPreInstalledOLM() { + const apiApi = this.kc.makeApiClient(ApisApi) + try { + const { body } = await apiApi.getAPIVersions() + const OLMAPIGroup = body.groups.find((apiGroup) => apiGroup.name === 'operators.coreos.com') + if (OLMAPIGroup) { + return true + } + } catch {} + + return false + } + async operatorSourceExists(name: string, namespace: string): Promise { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { @@ -1387,19 +1400,6 @@ export class KubeHelper { }) } - async isPreInstalledOLM() { - const apiApi = this.kc.makeApiClient(ApisApi) - try { - - const { body } = await apiApi.getAPIVersions() - const olmGroup = body.groups.find((apiGroup) => apiGroup.name === 'operators.coreos.com') - if (olmGroup) { - return true - } - } catch {} - return false -} - async aproveOperatorInstallationPlan(name = '', namespace = '') { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { @@ -1704,8 +1704,7 @@ export class KubeHelper { // Set up watcher const watcher = new Watch(this.kc) request = watcher - .watch(`/api/v1/namespaces/${namespace}/secrets/`, - { fieldSelector: `metadata.name=${secretName}` }, + .watch(`/api/v1/namespaces/${namespace}/secrets/`, { fieldSelector: `metadata.name=${secretName}` }, (_phase: string, obj: any) => { const secret = obj as V1Secret diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index 417f9e193..de77b2c1f 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -11,19 +11,9 @@ declare module 'olm' { export interface OperatorSource { - /** - * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources - */ apiVersion: string; - /** - * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds - */ kind: string; - /** - * Standard object's metadata. - */ metadata: V1ObjectMeta; - spec: OperatorSourceSpec; } @@ -34,17 +24,8 @@ declare module 'olm' { } export interface OperatorGroup { - /** - * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources - */ apiVersion: string; - /** - * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds - */ kind: string; - /** - * Standard object's metadata. - */ metadata: V1ObjectMeta; spec: OperatorGroupSpec; @@ -55,17 +36,8 @@ declare module 'olm' { } export interface Subscription { - /** - * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources - */ apiVersion: string; - /** - * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds - */ kind: string; - /** - * Standard object's metadata. - */ metadata: V1ObjectMeta; spec: SubscriptionSpec @@ -118,4 +90,3 @@ declare module 'olm' { status: string } } - diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 8b063d576..33518d00c 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -189,7 +189,7 @@ export default class Start extends Command { } // TODO when tls by default is implemented for all platforms, make `tls` flag turned on by default. - if (flags.installer === 'helm' && (flags.platform === 'k8s' || flags.platform === 'minikube' || flags.platform === 'microk8s')) { // todo olm + if (flags.installer === 'helm' && (flags.platform === 'k8s' || flags.platform === 'minikube' || flags.platform === 'microk8s')) { flags.tls = true } } diff --git a/src/constants.ts b/src/constants.ts index 0c9fc0813..16b9e2ae6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -20,11 +20,11 @@ export const CHE_TLS_SECRET_NAME = 'che-tls' export const operatorCheCluster = 'eclipse-che' -export const defaultOlmOpenshiftRegistryNamespace = 'eclipse-che-operator-openshift' -export const defaultOmlKubernetesRegistryNamespace = 'eclipse-che-operator-kubernetes' -export const defaultOlmOpenshiftOperatorSourceNamespace = 'openshift-marketplace' -export const defaultOlmKubernetesOperatorSourceNamespace = 'marketplace' -export const defaultOlmRegistry = 'https://quay.io/cnr' +export const defaultOLMOpenshiftRegistryNamespace = 'eclipse-che-operator-openshift' +export const defaultOLMKubernetesRegistryNamespace = 'eclipse-che-operator-kubernetes' +export const defaultOLMOpenshiftOperatorSourceNamespace = 'openshift-marketplace' +export const defaultOLMKubernetesOperatorSourceNamespace = 'marketplace' +export const defaultOLMRegistry = 'https://quay.io/cnr' // TODO figure out and handle, Do we have the same channels for CRW? export enum CheOLMChannel { diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index d27da0ed1..a8ec42722 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -39,7 +39,7 @@ export class InstallerTasks { return olmTasks.updateTasks(flags, command) } } else { - title = '🏃‍ Installer preflight check' + title = '🏃‍ Installer preflight check' task = () => { command.error(`Installer ${flags.installer} does not support update ¯\\_(ツ)_/¯`) } } From 0772409e44936e16398f84a6c01bc4dfce3ddf3b Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 31 Mar 2020 18:44:24 +0300 Subject: [PATCH 07/40] Improve OLM checks. Signed-off-by: Oleksandr Andriienko --- src/tasks/installers/common-tasks.ts | 2 +- src/tasks/installers/olm.ts | 36 +++++++++++++++++++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index 28e19b7c5..f33cf9cbc 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -126,7 +126,7 @@ export function checkPreCreatedTls(flags: any, kube: KubeHelper): ListrTask { return { title: 'Checking certificate', // If the flag is set no need to check if it is required diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 464fa1094..34e22d0b6 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -29,14 +29,7 @@ export class OLMTasks { startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ - { - title: "Check if OLM is pre-installed on the platform", - task: async (ctx: any, task: any) => { - if (!await kube.isPreInstalledOLM()) { - command.error("OLM isn't installed on your platfrom. If your platform hasn't got emdedded OML, you need install it manually.") - } - } - }, + this.isOlmPreInstalledTask(flags, command, kube), copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), checkPreCreatedTls(flags, kube), @@ -82,7 +75,7 @@ export class OLMTasks { { title: 'Wait while subscription is ready', task: async (ctx: any, task: any) => { - const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName) + const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName, 60) ctx.installPlanName = installPlan.name task.title = `${task.title}...OK` } @@ -108,6 +101,7 @@ export class OLMTasks { preUpdateTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ + this.isOlmPreInstalledTask(flags, command, kube), { title: 'Check if operator source exists', task: async (ctx: any, task: any) => { @@ -167,17 +161,24 @@ export class OLMTasks { { title: 'Wait while newer operator installed', task: async (ctx: any, task: any) => { - await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace) + await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 60) } }, ], { renderer: flags['listr-renderer'] as any }) } - deleteTasks(flags: any, command?: Command): ReadonlyArray { + deleteTasks(flags: any, command: Command): ReadonlyArray { const kube = new KubeHelper(flags) return [ + { + title: "Check if OLM is pre-installed on the platform", + task: async (ctx: any, task: any) => { + ctx.isPreInstalledOLM = await kube.isPreInstalledOLM() ? true : false + } + }, { title: `Delete(OLM) operator subscription 'Todo package name...'`, + enabled: (ctx) => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { // todo why do we need the same subscription name like package name. Are you sure? or move it upper. const packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') @@ -189,6 +190,7 @@ export class OLMTasks { }, { title: `Delete(OLM) operator group ${this.operatorGroupName}`, + enabled: (ctx) => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) @@ -198,6 +200,7 @@ export class OLMTasks { }, { title: `Delete(OLM) operator source ${this.OperatorSourceNamePrefix}`, // todo use name instead of prefix + enabled: (ctx) => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { // todo, maybe we should deploy source to the the same namespace with Che? ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace @@ -219,5 +222,16 @@ export class OLMTasks { } return CheOLMChannel.STABLE } + + isOlmPreInstalledTask(flags: any, command: Command, kube: KubeHelper): Listr.ListrTask { + return { + title: "Check if OLM is pre-installed on the platform", + task: async (ctx: any, task: any) => { + if (!await kube.isPreInstalledOLM()) { + command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") + } + } + } + } } From 12c91fae3a32a4404c46f288f240b517b096e25b Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sun, 5 Apr 2020 16:08:06 +0300 Subject: [PATCH 08/40] Fix installation using OLM on the minikube. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 110 +++++++++++++++++++++++++++++------- src/api/typings/olm.d.ts | 30 +++++++++- src/constants.ts | 15 +++-- src/tasks/installers/olm.ts | 91 +++++++++++++++++++---------- 4 files changed, 188 insertions(+), 58 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index d2c468bc4..433d46c16 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -20,11 +20,11 @@ import { merge } from 'lodash' import * as net from 'net' import { Writable } from 'stream' -import { DEFAULT_CHE_IMAGE, defaultOLMRegistry } from '../constants' +import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry, defaultOLMNamespace } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' -import { OperatorSource, OperatorGroup, Subscription, InstallPlan } from 'olm' +import { OperatorSource, OperatorGroup, Subscription, InstallPlan, ClusterServiceVersionList, CatalogSource } from 'olm' const AWAIT_TIMEOUT_S = 30 @@ -1244,7 +1244,7 @@ export class KubeHelper { namespace, }, spec: { - endpoint: defaultOLMRegistry, + endpoint: defaultApplicationRegistry, registryNamespace, type: "appregistry" } @@ -1269,6 +1269,59 @@ export class KubeHelper { } } + async catalogSourceExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) + return this.compare(body, name) + } catch { + return false + } + } + + async getCatalogSource(name: string, namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createCatalogSource(catalogSource: CatalogSource) { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const namespace = catalogSource.metadata.namespace + const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', catalogSource) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async waitCatalogSource(namespace: string, catalogSourceName: string, timeout = 60): Promise { + return new Promise(async (resolve, reject) => { + const watcher = new Watch(this.kc) + let request: any + request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/catalogsources`, + { fieldSelector: `metadata.name=${catalogSourceName}` }, + (_phase: string, obj: any) => { + resolve(obj as CatalogSource) + }, + error => { + if (error) { + reject(error) + } + }) + + setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${catalogSourceName}" catalog source is created.`) + }, timeout * 1000) + }) + } + async operatorGroupExists(name: string, namespace: string): Promise { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { @@ -1313,24 +1366,20 @@ export class KubeHelper { async createOperatorSubscription(packageName: string, namespace: string, marketplaceNamespace: string, channel: string, sourceName: string, csv?: string) { const subscription: Subscription = { - apiVersion: "operators.coreos.com/v1alpha1", - kind: 'Subscription', - metadata: { - name: packageName, - namespace - }, - spec: { - channel, - installPlanApproval: 'Manual', - name: packageName, - // we use package name the same like subscription name - source: sourceName, - // Todo let's use source namespace the same with Che installation - sourceNamespace: marketplaceNamespace, - // Todo: remove it. Applied to test update olm after installation older Che and stable channel. - // startingCSV: 'eclipse-che-preview-openshift.v7.9.0' + apiVersion: "operators.coreos.com/v1alpha1", + kind: 'Subscription', + metadata: { + name: packageName, + namespace + }, + spec: { + channel, + installPlanApproval: 'Manual', + name: packageName, + source: sourceName, + sourceNamespace: defaultOLMNamespace, + } } - } const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { @@ -1443,6 +1492,27 @@ export class KubeHelper { }) } + async getClusterServiceVersions(namespace: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.listNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions') + return body as ClusterServiceVersionList + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteClusterServiceVersion(namespace: string, csvName: string) { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const options = new V1DeleteOptions() + const { body } = await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', csvName, options) + return body as ClusterServiceVersionList + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + async deleteNamespace(namespace: string): Promise { const k8sCoreApi = this.kc.makeApiClient(CoreV1Api) try { diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index de77b2c1f..0df8c0c87 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -45,8 +45,8 @@ declare module 'olm' { } export interface SubscriptionSpec { - channel: string - installPlanApproval: string + channel?: string + installPlanApproval?: string name: string source: string sourceNamespace: string @@ -72,7 +72,6 @@ declare module 'olm' { kind?: string name?: string namespace?: string - uuid?: string spec?: InstallPlanSpec status?: InstallPlanStatus } @@ -89,4 +88,29 @@ declare module 'olm' { type: string status: string } + + export interface ClusterServiceVersion { + kind: string; + metadata: V1ObjectMeta; + } + + export interface ClusterServiceVersionList { + apiVersion: string + kind: string + items: Array + } + + export interface CatalogSource { + apiVersion: string + kind: string + metadata: V1ObjectMeta + spec: CatalogSourceSpec + } + + export interface CatalogSourceSpec { + address: string + base64data: string + mediatype: string + sourceType: string + } } diff --git a/src/constants.ts b/src/constants.ts index 16b9e2ae6..10ec14281 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -20,11 +20,16 @@ export const CHE_TLS_SECRET_NAME = 'che-tls' export const operatorCheCluster = 'eclipse-che' -export const defaultOLMOpenshiftRegistryNamespace = 'eclipse-che-operator-openshift' -export const defaultOLMKubernetesRegistryNamespace = 'eclipse-che-operator-kubernetes' -export const defaultOLMOpenshiftOperatorSourceNamespace = 'openshift-marketplace' -export const defaultOLMKubernetesOperatorSourceNamespace = 'marketplace' -export const defaultOLMRegistry = 'https://quay.io/cnr' +export const openshiftApplicationPreviewRegistryNamespace = 'eclipse-che-operator-openshift' +export const kubernetesApplicationPreviewRegistryNamespace = 'eclipse-che-operator-kubernetes' +// application registry namespace from operator-hub.io +export const applicationRegistryNamespace = 'eclipse-che' + +export const defaultOpenshiftMarketPlaceNamespace = 'openshift-marketplace' +export const defaultKubernetesMarketPlaceNamespace = 'marketplace' +export const defaultOLMNamespace = 'olm' + +export const defaultApplicationRegistry = 'https://quay.io/cnr' // TODO figure out and handle, Do we have the same channels for CRW? export enum CheOLMChannel { diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 34e22d0b6..1fe8ca88f 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -10,18 +10,18 @@ import Command from '@oclif/command'; import Listr = require('listr'); -import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultOLMOpenshiftRegistryNamespace, defaultOLMKubernetesRegistryNamespace, defaultOLMOpenshiftOperatorSourceNamespace, defaultOLMKubernetesOperatorSourceNamespace } from '../../constants'; +import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMNamespace } from '../../constants'; import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; -import { SubscriptionStatusCondition, Subscription } from 'olm'; +import { SubscriptionStatusCondition, Subscription, CatalogSource } from 'olm'; export class OLMTasks { - OperatorSourceNamePrefix = 'eclipse-che-preview-' - operatorGroupName = 'cheoperatorgroup' - packageNamePrefix = 'eclipse-che-preview-' - channel = this.getDefaultChannel() + private operatorSourceName = 'eclipse-che1' + private operatorGroupName = 'che-operator-group' + private packageNamePrefix = 'eclipse-che-preview-' + private channel = this.getDefaultChannel() /** * Returns list of tasks which perform preflight platform checks. @@ -34,29 +34,54 @@ export class OLMTasks { createNamespaceTask(flags), checkPreCreatedTls(flags, kube), checkTlsSertificate(flags), + { + title: 'Create operator group', + task: async (ctx: any, task: any) => { + if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { + task.title = `${task.title}...It already exists.` + } else { + await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) + task.title = `${task.title}...OK` + } + } + }, { title: "Create operator source", task: async (ctx: any, task: any) => { - ctx.operatorRegistryNamespace = ctx.isOpenShift ? defaultOLMOpenshiftRegistryNamespace : defaultOLMKubernetesRegistryNamespace - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace - ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace + ctx.operatorRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace : kubernetesApplicationPreviewRegistryNamespace + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace - if (await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { + if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSource(ctx.operatorSourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) + await kube.createOperatorSource(this.operatorSourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) + await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) task.title = `${task.title}...OK` } } }, { - title: 'Create operator group', + title: "Create catalog source", task: async (ctx: any, task: any) => { - if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { - task.title = `${task.title}...It already exists.` - } else { - await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) - task.title = `${task.title}...OK` + if (!await kube.catalogSourceExists(this.operatorSourceName, defaultOLMNamespace)) { + const catalogSourceInTheMarketPlaceNamespace = await kube.getCatalogSource(this.operatorSourceName, ctx.marketplaceNamespace) + const catalogSource: CatalogSource = { + apiVersion: 'operators.coreos.com/v1alpha1', + kind: 'CatalogSource', + metadata: { + name: this.operatorSourceName, + namespace: defaultOLMNamespace, + }, + spec: { + address: catalogSourceInTheMarketPlaceNamespace.spec.address, + base64data: '', + mediatype: '', + sourceType: 'grpc' + } + } + // Create catalog source in the olm namespace to make it working in the namespace differ than marketplace + await kube.createCatalogSource(catalogSource) + await kube.waitCatalogSource(defaultOLMNamespace, this.operatorSourceName) } } }, @@ -67,7 +92,7 @@ export class OLMTasks { if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel, ctx.operatorSourceName) + await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel, this.operatorSourceName) task.title = `${task.title}...OK` } } @@ -75,7 +100,7 @@ export class OLMTasks { { title: 'Wait while subscription is ready', task: async (ctx: any, task: any) => { - const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName, 60) + const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName, 600) ctx.installPlanName = installPlan.name task.title = `${task.title}...OK` } @@ -105,10 +130,9 @@ export class OLMTasks { { title: 'Check if operator source exists', task: async (ctx: any, task: any) => { - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace - ctx.operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace - if (!await kube.operatorSourceExists(ctx.operatorSourceName, ctx.marketplaceNamespace)) { - command.error(`Unable to find operator source ${ctx.operatorSourceName}`) + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace + if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { + command.error(`Unable to find operator source ${this.operatorSourceName}`) } } }, @@ -176,6 +200,15 @@ export class OLMTasks { ctx.isPreInstalledOLM = await kube.isPreInstalledOLM() ? true : false } }, + { + title: 'Delete(OLM) Eclipse Che cluster service versions', + enabled: (ctx) => ctx.isPreInstalledOLM, + task: async (ctx: any, task: any) => { + const csvs = await kube.getClusterServiceVersions(flags.chenamespace) + const csvsToDelete = csvs.items.filter((csv) => csv.metadata.name.startsWith("eclipse-che")) + csvsToDelete.forEach((csv) => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) + } + }, { title: `Delete(OLM) operator subscription 'Todo package name...'`, enabled: (ctx) => ctx.isPreInstalledOLM, @@ -199,19 +232,17 @@ export class OLMTasks { } }, { - title: `Delete(OLM) operator source ${this.OperatorSourceNamePrefix}`, // todo use name instead of prefix + title: `Delete(OLM) operator source ${this.operatorSourceName}`, enabled: (ctx) => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { - // todo, maybe we should deploy source to the the same namespace with Che? - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOLMOpenshiftOperatorSourceNamespace : defaultOLMKubernetesOperatorSourceNamespace - const operatorSourceName = this.OperatorSourceNamePrefix + flags.chenamespace - if (await kube.operatorSourceExists(operatorSourceName, ctx.marketplaceNamespace)) { - await kube.deleteOperatorSource(operatorSourceName, ctx.marketplaceNamespace) + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace + if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { + await kube.deleteOperatorSource(this.operatorSourceName, ctx.marketplaceNamespace) } task.title = `${task.title}...OK` } } - ] + ] } // To update chectl stable channel we are patching src/constants.ts from nightly to release version. From 47da78b00802a5c7b55ca1fd5647ed5b548f44c6 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sun, 5 Apr 2020 19:52:55 +0300 Subject: [PATCH 09/40] Fix catalogsource namespaces for minikube, so crc and minikube should work both. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 6 +++--- src/constants.ts | 2 +- src/tasks/installers/olm.ts | 12 +++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 433d46c16..eab1d6144 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -20,7 +20,7 @@ import { merge } from 'lodash' import * as net from 'net' import { Writable } from 'stream' -import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry, defaultOLMNamespace } from '../constants' +import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' @@ -1364,7 +1364,7 @@ export class KubeHelper { } } - async createOperatorSubscription(packageName: string, namespace: string, marketplaceNamespace: string, channel: string, sourceName: string, csv?: string) { + async createOperatorSubscription(packageName: string, namespace: string, defaultCatalogSourceKubernetesNamespace: string, channel: string, sourceName: string) { const subscription: Subscription = { apiVersion: "operators.coreos.com/v1alpha1", kind: 'Subscription', @@ -1377,7 +1377,7 @@ export class KubeHelper { installPlanApproval: 'Manual', name: packageName, source: sourceName, - sourceNamespace: defaultOLMNamespace, + sourceNamespace: defaultCatalogSourceKubernetesNamespace, } } diff --git a/src/constants.ts b/src/constants.ts index 10ec14281..c0f5eb77a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -27,7 +27,7 @@ export const applicationRegistryNamespace = 'eclipse-che' export const defaultOpenshiftMarketPlaceNamespace = 'openshift-marketplace' export const defaultKubernetesMarketPlaceNamespace = 'marketplace' -export const defaultOLMNamespace = 'olm' +export const defaultOLMKubernetesNamespace = 'olm' export const defaultApplicationRegistry = 'https://quay.io/cnr' diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 1fe8ca88f..3ac8809df 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -10,7 +10,7 @@ import Command from '@oclif/command'; import Listr = require('listr'); -import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMNamespace } from '../../constants'; +import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace } from '../../constants'; import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; @@ -63,14 +63,16 @@ export class OLMTasks { { title: "Create catalog source", task: async (ctx: any, task: any) => { - if (!await kube.catalogSourceExists(this.operatorSourceName, defaultOLMNamespace)) { + // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' + ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace + if (!await kube.catalogSourceExists(this.operatorSourceName, ctx.defaultCatalogSourceNamespace)) { const catalogSourceInTheMarketPlaceNamespace = await kube.getCatalogSource(this.operatorSourceName, ctx.marketplaceNamespace) const catalogSource: CatalogSource = { apiVersion: 'operators.coreos.com/v1alpha1', kind: 'CatalogSource', metadata: { name: this.operatorSourceName, - namespace: defaultOLMNamespace, + namespace: ctx.defaultCatalogSourceNamespace, }, spec: { address: catalogSourceInTheMarketPlaceNamespace.spec.address, @@ -81,7 +83,7 @@ export class OLMTasks { } // Create catalog source in the olm namespace to make it working in the namespace differ than marketplace await kube.createCatalogSource(catalogSource) - await kube.waitCatalogSource(defaultOLMNamespace, this.operatorSourceName) + await kube.waitCatalogSource(ctx.defaultCatalogSourceNamespace, this.operatorSourceName) } } }, @@ -92,7 +94,7 @@ export class OLMTasks { if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.marketplaceNamespace, this.channel, this.operatorSourceName) + await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName) task.title = `${task.title}...OK` } } From 43a94c367f2289b533855d8a55e02062c85b2521 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 6 Apr 2020 09:19:03 +0300 Subject: [PATCH 10/40] Install stable Che using community catalog. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 34 ++++----- src/api/typings/olm.d.ts | 10 +++ src/tasks/installers/olm.ts | 145 ++++++++++++++++++++++++------------ 3 files changed, 120 insertions(+), 69 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index eab1d6144..05cf32fd4 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -24,7 +24,7 @@ import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' -import { OperatorSource, OperatorGroup, Subscription, InstallPlan, ClusterServiceVersionList, CatalogSource } from 'olm' +import { OperatorSource, OperatorGroup, Subscription, InstallPlan, ClusterServiceVersionList, CatalogSource, PackageManifest } from 'olm' const AWAIT_TIMEOUT_S = 30 @@ -1364,26 +1364,10 @@ export class KubeHelper { } } - async createOperatorSubscription(packageName: string, namespace: string, defaultCatalogSourceKubernetesNamespace: string, channel: string, sourceName: string) { - const subscription: Subscription = { - apiVersion: "operators.coreos.com/v1alpha1", - kind: 'Subscription', - metadata: { - name: packageName, - namespace - }, - spec: { - channel, - installPlanApproval: 'Manual', - name: packageName, - source: sourceName, - sourceNamespace: defaultCatalogSourceKubernetesNamespace, - } - } - + async createOperatorSubscription(subscription: Subscription) { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { - const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', subscription) + const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', subscription.metadata.namespace, 'subscriptions', subscription) return body } catch (e) { throw this.wrapK8sClientError(e) @@ -1449,7 +1433,7 @@ export class KubeHelper { }) } - async aproveOperatorInstallationPlan(name = '', namespace = '') { + async approveOperatorInstallationPlan(name = '', namespace = '') { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { const patch: InstallPlan = { @@ -1513,6 +1497,16 @@ export class KubeHelper { } } + async getPackageManifect(name: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('packages.operators.coreos.com', 'v1', 'default', 'packagemanifests', name) + return body as PackageManifest + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + async deleteNamespace(namespace: string): Promise { const k8sCoreApi = this.kc.makeApiClient(CoreV1Api) try { diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index 0df8c0c87..6e98e748c 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -113,4 +113,14 @@ declare module 'olm' { mediatype: string sourceType: string } + + export interface PackageManifest { + name: string + status?: PackageManifestStatus + } + + export interface PackageManifestStatus { + catalogSource: string + catalogSourceNamespace: string + } } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 3ac8809df..8910c0af9 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -14,7 +14,7 @@ import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNa import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; -import { SubscriptionStatusCondition, Subscription, CatalogSource } from 'olm'; +import { Subscription, CatalogSource } from 'olm'; export class OLMTasks { @@ -46,55 +46,37 @@ export class OLMTasks { } }, { - title: "Create operator source", + title: 'Configure context information', task: async (ctx: any, task: any) => { - ctx.operatorRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace : kubernetesApplicationPreviewRegistryNamespace - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace - - if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { - task.title = `${task.title}...It already exists.` - } else { - await kube.createOperatorSource(this.operatorSourceName, ctx.operatorRegistryNamespace, ctx.marketplaceNamespace) - await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) - task.title = `${task.title}...OK` - } + ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace + // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' + ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace + task.title = `${task.title}...OK` } }, { - title: "Create catalog source", - task: async (ctx: any, task: any) => { - // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' - ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace - if (!await kube.catalogSourceExists(this.operatorSourceName, ctx.defaultCatalogSourceNamespace)) { - const catalogSourceInTheMarketPlaceNamespace = await kube.getCatalogSource(this.operatorSourceName, ctx.marketplaceNamespace) - const catalogSource: CatalogSource = { - apiVersion: 'operators.coreos.com/v1alpha1', - kind: 'CatalogSource', - metadata: { - name: this.operatorSourceName, - namespace: ctx.defaultCatalogSourceNamespace, - }, - spec: { - address: catalogSourceInTheMarketPlaceNamespace.spec.address, - base64data: '', - mediatype: '', - sourceType: 'grpc' - } - } - // Create catalog source in the olm namespace to make it working in the namespace differ than marketplace - await kube.createCatalogSource(catalogSource) - await kube.waitCatalogSource(ctx.defaultCatalogSourceNamespace, this.operatorSourceName) - } + title: 'Create custom catalog source for "nightly" channel...OK', + enabled: () => this.channel === CheOLMChannel.NIGHTLY, + task: async () => { + return this.customCatalogTasks(flags, command, kube) } }, { title: 'Create operator subscription', task: async (ctx: any, task: any) => { ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + var subscription: Subscription + if (this.channel === CheOLMChannel.STABLE) { + const packageManifest = await kube.getPackageManifect('eclipse-che') + subscription = this.createSubscription(ctx.packageName, 'eclipse-che', flags.chenamespace, packageManifest.status!.catalogSourceNamespace, 'stable', packageManifest.status!.catalogSource) + } else { + subscription = this.createSubscription(ctx.packageName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName) + } + if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { - await kube.createOperatorSubscription(ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName) + await kube.createOperatorSubscription(subscription) task.title = `${task.title}...OK` } } @@ -110,7 +92,7 @@ export class OLMTasks { { title: 'Approve installation', task: async (ctx: any, task: any) => { - await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) task.title = `${task.title}...OK` } }, @@ -131,6 +113,7 @@ export class OLMTasks { this.isOlmPreInstalledTask(flags, command, kube), { title: 'Check if operator source exists', + enabled: () => this.channel === CheOLMChannel.NIGHTLY, task: async (ctx: any, task: any) => { ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { @@ -163,25 +146,24 @@ export class OLMTasks { return new Listr([ { title: 'Get operator installation plan', - task: async (ctx: any, task: any) => { + task: async (ctx: any) => { ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') const subscription: Subscription = await kube.getOperatorSubscription(ctx.packageName, flags.chenamespace) if (subscription.status && subscription.status!.conditions) { - const installationCondition = subscription.status.conditions.find((condition: SubscriptionStatusCondition) => { - return condition.type === 'InstallPlanPending' && condition.status === 'True' - }) - if (installationCondition) { + const installCondition = subscription.status.conditions.find(condition => condition.type === 'InstallPlanPending' && condition.status === 'True') + if (installCondition) { ctx.installPlanName = subscription.status.installplan.name - return + return } } + // Todo... You had already installed the latest version Che. command.error("Unable to find installation plan to update.") } }, { title: 'Approve installation', task: async (ctx: any, task: any) => { - await kube.aproveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) } }, { @@ -193,7 +175,7 @@ export class OLMTasks { ], { renderer: flags['listr-renderer'] as any }) } - deleteTasks(flags: any, command: Command): ReadonlyArray { + deleteTasks(flags: any): ReadonlyArray { const kube = new KubeHelper(flags) return [ { @@ -249,14 +231,14 @@ export class OLMTasks { // To update chectl stable channel we are patching src/constants.ts from nightly to release version. // Let's use it to determine which olm channel should we use by default. - getDefaultChannel(): CheOLMChannel { + private getDefaultChannel(): CheOLMChannel { if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { return CheOLMChannel.NIGHTLY } return CheOLMChannel.STABLE } - isOlmPreInstalledTask(flags: any, command: Command, kube: KubeHelper): Listr.ListrTask { + private isOlmPreInstalledTask(flags: any, command: Command, kube: KubeHelper): Listr.ListrTask { return { title: "Check if OLM is pre-installed on the platform", task: async (ctx: any, task: any) => { @@ -266,5 +248,70 @@ export class OLMTasks { } } } -} + private customCatalogTasks(flags: any, command: Command, kube: KubeHelper): Listr { + return new Listr([ + { + title: "Create operator source", + task: async (ctx: any, task: any) => { + const applicationRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace + : kubernetesApplicationPreviewRegistryNamespace + if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { + task.title = `${task.title}...It already exists.` + } else { + await kube.createOperatorSource(this.operatorSourceName, applicationRegistryNamespace, ctx.marketplaceNamespace) + await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) + task.title = `${task.title}...OK` + } + } + }, + { + title: "Create catalog source", + task: async (ctx: any, task: any) => { + if (!await kube.catalogSourceExists(this.operatorSourceName, ctx.defaultCatalogSourceNamespace)) { + const catalogSourceInTheMarketPlaceNamespace = await kube.getCatalogSource(this.operatorSourceName, ctx.marketplaceNamespace) + + const catalogSource: CatalogSource = { + apiVersion: 'operators.coreos.com/v1alpha1', + kind: 'CatalogSource', + metadata: { + name: this.operatorSourceName, + namespace: ctx.defaultCatalogSourceNamespace, + }, + spec: { + address: catalogSourceInTheMarketPlaceNamespace.spec.address, + base64data: '', + mediatype: '', + sourceType: 'grpc' + } + } + // Create catalog source in the olm namespace to make it working in the namespace differ than marketplace + await kube.createCatalogSource(catalogSource) + await kube.waitCatalogSource(ctx.defaultCatalogSourceNamespace, this.operatorSourceName) + task.title = `${task.title}...OK` + } else { + task.title = `${task.title}...It already exists.` + } + } + } + ]) + } + + private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string): Subscription { + return { + apiVersion: "operators.coreos.com/v1alpha1", + kind: 'Subscription', + metadata: { + name: name, + namespace + }, + spec: { + channel, + installPlanApproval: 'Manual', + name: packageName, + source: sourceName, + sourceNamespace, + } + } + } +} From 25da32d47e28e99f5dee147a69645dc4f6dbfce5 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 6 Apr 2020 09:37:50 +0300 Subject: [PATCH 11/40] Don't remove operatorsource, for optimization we can share it between Che instalations... Signed-off-by: Oleksandr Andriienko --- src/tasks/installers/olm.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 8910c0af9..36674c465 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -18,7 +18,7 @@ import { Subscription, CatalogSource } from 'olm'; export class OLMTasks { - private operatorSourceName = 'eclipse-che1' + private operatorSourceName = 'eclipse-che' private operatorGroupName = 'che-operator-group' private packageNamePrefix = 'eclipse-che-preview-' private channel = this.getDefaultChannel() @@ -51,6 +51,7 @@ export class OLMTasks { ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace + ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') task.title = `${task.title}...OK` } }, @@ -64,7 +65,6 @@ export class OLMTasks { { title: 'Create operator subscription', task: async (ctx: any, task: any) => { - ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') var subscription: Subscription if (this.channel === CheOLMChannel.STABLE) { const packageManifest = await kube.getPackageManifect('eclipse-che') @@ -214,17 +214,6 @@ export class OLMTasks { } task.title = `${task.title}...OK` } - }, - { - title: `Delete(OLM) operator source ${this.operatorSourceName}`, - enabled: (ctx) => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace - if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { - await kube.deleteOperatorSource(this.operatorSourceName, ctx.marketplaceNamespace) - } - task.title = `${task.title}...OK` - } } ] } From ba19ec0e48e2271e97ec4e546699a754b2237d6c Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 6 Apr 2020 17:54:00 +0300 Subject: [PATCH 12/40] Code clean up. Signed-off-by: Oleksandr Andriienko --- src/constants.ts | 1 - src/tasks/installers/olm.ts | 45 ++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index c0f5eb77a..ac0fb4d9d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -31,7 +31,6 @@ export const defaultOLMKubernetesNamespace = 'olm' export const defaultApplicationRegistry = 'https://quay.io/cnr' -// TODO figure out and handle, Do we have the same channels for CRW? export enum CheOLMChannel { NIGHTLY = 'nightly', STABLE = 'stable' diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 36674c465..973f2c810 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -15,10 +15,12 @@ import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNa import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; import { Subscription, CatalogSource } from 'olm'; +import { isKubernetesPlatformFamily } from '../../util'; export class OLMTasks { private operatorSourceName = 'eclipse-che' + private subscriptionName = 'eclipse-che-subscription' private operatorGroupName = 'che-operator-group' private packageNamePrefix = 'eclipse-che-preview-' private channel = this.getDefaultChannel() @@ -51,7 +53,10 @@ export class OLMTasks { ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace + // preview package name ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') + // catalog source name for stable Che version + ctx.catalogSourceNameStable = isKubernetesPlatformFamily(flags.platform) ? 'operatorhubio-catalog' : 'community-operators' task.title = `${task.title}...OK` } }, @@ -65,17 +70,15 @@ export class OLMTasks { { title: 'Create operator subscription', task: async (ctx: any, task: any) => { - var subscription: Subscription - if (this.channel === CheOLMChannel.STABLE) { - const packageManifest = await kube.getPackageManifect('eclipse-che') - subscription = this.createSubscription(ctx.packageName, 'eclipse-che', flags.chenamespace, packageManifest.status!.catalogSourceNamespace, 'stable', packageManifest.status!.catalogSource) - } else { - subscription = this.createSubscription(ctx.packageName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName) - } - - if (await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { + if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { + var subscription: Subscription + if (this.channel === CheOLMChannel.STABLE) { + subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable) + } else { + subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName) + } await kube.createOperatorSubscription(subscription) task.title = `${task.title}...OK` } @@ -84,7 +87,7 @@ export class OLMTasks { { title: 'Wait while subscription is ready', task: async (ctx: any, task: any) => { - const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, ctx.packageName, 600) + const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, this.subscriptionName, 600) ctx.installPlanName = installPlan.name task.title = `${task.title}...OK` } @@ -132,9 +135,8 @@ export class OLMTasks { { title: 'Check if operator subscription exists', task: async (ctx: any, task: any) => { - ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - if (!await kube.operatorSubscriptionExists(ctx.packageName, flags.chenamespace)) { - command.error(`Unable to find operator subscription ${ctx.packageName}`) + if (!await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { + command.error(`Unable to find operator subscription ${this.subscriptionName}`) } } }, @@ -147,8 +149,7 @@ export class OLMTasks { { title: 'Get operator installation plan', task: async (ctx: any) => { - ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - const subscription: Subscription = await kube.getOperatorSubscription(ctx.packageName, flags.chenamespace) + const subscription: Subscription = await kube.getOperatorSubscription(this.subscriptionName, flags.chenamespace) if (subscription.status && subscription.status!.conditions) { const installCondition = subscription.status.conditions.find(condition => condition.type === 'InstallPlanPending' && condition.status === 'True') if (installCondition) { @@ -156,7 +157,7 @@ export class OLMTasks { return } } - // Todo... You had already installed the latest version Che. + // Todo... Handle situation `You had already installed the latest version Che`. command.error("Unable to find installation plan to update.") } }, @@ -194,13 +195,11 @@ export class OLMTasks { } }, { - title: `Delete(OLM) operator subscription 'Todo package name...'`, + title: `Delete(OLM) operator subscription ${this.subscriptionName}`, enabled: (ctx) => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { - // todo why do we need the same subscription name like package name. Are you sure? or move it upper. - const packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') - if (await kube.operatorSubscriptionExists(packageName, flags.chenamespace)) { - await kube.deleteOperatorSubscription(packageName, flags.chenamespace) + if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { + await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) } task.title = `${task.title}...OK` } @@ -243,8 +242,8 @@ export class OLMTasks { { title: "Create operator source", task: async (ctx: any, task: any) => { - const applicationRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace - : kubernetesApplicationPreviewRegistryNamespace + const applicationRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace + : kubernetesApplicationPreviewRegistryNamespace if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { task.title = `${task.title}...It already exists.` } else { From dc1ad647e921d5acc862f01aad8c899426b015b4 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 13 Apr 2020 10:00:42 +0300 Subject: [PATCH 13/40] Address request changes. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 12 +++++------- src/tasks/installers/olm.ts | 10 +++++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 05cf32fd4..b5e1718bf 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1212,17 +1212,15 @@ export class KubeHelper { } } - async isPreInstalledOLM() { + async isPreInstalledOLM(): Promise { const apiApi = this.kc.makeApiClient(ApisApi) try { const { body } = await apiApi.getAPIVersions() const OLMAPIGroup = body.groups.find((apiGroup) => apiGroup.name === 'operators.coreos.com') - if (OLMAPIGroup) { - return true - } - } catch {} - - return false + return !!OLMAPIGroup + } catch { + return false + } } async operatorSourceExists(name: string, namespace: string): Promise { diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 973f2c810..dd46acd9a 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -61,10 +61,11 @@ export class OLMTasks { } }, { - title: 'Create custom catalog source for "nightly" channel...OK', + title: 'Create custom catalog source for "nightly" channel', enabled: () => this.channel === CheOLMChannel.NIGHTLY, - task: async () => { - return this.customCatalogTasks(flags, command, kube) + task: async (ctx: any, task: any) => { + await this.customCatalogTasks(flags, command, kube).run(ctx) + task.title = `${task.title}...OK` } }, { @@ -183,6 +184,7 @@ export class OLMTasks { title: "Check if OLM is pre-installed on the platform", task: async (ctx: any, task: any) => { ctx.isPreInstalledOLM = await kube.isPreInstalledOLM() ? true : false + task.title = `${task.title}...OK` } }, { @@ -192,6 +194,7 @@ export class OLMTasks { const csvs = await kube.getClusterServiceVersions(flags.chenamespace) const csvsToDelete = csvs.items.filter((csv) => csv.metadata.name.startsWith("eclipse-che")) csvsToDelete.forEach((csv) => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) + task.title = `${task.title}...OK` } }, { @@ -233,6 +236,7 @@ export class OLMTasks { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") } + task.title = `${task.title}...OK` } } } From c0e6701b892e88e8532babc040d05ebff29e050e Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 13 Apr 2020 11:03:14 +0300 Subject: [PATCH 14/40] Notify user that minishift platform is not supported. Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 3 +++ src/commands/server/update.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index f7c2a9be8..ddf8c8827 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -246,6 +246,9 @@ export default class Start extends Command { this.error(`You requested to enable OpenShift OAuth but that's only possible when using the operator as installer. The current installer is ${flags.installer}. To use the operator add parameter "--installer operator".`) } } + if (flags.installer === 'olm' && flags.platform === 'minishift') { + this.error(`🛑 The specified installer ${flags.installer} does not support Minishift`) + } } } diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index ece1c00ae..e7fb70b45 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -84,6 +84,9 @@ export default class Update extends Command { if (flags.installer === 'minishift-addon' || flags.installer === 'helm') { this.error(`🛑 The specified installer ${flags.installer} does not support updating yet.`) } + if (flags.installer === 'olm' && flags.platform === 'minishift') { + this.error(`🛑 The specified installer ${flags.installer} does not support Minishift`) + } this.error(`🛑 Unknown installer ${flags.installer} is specified.`) } From b63a21df495e097c47458038597bbd4d05c40410 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 13 Apr 2020 17:14:03 +0300 Subject: [PATCH 15/40] Improve olm update method. Signed-off-by: Oleksandr Andriienko --- src/commands/server/update.ts | 2 +- src/tasks/installers/olm.ts | 31 ++++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index e7fb70b45..d8d573dfb 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -132,7 +132,7 @@ export default class Update extends Command { await preUpdateTasks.run(ctx) - if (!flags['skip-version-check']) { + if (!flags['skip-version-check'] && flags.installer !== 'olm') { await cli.anykey(` Found deployed Eclipse Che with operator [${ctx.deployedCheOperatorImage}]:${ctx.deployedCheOperatorTag}. You are going to update it to [${ctx.newCheOperatorImage}]:${ctx.newCheOperatorTag}. Note that Eclipse Che operator will update component images (server, plugin registry) only if their values diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index dd46acd9a..ff4b2768e 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -90,7 +90,7 @@ export class OLMTasks { task: async (ctx: any, task: any) => { const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, this.subscriptionName, 600) ctx.installPlanName = installPlan.name - task.title = `${task.title}...OK` + task.title = `${task.title}...OK.` } }, { @@ -123,6 +123,7 @@ export class OLMTasks { if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { command.error(`Unable to find operator source ${this.operatorSourceName}`) } + task.title = `${task.title}...OK` } }, { @@ -131,6 +132,7 @@ export class OLMTasks { if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)){ command.error(`Unable to find operator group ${this.operatorGroupName}`) } + task.title = `${task.title}...OK` } }, { @@ -139,6 +141,7 @@ export class OLMTasks { if (!await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { command.error(`Unable to find operator subscription ${this.subscriptionName}`) } + task.title = `${task.title}...OK` } }, ], { renderer: flags['listr-renderer'] as any }) @@ -149,28 +152,38 @@ export class OLMTasks { return new Listr([ { title: 'Get operator installation plan', - task: async (ctx: any) => { + task: async (ctx: any, task: any) => { const subscription: Subscription = await kube.getOperatorSubscription(this.subscriptionName, flags.chenamespace) - if (subscription.status && subscription.status!.conditions) { - const installCondition = subscription.status.conditions.find(condition => condition.type === 'InstallPlanPending' && condition.status === 'True') - if (installCondition) { - ctx.installPlanName = subscription.status.installplan.name + + if (subscription.status) { + if (subscription.status.state === 'AtLatestKnown') { + task.title = `Everything is up to date. Installed the latest known version '${subscription.status.currentCSV}' from channel '${this.channel}.` return } + + if (subscription.status.state === 'UpgradePending' && subscription.status!.conditions) { + const installCondition = subscription.status.conditions.find(condition => condition.type === 'InstallPlanPending' && condition.status === 'True') + if (installCondition) { + ctx.installPlanName = subscription.status.installplan.name + task.title = `${task.title}...OK` + return + } + } } - // Todo... Handle situation `You had already installed the latest version Che`. command.error("Unable to find installation plan to update.") } }, { title: 'Approve installation', - task: async (ctx: any, task: any) => { + enabled: (ctx: any) => ctx.installPlanName, + task: async (ctx: any) => { await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) } }, { title: 'Wait while newer operator installed', - task: async (ctx: any, task: any) => { + enabled: (ctx: any) => ctx.installPlanName, + task: async (ctx: any) => { await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 60) } }, From e204fb91f40e988b792acb979456c6032a4bb64c Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 13 Apr 2020 23:51:02 +0300 Subject: [PATCH 16/40] Fix up. Signed-off-by: Oleksandr Andriienko --- src/tasks/installers/olm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index ff4b2768e..55c49b849 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -157,7 +157,7 @@ export class OLMTasks { if (subscription.status) { if (subscription.status.state === 'AtLatestKnown') { - task.title = `Everything is up to date. Installed the latest known version '${subscription.status.currentCSV}' from channel '${this.channel}.` + task.title = `Everything is up to date. Installed the latest known version '${subscription.status.currentCSV}' from channel '${this.channel}'.` return } From b13ec1c4b685d9303f6d5d99b4553e0930b30586 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 14 Apr 2020 18:54:11 +0300 Subject: [PATCH 17/40] Add ability to use OLM automatic installation mode. Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 14 +++++++++++++- src/constants.ts | 5 +++++ src/tasks/installers/olm.ts | 28 +++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index ddf8c8827..24f4ed8df 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -150,7 +150,16 @@ export default class Start extends Command { 'skip-cluster-availability-check': flags.boolean({ description: 'Skip cluster availability check. The check is a simple request to ensure the cluster is reachable.', default: false - }) + }), + 'approval-strategy': flags.string({ + description: `Approval strategy for installation Eclipse Che. + There are two strategies: 'automatic' and 'manual'. + 'automatic' strategy used to enable auto-update Eclipse Che without any human interaction. + By default strategy is 'manual'. It requires approval from user. + To approve installation newer version Eclipse Che user should execute 'chectl server:update ...' command. + This parameter is used only when the installer is 'olm'.`, + default: 'manual' + }), } static getTemplatesDir(): string { @@ -249,6 +258,9 @@ export default class Start extends Command { if (flags.installer === 'olm' && flags.platform === 'minishift') { this.error(`🛑 The specified installer ${flags.installer} does not support Minishift`) } + if (flags.installer !== 'olm' && flags['approval-strategy']) { + this.error('"approval-strategy" flag should be used only with "olm" installer.') + } } } diff --git a/src/constants.ts b/src/constants.ts index ac0fb4d9d..6f8088c38 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -35,3 +35,8 @@ export enum CheOLMChannel { NIGHTLY = 'nightly', STABLE = 'stable' } + +export enum InstallPlanApprovalFlags { + Manual = 'manual', + Automatic = 'auto' +} diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 55c49b849..97457fb09 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -10,7 +10,7 @@ import Command from '@oclif/command'; import Listr = require('listr'); -import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace } from '../../constants'; +import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, InstallPlanApprovalFlags } from '../../constants'; import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; @@ -32,6 +32,23 @@ export class OLMTasks { const kube = new KubeHelper(flags) return new Listr([ this.isOlmPreInstalledTask(flags, command, kube), + { + title: 'Set up approval strategy', + task: async (ctx: any, task: any) => { + switch(flags['approval-strategy']) { + case InstallPlanApprovalFlags.Automatic: + ctx.approvalStarategy = 'Automatic' + break; + case InstallPlanApprovalFlags.Manual: + ctx.approvalStarategy = 'Manual' + break; + default: + command.error(`Invalid 'approval-strategy' flag value. Valied values are "${Object.values(InstallPlanApprovalFlags).join('", "')}"`) + } + + task.title = `${task.title}...OK ${ctx.approvalStarategy}` + } + }, copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), checkPreCreatedTls(flags, kube), @@ -76,9 +93,9 @@ export class OLMTasks { } else { var subscription: Subscription if (this.channel === CheOLMChannel.STABLE) { - subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable) + subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy) } else { - subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName) + subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName, ctx.approvalStarategy) } await kube.createOperatorSubscription(subscription) task.title = `${task.title}...OK` @@ -95,6 +112,7 @@ export class OLMTasks { }, { title: 'Approve installation', + enabled: ctx => ctx.approvalStarategy === 'Manual', task: async (ctx: any, task: any) => { await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) task.title = `${task.title}...OK` @@ -302,7 +320,7 @@ export class OLMTasks { ]) } - private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string): Subscription { + private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string): Subscription { return { apiVersion: "operators.coreos.com/v1alpha1", kind: 'Subscription', @@ -312,7 +330,7 @@ export class OLMTasks { }, spec: { channel, - installPlanApproval: 'Manual', + installPlanApproval, name: packageName, source: sourceName, sourceNamespace, From 2ff5de07144054bbf645fcc7f4ca42b0ab036c84 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 14 Apr 2020 22:08:31 +0300 Subject: [PATCH 18/40] Add ability to set up starting csv for olm installer. Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 9 +++++++++ src/tasks/installers/olm.ts | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 24f4ed8df..4623c12be 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -158,8 +158,14 @@ export default class Start extends Command { By default strategy is 'manual'. It requires approval from user. To approve installation newer version Eclipse Che user should execute 'chectl server:update ...' command. This parameter is used only when the installer is 'olm'.`, + options: ['manual', 'auto'], default: 'manual' }), + 'starting-csv': flags.string({ + description: `Start cluster service version for installation Eclipse Che. + Flags uses to set up installation version Che matched to the OLM CSV. + This parameter is used only when the installer is 'olm'.` + }) } static getTemplatesDir(): string { @@ -261,6 +267,9 @@ export default class Start extends Command { if (flags.installer !== 'olm' && flags['approval-strategy']) { this.error('"approval-strategy" flag should be used only with "olm" installer.') } + if (flags.installer !== 'olm' && flags['starting-csv']) { + this.error('"starting-csv" flag should be used only with "olm" installer.') + } } } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 97457fb09..28e116c18 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -93,9 +93,9 @@ export class OLMTasks { } else { var subscription: Subscription if (this.channel === CheOLMChannel.STABLE) { - subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy) + subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { - subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName, ctx.approvalStarategy) + subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName, ctx.approvalStarategy, flags['starting-csv']) } await kube.createOperatorSubscription(subscription) task.title = `${task.title}...OK` @@ -320,7 +320,7 @@ export class OLMTasks { ]) } - private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string): Subscription { + private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string, startingCSV?: string): Subscription { return { apiVersion: "operators.coreos.com/v1alpha1", kind: 'Subscription', @@ -334,6 +334,7 @@ export class OLMTasks { name: packageName, source: sourceName, sourceNamespace, + startingCSV, } } } From 2cbaf988128716ff25d5e50f680f56423fa61f96 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 14 Apr 2020 22:36:34 +0300 Subject: [PATCH 19/40] Simplify code . Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 18 ++++++++---------- src/constants.ts | 5 ----- src/tasks/installers/olm.ts | 22 ++++------------------ 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 4623c12be..48b24c21d 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -151,15 +151,13 @@ export default class Start extends Command { description: 'Skip cluster availability check. The check is a simple request to ensure the cluster is reachable.', default: false }), - 'approval-strategy': flags.string({ - description: `Approval strategy for installation Eclipse Che. - There are two strategies: 'automatic' and 'manual'. - 'automatic' strategy used to enable auto-update Eclipse Che without any human interaction. - By default strategy is 'manual'. It requires approval from user. + 'auto-update': flags.boolean({ + description: `Auto update approval strategy for installation Eclipse Che. + With this strategy will be provided auto-update Eclipse Che without any human interaction. + By default strategy this flag is false. It requires approval from user. To approve installation newer version Eclipse Che user should execute 'chectl server:update ...' command. - This parameter is used only when the installer is 'olm'.`, - options: ['manual', 'auto'], - default: 'manual' + This parameter is used only when the installer is 'olm'.`, + default: false }), 'starting-csv': flags.string({ description: `Start cluster service version for installation Eclipse Che. @@ -264,8 +262,8 @@ export default class Start extends Command { if (flags.installer === 'olm' && flags.platform === 'minishift') { this.error(`🛑 The specified installer ${flags.installer} does not support Minishift`) } - if (flags.installer !== 'olm' && flags['approval-strategy']) { - this.error('"approval-strategy" flag should be used only with "olm" installer.') + if (flags.installer !== 'olm' && flags['auto-update']) { + this.error('"auto-update" flag should be used only with "olm" installer.') } if (flags.installer !== 'olm' && flags['starting-csv']) { this.error('"starting-csv" flag should be used only with "olm" installer.') diff --git a/src/constants.ts b/src/constants.ts index 6f8088c38..ac0fb4d9d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -35,8 +35,3 @@ export enum CheOLMChannel { NIGHTLY = 'nightly', STABLE = 'stable' } - -export enum InstallPlanApprovalFlags { - Manual = 'manual', - Automatic = 'auto' -} diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 28e116c18..eff446e19 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -10,7 +10,7 @@ import Command from '@oclif/command'; import Listr = require('listr'); -import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, InstallPlanApprovalFlags } from '../../constants'; +import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace } from '../../constants'; import { KubeHelper } from '../../api/kube'; import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; @@ -32,23 +32,6 @@ export class OLMTasks { const kube = new KubeHelper(flags) return new Listr([ this.isOlmPreInstalledTask(flags, command, kube), - { - title: 'Set up approval strategy', - task: async (ctx: any, task: any) => { - switch(flags['approval-strategy']) { - case InstallPlanApprovalFlags.Automatic: - ctx.approvalStarategy = 'Automatic' - break; - case InstallPlanApprovalFlags.Manual: - ctx.approvalStarategy = 'Manual' - break; - default: - command.error(`Invalid 'approval-strategy' flag value. Valied values are "${Object.values(InstallPlanApprovalFlags).join('", "')}"`) - } - - task.title = `${task.title}...OK ${ctx.approvalStarategy}` - } - }, copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), checkPreCreatedTls(flags, kube), @@ -74,6 +57,9 @@ export class OLMTasks { ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') // catalog source name for stable Che version ctx.catalogSourceNameStable = isKubernetesPlatformFamily(flags.platform) ? 'operatorhubio-catalog' : 'community-operators' + + ctx.approvalStarategy = flags['auto-update'] ? 'Automatic' : 'Manual' + task.title = `${task.title}...OK` } }, From 13d39f1fee2b80f86853effd5115b623685d60a5 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 15 Apr 2020 13:48:34 +0300 Subject: [PATCH 20/40] Fix format. Signed-off-by: Oleksandr Andriienko --- README.md | 19 +++- src/api/kube.ts | 84 +++++++------- src/api/typings/olm.d.ts | 4 +- src/api/version.ts | 14 +-- src/commands/server/start.ts | 11 +- src/constants.ts | 4 +- src/tasks/installers/common-tasks.ts | 158 +++++++++++++-------------- src/tasks/installers/olm.ts | 24 ++-- src/tasks/platforms/api.ts | 10 +- 9 files changed, 174 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 53e03569f..6cb3b8637 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ OPTIONS show CLI help -i, --cheimage=cheimage - [default: quay.io/eclipse/che-server:nightly] Eclipse Che server container image + [default: quay.io/eclipse/che-server:7.9.0] Eclipse Che server container image -m, --multiuser Starts Eclipse Che in multi-user mode @@ -293,6 +293,14 @@ OPTIONS -t, --templates=templates [default: templates] Path to the templates folder + --auto-update + Auto update approval strategy for installation Eclipse Che. + With this strategy will be provided auto-update Eclipse Che without any human interaction. + By default strategy this flag is false. It requires approval from user. + To approve installation newer version Eclipse Che user should execute 'chectl server:update' + command. + This parameter is used only when the installer is 'olm'. + --che-operator-cr-patch-yaml=che-operator-cr-patch-yaml Path to a yaml file that overrides the default values in CheCluster CR used by the operator. This parameter is used only when the installer is the operator. @@ -345,6 +353,15 @@ OPTIONS --skip-version-check Skip minimal versions check. + --starting-csv=starting-csv + Starting cluster service version(CSV) for installation Eclipse Che. + Flags uses to set up start installation version Che. + For example: 'starting-csv' provided with value 'eclipse-che.v7.10.0' for stable channel. + Then OLM will install Eclipse Che with version 7.10.0. + Notice: this flag will be ignored with 'auto-update' flag. OLM with auto-update mode installs + the latest known version. + This parameter is used only when the installer is 'olm'. + --workspace-pvc-storage-class-name=workspace-pvc-storage-class-name persistent volume(s) storage class name to use to store Eclipse Che workspaces data ``` diff --git a/src/api/kube.ts b/src/api/kube.ts index b5e1718bf..11b119cd0 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1302,16 +1302,16 @@ export class KubeHelper { return new Promise(async (resolve, reject) => { const watcher = new Watch(this.kc) let request: any - request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/catalogsources`, - { fieldSelector: `metadata.name=${catalogSourceName}` }, - (_phase: string, obj: any) => { - resolve(obj as CatalogSource) - }, - error => { - if (error) { - reject(error) - } - }) + request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/catalogsources`, + { fieldSelector: `metadata.name=${catalogSourceName}` }, + (_phase: string, obj: any) => { + resolve(obj as CatalogSource) + }, + error => { + if (error) { + reject(error) + } + }) setTimeout(() => { request.abort() @@ -1339,7 +1339,7 @@ export class KubeHelper { namespace, }, spec: { - targetNamespaces: [ namespace ] + targetNamespaces: [namespace] } } @@ -1406,23 +1406,23 @@ export class KubeHelper { return new Promise(async (resolve, reject) => { const watcher = new Watch(this.kc) let request: any - request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/subscriptions`, - { fieldSelector: `metadata.name=${subscriptionName}` }, - (_phase: string, obj: any) => { - const subscription = obj as Subscription - if (subscription.status && subscription.status.conditions) { - for (const condition of subscription.status.conditions) { - if (condition.type === 'InstallPlanPending' && condition.status === 'True') { - resolve(subscription.status.installplan) + request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/subscriptions`, + { fieldSelector: `metadata.name=${subscriptionName}` }, + (_phase: string, obj: any) => { + const subscription = obj as Subscription + if (subscription.status && subscription.status.conditions) { + for (const condition of subscription.status.conditions) { + if (condition.type === 'InstallPlanPending' && condition.status === 'True') { + resolve(subscription.status.installplan) + } } } - } - }, - error => { - if (error) { - reject(error) - } - }) + }, + error => { + if (error) { + reject(error) + } + }) setTimeout(() => { request.abort() @@ -1439,7 +1439,7 @@ export class KubeHelper { approved: true } } - await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'installplans', name, patch, {headers: {'Content-Type': 'application/merge-patch+json'}}) + await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'installplans', name, patch, { headers: { 'Content-Type': 'application/merge-patch+json' } }) } catch (e) { throw this.wrapK8sClientError(e) } @@ -1449,23 +1449,23 @@ export class KubeHelper { return new Promise(async (resolve, reject) => { const watcher = new Watch(this.kc) let request: any - request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/installplans`, - { fieldSelector: `metadata.name=${installPlanName}` }, - (_phase: string, obj: any) => { - const installPlan = obj as InstallPlan - if (installPlan.status && installPlan.status.conditions) { - for (const condition of installPlan.status.conditions) { - if (condition.type === 'Installed' && condition.status === 'True') { - resolve() + request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/installplans`, + { fieldSelector: `metadata.name=${installPlanName}` }, + (_phase: string, obj: any) => { + const installPlan = obj as InstallPlan + if (installPlan.status && installPlan.status.conditions) { + for (const condition of installPlan.status.conditions) { + if (condition.type === 'Installed' && condition.status === 'True') { + resolve() + } } } - } - }, - error => { - if (error) { - reject(error) - } - }) + }, + error => { + if (error) { + reject(error) + } + }) setTimeout(() => { request.abort() diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index 6e98e748c..72d18b933 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -14,7 +14,7 @@ declare module 'olm' { apiVersion: string; kind: string; metadata: V1ObjectMeta; - spec: OperatorSourceSpec; + spec: OperatorSourceSpec; } export interface OperatorSourceSpec { @@ -122,5 +122,5 @@ declare module 'olm' { export interface PackageManifestStatus { catalogSource: string catalogSourceNamespace: string - } + } } diff --git a/src/api/version.ts b/src/api/version.ts index 2b67489b1..08e12b3ff 100644 --- a/src/api/version.ts +++ b/src/api/version.ts @@ -42,13 +42,13 @@ export namespace VersionHelper { 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() + case 'minishift': + case 'openshift': + case 'crc': + actualVersion = await getK8sVersionWithOC() + break + default: + actualVersion = await getK8sVersionWithKubectl() } if (actualVersion) { diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 48b24c21d..51b1f4bea 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -155,13 +155,16 @@ export default class Start extends Command { description: `Auto update approval strategy for installation Eclipse Che. With this strategy will be provided auto-update Eclipse Che without any human interaction. By default strategy this flag is false. It requires approval from user. - To approve installation newer version Eclipse Che user should execute 'chectl server:update ...' command. - This parameter is used only when the installer is 'olm'.`, + To approve installation newer version Eclipse Che user should execute 'chectl server:update' command. + This parameter is used only when the installer is 'olm'.`, default: false }), 'starting-csv': flags.string({ - description: `Start cluster service version for installation Eclipse Che. - Flags uses to set up installation version Che matched to the OLM CSV. + description: `Starting cluster service version(CSV) for installation Eclipse Che. + Flags uses to set up start installation version Che. + For example: 'starting-csv' provided with value 'eclipse-che.v7.10.0' for stable channel. + Then OLM will install Eclipse Che with version 7.10.0. + Notice: this flag will be ignored with 'auto-update' flag. OLM with auto-update mode installs the latest known version. This parameter is used only when the installer is 'olm'.` }) } diff --git a/src/constants.ts b/src/constants.ts index ac0fb4d9d..d85a6574f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -32,6 +32,6 @@ export const defaultOLMKubernetesNamespace = 'olm' export const defaultApplicationRegistry = 'https://quay.io/cnr' export enum CheOLMChannel { - NIGHTLY = 'nightly', - STABLE = 'stable' + NIGHTLY = 'nightly', + STABLE = 'stable' } diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index f33cf9cbc..8f818902e 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -8,7 +8,7 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import { ListrTask, ListrContext} from 'listr' +import { ListrTask, ListrContext } from 'listr' import { CheHelper } from '../../api/che' import * as execa from 'execa' import { copy, mkdirp, remove } from 'fs-extra' @@ -21,78 +21,78 @@ import * as yaml from 'js-yaml' import { cli } from 'cli-ux' export function createNamespaceTask(flags: any): ListrTask { - return { - 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}...It already exists.` - } else if (isKubernetesPlatformFamily(flags.platform)) { - await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true }) - task.title = `${task.title}...done.` - } else if (isOpenshiftPlatformFamily(flags.platform)) { - await execa(`oc new-project ${flags.chenamespace}`, { shell: true }) - task.title = `${task.title}...done.` - } - } + return { + 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}...It already exists.` + } else if (isKubernetesPlatformFamily(flags.platform)) { + await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true }) + task.title = `${task.title}...done.` + } else if (isOpenshiftPlatformFamily(flags.platform)) { + await execa(`oc new-project ${flags.chenamespace}`, { shell: true }) + task.title = `${task.title}...done.` + } } + } } export function copyOperatorResources(flags: any, cacheDir: string): ListrTask { - return { - title: 'Copying operator resources', - task: async (ctx: any, task: any) => { - ctx.resourcesPath = await copyCheOperatorResources(flags.templates, cacheDir) - task.title = `${task.title}...done.` - } + return { + title: 'Copying operator resources', + task: async (ctx: any, task: any) => { + ctx.resourcesPath = await copyCheOperatorResources(flags.templates, cacheDir) + task.title = `${task.title}...done.` } + } } async function copyCheOperatorResources(templatesDir: string, cacheDir: string): Promise { - const srcDir = path.join(templatesDir, '/che-operator/') - const destDir = path.join(cacheDir, '/templates/che-operator/') + const srcDir = path.join(templatesDir, '/che-operator/') + const destDir = path.join(cacheDir, '/templates/che-operator/') - await remove(destDir) - await mkdirp(destDir) - await copy(srcDir, destDir) + await remove(destDir) + await mkdirp(destDir) + await copy(srcDir, destDir) - return destDir + return destDir } export function createEclipeCheCluster(flags: any): ListrTask { - return { - title: `Create Eclipse Che cluster ${operatorCheCluster} in namespace ${flags.chenamespace}`, - task: async (ctx: any, task: any) => { - const kube = new KubeHelper(flags) - const exist = await kube.cheClusterExist(operatorCheCluster, flags.chenamespace) - if (exist) { - task.title = `${task.title}...It already exists.` - } else { - // Eclipse Che operator supports only Multi-User Che - ctx.isCheDeployed = true - ctx.isPostgresDeployed = true - ctx.isKeycloakDeployed = true - - // plugin and devfile registry will be deployed only when external ones are not configured - ctx.isPluginRegistryDeployed = !(flags['plugin-registry-url'] as boolean) - ctx.isDevfileRegistryDeployed = !(flags['devfile-registry-url'] as boolean) - - const yamlFilePath = flags['che-operator-cr-yaml'] === '' ? ctx.resourcesPath + 'crds/org_v1_che_cr.yaml' : flags['che-operator-cr-yaml'] - const cr = await kube.createCheClusterFromFile(yamlFilePath, flags, flags['che-operator-cr-yaml'] === '') - ctx.isKeycloakReady = ctx.isKeycloakReady || cr.spec.auth.externalIdentityProvider - ctx.isPostgresReady = ctx.isPostgresReady || cr.spec.database.externalDb - ctx.isDevfileRegistryReady = ctx.isDevfileRegistryReady || cr.spec.server.externalDevfileRegistry - ctx.isPluginRegistryReady = ctx.isPluginRegistryReady || cr.spec.server.externalPluginRegistry - - if (cr.spec.server.customCheProperties && cr.spec.server.customCheProperties.CHE_MULTIUSER === 'false') { - flags.multiuser = false - } - - task.title = `${task.title}...done.` - } + return { + title: `Create Eclipse Che cluster ${operatorCheCluster} in namespace ${flags.chenamespace}`, + task: async (ctx: any, task: any) => { + const kube = new KubeHelper(flags) + const exist = await kube.cheClusterExist(operatorCheCluster, flags.chenamespace) + if (exist) { + task.title = `${task.title}...It already exists.` + } else { + // Eclipse Che operator supports only Multi-User Che + ctx.isCheDeployed = true + ctx.isPostgresDeployed = true + ctx.isKeycloakDeployed = true + + // plugin and devfile registry will be deployed only when external ones are not configured + ctx.isPluginRegistryDeployed = !(flags['plugin-registry-url'] as boolean) + ctx.isDevfileRegistryDeployed = !(flags['devfile-registry-url'] as boolean) + + const yamlFilePath = flags['che-operator-cr-yaml'] === '' ? ctx.resourcesPath + 'crds/org_v1_che_cr.yaml' : flags['che-operator-cr-yaml'] + const cr = await kube.createCheClusterFromFile(yamlFilePath, flags, flags['che-operator-cr-yaml'] === '') + ctx.isKeycloakReady = ctx.isKeycloakReady || cr.spec.auth.externalIdentityProvider + ctx.isPostgresReady = ctx.isPostgresReady || cr.spec.database.externalDb + ctx.isDevfileRegistryReady = ctx.isDevfileRegistryReady || cr.spec.server.externalDevfileRegistry + ctx.isPluginRegistryReady = ctx.isPluginRegistryReady || cr.spec.server.externalPluginRegistry + + if (cr.spec.server.customCheProperties && cr.spec.server.customCheProperties.CHE_MULTIUSER === 'false') { + flags.multiuser = false } + + task.title = `${task.title}...done.` } + } + } } export function checkPreCreatedTls(flags: any, kube: KubeHelper): ListrTask { @@ -168,31 +168,31 @@ export function checkTlsSertificate(flags: any): ListrTask { * Returns true if TLS is enabled (or omitted) and false if it is explicitly disabled. */ async function checkTlsMode(flags: any): Promise { - if (flags['che-operator-cr-yaml']) { - const cheOperatorCrYamlPath = flags['che-operator-cr-yaml'] - if (fs.existsSync(cheOperatorCrYamlPath)) { - const cr = yaml.safeLoad(fs.readFileSync(cheOperatorCrYamlPath).toString()) - if (cr && cr.spec && cr.spec.server && cr.spec.server.tlsSupport === false) { - return false - } + if (flags['che-operator-cr-yaml']) { + const cheOperatorCrYamlPath = flags['che-operator-cr-yaml'] + if (fs.existsSync(cheOperatorCrYamlPath)) { + const cr = yaml.safeLoad(fs.readFileSync(cheOperatorCrYamlPath).toString()) + if (cr && cr.spec && cr.spec.server && cr.spec.server.tlsSupport === false) { + return false } } + } - if (flags['che-operator-cr-patch-yaml']) { - const cheOperatorCrPatchYamlPath = flags['che-operator-cr-patch-yaml'] - if (fs.existsSync(cheOperatorCrPatchYamlPath)) { - const crPatch = yaml.safeLoad(fs.readFileSync(cheOperatorCrPatchYamlPath).toString()) - if (crPatch && crPatch.spec && crPatch.spec.server && crPatch.spec.server.tlsSupport === false) { - return false - } + if (flags['che-operator-cr-patch-yaml']) { + const cheOperatorCrPatchYamlPath = flags['che-operator-cr-patch-yaml'] + if (fs.existsSync(cheOperatorCrPatchYamlPath)) { + const crPatch = yaml.safeLoad(fs.readFileSync(cheOperatorCrPatchYamlPath).toString()) + if (crPatch && crPatch.spec && crPatch.spec.server && crPatch.spec.server.tlsSupport === false) { + return false } } + } - // If tls flag is undefined we suppose that tls is turned on - if (flags.tls === false) { - return false - } - - // TLS is on - return true + // If tls flag is undefined we suppose that tls is turned on + if (flags.tls === false) { + return false } + + // TLS is on + return true +} diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index eff446e19..ba1b1f176 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -32,7 +32,7 @@ export class OLMTasks { const kube = new KubeHelper(flags) return new Listr([ this.isOlmPreInstalledTask(flags, command, kube), - copyOperatorResources(flags, command.config.cacheDir), + copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), checkPreCreatedTls(flags, kube), checkTlsSertificate(flags), @@ -133,7 +133,7 @@ export class OLMTasks { { title: 'Check if operator group exists', task: async (ctx: any, task: any) => { - if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)){ + if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { command.error(`Unable to find operator group ${this.operatorGroupName}`) } task.title = `${task.title}...OK` @@ -199,7 +199,7 @@ export class OLMTasks { return [ { title: "Check if OLM is pre-installed on the platform", - task: async (ctx: any, task: any) => { + task: async (ctx: any, task: any) => { ctx.isPreInstalledOLM = await kube.isPreInstalledOLM() ? true : false task.title = `${task.title}...OK` } @@ -249,7 +249,7 @@ export class OLMTasks { private isOlmPreInstalledTask(flags: any, command: Command, kube: KubeHelper): Listr.ListrTask { return { title: "Check if OLM is pre-installed on the platform", - task: async (ctx: any, task: any) => { + task: async (ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") } @@ -264,14 +264,14 @@ export class OLMTasks { title: "Create operator source", task: async (ctx: any, task: any) => { const applicationRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace - : kubernetesApplicationPreviewRegistryNamespace - if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { - task.title = `${task.title}...It already exists.` - } else { - await kube.createOperatorSource(this.operatorSourceName, applicationRegistryNamespace, ctx.marketplaceNamespace) - await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) - task.title = `${task.title}...OK` - } + : kubernetesApplicationPreviewRegistryNamespace + if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { + task.title = `${task.title}...It already exists.` + } else { + await kube.createOperatorSource(this.operatorSourceName, applicationRegistryNamespace, ctx.marketplaceNamespace) + await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) + task.title = `${task.title}...OK` + } } }, { diff --git a/src/tasks/platforms/api.ts b/src/tasks/platforms/api.ts index 2bd8e1e97..78fcc7877 100644 --- a/src/tasks/platforms/api.ts +++ b/src/tasks/platforms/api.ts @@ -13,11 +13,11 @@ import * as Listr from 'listr' import { KubeHelper } from '../../api/kube' export class ApiTasks { - /** - * Returns tasks which tests if K8s or OpenShift API is configured in the current context. - * - * `isOpenShift` property is provisioned into context. - */ + /** + * Returns tasks which tests if K8s or OpenShift API is configured in the current context. + * + * `isOpenShift` property is provisioned into context. + */ testApiTasks(flags: any, command: Command): Listr.ListrTask { let kube = new KubeHelper(flags) return { From 6265a5ed23b536477b018bcfd763131a2a236f9f Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 15 Apr 2020 15:36:47 +0300 Subject: [PATCH 21/40] Fix lint. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 6 ++-- src/api/version.ts | 14 ++++---- src/commands/server/delete.ts | 2 +- src/commands/server/start.ts | 6 ++-- src/tasks/installers/common-tasks.ts | 23 +++++++------ src/tasks/installers/installer.ts | 4 +-- src/tasks/installers/olm.ts | 51 ++++++++++++++-------------- src/tasks/installers/operator.ts | 6 ++-- 8 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 11b119cd0..2fae4b652 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -18,13 +18,13 @@ import https = require('https') import * as yaml from 'js-yaml' import { merge } from 'lodash' import * as net from 'net' +import { CatalogSource, ClusterServiceVersionList, InstallPlan, OperatorGroup, OperatorSource, PackageManifest, Subscription } from 'olm' import { Writable } from 'stream' import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' -import { OperatorSource, OperatorGroup, Subscription, InstallPlan, ClusterServiceVersionList, CatalogSource, PackageManifest } from 'olm' const AWAIT_TIMEOUT_S = 30 @@ -1216,7 +1216,7 @@ export class KubeHelper { const apiApi = this.kc.makeApiClient(ApisApi) try { const { body } = await apiApi.getAPIVersions() - const OLMAPIGroup = body.groups.find((apiGroup) => apiGroup.name === 'operators.coreos.com') + const OLMAPIGroup = body.groups.find(apiGroup => apiGroup.name === 'operators.coreos.com') return !!OLMAPIGroup } catch { return false @@ -1244,7 +1244,7 @@ export class KubeHelper { spec: { endpoint: defaultApplicationRegistry, registryNamespace, - type: "appregistry" + type: 'appregistry' } } diff --git a/src/api/version.ts b/src/api/version.ts index 08e12b3ff..2b67489b1 100644 --- a/src/api/version.ts +++ b/src/api/version.ts @@ -42,13 +42,13 @@ export namespace VersionHelper { 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() + case 'minishift': + case 'openshift': + case 'crc': + actualVersion = await getK8sVersionWithOC() + break + default: + actualVersion = await getK8sVersionWithKubectl() } if (actualVersion) { diff --git a/src/commands/server/delete.ts b/src/commands/server/delete.ts index 648f1b9d5..f8236c6df 100644 --- a/src/commands/server/delete.ts +++ b/src/commands/server/delete.ts @@ -18,8 +18,8 @@ import { cheNamespace, listrRenderer } from '../../common-flags' import { CheTasks } from '../../tasks/che' import { HelmTasks } from '../../tasks/installers/helm' import { MinishiftAddonTasks } from '../../tasks/installers/minishift-addon' -import { OperatorTasks } from '../../tasks/installers/operator' import { OLMTasks } from '../../tasks/installers/olm' +import { OperatorTasks } from '../../tasks/installers/operator' import { ApiTasks } from '../../tasks/platforms/api' export default class Delete extends Command { diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 51b1f4bea..881dea057 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -152,10 +152,10 @@ export default class Start extends Command { default: false }), 'auto-update': flags.boolean({ - description: `Auto update approval strategy for installation Eclipse Che. - With this strategy will be provided auto-update Eclipse Che without any human interaction. + description: `Auto update approval strategy for installation Eclipse Che. + With this strategy will be provided auto-update Eclipse Che without any human interaction. By default strategy this flag is false. It requires approval from user. - To approve installation newer version Eclipse Che user should execute 'chectl server:update' command. + To approve installation newer version Eclipse Che user should execute 'chectl server:update' command. This parameter is used only when the installer is 'olm'.`, default: false }), diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index 8f818902e..895ff92af 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -8,19 +8,20 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import { ListrTask, ListrContext } from 'listr' -import { CheHelper } from '../../api/che' +import { cli } from 'cli-ux' import * as execa from 'execa' +import * as fs from 'fs' import { copy, mkdirp, remove } from 'fs-extra' +import * as yaml from 'js-yaml' +import { ListrTask } from 'listr' import * as path from 'path' + +import { CheHelper } from '../../api/che' import { KubeHelper } from '../../api/kube' import { operatorCheCluster } from '../../constants' -import { isOpenshiftPlatformFamily, isKubernetesPlatformFamily } from '../../util' -import * as fs from 'fs' -import * as yaml from 'js-yaml' -import { cli } from 'cli-ux' +import { isKubernetesPlatformFamily, isOpenshiftPlatformFamily } from '../../util' -export function createNamespaceTask(flags: any): ListrTask { +export function createNamespaceTask(flags: any): ListrTask { return { title: `Create Namespace (${flags.chenamespace})`, task: async (_ctx: any, task: any) => { @@ -39,7 +40,7 @@ export function createNamespaceTask(flags: any): ListrTask { } } -export function copyOperatorResources(flags: any, cacheDir: string): ListrTask { +export function copyOperatorResources(flags: any, cacheDir: string): ListrTask { return { title: 'Copying operator resources', task: async (ctx: any, task: any) => { @@ -60,7 +61,7 @@ async function copyCheOperatorResources(templatesDir: string, cacheDir: string): return destDir } -export function createEclipeCheCluster(flags: any): ListrTask { +export function createEclipeCheCluster(flags: any): ListrTask { return { title: `Create Eclipse Che cluster ${operatorCheCluster} in namespace ${flags.chenamespace}`, task: async (ctx: any, task: any) => { @@ -95,7 +96,7 @@ export function createEclipeCheCluster(flags: any): ListrTask { } } -export function checkPreCreatedTls(flags: any, kube: KubeHelper): ListrTask { +export function checkPreCreatedTls(flags: any, kube: KubeHelper): ListrTask { return { title: 'Checking for pre-created TLS secret', // In case of Openshift infrastructure the certificate from cluster router will be used, so no need in the `che-tls` secret. @@ -126,7 +127,7 @@ export function checkPreCreatedTls(flags: any, kube: KubeHelper): ListrTask { +export function checkTlsSertificate(flags: any): ListrTask { return { title: 'Checking certificate', // If the flag is set no need to check if it is required diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index a8ec42722..aa8f538f8 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -13,8 +13,8 @@ import * as Listr from 'listr' import { HelmTasks } from './helm' import { MinishiftAddonTasks } from './minishift-addon' -import { OperatorTasks } from './operator' import { OLMTasks } from './olm' +import { OperatorTasks } from './operator' /** * Tasks related to installation way. @@ -100,7 +100,7 @@ export class InstallerTasks { return operatorTasks.startTasks(flags, command) } // installer.ts BEGIN CHE ONLY - } else if (flags.installer == 'olm') { + } else if (flags.installer === 'olm') { title = '🏃‍ Running Olm installaion Eclipse Che' task = () => olmTasks.startTasks(flags, command) } else if (flags.installer === 'helm') { diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index ba1b1f176..cf4864e13 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -8,22 +8,23 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import Command from '@oclif/command'; -import Listr = require('listr'); -import { CheOLMChannel, DEFAULT_CHE_IMAGE, openshiftApplicationPreviewRegistryNamespace, kubernetesApplicationPreviewRegistryNamespace, defaultOpenshiftMarketPlaceNamespace, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace } from '../../constants'; +import Command from '@oclif/command' +import Listr = require('listr') +import { CatalogSource, Subscription } from 'olm' -import { KubeHelper } from '../../api/kube'; -import { createNamespaceTask, createEclipeCheCluster, copyOperatorResources, checkPreCreatedTls, checkTlsSertificate } from './common-tasks'; -import { Subscription, CatalogSource } from 'olm'; -import { isKubernetesPlatformFamily } from '../../util'; +import { KubeHelper } from '../../api/kube' +import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, kubernetesApplicationPreviewRegistryNamespace, openshiftApplicationPreviewRegistryNamespace } from '../../constants' +import { isKubernetesPlatformFamily } from '../../util' + +import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' export class OLMTasks { - private operatorSourceName = 'eclipse-che' - private subscriptionName = 'eclipse-che-subscription' - private operatorGroupName = 'che-operator-group' - private packageNamePrefix = 'eclipse-che-preview-' - private channel = this.getDefaultChannel() + private readonly operatorSourceName = 'eclipse-che' + private readonly subscriptionName = 'eclipse-che-subscription' + private readonly operatorGroupName = 'che-operator-group' + private readonly packageNamePrefix = 'eclipse-che-preview-' + private readonly channel = this.getDefaultChannel() /** * Returns list of tasks which perform preflight platform checks. @@ -77,7 +78,7 @@ export class OLMTasks { if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { - var subscription: Subscription + let subscription: Subscription if (this.channel === CheOLMChannel.STABLE) { subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { @@ -174,7 +175,7 @@ export class OLMTasks { } } } - command.error("Unable to find installation plan to update.") + command.error('Unable to find installation plan to update.') } }, { @@ -198,7 +199,7 @@ export class OLMTasks { const kube = new KubeHelper(flags) return [ { - title: "Check if OLM is pre-installed on the platform", + title: 'Check if OLM is pre-installed on the platform', task: async (ctx: any, task: any) => { ctx.isPreInstalledOLM = await kube.isPreInstalledOLM() ? true : false task.title = `${task.title}...OK` @@ -206,17 +207,17 @@ export class OLMTasks { }, { title: 'Delete(OLM) Eclipse Che cluster service versions', - enabled: (ctx) => ctx.isPreInstalledOLM, + enabled: ctx => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { const csvs = await kube.getClusterServiceVersions(flags.chenamespace) - const csvsToDelete = csvs.items.filter((csv) => csv.metadata.name.startsWith("eclipse-che")) - csvsToDelete.forEach((csv) => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) + const csvsToDelete = csvs.items.filter(csv => csv.metadata.name.startsWith('eclipse-che')) + csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) task.title = `${task.title}...OK` } }, { title: `Delete(OLM) operator subscription ${this.subscriptionName}`, - enabled: (ctx) => ctx.isPreInstalledOLM, + enabled: ctx => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) @@ -226,7 +227,7 @@ export class OLMTasks { }, { title: `Delete(OLM) operator group ${this.operatorGroupName}`, - enabled: (ctx) => ctx.isPreInstalledOLM, + enabled: ctx => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) @@ -248,7 +249,7 @@ export class OLMTasks { private isOlmPreInstalledTask(flags: any, command: Command, kube: KubeHelper): Listr.ListrTask { return { - title: "Check if OLM is pre-installed on the platform", + title: 'Check if OLM is pre-installed on the platform', task: async (ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") @@ -261,7 +262,7 @@ export class OLMTasks { private customCatalogTasks(flags: any, command: Command, kube: KubeHelper): Listr { return new Listr([ { - title: "Create operator source", + title: 'Create operator source', task: async (ctx: any, task: any) => { const applicationRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace : kubernetesApplicationPreviewRegistryNamespace @@ -275,7 +276,7 @@ export class OLMTasks { } }, { - title: "Create catalog source", + title: 'Create catalog source', task: async (ctx: any, task: any) => { if (!await kube.catalogSourceExists(this.operatorSourceName, ctx.defaultCatalogSourceNamespace)) { const catalogSourceInTheMarketPlaceNamespace = await kube.getCatalogSource(this.operatorSourceName, ctx.marketplaceNamespace) @@ -308,10 +309,10 @@ export class OLMTasks { private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string, startingCSV?: string): Subscription { return { - apiVersion: "operators.coreos.com/v1alpha1", + apiVersion: 'operators.coreos.com/v1alpha1', kind: 'Subscription', metadata: { - name: name, + name, namespace }, spec: { diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index ed58752c7..03f2dde4e 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -13,10 +13,12 @@ import { cli } from 'cli-ux' import * as fs from 'fs' import * as yaml from 'js-yaml' import * as Listr from 'listr' + import { KubeHelper } from '../../api/kube' -import { createEclipeCheCluster, copyOperatorResources, createNamespaceTask, checkPreCreatedTls, checkTlsSertificate } from './common-tasks' import { operatorCheCluster } from '../../constants' +import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' + export class OperatorTasks { operatorServiceAccount = 'che-operator' operatorRole = 'che-operator' @@ -96,7 +98,7 @@ export class OperatorTasks { }, { title: `Create ClusterRoleBinding ${this.operatorClusterRoleBinding}`, - task: async (ctx: any, task: any) => { + task: async (task: any) => { const exist = await kube.clusterRoleBindingExist(this.operatorRoleBinding) if (exist) { task.title = `${task.title}...It already exists.` From c583ce5672c74c6b61e863321753f4a278fad2b6 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 15 Apr 2020 16:21:34 +0300 Subject: [PATCH 22/40] Fix lint. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 2 +- src/api/typings/olm.d.ts | 227 ++++++++++++++++++------------------ src/tasks/installers/olm.ts | 27 +++-- 3 files changed, 126 insertions(+), 130 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 2fae4b652..472b32dcc 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -18,13 +18,13 @@ import https = require('https') import * as yaml from 'js-yaml' import { merge } from 'lodash' import * as net from 'net' -import { CatalogSource, ClusterServiceVersionList, InstallPlan, OperatorGroup, OperatorSource, PackageManifest, Subscription } from 'olm' import { Writable } from 'stream' import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' +import { CatalogSource, ClusterServiceVersionList, InstallPlan, OperatorGroup, OperatorSource, PackageManifest, Subscription } from './typings/olm' const AWAIT_TIMEOUT_S = 30 diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index 72d18b933..f664f155e 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -8,119 +8,116 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -declare module 'olm' { - - export interface OperatorSource { - apiVersion: string; - kind: string; - metadata: V1ObjectMeta; - spec: OperatorSourceSpec; - } - - export interface OperatorSourceSpec { - endpoint: string; - registryNamespace: string - type: string; - } - - export interface OperatorGroup { - apiVersion: string; - kind: string; - metadata: V1ObjectMeta; - - spec: OperatorGroupSpec; - } - - export interface OperatorGroupSpec { - targetNamespaces: string[]; - } - - export interface Subscription { - apiVersion: string; - kind: string; - metadata: V1ObjectMeta; - - spec: SubscriptionSpec - status?: SubscriptionStatus - } - - export interface SubscriptionSpec { - channel?: string - installPlanApproval?: string - name: string - source: string - sourceNamespace: string - startingCSV?: string - } - - export interface SubscriptionStatus { - conditions: SubscriptionStatusCondition[] - currentCSV: string - installplan: InstallPlan - state: string - } - - export interface SubscriptionStatusCondition { - message: string - reason: string - status: string - type: string - } - - export interface InstallPlan { - apiVersion?: string - kind?: string - name?: string - namespace?: string - spec?: InstallPlanSpec - status?: InstallPlanStatus - } - - export interface InstallPlanSpec { - approved?: boolean - } - - export interface InstallPlanStatus { - conditions: InstallPlanCondition[] - } - - export interface InstallPlanCondition { - type: string - status: string - } - - export interface ClusterServiceVersion { - kind: string; - metadata: V1ObjectMeta; - } - - export interface ClusterServiceVersionList { - apiVersion: string - kind: string - items: Array - } - - export interface CatalogSource { - apiVersion: string - kind: string - metadata: V1ObjectMeta - spec: CatalogSourceSpec - } - - export interface CatalogSourceSpec { - address: string - base64data: string - mediatype: string - sourceType: string - } - - export interface PackageManifest { - name: string - status?: PackageManifestStatus - } - - export interface PackageManifestStatus { - catalogSource: string - catalogSourceNamespace: string - } +export interface OperatorSource { + apiVersion: string; + kind: string; + metadata: V1ObjectMeta; + spec: OperatorSourceSpec; +} + +export interface OperatorSourceSpec { + endpoint: string; + registryNamespace: string + type: string; +} + +export interface OperatorGroup { + apiVersion: string; + kind: string; + metadata: V1ObjectMeta; + + spec: OperatorGroupSpec; +} + +export interface OperatorGroupSpec { + targetNamespaces: string[]; +} + +export interface Subscription { + apiVersion: string; + kind: string; + metadata: V1ObjectMeta; + + spec: SubscriptionSpec + status?: SubscriptionStatus +} + +export interface SubscriptionSpec { + channel?: string + installPlanApproval?: string + name: string + source: string + sourceNamespace: string + startingCSV?: string +} + +export interface SubscriptionStatus { + conditions: SubscriptionStatusCondition[] + currentCSV: string + installplan: InstallPlan + state: string +} + +export interface SubscriptionStatusCondition { + message: string + reason: string + status: string + type: string +} + +export interface InstallPlan { + apiVersion?: string + kind?: string + name?: string + namespace?: string + spec?: InstallPlanSpec + status?: InstallPlanStatus +} + +export interface InstallPlanSpec { + approved?: boolean +} + +export interface InstallPlanStatus { + conditions: InstallPlanCondition[] +} + +export interface InstallPlanCondition { + type: string + status: string +} + +export interface ClusterServiceVersion { + kind: string; + metadata: V1ObjectMeta; +} + +export interface ClusterServiceVersionList { + apiVersion: string + kind: string + items: Array +} + +export interface CatalogSource { + apiVersion: string + kind: string + metadata: V1ObjectMeta + spec: CatalogSourceSpec +} + +export interface CatalogSourceSpec { + address: string + base64data: string + mediatype: string + sourceType: string +} + +export interface PackageManifest { + name: string + status?: PackageManifestStatus +} + +export interface PackageManifestStatus { + catalogSource: string + catalogSourceNamespace: string } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index cf4864e13..4e4e11ac3 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -10,16 +10,15 @@ import Command from '@oclif/command' import Listr = require('listr') -import { CatalogSource, Subscription } from 'olm' import { KubeHelper } from '../../api/kube' +import { CatalogSource, Subscription } from '../../api/typings/olm' import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, kubernetesApplicationPreviewRegistryNamespace, openshiftApplicationPreviewRegistryNamespace } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' export class OLMTasks { - private readonly operatorSourceName = 'eclipse-che' private readonly subscriptionName = 'eclipse-che-subscription' private readonly operatorGroupName = 'che-operator-group' @@ -32,14 +31,14 @@ export class OLMTasks { startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ - this.isOlmPreInstalledTask(flags, command, kube), + this.isOlmPreInstalledTask(command, kube), copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), checkPreCreatedTls(flags, kube), checkTlsSertificate(flags), { title: 'Create operator group', - task: async (ctx: any, task: any) => { + task: async (task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { @@ -68,7 +67,7 @@ export class OLMTasks { title: 'Create custom catalog source for "nightly" channel', enabled: () => this.channel === CheOLMChannel.NIGHTLY, task: async (ctx: any, task: any) => { - await this.customCatalogTasks(flags, command, kube).run(ctx) + await this.customCatalogTasks(kube).run(ctx) task.title = `${task.title}...OK` } }, @@ -119,7 +118,7 @@ export class OLMTasks { preUpdateTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) return new Listr([ - this.isOlmPreInstalledTask(flags, command, kube), + this.isOlmPreInstalledTask(command, kube), { title: 'Check if operator source exists', enabled: () => this.channel === CheOLMChannel.NIGHTLY, @@ -133,7 +132,7 @@ export class OLMTasks { }, { title: 'Check if operator group exists', - task: async (ctx: any, task: any) => { + task: async (task: any) => { if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { command.error(`Unable to find operator group ${this.operatorGroupName}`) } @@ -142,7 +141,7 @@ export class OLMTasks { }, { title: 'Check if operator subscription exists', - task: async (ctx: any, task: any) => { + task: async (task: any) => { if (!await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { command.error(`Unable to find operator subscription ${this.subscriptionName}`) } @@ -208,7 +207,7 @@ export class OLMTasks { { title: 'Delete(OLM) Eclipse Che cluster service versions', enabled: ctx => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { + task: async (task: any) => { const csvs = await kube.getClusterServiceVersions(flags.chenamespace) const csvsToDelete = csvs.items.filter(csv => csv.metadata.name.startsWith('eclipse-che')) csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) @@ -218,7 +217,7 @@ export class OLMTasks { { title: `Delete(OLM) operator subscription ${this.subscriptionName}`, enabled: ctx => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { + task: async (task: any) => { if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) } @@ -228,7 +227,7 @@ export class OLMTasks { { title: `Delete(OLM) operator group ${this.operatorGroupName}`, enabled: ctx => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { + task: async (task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) } @@ -247,10 +246,10 @@ export class OLMTasks { return CheOLMChannel.STABLE } - private isOlmPreInstalledTask(flags: any, command: Command, kube: KubeHelper): Listr.ListrTask { + private isOlmPreInstalledTask(command: Command, kube: KubeHelper): Listr.ListrTask { return { title: 'Check if OLM is pre-installed on the platform', - task: async (ctx: any, task: any) => { + task: async (task: any) => { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") } @@ -259,7 +258,7 @@ export class OLMTasks { } } - private customCatalogTasks(flags: any, command: Command, kube: KubeHelper): Listr { + private customCatalogTasks(kube: KubeHelper): Listr { return new Listr([ { title: 'Create operator source', From ebb1442dcc5ca283a856484b896a3120d0c5b0d7 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 15 Apr 2020 16:49:22 +0300 Subject: [PATCH 23/40] Fix bad merge. Signed-off-by: Oleksandr Andriienko --- src/tasks/installers/olm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 4e4e11ac3..73547679b 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -111,7 +111,7 @@ export class OLMTasks { task.title = `${task.title}...OK` } }, - createEclipeCheCluster(flags) + createEclipeCheCluster(flags, kube) ], { renderer: flags['listr-renderer'] as any }) } From 448d0fc1951578f1622552117bc469a074a3d299 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 16 Apr 2020 12:59:06 +0300 Subject: [PATCH 24/40] Address requsted changes. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 2 +- src/tasks/installers/olm.ts | 38 +++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index fb3980203..7b9d5ecc8 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1445,7 +1445,7 @@ export class KubeHelper { } } - async waitWhileOperatorInstalled(installPlanName: string, namespace: string, timeout = 30) { + async waitUntilOperatorIsInstalled(installPlanName: string, namespace: string, timeout = 30) { return new Promise(async (resolve, reject) => { const watcher = new Watch(this.kc) let request: any diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 73547679b..fd1726cf8 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -43,7 +43,7 @@ export class OLMTasks { task.title = `${task.title}...It already exists.` } else { await kube.createOperatorGroup(this.operatorGroupName, flags.chenamespace) - task.title = `${task.title}...OK` + task.title = `${task.title}...created new one.` } } }, @@ -60,7 +60,7 @@ export class OLMTasks { ctx.approvalStarategy = flags['auto-update'] ? 'Automatic' : 'Manual' - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } }, { @@ -68,7 +68,7 @@ export class OLMTasks { enabled: () => this.channel === CheOLMChannel.NIGHTLY, task: async (ctx: any, task: any) => { await this.customCatalogTasks(kube).run(ctx) - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } }, { @@ -84,7 +84,7 @@ export class OLMTasks { subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName, ctx.approvalStarategy, flags['starting-csv']) } await kube.createOperatorSubscription(subscription) - task.title = `${task.title}...OK` + task.title = `${task.title}...created new one.` } } }, @@ -93,7 +93,7 @@ export class OLMTasks { task: async (ctx: any, task: any) => { const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, this.subscriptionName, 600) ctx.installPlanName = installPlan.name - task.title = `${task.title}...OK.` + task.title = `${task.title}...done.` } }, { @@ -101,14 +101,14 @@ export class OLMTasks { enabled: ctx => ctx.approvalStarategy === 'Manual', task: async (ctx: any, task: any) => { await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } }, { title: 'Wait while operator installed', task: async (ctx: any, task: any) => { - await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace) - task.title = `${task.title}...OK` + await kube.waitUntilOperatorIsInstalled(ctx.installPlanName, flags.chenamespace) + task.title = `${task.title}...done.` } }, createEclipeCheCluster(flags, kube) @@ -127,7 +127,7 @@ export class OLMTasks { if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { command.error(`Unable to find operator source ${this.operatorSourceName}`) } - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } }, { @@ -136,7 +136,7 @@ export class OLMTasks { if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { command.error(`Unable to find operator group ${this.operatorGroupName}`) } - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } }, { @@ -145,7 +145,7 @@ export class OLMTasks { if (!await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { command.error(`Unable to find operator subscription ${this.subscriptionName}`) } - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } }, ], { renderer: flags['listr-renderer'] as any }) @@ -169,7 +169,7 @@ export class OLMTasks { const installCondition = subscription.status.conditions.find(condition => condition.type === 'InstallPlanPending' && condition.status === 'True') if (installCondition) { ctx.installPlanName = subscription.status.installplan.name - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` return } } @@ -180,15 +180,17 @@ export class OLMTasks { { title: 'Approve installation', enabled: (ctx: any) => ctx.installPlanName, - task: async (ctx: any) => { + task: async (ctx: any, task: any) => { await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) + task.title = `${task.title}...done.` } }, { title: 'Wait while newer operator installed', enabled: (ctx: any) => ctx.installPlanName, - task: async (ctx: any) => { - await kube.waitWhileOperatorInstalled(ctx.installPlanName, flags.chenamespace, 60) + task: async (ctx: any, task: any) => { + await kube.waitUntilOperatorIsInstalled(ctx.installPlanName, flags.chenamespace, 60) + task.title = `${task.title}...done.` } }, ], { renderer: flags['listr-renderer'] as any }) @@ -253,7 +255,7 @@ export class OLMTasks { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") } - task.title = `${task.title}...OK` + task.title = `${task.title}...done.` } } } @@ -270,7 +272,7 @@ export class OLMTasks { } else { await kube.createOperatorSource(this.operatorSourceName, applicationRegistryNamespace, ctx.marketplaceNamespace) await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) - task.title = `${task.title}...OK` + task.title = `${task.title}...created new one.` } } }, @@ -297,7 +299,7 @@ export class OLMTasks { // Create catalog source in the olm namespace to make it working in the namespace differ than marketplace await kube.createCatalogSource(catalogSource) await kube.waitCatalogSource(ctx.defaultCatalogSourceNamespace, this.operatorSourceName) - task.title = `${task.title}...OK` + task.title = `${task.title}...created new one.` } else { task.title = `${task.title}...It already exists.` } From 9a89da4c2ee6348de2865a0939dbf58b98e0c9f7 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 16 Apr 2020 13:03:54 +0300 Subject: [PATCH 25/40] Fix up Signed-off-by: Oleksandr Andriienko --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6cb3b8637..8397f36a1 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ OPTIONS show CLI help -i, --cheimage=cheimage - [default: quay.io/eclipse/che-server:7.9.0] Eclipse Che server container image + [default: quay.io/eclipse/che-server:nightly] Eclipse Che server container image -m, --multiuser Starts Eclipse Che in multi-user mode @@ -294,11 +294,11 @@ OPTIONS [default: templates] Path to the templates folder --auto-update - Auto update approval strategy for installation Eclipse Che. - With this strategy will be provided auto-update Eclipse Che without any human interaction. + Auto update approval strategy for installation Eclipse Che. + With this strategy will be provided auto-update Eclipse Che without any human interaction. By default strategy this flag is false. It requires approval from user. To approve installation newer version Eclipse Che user should execute 'chectl server:update' - command. + command. This parameter is used only when the installer is 'olm'. --che-operator-cr-patch-yaml=che-operator-cr-patch-yaml From faed04067139685cdaf8c5b9996f4c034a7ec54f Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sat, 18 Apr 2020 20:03:05 +0300 Subject: [PATCH 26/40] Create custom catalogsource from yaml. Remove deprecated operator source. Signed-off-by: Oleksandr Andriienko --- README.md | 5 ++ src/api/kube.ts | 42 +++------------- src/api/typings/olm.d.ts | 13 ----- src/commands/server/start.ts | 9 ++++ src/constants.ts | 1 + src/tasks/installers/olm.ts | 96 +++++++++++------------------------- 6 files changed, 50 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index 72806ed3e..b5a5e337c 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,11 @@ OPTIONS command. This parameter is used only when the installer is 'olm'. + --catalog-source-yaml=catalog-source-yaml + Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. + Catalog source will be applied to the namespace with Che operator. + This parameter is used only when the installer is the 'olm'. + --che-operator-cr-patch-yaml=che-operator-cr-patch-yaml Path to a yaml file that overrides the default values in CheCluster CR used by the operator. This parameter is used only when the installer is the operator. diff --git a/src/api/kube.ts b/src/api/kube.ts index 7b9d5ecc8..e5ab698e9 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -20,11 +20,11 @@ import { merge } from 'lodash' import * as net from 'net' import { Writable } from 'stream' -import { DEFAULT_CHE_IMAGE, defaultApplicationRegistry } from '../constants' +import { DEFAULT_CHE_IMAGE } from '../constants' import { getClusterClientCommand } from '../util' import { V1alpha2Certificate } from './typings/cert-manager' -import { CatalogSource, ClusterServiceVersionList, InstallPlan, OperatorGroup, OperatorSource, PackageManifest, Subscription } from './typings/olm' +import { CatalogSource, ClusterServiceVersionList, InstallPlan, OperatorGroup, PackageManifest, Subscription } from './typings/olm' const AWAIT_TIMEOUT_S = 30 @@ -1233,40 +1233,6 @@ export class KubeHelper { } } - async createOperatorSource(name: string, registryNamespace: string, namespace: string) { - const operatorSource: OperatorSource = { - apiVersion: 'operators.coreos.com/v1', - kind: 'OperatorSource', - metadata: { - name, - namespace, - }, - spec: { - endpoint: defaultApplicationRegistry, - registryNamespace, - type: 'appregistry' - } - } - - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', operatorSource) - return body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async deleteOperatorSource(name: string, namespace: string) { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) - try { - const options = new V1DeleteOptions() - await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', name, options) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - async catalogSourceExists(name: string, namespace: string): Promise { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { @@ -1287,6 +1253,10 @@ export class KubeHelper { } } + readCatalogSourceFromFile(filePath: string): CatalogSource { + return this.safeLoadFromYamlFile(filePath) as CatalogSource + } + async createCatalogSource(catalogSource: CatalogSource) { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index f664f155e..886a8c403 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -8,19 +8,6 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -export interface OperatorSource { - apiVersion: string; - kind: string; - metadata: V1ObjectMeta; - spec: OperatorSourceSpec; -} - -export interface OperatorSourceSpec { - endpoint: string; - registryNamespace: string - type: string; -} - export interface OperatorGroup { apiVersion: string; kind: string; diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 881dea057..63afeeb25 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -166,6 +166,12 @@ export default class Start extends Command { Then OLM will install Eclipse Che with version 7.10.0. Notice: this flag will be ignored with 'auto-update' flag. OLM with auto-update mode installs the latest known version. This parameter is used only when the installer is 'olm'.` + }), + 'catalog-source-yaml': string({ + description: `Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. + Catalog source will be applied to the namespace with Che operator. + This parameter is used only when the installer is the 'olm'.`, + default: '' }) } @@ -271,6 +277,9 @@ export default class Start extends Command { if (flags.installer !== 'olm' && flags['starting-csv']) { this.error('"starting-csv" flag should be used only with "olm" installer.') } + if (flags.installer !== 'olm' && 'catalog-source-yaml') { + this.error('"catalog-source-yaml" flag should be used only with "olm" installer.') + } } } diff --git a/src/constants.ts b/src/constants.ts index 2c599a353..351553f6e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,6 +10,7 @@ export const DEFAULT_CHE_IMAGE = 'quay.io/eclipse/che-server:nightly' export const DEFAULT_CHE_OPERATOR_IMAGE = 'quay.io/eclipse/che-operator:nightly' +export const DEFAULT_CHE_OLM_PACKAGE_NAME = 'eclipse-che' // This image should be updated manually when needed. // Repository location: https://github.com/che-dockerfiles/che-cert-manager-ca-cert-generator-image diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index fd1726cf8..0c5cda520 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -13,13 +13,13 @@ import Listr = require('listr') import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/typings/olm' -import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, kubernetesApplicationPreviewRegistryNamespace, openshiftApplicationPreviewRegistryNamespace } from '../../constants' +import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, DEFAULT_CHE_OLM_PACKAGE_NAME } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' export class OLMTasks { - private readonly operatorSourceName = 'eclipse-che' + private readonly customCatalogSourceName = 'eclipse-che-custom-catalog-source' private readonly subscriptionName = 'eclipse-che-subscription' private readonly operatorGroupName = 'che-operator-group' private readonly packageNamePrefix = 'eclipse-che-preview-' @@ -60,15 +60,25 @@ export class OLMTasks { ctx.approvalStarategy = flags['auto-update'] ? 'Automatic' : 'Manual' + ctx.sourceName = this.customCatalogSourceName + task.title = `${task.title}...done.` } }, { - title: 'Create custom catalog source for "nightly" channel', - enabled: () => this.channel === CheOLMChannel.NIGHTLY, + enabled: () => flags['catalog-source-yaml'] !== '', + title: `Create custom catalog source from file`, task: async (ctx: any, task: any) => { - await this.customCatalogTasks(kube).run(ctx) - task.title = `${task.title}...done.` + if (!await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { + const customCatalogSource: CatalogSource = kube.readCatalogSourceFromFile(flags['catalog-source-yaml']) + customCatalogSource.metadata.name = ctx.sourceName + customCatalogSource.metadata.namespace = flags.chenamespace + await kube.createCatalogSource(customCatalogSource) + await kube.waitCatalogSource(flags.chenamespace, this.customCatalogSourceName) + task.title = `${task.title}...created new one, but with name ${this.customCatalogSourceName} in the namespace ${flags.chenamespace}.` + } else { + task.title = `${task.title}...It already exists.` + } } }, { @@ -79,9 +89,9 @@ export class OLMTasks { } else { let subscription: Subscription if (this.channel === CheOLMChannel.STABLE) { - subscription = this.createSubscription(this.subscriptionName, 'eclipse-che', flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) + subscription = this.createSubscription(this.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { - subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, ctx.defaultCatalogSourceNamespace, this.channel, this.operatorSourceName, ctx.approvalStarategy, flags['starting-csv']) + subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, flags.chenamespace, this.channel, ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) } await kube.createOperatorSubscription(subscription) task.title = `${task.title}...created new one.` @@ -119,17 +129,17 @@ export class OLMTasks { const kube = new KubeHelper(flags) return new Listr([ this.isOlmPreInstalledTask(command, kube), - { - title: 'Check if operator source exists', - enabled: () => this.channel === CheOLMChannel.NIGHTLY, - task: async (ctx: any, task: any) => { - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace - if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { - command.error(`Unable to find operator source ${this.operatorSourceName}`) - } - task.title = `${task.title}...done.` - } - }, + // { + // title: 'Check if operator catalog source exists', + // enabled: () => this.channel === CheOLMChannel.NIGHTLY, + // task: async (ctx: any, task: any) => { + // ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace + // if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { + // command.error(`Unable to find operator source ${this.operatorSourceName}`) + // } + // task.title = `${task.title}...done.` + // } + // }, { title: 'Check if operator group exists', task: async (task: any) => { @@ -260,54 +270,6 @@ export class OLMTasks { } } - private customCatalogTasks(kube: KubeHelper): Listr { - return new Listr([ - { - title: 'Create operator source', - task: async (ctx: any, task: any) => { - const applicationRegistryNamespace = ctx.isOpenShift ? openshiftApplicationPreviewRegistryNamespace - : kubernetesApplicationPreviewRegistryNamespace - if (await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { - task.title = `${task.title}...It already exists.` - } else { - await kube.createOperatorSource(this.operatorSourceName, applicationRegistryNamespace, ctx.marketplaceNamespace) - await kube.waitCatalogSource(ctx.marketplaceNamespace, this.operatorSourceName) - task.title = `${task.title}...created new one.` - } - } - }, - { - title: 'Create catalog source', - task: async (ctx: any, task: any) => { - if (!await kube.catalogSourceExists(this.operatorSourceName, ctx.defaultCatalogSourceNamespace)) { - const catalogSourceInTheMarketPlaceNamespace = await kube.getCatalogSource(this.operatorSourceName, ctx.marketplaceNamespace) - - const catalogSource: CatalogSource = { - apiVersion: 'operators.coreos.com/v1alpha1', - kind: 'CatalogSource', - metadata: { - name: this.operatorSourceName, - namespace: ctx.defaultCatalogSourceNamespace, - }, - spec: { - address: catalogSourceInTheMarketPlaceNamespace.spec.address, - base64data: '', - mediatype: '', - sourceType: 'grpc' - } - } - // Create catalog source in the olm namespace to make it working in the namespace differ than marketplace - await kube.createCatalogSource(catalogSource) - await kube.waitCatalogSource(ctx.defaultCatalogSourceNamespace, this.operatorSourceName) - task.title = `${task.title}...created new one.` - } else { - task.title = `${task.title}...It already exists.` - } - } - } - ]) - } - private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string, startingCSV?: string): Subscription { return { apiVersion: 'operators.coreos.com/v1alpha1', From 9e271634ad809792cb49452aa3ddf1fe2cd4fee0 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 12:55:13 +0300 Subject: [PATCH 27/40] Don't use nightly channel, we don't have it. Signed-off-by: Oleksandr Andriienko --- src/constants.ts | 6 +---- src/tasks/installers/olm.ts | 45 +++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 351553f6e..f662f85ba 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -11,6 +11,7 @@ export const DEFAULT_CHE_IMAGE = 'quay.io/eclipse/che-server:nightly' export const DEFAULT_CHE_OPERATOR_IMAGE = 'quay.io/eclipse/che-operator:nightly' export const DEFAULT_CHE_OLM_PACKAGE_NAME = 'eclipse-che' +export const OLM_STABLE_CHANNEL_NAME = 'stable' // This image should be updated manually when needed. // Repository location: https://github.com/che-dockerfiles/che-cert-manager-ca-cert-generator-image @@ -32,8 +33,3 @@ export const defaultKubernetesMarketPlaceNamespace = 'marketplace' export const defaultOLMKubernetesNamespace = 'olm' export const defaultApplicationRegistry = 'https://quay.io/cnr' - -export enum CheOLMChannel { - NIGHTLY = 'nightly', - STABLE = 'stable' -} diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 0c5cda520..32e19eab1 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -13,7 +13,7 @@ import Listr = require('listr') import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/typings/olm' -import { CheOLMChannel, DEFAULT_CHE_IMAGE, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, DEFAULT_CHE_OLM_PACKAGE_NAME } from '../../constants' +import { defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, DEFAULT_CHE_OLM_PACKAGE_NAME, OLM_STABLE_CHANNEL_NAME } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' @@ -23,7 +23,6 @@ export class OLMTasks { private readonly subscriptionName = 'eclipse-che-subscription' private readonly operatorGroupName = 'che-operator-group' private readonly packageNamePrefix = 'eclipse-che-preview-' - private readonly channel = this.getDefaultChannel() /** * Returns list of tasks which perform preflight platform checks. @@ -75,7 +74,7 @@ export class OLMTasks { customCatalogSource.metadata.namespace = flags.chenamespace await kube.createCatalogSource(customCatalogSource) await kube.waitCatalogSource(flags.chenamespace, this.customCatalogSourceName) - task.title = `${task.title}...created new one, but with name ${this.customCatalogSourceName} in the namespace ${flags.chenamespace}.` + task.title = `${task.title}...created new one, with name ${this.customCatalogSourceName} in the namespace ${flags.chenamespace}.` } else { task.title = `${task.title}...It already exists.` } @@ -88,10 +87,10 @@ export class OLMTasks { task.title = `${task.title}...It already exists.` } else { let subscription: Subscription - if (this.channel === CheOLMChannel.STABLE) { + if (flags['catalog-source-yaml'] === '') { subscription = this.createSubscription(this.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { - subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, flags.chenamespace, this.channel, ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) + subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, flags.chenamespace, OLM_STABLE_CHANNEL_NAME, ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) } await kube.createOperatorSubscription(subscription) task.title = `${task.title}...created new one.` @@ -129,17 +128,16 @@ export class OLMTasks { const kube = new KubeHelper(flags) return new Listr([ this.isOlmPreInstalledTask(command, kube), - // { - // title: 'Check if operator catalog source exists', - // enabled: () => this.channel === CheOLMChannel.NIGHTLY, - // task: async (ctx: any, task: any) => { - // ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace - // if (!await kube.operatorSourceExists(this.operatorSourceName, ctx.marketplaceNamespace)) { - // command.error(`Unable to find operator source ${this.operatorSourceName}`) - // } - // task.title = `${task.title}...done.` - // } - // }, + { + title: 'Check if operator catalog source exists', + enabled: () => flags['catalog-source-yaml'] !== '', + task: async (ctx: any, task: any) => { + if (!await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { + command.error(`Unable to find custom operator catalog source ${this.customCatalogSourceName} in the namespace ${flags.chenamespace}`) + } + task.title = `${task.title}...done.` + } + }, { title: 'Check if operator group exists', task: async (task: any) => { @@ -171,7 +169,7 @@ export class OLMTasks { if (subscription.status) { if (subscription.status.state === 'AtLatestKnown') { - task.title = `Everything is up to date. Installed the latest known version '${subscription.status.currentCSV}' from channel '${this.channel}'.` + task.title = `Everything is up to date. Installed the latest known version '${subscription.status.currentCSV}'.` return } @@ -250,13 +248,12 @@ export class OLMTasks { } // To update chectl stable channel we are patching src/constants.ts from nightly to release version. - // Let's use it to determine which olm channel should we use by default. - private getDefaultChannel(): CheOLMChannel { - if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { - return CheOLMChannel.NIGHTLY - } - return CheOLMChannel.STABLE - } + // private isNightlyChectlChannel(): boolean { + // if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { + // return true + // } + // return false + // } private isOlmPreInstalledTask(command: Command, kube: KubeHelper): Listr.ListrTask { return { From 628daf6e68d5b5be702e98303c7f81fcb1e53769 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 14:51:14 +0300 Subject: [PATCH 28/40] Delete custom catalogsource subscription on Che deletion. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 10 ++++++++++ src/commands/server/start.ts | 2 +- src/tasks/installers/olm.ts | 37 ++++++++++++++++++------------------ 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index e5ab698e9..428768954 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1290,6 +1290,16 @@ export class KubeHelper { }) } + async deleteCatalogSource(namespace: string, catalogSourceName: string): Promise { + const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + try { + const options = new V1DeleteOptions() + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', catalogSourceName, options) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + async operatorGroupExists(name: string, namespace: string): Promise { const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) try { diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 63afeeb25..e9440e83e 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -169,7 +169,7 @@ export default class Start extends Command { }), 'catalog-source-yaml': string({ description: `Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. - Catalog source will be applied to the namespace with Che operator. + Catalog source will be applied to the namespace with Che operator. This parameter is used only when the installer is the 'olm'.`, default: '' }) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 32e19eab1..835ab71aa 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -13,7 +13,7 @@ import Listr = require('listr') import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/typings/olm' -import { defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, DEFAULT_CHE_OLM_PACKAGE_NAME, OLM_STABLE_CHANNEL_NAME } from '../../constants' +import { DEFAULT_CHE_OLM_PACKAGE_NAME, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' @@ -37,7 +37,7 @@ export class OLMTasks { checkTlsSertificate(flags), { title: 'Create operator group', - task: async (task: any) => { + task: async (ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { @@ -66,7 +66,7 @@ export class OLMTasks { }, { enabled: () => flags['catalog-source-yaml'] !== '', - title: `Create custom catalog source from file`, + title: 'Create custom catalog source from file', task: async (ctx: any, task: any) => { if (!await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { const customCatalogSource: CatalogSource = kube.readCatalogSourceFromFile(flags['catalog-source-yaml']) @@ -140,7 +140,7 @@ export class OLMTasks { }, { title: 'Check if operator group exists', - task: async (task: any) => { + task: async (ctx: any, task: any) => { if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { command.error(`Unable to find operator group ${this.operatorGroupName}`) } @@ -149,7 +149,7 @@ export class OLMTasks { }, { title: 'Check if operator subscription exists', - task: async (task: any) => { + task: async (ctx: any, task: any) => { if (!await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { command.error(`Unable to find operator subscription ${this.subscriptionName}`) } @@ -211,13 +211,13 @@ export class OLMTasks { title: 'Check if OLM is pre-installed on the platform', task: async (ctx: any, task: any) => { ctx.isPreInstalledOLM = await kube.isPreInstalledOLM() ? true : false - task.title = `${task.title}...OK` + task.title = `${task.title}: ${ctx.isPreInstalledOLM}...OK` } }, { title: 'Delete(OLM) Eclipse Che cluster service versions', enabled: ctx => ctx.isPreInstalledOLM, - task: async (task: any) => { + task: async (ctx: any, task: any) => { const csvs = await kube.getClusterServiceVersions(flags.chenamespace) const csvsToDelete = csvs.items.filter(csv => csv.metadata.name.startsWith('eclipse-che')) csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) @@ -227,7 +227,7 @@ export class OLMTasks { { title: `Delete(OLM) operator subscription ${this.subscriptionName}`, enabled: ctx => ctx.isPreInstalledOLM, - task: async (task: any) => { + task: async (ctx: any, task: any) => { if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) } @@ -237,28 +237,29 @@ export class OLMTasks { { title: `Delete(OLM) operator group ${this.operatorGroupName}`, enabled: ctx => ctx.isPreInstalledOLM, - task: async (task: any) => { + task: async (ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) } task.title = `${task.title}...OK` } + }, + { + title: `Delete(OLM) custom catalog source ${this.customCatalogSourceName}`, + task: async (ctx: any, task: any) => { + if (await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { + await kube.deleteCatalogSource(flags.chenamespace, this.customCatalogSourceName) + } + task.title = `${task.title}...OK` + } } ] } - // To update chectl stable channel we are patching src/constants.ts from nightly to release version. - // private isNightlyChectlChannel(): boolean { - // if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { - // return true - // } - // return false - // } - private isOlmPreInstalledTask(command: Command, kube: KubeHelper): Listr.ListrTask { return { title: 'Check if OLM is pre-installed on the platform', - task: async (task: any) => { + task: async (ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") } From 9a551ec3ec11c264191caa2244e04e9f25894d6b Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 15:54:02 +0300 Subject: [PATCH 29/40] Add to more flags for custom operator source: olm-channel and package-manifest-name Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 11 +++++++++++ src/tasks/installers/olm.ts | 5 +---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index e9440e83e..529f75fb7 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -167,6 +167,17 @@ export default class Start extends Command { Notice: this flag will be ignored with 'auto-update' flag. OLM with auto-update mode installs the latest known version. This parameter is used only when the installer is 'olm'.` }), + 'olm-channel': string({ + description: `Olm channel to install Eclipse Che, f.e. stable. + If options was not set, will be used default version for package manifest. + This parameter is used only when the installer is the 'olm'.`, + dependsOn: ['catalog-source-yaml'] + }), + 'package-manifest-name': string({ + description: `Package manifest name to subscribe to Eclipse Che OLM package manifest. + This parameter is used only when the installer is the 'olm'.`, + dependsOn: ['catalog-source-yaml'] + }), 'catalog-source-yaml': string({ description: `Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. Catalog source will be applied to the namespace with Che operator. diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 835ab71aa..0acdd7120 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -22,7 +22,6 @@ export class OLMTasks { private readonly customCatalogSourceName = 'eclipse-che-custom-catalog-source' private readonly subscriptionName = 'eclipse-che-subscription' private readonly operatorGroupName = 'che-operator-group' - private readonly packageNamePrefix = 'eclipse-che-preview-' /** * Returns list of tasks which perform preflight platform checks. @@ -52,8 +51,6 @@ export class OLMTasks { ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace - // preview package name - ctx.packageName = this.packageNamePrefix + (ctx.isOpenShift ? 'openshift' : 'kubernetes') // catalog source name for stable Che version ctx.catalogSourceNameStable = isKubernetesPlatformFamily(flags.platform) ? 'operatorhubio-catalog' : 'community-operators' @@ -90,7 +87,7 @@ export class OLMTasks { if (flags['catalog-source-yaml'] === '') { subscription = this.createSubscription(this.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { - subscription = this.createSubscription(this.subscriptionName, ctx.packageName, flags.chenamespace, flags.chenamespace, OLM_STABLE_CHANNEL_NAME, ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) + subscription = this.createSubscription(this.subscriptionName, flags['package-manifest-name'], flags.chenamespace, flags.chenamespace, flags['olm-channel'], ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) } await kube.createOperatorSubscription(subscription) task.title = `${task.title}...created new one.` From 668953d29be99a5c8da223dab4e5108eafc46593 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 20:34:31 +0300 Subject: [PATCH 30/40] Add link with OLM install script. Signed-off-by: Oleksandr Andriienko --- src/tasks/installers/olm.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 0acdd7120..bd26b5f6f 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -212,22 +212,22 @@ export class OLMTasks { } }, { - title: 'Delete(OLM) Eclipse Che cluster service versions', + title: `Delete(OLM) operator subscription ${this.subscriptionName}`, enabled: ctx => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { - const csvs = await kube.getClusterServiceVersions(flags.chenamespace) - const csvsToDelete = csvs.items.filter(csv => csv.metadata.name.startsWith('eclipse-che')) - csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) + if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { + await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) + } task.title = `${task.title}...OK` } }, { - title: `Delete(OLM) operator subscription ${this.subscriptionName}`, + title: 'Delete(OLM) Eclipse Che cluster service versions', enabled: ctx => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { - if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { - await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) - } + const csvs = await kube.getClusterServiceVersions(flags.chenamespace) + const csvsToDelete = csvs.items.filter(csv => csv.metadata.name.startsWith('eclipse-che')) + csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) task.title = `${task.title}...OK` } }, @@ -258,7 +258,7 @@ export class OLMTasks { title: 'Check if OLM is pre-installed on the platform', task: async (ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { - command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually.") + command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually. You can use script https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/install.sh") } task.title = `${task.title}...done.` } From 82ba76b7a0274efb8b52122e7a989105c2c4e157 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 21:40:57 +0300 Subject: [PATCH 31/40] Fix tslint, remove bad changes, update README.md, remove unused code. Signed-off-by: Oleksandr Andriienko --- README.md | 11 ++++++++++- src/commands/server/start.ts | 4 ++-- src/constants.ts | 7 ------- src/tasks/installers/olm.ts | 20 ++++++++++---------- src/tasks/installers/operator.ts | 2 +- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index b5a5e337c..3bc4ada30 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ OPTIONS --catalog-source-yaml=catalog-source-yaml Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. - Catalog source will be applied to the namespace with Che operator. + Catalog source will be applied to the namespace with Che operator. This parameter is used only when the installer is the 'olm'. --che-operator-cr-patch-yaml=che-operator-cr-patch-yaml @@ -337,9 +337,18 @@ OPTIONS --listr-renderer=default|silent|verbose [default: default] Listr renderer + --olm-channel=olm-channel + Olm channel to install Eclipse Che, f.e. stable. + If options was not set, will be used default version for package manifest. + This parameter is used only when the installer is the 'olm'. + --os-oauth Enable use of OpenShift credentials to log into Eclipse Che + --package-manifest-name=package-manifest-name + Package manifest name to subscribe to Eclipse Che OLM package manifest. + This parameter is used only when the installer is the 'olm'. + --plugin-registry-url=plugin-registry-url The URL of the external plugin registry. diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 529f75fb7..38e39fc56 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -171,7 +171,7 @@ export default class Start extends Command { description: `Olm channel to install Eclipse Che, f.e. stable. If options was not set, will be used default version for package manifest. This parameter is used only when the installer is the 'olm'.`, - dependsOn: ['catalog-source-yaml'] + dependsOn: ['catalog-source-yaml'] }), 'package-manifest-name': string({ description: `Package manifest name to subscribe to Eclipse Che OLM package manifest. @@ -225,7 +225,7 @@ export default class Start extends Command { if (flags.installer === '') { flags.installer = 'helm' } - } else if (flags.platform === 'crc') { + } else if (flags.platform === 'operator') { if (flags.installer === '') { flags.installer = 'olm' } diff --git a/src/constants.ts b/src/constants.ts index f662f85ba..85a0e2fe4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -23,13 +23,6 @@ export const CHE_TLS_SECRET_NAME = 'che-tls' export const operatorCheCluster = 'eclipse-che' export const CHE_CLUSTER_CR_NAME = 'eclipse-che' -export const openshiftApplicationPreviewRegistryNamespace = 'eclipse-che-operator-openshift' -export const kubernetesApplicationPreviewRegistryNamespace = 'eclipse-che-operator-kubernetes' -// application registry namespace from operator-hub.io -export const applicationRegistryNamespace = 'eclipse-che' - export const defaultOpenshiftMarketPlaceNamespace = 'openshift-marketplace' export const defaultKubernetesMarketPlaceNamespace = 'marketplace' export const defaultOLMKubernetesNamespace = 'olm' - -export const defaultApplicationRegistry = 'https://quay.io/cnr' diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index bd26b5f6f..8e20a5e5c 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -36,7 +36,7 @@ export class OLMTasks { checkTlsSertificate(flags), { title: 'Create operator group', - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { task.title = `${task.title}...It already exists.` } else { @@ -85,7 +85,7 @@ export class OLMTasks { } else { let subscription: Subscription if (flags['catalog-source-yaml'] === '') { - subscription = this.createSubscription(this.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, flags.chenamespace, ctx.defaultCatalogSourceNamespace, 'stable', ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) + subscription = this.createSubscription(this.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, flags.chenamespace, ctx.defaultCatalogSourceNamespace, OLM_STABLE_CHANNEL_NAME, ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { subscription = this.createSubscription(this.subscriptionName, flags['package-manifest-name'], flags.chenamespace, flags.chenamespace, flags['olm-channel'], ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) } @@ -128,7 +128,7 @@ export class OLMTasks { { title: 'Check if operator catalog source exists', enabled: () => flags['catalog-source-yaml'] !== '', - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (!await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { command.error(`Unable to find custom operator catalog source ${this.customCatalogSourceName} in the namespace ${flags.chenamespace}`) } @@ -137,7 +137,7 @@ export class OLMTasks { }, { title: 'Check if operator group exists', - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (!await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { command.error(`Unable to find operator group ${this.operatorGroupName}`) } @@ -146,7 +146,7 @@ export class OLMTasks { }, { title: 'Check if operator subscription exists', - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (!await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { command.error(`Unable to find operator subscription ${this.subscriptionName}`) } @@ -214,7 +214,7 @@ export class OLMTasks { { title: `Delete(OLM) operator subscription ${this.subscriptionName}`, enabled: ctx => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (await kube.operatorSubscriptionExists(this.subscriptionName, flags.chenamespace)) { await kube.deleteOperatorSubscription(this.subscriptionName, flags.chenamespace) } @@ -224,7 +224,7 @@ export class OLMTasks { { title: 'Delete(OLM) Eclipse Che cluster service versions', enabled: ctx => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { const csvs = await kube.getClusterServiceVersions(flags.chenamespace) const csvsToDelete = csvs.items.filter(csv => csv.metadata.name.startsWith('eclipse-che')) csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name)) @@ -234,7 +234,7 @@ export class OLMTasks { { title: `Delete(OLM) operator group ${this.operatorGroupName}`, enabled: ctx => ctx.isPreInstalledOLM, - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (await kube.operatorGroupExists(this.operatorGroupName, flags.chenamespace)) { await kube.deleteOperatorGroup(this.operatorGroupName, flags.chenamespace) } @@ -243,7 +243,7 @@ export class OLMTasks { }, { title: `Delete(OLM) custom catalog source ${this.customCatalogSourceName}`, - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { await kube.deleteCatalogSource(flags.chenamespace, this.customCatalogSourceName) } @@ -256,7 +256,7 @@ export class OLMTasks { private isOlmPreInstalledTask(command: Command, kube: KubeHelper): Listr.ListrTask { return { title: 'Check if OLM is pre-installed on the platform', - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually. You can use script https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/install.sh") } diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 5b1efe23c..6b45ecfca 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -98,7 +98,7 @@ export class OperatorTasks { }, { title: `Create ClusterRoleBinding ${this.operatorClusterRoleBinding}`, - task: async (task: any) => { + task: async (_ctx: any, task: any) => { const exist = await kube.clusterRoleBindingExist(this.operatorRoleBinding) if (exist) { task.title = `${task.title}...It already exists.` From 3d3eeaaf34abf338ee40d1b2999fb08e4e035f14 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 21:52:26 +0300 Subject: [PATCH 32/40] Fix up. Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 38e39fc56..9d930e7a3 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -225,9 +225,9 @@ export default class Start extends Command { if (flags.installer === '') { flags.installer = 'helm' } - } else if (flags.platform === 'operator') { + } else if (flags.platform === 'crc') { if (flags.installer === '') { - flags.installer = 'olm' + flags.installer = 'operator' } } From c11fee4cb4c047c75ae4a653fc74001a6614f5e8 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2020 22:02:47 +0300 Subject: [PATCH 33/40] Add more checks Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 9d930e7a3..4aab1259b 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -291,6 +291,12 @@ export default class Start extends Command { if (flags.installer !== 'olm' && 'catalog-source-yaml') { this.error('"catalog-source-yaml" flag should be used only with "olm" installer.') } + if (flags.installer !== 'olm' && 'olm-channel') { + this.error('"olm-channel" flag should be used only with "olm" installer.') + } + if (flags.installer !== 'olm' && 'package-manifest-name') { + this.error('"package-manifest-name" flag should be used only with "olm" installer.') + } } } From 2f9c87337983e2073ef8128ac1cf3a278ca2d2ed Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 21 Apr 2020 09:33:19 +0300 Subject: [PATCH 34/40] Clean up unused const Signed-off-by: Oleksandr Andriienko --- src/constants.ts | 1 - src/tasks/installers/olm.ts | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 85a0e2fe4..d2d3ddf9e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -24,5 +24,4 @@ export const operatorCheCluster = 'eclipse-che' export const CHE_CLUSTER_CR_NAME = 'eclipse-che' export const defaultOpenshiftMarketPlaceNamespace = 'openshift-marketplace' -export const defaultKubernetesMarketPlaceNamespace = 'marketplace' export const defaultOLMKubernetesNamespace = 'olm' diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 8e20a5e5c..583003a98 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -13,7 +13,7 @@ import Listr = require('listr') import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/typings/olm' -import { DEFAULT_CHE_OLM_PACKAGE_NAME, defaultKubernetesMarketPlaceNamespace, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME } from '../../constants' +import { DEFAULT_CHE_OLM_PACKAGE_NAME, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' @@ -48,7 +48,6 @@ export class OLMTasks { { title: 'Configure context information', task: async (ctx: any, task: any) => { - ctx.marketplaceNamespace = ctx.isOpenShift ? defaultOpenshiftMarketPlaceNamespace : defaultKubernetesMarketPlaceNamespace // Todo: should we do check for installer openshift? flags.platform === 'crc' || flags.platform === 'openshift' ctx.defaultCatalogSourceNamespace = flags.platform === 'crc' ? defaultOpenshiftMarketPlaceNamespace : defaultOLMKubernetesNamespace // catalog source name for stable Che version From fa6a7a2c47ae2d897f9118663202fedafe4bb0ec Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 21 Apr 2020 16:18:32 +0300 Subject: [PATCH 35/40] Fix installation stable Che image for nightly chectl, add useful warnings for user. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 4 ++++ src/commands/server/start.ts | 3 ++- src/tasks/installers/helm.ts | 1 + src/tasks/installers/olm.ts | 28 +++++++++++++++------------- src/tasks/installers/operator.ts | 1 + 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 428768954..7e2ebd5a2 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1137,6 +1137,10 @@ export class KubeHelper { const imageAndTag = cheImage.split(':', 2) yamlCr.spec.server.cheImage = imageAndTag[0] yamlCr.spec.server.cheImageTag = imageAndTag.length === 2 ? imageAndTag[1] : 'latest' + if (flags.installer === 'olm') { + // use default image tag for `olm` to install stable Che, because we don't have nightly channel for OLM catalog. + yamlCr.spec.server.cheImageTag = '' + } yamlCr.spec.server.cheDebug = flags.debug ? flags.debug.toString() : 'false' yamlCr.spec.auth.openShiftoAuth = flags['os-oauth'] diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 4aab1259b..6c503d59e 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -157,7 +157,8 @@ export default class Start extends Command { By default strategy this flag is false. It requires approval from user. To approve installation newer version Eclipse Che user should execute 'chectl server:update' command. This parameter is used only when the installer is 'olm'.`, - default: false + default: false, + exclusive: ['starting-csv'] }), 'starting-csv': flags.string({ description: `Starting cluster service version(CSV) for installation Eclipse Che. diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index 37fa497fc..8adbe9152 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -33,6 +33,7 @@ export class HelmTasks { * Returns list of tasks which perform preflight platform checks. */ startTasks(flags: any, command: Command): Listr { + command.warn(`You can also use features rich 'OLM' installer to deploy Eclipse Che.`) return new Listr([ { title: 'Verify if helm is installed', diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 583003a98..9a95df681 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -13,10 +13,10 @@ import Listr = require('listr') import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/typings/olm' -import { DEFAULT_CHE_OLM_PACKAGE_NAME, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME } from '../../constants' +import { DEFAULT_CHE_OLM_PACKAGE_NAME, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME, DEFAULT_CHE_IMAGE } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' - import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' +import { cli } from 'cli-ux' export class OLMTasks { private readonly customCatalogSourceName = 'eclipse-che-custom-catalog-source' @@ -28,6 +28,9 @@ export class OLMTasks { */ startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) + if (this.isNightlyChectlChannel() && flags['catalog-source-yaml'] === '') { + command.warn(`> OLM catalog hasn't got nightly channel, that's why will be deployed stable Eclipse Che. <`) + } return new Listr([ this.isOlmPreInstalledTask(command, kube), copyOperatorResources(flags, command.config.cacheDir), @@ -124,16 +127,6 @@ export class OLMTasks { const kube = new KubeHelper(flags) return new Listr([ this.isOlmPreInstalledTask(command, kube), - { - title: 'Check if operator catalog source exists', - enabled: () => flags['catalog-source-yaml'] !== '', - task: async (_ctx: any, task: any) => { - if (!await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { - command.error(`Unable to find custom operator catalog source ${this.customCatalogSourceName} in the namespace ${flags.chenamespace}`) - } - task.title = `${task.title}...done.` - } - }, { title: 'Check if operator group exists', task: async (_ctx: any, task: any) => { @@ -257,13 +250,22 @@ export class OLMTasks { title: 'Check if OLM is pre-installed on the platform', task: async (_ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { - command.error("OLM isn't installed on your platfrom. If your platform hasn't got embedded OML, you need install it manually. You can use script https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/install.sh") + cli.warn(`Looks like your platform hasn't got embedded OLM, so you should install it manually. For quick start you can use: `, ); + cli.url(`install.sh`, 'https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/install.sh') + command.error(`OLM is required for installation Eclipse Che with installer flag 'olm'`) } task.title = `${task.title}...done.` } } } + private isNightlyChectlChannel(): boolean { + if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { + return true + } + return false + } + private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string, startingCSV?: string): Subscription { return { apiVersion: 'operators.coreos.com/v1alpha1', diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 6b45ecfca..42d03147f 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -33,6 +33,7 @@ export class OperatorTasks { */ startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) + command.warn(`You can also use features rich 'OLM' installer to deploy Eclipse Che.`) return new Listr([ copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), From 320161c8b6e96c5a83daa874752bc1cc7c8559d3 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 21 Apr 2020 22:42:46 +0300 Subject: [PATCH 36/40] Fix compilation after resolve merge conflict. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index b8ec25228..0c2ab6d06 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1219,7 +1219,7 @@ export class KubeHelper { } async isPreInstalledOLM(): Promise { - const apiApi = this.kc.makeApiClient(ApisApi) + const apiApi = KubeHelper.KUBE_CONFIG.makeApiClient(ApisApi) try { const { body } = await apiApi.getAPIVersions() const OLMAPIGroup = body.groups.find(apiGroup => apiGroup.name === 'operators.coreos.com') @@ -1230,7 +1230,7 @@ export class KubeHelper { } async operatorSourceExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', name) return this.compare(body, name) @@ -1240,7 +1240,7 @@ export class KubeHelper { } async catalogSourceExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) return this.compare(body, name) @@ -1250,7 +1250,7 @@ export class KubeHelper { } async getCatalogSource(name: string, namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) return body @@ -1264,7 +1264,7 @@ export class KubeHelper { } async createCatalogSource(catalogSource: CatalogSource) { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const namespace = catalogSource.metadata.namespace const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', catalogSource) @@ -1276,7 +1276,7 @@ export class KubeHelper { async waitCatalogSource(namespace: string, catalogSourceName: string, timeout = 60): Promise { return new Promise(async (resolve, reject) => { - const watcher = new Watch(this.kc) + const watcher = new Watch(KubeHelper.KUBE_CONFIG) let request: any request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/catalogsources`, { fieldSelector: `metadata.name=${catalogSourceName}` }, @@ -1297,7 +1297,7 @@ export class KubeHelper { } async deleteCatalogSource(namespace: string, catalogSourceName: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const options = new V1DeleteOptions() await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', catalogSourceName, options) @@ -1307,7 +1307,7 @@ export class KubeHelper { } async operatorGroupExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', name) return this.compare(body, name) @@ -1329,7 +1329,7 @@ export class KubeHelper { } } - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', operatorGroup) return body @@ -1339,7 +1339,7 @@ export class KubeHelper { } async deleteOperatorGroup(operatorGroupName: string, namespace: string) { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const options = new V1DeleteOptions() await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', operatorGroupName, options) @@ -1349,7 +1349,7 @@ export class KubeHelper { } async createOperatorSubscription(subscription: Subscription) { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', subscription.metadata.namespace, 'subscriptions', subscription) return body @@ -1359,7 +1359,7 @@ export class KubeHelper { } async getOperatorSubscription(name: string, namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', name) return body as Subscription @@ -1369,7 +1369,7 @@ export class KubeHelper { } async operatorSubscriptionExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', name) return this.compare(body, name) @@ -1379,7 +1379,7 @@ export class KubeHelper { } async deleteOperatorSubscription(operatorSubscriptionName: string, namespace: string) { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const options = new V1DeleteOptions() await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', operatorSubscriptionName, options) @@ -1390,7 +1390,7 @@ export class KubeHelper { async waitOperatorSubscriptionReadyForApproval(namespace: string, subscriptionName: string, timeout = AWAIT_TIMEOUT_S): Promise { return new Promise(async (resolve, reject) => { - const watcher = new Watch(this.kc) + const watcher = new Watch(KubeHelper.KUBE_CONFIG) let request: any request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/subscriptions`, { fieldSelector: `metadata.name=${subscriptionName}` }, @@ -1418,7 +1418,7 @@ export class KubeHelper { } async approveOperatorInstallationPlan(name = '', namespace = '') { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const patch: InstallPlan = { spec: { @@ -1433,7 +1433,7 @@ export class KubeHelper { async waitUntilOperatorIsInstalled(installPlanName: string, namespace: string, timeout = 30) { return new Promise(async (resolve, reject) => { - const watcher = new Watch(this.kc) + const watcher = new Watch(KubeHelper.KUBE_CONFIG) let request: any request = watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/installplans`, { fieldSelector: `metadata.name=${installPlanName}` }, @@ -1461,7 +1461,7 @@ export class KubeHelper { } async getClusterServiceVersions(namespace: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.listNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions') return body as ClusterServiceVersionList @@ -1471,7 +1471,7 @@ export class KubeHelper { } async deleteClusterServiceVersion(namespace: string, csvName: string) { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const options = new V1DeleteOptions() const { body } = await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', csvName, options) @@ -1482,7 +1482,7 @@ export class KubeHelper { } async getPackageManifect(name: string): Promise { - const customObjectsApi = this.kc.makeApiClient(CustomObjectsApi) + const customObjectsApi = KubeHelper.KUBE_CONFIG.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.getNamespacedCustomObject('packages.operators.coreos.com', 'v1', 'default', 'packagemanifests', name) return body as PackageManifest From 2c06ec268e2fbd17a73c18c8883cff9b0929eca4 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 21 Apr 2020 23:08:32 +0300 Subject: [PATCH 37/40] Fix lints. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 2 +- src/tasks/installers/helm.ts | 2 +- src/tasks/installers/olm.ts | 21 +++++++++++---------- src/tasks/installers/operator.ts | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 0c2ab6d06..e60e9565a 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1140,7 +1140,7 @@ export class KubeHelper { yamlCr.spec.server.cheImage = imageAndTag[0] yamlCr.spec.server.cheImageTag = imageAndTag.length === 2 ? imageAndTag[1] : 'latest' if (flags.installer === 'olm') { - // use default image tag for `olm` to install stable Che, because we don't have nightly channel for OLM catalog. + // use default image tag for `olm` to install stable Che, because we don't have nightly channel for OLM catalog. yamlCr.spec.server.cheImageTag = '' } yamlCr.spec.server.cheDebug = flags.debug ? flags.debug.toString() : 'false' diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index 8adbe9152..8d9524c52 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -33,7 +33,7 @@ export class HelmTasks { * Returns list of tasks which perform preflight platform checks. */ startTasks(flags: any, command: Command): Listr { - command.warn(`You can also use features rich 'OLM' installer to deploy Eclipse Che.`) + command.warn('You can also use features rich \'OLM\' installer to deploy Eclipse Che.') return new Listr([ { title: 'Verify if helm is installed', diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 9a95df681..633fd16d7 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -9,14 +9,15 @@ **********************************************************************/ import Command from '@oclif/command' +import { cli } from 'cli-ux' import Listr = require('listr') import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/typings/olm' -import { DEFAULT_CHE_OLM_PACKAGE_NAME, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME, DEFAULT_CHE_IMAGE } from '../../constants' +import { DEFAULT_CHE_IMAGE, DEFAULT_CHE_OLM_PACKAGE_NAME, defaultOLMKubernetesNamespace, defaultOpenshiftMarketPlaceNamespace, OLM_STABLE_CHANNEL_NAME } from '../../constants' import { isKubernetesPlatformFamily } from '../../util' + import { checkPreCreatedTls, checkTlsSertificate, copyOperatorResources, createEclipeCheCluster, createNamespaceTask } from './common-tasks' -import { cli } from 'cli-ux' export class OLMTasks { private readonly customCatalogSourceName = 'eclipse-che-custom-catalog-source' @@ -29,7 +30,7 @@ export class OLMTasks { startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) if (this.isNightlyChectlChannel() && flags['catalog-source-yaml'] === '') { - command.warn(`> OLM catalog hasn't got nightly channel, that's why will be deployed stable Eclipse Che. <`) + command.warn('OLM catalog hasn\'t got nightly channel, that\'s why will be deployed stable Eclipse Che.') } return new Listr([ this.isOlmPreInstalledTask(command, kube), @@ -250,9 +251,9 @@ export class OLMTasks { title: 'Check if OLM is pre-installed on the platform', task: async (_ctx: any, task: any) => { if (!await kube.isPreInstalledOLM()) { - cli.warn(`Looks like your platform hasn't got embedded OLM, so you should install it manually. For quick start you can use: `, ); - cli.url(`install.sh`, 'https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/install.sh') - command.error(`OLM is required for installation Eclipse Che with installer flag 'olm'`) + cli.warn('Looks like your platform hasn\'t got embedded OLM, so you should install it manually. For quick start you can use:') + cli.url('install.sh', 'https://raw.githubusercontent.com/operator-framework/operator-lifecycle-manager/master/deploy/upstream/quickstart/install.sh') + command.error('OLM is required for installation Eclipse Che with installer flag \'olm\'') } task.title = `${task.title}...done.` } @@ -260,10 +261,10 @@ export class OLMTasks { } private isNightlyChectlChannel(): boolean { - if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { - return true - } - return false + if (DEFAULT_CHE_IMAGE.endsWith(':nightly')) { + return true + } + return false } private createSubscription(name: string, packageName: string, namespace: string, sourceNamespace: string, channel: string, sourceName: string, installPlanApproval: string, startingCSV?: string): Subscription { diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 42d03147f..ac7804d45 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -33,7 +33,7 @@ export class OperatorTasks { */ startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) - command.warn(`You can also use features rich 'OLM' installer to deploy Eclipse Che.`) + command.warn('You can also use features rich \'OLM\' installer to deploy Eclipse Che.') return new Listr([ copyOperatorResources(flags, command.config.cacheDir), createNamespaceTask(flags), From 87a4e8e3ea50b045c7ab4b034c12f8b44a6533ec Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 22 Apr 2020 12:04:33 +0300 Subject: [PATCH 38/40] Fix up. Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 5495afb10..114e207cd 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -295,13 +295,13 @@ export default class Start extends Command { if (flags.installer !== 'olm' && flags['starting-csv']) { this.error('"starting-csv" flag should be used only with "olm" installer.') } - if (flags.installer !== 'olm' && 'catalog-source-yaml') { + if (flags.installer !== 'olm' && flags['catalog-source-yaml']) { this.error('"catalog-source-yaml" flag should be used only with "olm" installer.') } - if (flags.installer !== 'olm' && 'olm-channel') { + if (flags.installer !== 'olm' && flags['olm-channel']) { this.error('"olm-channel" flag should be used only with "olm" installer.') } - if (flags.installer !== 'olm' && 'package-manifest-name') { + if (flags.installer !== 'olm' && flags['package-manifest-name']) { this.error('"package-manifest-name" flag should be used only with "olm" installer.') } } From bbdf48c456e664e6b69c432cca09de1ab83e9546 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 22 Apr 2020 22:27:43 +0300 Subject: [PATCH 39/40] Fix custom catalog source with nightly channel. Signed-off-by: Oleksandr Andriienko --- src/api/kube.ts | 2 +- src/commands/server/start.ts | 10 +++++++++- src/tasks/installers/olm.ts | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index e60e9565a..a3472fb49 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1139,7 +1139,7 @@ export class KubeHelper { const imageAndTag = cheImage.split(':', 2) yamlCr.spec.server.cheImage = imageAndTag[0] yamlCr.spec.server.cheImageTag = imageAndTag.length === 2 ? imageAndTag[1] : 'latest' - if (flags.installer === 'olm') { + if ((flags.installer === 'olm' && !flags['catalog-source-yaml']) || (flags['catalog-source-yaml'] && flags['olm-channel'] === 'stable')) { // use default image tag for `olm` to install stable Che, because we don't have nightly channel for OLM catalog. yamlCr.spec.server.cheImageTag = '' } diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 114e207cd..0a44c1a86 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -182,7 +182,6 @@ export default class Start extends Command { description: `Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. Catalog source will be applied to the namespace with Che operator. This parameter is used only when the installer is the 'olm'.`, - default: '' }) } @@ -286,9 +285,11 @@ export default class Start extends Command { this.error(`You requested to enable OpenShift OAuth but that's only possible when using the operator as installer. The current installer is ${flags.installer}. To use the operator add parameter "--installer operator".`) } } + if (flags.installer === 'olm' && flags.platform === 'minishift') { this.error(`🛑 The specified installer ${flags.installer} does not support Minishift`) } + if (flags.installer !== 'olm' && flags['auto-update']) { this.error('"auto-update" flag should be used only with "olm" installer.') } @@ -304,6 +305,13 @@ export default class Start extends Command { if (flags.installer !== 'olm' && flags['package-manifest-name']) { this.error('"package-manifest-name" flag should be used only with "olm" installer.') } + + if (!flags['package-manifest-name'] && flags['catalog-source-yaml']) { + this.error('you need define "package-manifest-name" flag to use "catalog-source-yaml".') + } + if (!flags['olm-channel'] && flags['catalog-source-yaml']) { + this.error('you need define "olm-channel" flag to use "catalog-source-yaml".') + } } } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 633fd16d7..d2d367501 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -29,7 +29,7 @@ export class OLMTasks { */ startTasks(flags: any, command: Command): Listr { const kube = new KubeHelper(flags) - if (this.isNightlyChectlChannel() && flags['catalog-source-yaml'] === '') { + if (this.isNightlyChectlChannel() && !flags['catalog-source-yaml']) { command.warn('OLM catalog hasn\'t got nightly channel, that\'s why will be deployed stable Eclipse Che.') } return new Listr([ @@ -65,7 +65,7 @@ export class OLMTasks { } }, { - enabled: () => flags['catalog-source-yaml'] !== '', + enabled: () => flags['catalog-source-yaml'], title: 'Create custom catalog source from file', task: async (ctx: any, task: any) => { if (!await kube.catalogSourceExists(this.customCatalogSourceName, flags.chenamespace)) { @@ -87,7 +87,7 @@ export class OLMTasks { task.title = `${task.title}...It already exists.` } else { let subscription: Subscription - if (flags['catalog-source-yaml'] === '') { + if (!flags['catalog-source-yaml']) { subscription = this.createSubscription(this.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, flags.chenamespace, ctx.defaultCatalogSourceNamespace, OLM_STABLE_CHANNEL_NAME, ctx.catalogSourceNameStable, ctx.approvalStarategy, flags['starting-csv']) } else { subscription = this.createSubscription(this.subscriptionName, flags['package-manifest-name'], flags.chenamespace, flags.chenamespace, flags['olm-channel'], ctx.sourceName, ctx.approvalStarategy, flags['starting-csv']) From 5609ff921376436f2ac1062b176d68795285fa38 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 22 Apr 2020 22:39:36 +0300 Subject: [PATCH 40/40] Fix lint again. Signed-off-by: Oleksandr Andriienko --- src/commands/server/start.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 0a44c1a86..05721ff31 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -181,6 +181,7 @@ export default class Start extends Command { 'catalog-source-yaml': string({ description: `Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. Catalog source will be applied to the namespace with Che operator. + Also you need define 'olm-channel' name and 'package-manifest-name'. This parameter is used only when the installer is the 'olm'.`, }) } @@ -306,10 +307,10 @@ export default class Start extends Command { this.error('"package-manifest-name" flag should be used only with "olm" installer.') } - if (!flags['package-manifest-name'] && flags['catalog-source-yaml']) { + if (!flags['package-manifest-name'] && flags['catalog-source-yaml']) { this.error('you need define "package-manifest-name" flag to use "catalog-source-yaml".') } - if (!flags['olm-channel'] && flags['catalog-source-yaml']) { + if (!flags['olm-channel'] && flags['catalog-source-yaml']) { this.error('you need define "olm-channel" flag to use "catalog-source-yaml".') } }