Skip to content

Commit

Permalink
feat: Install next dev-workspace operator for all-namespaces next cha…
Browse files Browse the repository at this point in the history
…nnel (#1913)

* feat: Use custom catalogs to deploy Dev Workspace operator

Signed-off-by: Oleksandr Andriienko <[email protected]>
Signed-off-by: Anatolii Bazko <[email protected]>
Co-authored-by: Anatolii Bazko <[email protected]>
  • Loading branch information
AndrienkoAleksandr and tolusha authored Jan 13, 2022
1 parent 06d46ca commit dbe5408
Show file tree
Hide file tree
Showing 13 changed files with 569 additions and 163 deletions.
6 changes: 6 additions & 0 deletions src/api/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ export namespace OLM {
export const PACKAGE_MANIFEST_NAME = 'package-manifest-name'
}

export namespace DevWorkspaceContextKeys {
export const IS_DEV_WORKSPACE_INSTALLED_VIA_OPERATOR_HUB = 'is-dev-workspace-installed-via-operator-hub'
export const CATALOG_SOURCE_NAME = 'dev-workspace-catalog-source-name'
export const INSTALL_PLAN = 'dev-workspace-install-plan'
}

export enum OLMInstallationUpdate {
MANUAL = 'Manual',
AUTO = 'Automatic'
Expand Down
101 changes: 93 additions & 8 deletions src/api/kube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import * as https from 'https'
import { merge } from 'lodash'
import * as net from 'net'
import { Writable } from 'stream'
import { CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_BACKUP_KIND_PLURAL, CHE_CLUSTER_KIND_PLURAL, CHE_CLUSTER_RESTORE_KIND_PLURAL, DEFAULT_CHE_TLS_SECRET_NAME, DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT, DEFAULT_K8S_POD_WAIT_TIMEOUT, OLM_STABLE_CHANNEL_NAME } from '../constants'
import { CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_BACKUP_KIND_PLURAL, CHE_CLUSTER_KIND_PLURAL, CHE_CLUSTER_RESTORE_KIND_PLURAL, DEFAULT_CHE_TLS_SECRET_NAME, DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT, DEFAULT_K8S_POD_WAIT_TIMEOUT, DEVFILE_WORKSPACE_API_GROUP, DEVFILE_WORKSPACE_API_VERSION, DEVFILE_WORKSPACE_KIND_PLURAL, OLM_STABLE_CHANNEL_NAME } from '../constants'
import { base64Encode, getClusterClientCommand, getImageNameAndTag, isKubernetesPlatformFamily, newError, safeLoadFromYamlFile } from '../util'
import { ChectlContext, OLM } from './context'
import { V1CheClusterBackup, V1CheClusterRestore } from './types/backup-restore-crds'
Expand Down Expand Up @@ -93,6 +93,18 @@ export class KubeHelper {
throw new Error(`Namespace '${name}' is not in 'Active' phase.`)
}

async deleteService(name: string, namespace: string): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api)

try {
await k8sApi.deleteNamespacedService(name, namespace)
} catch (e) {
if (e.response.statusCode !== 404) {
throw this.wrapK8sClientError(e)
}
}
}

async deleteAllServices(namespace: string): Promise<void> {
const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api)
try {
Expand All @@ -102,7 +114,7 @@ export class KubeHelper {
await serviceList.items.forEach(async service => {
try {
await k8sApi.deleteNamespacedService(service.metadata!.name!, namespace)
} catch (error) {
} catch (error: any) {
if (error.response.statusCode !== 404) {
throw error
}
Expand Down Expand Up @@ -1268,12 +1280,12 @@ export class KubeHelper {
async deleteDeployment(namespace: string, name: string): Promise<void> {
const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api)
try {
k8sAppsApi.deleteNamespacedDeployment(name, namespace)
} catch (error) {
if (error.response && error.response.statusCode === 404) {
await k8sAppsApi.deleteNamespacedDeployment(name, namespace)
} catch (e: any) {
if (e.response && e.response.statusCode === 404) {
return
}
throw this.wrapK8sClientError(error)
throw this.wrapK8sClientError(e)
}
}

Expand Down Expand Up @@ -1731,6 +1743,55 @@ export class KubeHelper {
return this.getAllCustomResources(CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL)
}

async getAllDevfileWorkspaces(): Promise<any[]> {
return this.getAllCustomResources(DEVFILE_WORKSPACE_API_GROUP, DEVFILE_WORKSPACE_API_VERSION, DEVFILE_WORKSPACE_KIND_PLURAL)
}

async deleteAllCustomResources(apiGroup: string, version: string, plural: string): Promise<void> {
const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi)

let resources = await this.getAllCustomResources(apiGroup, version, plural)
for (const resource of resources) {
const name = resource.metadata.name
const namespace = resource.metadata.namespace
try {
await customObjectsApi.deleteNamespacedCustomObject(apiGroup, version, namespace, plural, name, 60)
} catch (e) {
// ignore, check existence later
}
}

for (let i = 0; i < 12; i++) {
await cli.wait(5 * 1000)
const resources = await this.getAllCustomResources(apiGroup, version, plural)
if (resources.length === 0) {
return
}
}

// remove finalizers
for (const resource of resources) {
const name = resource.metadata.name
const namespace = resource.metadata.namespace
try {
await this.patchCustomResource(name, namespace, { metadata: { finalizers: null } }, apiGroup, version, plural)
} catch (error) {
if (!await this.getCustomResource(namespace, name, apiGroup, version, plural)) {
continue // successfully removed
}
throw error
}
}

// wait for some time and check again
await cli.wait(5000)

resources = await this.getAllCustomResources(apiGroup, version, plural)
if (resources.length !== 0) {
throw new Error(`Failed to remove Custom Resource ${apiGroup}/${version}, ${resources.length} left.`)
}
}

/**
* Returns custom resource object by its name in the given namespace.
*/
Expand Down Expand Up @@ -1781,7 +1842,7 @@ export class KubeHelper {
try {
const { body } = await customObjectsApi.listClusterCustomObject(resourceAPIGroup, resourceAPIVersion, resourcePlural)
return (body as any).items ? (body as any).items : []
} catch (e) {
} catch (e: any) {
if (e.response && e.response.statusCode === 404) {
// There is no CRD
return []
Expand Down Expand Up @@ -2145,6 +2206,30 @@ export class KubeHelper {
}
}

async waitInstalledCSV(namespace: string, subscriptionName: string, timeout = AWAIT_TIMEOUT_S): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
const watcher = new Watch(this.kubeConfig)
const request = await 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.installedCSV) {
resolve(subscription.status.installedCSV)
}
},
error => {
if (error) {
reject(error)
}
})

setTimeout(() => {
request.abort()
reject(`Timeout reached while waiting for installed CSV of '${subscriptionName}' subscription.`)
}, timeout * 1000)
})
}

async listOperatorSubscriptions(namespace: string): Promise<Subscription[]> {
const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi)
try {
Expand Down Expand Up @@ -2227,7 +2312,7 @@ export class KubeHelper {
}
}

async waitUntilOperatorIsInstalled(installPlanName: string, namespace: string, timeout = 240) {
async waitOperatorInstallPlan(installPlanName: string, namespace: string, timeout = 240) {
return new Promise<InstallPlan>(async (resolve, reject) => {
const watcher = new Watch(this.kubeConfig)
const request = await watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/installplans`,
Expand Down
8 changes: 8 additions & 0 deletions src/api/types/olm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,19 @@ export interface ClusterServiceVersion {
kind: string
metadata: V1ObjectMeta
spec: ClusterServiceVersionSpec
status: ClusterServiceVersionStatus
}

export interface ClusterServiceVersionSpec {
displayName: string
install: OperatorInstall
version: string
}

export interface ClusterServiceVersionStatus {
phase: string
message: string
reason: string
}

export interface OperatorInstall {
Expand Down
29 changes: 22 additions & 7 deletions src/commands/server/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ import { Command, flags } from '@oclif/command'
import { boolean } from '@oclif/command/lib/flags'
import { cli } from 'cli-ux'
import * as Listrq from 'listr'
import Listr = require('listr')

import { OLMDevWorkspaceTasks } from '../../tasks/installers/olm-dev-workspace-operator'
import { ChectlContext } from '../../api/context'
import { KubeHelper } from '../../api/kube'
import { assumeYes, batch, cheDeployment, cheNamespace, CHE_TELEMETRY, listrRenderer, skipKubeHealthzCheck } from '../../common-flags'
import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants'
import { DEFAULT_ANALYTIC_HOOK_NAME, DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME } from '../../constants'
import { CheTasks } from '../../tasks/che'
import { DevWorkspaceTasks } from '../../tasks/component-installers/devfile-workspace-operator-installer'
import { OLMTasks } from '../../tasks/installers/olm'
import { OperatorTasks } from '../../tasks/installers/operator'
import { ApiTasks } from '../../tasks/platforms/api'
import { findWorkingNamespace, getCommandSuccessMessage, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util'
import Listr = require('listr')

export default class Delete extends Command {
static description = 'delete any Eclipse Che related resource: Kubernetes/OpenShift'
Expand Down Expand Up @@ -66,8 +66,9 @@ export default class Delete extends Command {
const apiTasks = new ApiTasks()
const kube = new KubeHelper(flags)
const operatorTasks = new OperatorTasks()
const olmTasks = new OLMTasks()
const olmTasks = new OLMTasks(flags)
const cheTasks = new CheTasks(flags)
const olmDevWorkspaceTasks = new OLMDevWorkspaceTasks(flags)
const devWorkspaceTasks = new DevWorkspaceTasks(flags)

const tasks = new Listrq([], ctx.listrOptions)
Expand All @@ -79,11 +80,25 @@ export default class Delete extends Command {

// Remove devworkspace controller only if there are no more cheClusters after olm/operator tasks
tasks.add({
title: 'Uninstall DevWorkspace Controller',
title: 'Uninstall Dev Workspace Controller',
task: async (_ctx: any, task: any) => {
const checlusters = await kube.getAllCheClusters()
if (checlusters.length === 0) {
return new Listr(devWorkspaceTasks.getUninstallTasks())
const tasks = new Listr()

if (await olmDevWorkspaceTasks.isCustomDevWorkspaceCatalogExists()) {
tasks.add(devWorkspaceTasks.deleteDevOperatorCRsAndCRDsTasks())
tasks.add(olmDevWorkspaceTasks.deleteResourcesTasks())
tasks.add(devWorkspaceTasks.deleteDevWorkspaceWebhooksTasks(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME))
}

if (!await olmDevWorkspaceTasks.isDevWorkspaceOperatorInstalledViaOLM()) {
tasks.add(devWorkspaceTasks.deleteDevOperatorCRsAndCRDsTasks())
tasks.add(devWorkspaceTasks.deleteResourcesTasks())
tasks.add(devWorkspaceTasks.deleteDevWorkspaceWebhooksTasks(DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE))
}

return tasks
}
task.title = `${task.title}...Skipped: another Eclipse Che deployment found.`
},
Expand All @@ -97,7 +112,7 @@ export default class Delete extends Command {
try {
await tasks.run()
cli.log(getCommandSuccessMessage())
} catch (err) {
} catch (err: any) {
this.error(wrapCommandError(err))
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/server/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export default class Deploy extends Command {
preInstallTasks.add(checkChectlAndCheVersionCompatibility(flags))
preInstallTasks.add(downloadTemplates(flags))
preInstallTasks.add({
title: '🧪 DevWorkspace engine (experimental / technology preview) 🚨',
title: '🧪 DevWorkspace engine',
enabled: () => isDevWorkspaceEnabled(ctx) && !ctx.isOpenShift,
task: () => new Listr(devWorkspaceTasks.getInstallTasks()),
})
Expand Down
8 changes: 4 additions & 4 deletions src/commands/server/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { cheNamespace } from '../../common-flags'
import { getBackupServerConfigurationName, parseBackupServerConfig, requestRestore } from '../../api/backup-restore'
import { cli } from 'cli-ux'
import { ApiTasks } from '../../tasks/platforms/api'
import { TASK_TITLE_CREATE_CUSTOM_CATALOG_SOURCE_FROM_FILE, TASK_TITLE_DELETE_CUSTOM_CATALOG_SOURCE, TASK_TITLE_DELETE_NIGHTLY_CATALOG_SOURCE, OLMTasks, TASK_TITLE_SET_CUSTOM_OPERATOR_IMAGE, TASK_TITLE_PREPARE_CHE_CLUSTER_CR } from '../../tasks/installers/olm'
import { TASK_TITLE_CREATE_CUSTOM_CATALOG_SOURCE_FROM_FILE, TASK_TITLE_DELETE_CUSTOM_CATALOG_SOURCE, TASK_TITLE_DELETE_NEXT_CATALOG_SOURCE, OLMTasks, TASK_TITLE_SET_CUSTOM_OPERATOR_IMAGE, TASK_TITLE_PREPARE_CHE_CLUSTER_CR } from '../../tasks/installers/olm'
import { OperatorTasks } from '../../tasks/installers/operator'
import { checkChectlAndCheVersionCompatibility, downloadTemplates, TASK_TITLE_CREATE_CHE_CLUSTER_CRD, TASK_TITLE_PATCH_CHECLUSTER_CR } from '../../tasks/installers/common-tasks'
import { confirmYN, findWorkingNamespace, getCommandSuccessMessage, getEmbeddedTemplatesDirectory, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util'
Expand Down Expand Up @@ -365,11 +365,11 @@ export default class Restore extends Command {
// All preparations and validations must be done before this task!
// Delete old operator if any in case of OLM installer.
// For Operator installer, the operator deployment will be downgraded if needed.
const olmTasks = new OLMTasks()
const olmTasks = new OLMTasks(flags)
let olmDeleteTasks = olmTasks.deleteTasks(flags)
const tasksToDelete = [
TASK_TITLE_DELETE_CUSTOM_CATALOG_SOURCE,
TASK_TITLE_DELETE_NIGHTLY_CATALOG_SOURCE,
TASK_TITLE_DELETE_NEXT_CATALOG_SOURCE,
]
olmDeleteTasks = olmDeleteTasks.filter(task => tasksToDelete.indexOf(task.title) === -1)
return new Listr(olmDeleteTasks, ctx.listrOptions)
Expand Down Expand Up @@ -402,7 +402,7 @@ export default class Restore extends Command {

return new Listr(operatorUpdateTasks, ctx.listrOptions)
} else { // OLM
const olmTasks = new OLMTasks()
const olmTasks = new OLMTasks(flags)
let olmInstallTasks = olmTasks.startTasks(flags, this)
// Remove redundant for restoring tasks
const tasksToDelete = [
Expand Down
17 changes: 15 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const DEFAULT_CHE_OPERATOR_IMAGE_NAME = 'quay.io/eclipse/che-operator'
// This image should be updated manually when needed.
// Repository location: https://github.com/che-dockerfiles/che-cert-manager-ca-cert-generator-image
export const CA_CERT_GENERATION_JOB_IMAGE = 'quay.io/eclipse/che-cert-manager-ca-cert-generator:671342c'
export const INDEX_IMG = 'quay.io/eclipse/eclipse-che-openshift-opm-catalog:next'
export const DEV_WORKSPACE_NEXT_CATALOG_SOURCE_IMAGE = 'quay.io/devfile/devworkspace-operator-index:next'
export const DEV_WORKSPACE_STABLE_CATALOG_SOURCE_IMAGE = 'quay.io/devfile/devworkspace-operator-index:release'

export const NEXT_TAG = 'next'

Expand All @@ -44,8 +47,8 @@ export const LEGACY_CHE_NAMESPACE = 'che'
export const DEFAULT_CHE_OLM_PACKAGE_NAME = 'eclipse-che'
export const DEFAULT_CHE_OPERATOR_SUBSCRIPTION_NAME = 'eclipse-che-subscription'
export const OPERATOR_GROUP_NAME = 'che-operator-group'
export const CVS_PREFIX = 'eclipse-che'
export const DEVWORKSPACE_CVS_PREFIX = 'devworkspace-operator'
export const CSV_PREFIX = 'eclipse-che'
export const DEVWORKSPACE_CSV_PREFIX = 'devworkspace-operator'
// OLM channels
export const OLM_STABLE_CHANNEL_NAME = 'stable'
export const OLM_STABLE_CHANNEL_STARTING_CSV_TEMPLATE = 'eclipse-che.v{{VERSION}}'
Expand All @@ -57,6 +60,8 @@ export const DEFAULT_OPENSHIFT_OPERATORS_NS_NAME = 'openshift-operators'
// OLM catalogs
export const CUSTOM_CATALOG_SOURCE_NAME = 'eclipse-che-custom-catalog-source'
export const NEXT_CATALOG_SOURCE_NAME = 'eclipse-che-preview'
export const NEXT_CATALOG_SOURCE_DEV_WORKSPACE_OPERATOR = 'custom-devworkspace-operator-catalog'
export const STABLE_CATALOG_SOURCE_DEV_WORKSPACE_OPERATOR = 'stable-custom-devworkspace-operator-catalog'
export const DEFAULT_OLM_SUGGESTED_NAMESPACE = 'eclipse-che'
export const KUBERNETES_OLM_CATALOG = 'operatorhubio-catalog'
export const OPENSHIFT_OLM_CATALOG = 'community-operators'
Expand Down Expand Up @@ -96,4 +101,12 @@ export const CHE_CLUSTER_BACKUP_KIND_PLURAL = 'checlusterbackups'
export const CHE_CLUSTER_RESTORE_CRD = 'checlusterrestores.org.eclipse.che'
export const CHE_CLUSTER_RESTORE_KIND_PLURAL = 'checlusterrestores'

export const DEVFILE_WORKSPACE_API_GROUP = 'workspace.devfile.io'
export const DEVFILE_WORKSPACE_API_VERSION = 'v1alpha2'
export const DEVFILE_WORKSPACE_KIND_PLURAL = 'devworkspaces'

export const DEVFILE_WORKSPACE_ROUTINGS_API_GROUP = 'controller.devfile.io'
export const DEVFILE_WORKSPACE_ROUTINGS_VERSION = 'v1alpha1'
export const DEVFILE_WORKSPACE_ROUTINGS_KIND_PLURAL = 'devworkspaceroutings'

export const DEFAULT_CHE_TLS_SECRET_NAME = 'che-tls'
Loading

0 comments on commit dbe5408

Please sign in to comment.