diff --git a/src/api/kube.ts b/src/api/kube.ts index 99c67dfce..31a9865e9 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -2171,7 +2171,7 @@ export class KubeHelper { } } - async waitInstalledCSV(namespace: string, subscriptionName: string, timeout = AWAIT_TIMEOUT_S): Promise { + async waitInstalledCSVInSubscription(namespace: string, subscriptionName: string, timeout = AWAIT_TIMEOUT_S): Promise { return new Promise(async (resolve, reject) => { const watcher = new Watch(this.kubeConfig) const request = await watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/subscriptions`, @@ -2195,6 +2195,30 @@ export class KubeHelper { }) } + async waitCSVStatusPhase(namespace: string, csvName: string, timeout = AWAIT_TIMEOUT_S): Promise { + return new Promise(async (resolve, reject) => { + const watcher = new Watch(this.kubeConfig) + const request = await watcher.watch(`/apis/operators.coreos.com/v1alpha1/namespaces/${namespace}/clusterserviceversions`, + { fieldSelector: `metadata.name=${csvName}` }, + (_phase: string, obj: any) => { + const csv = obj as ClusterServiceVersion + if (csv.status && csv.status.phase) { + resolve(csv.status.phase) + } + }, + error => { + if (error) { + reject(error) + } + }) + + setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting CSV '${csvName}' status.`) + }, timeout * 1000) + }) + } + async listOperatorSubscriptions(namespace: string): Promise { const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) try { @@ -2315,12 +2339,28 @@ export class KubeHelper { }) } - async getCSV(csvName: string, namespace: string): Promise { - const csvs = await this.getClusterServiceVersions(namespace) - return csvs.items.find(item => item.metadata.name === csvName) + async getCSV(name: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', name) + return body as ClusterServiceVersion + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async getCSVWithPrefix(namePrefix: string, namespace: string): Promise { + try { + const csvs = await this.getAllCSV(namespace) + return csvs.items.filter(csv => csv.metadata.name!.startsWith(namePrefix)) + } catch (e) { + throw this.wrapK8sClientError(e) + } } - async getClusterServiceVersions(namespace: string): Promise { + async getAllCSV(namespace: string): Promise { const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) try { const { body } = await customObjectsApi.listNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions') diff --git a/src/tasks/installers/olm-dev-workspace-operator.ts b/src/tasks/installers/olm-dev-workspace-operator.ts index dfb8e6d57..6e8d2cdb1 100644 --- a/src/tasks/installers/olm-dev-workspace-operator.ts +++ b/src/tasks/installers/olm-dev-workspace-operator.ts @@ -93,12 +93,13 @@ export class OLMDevWorkspaceTasks { title: 'Wait Dev Workspace CSV', enabled: ctx => !ctx[DevWorkspaceContextKeys.IS_DEV_WORKSPACE_INSTALLED_VIA_OPERATOR_HUB], task: async (_ctx: any, task: any) => { - const installedCSV = await this.kube.waitInstalledCSV(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, this.DEV_WORKSPACE_OPERATOR_SUBSCRIPTION) - const csv = await this.kube.getCSV(installedCSV, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME) - if (!csv) { - throw new Error(`Cluster service version resource ${installedCSV} not found`) - } - if (csv.status.phase === 'Failed') { + const installedCSVName = await this.kube.waitInstalledCSVInSubscription(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, this.DEV_WORKSPACE_OPERATOR_SUBSCRIPTION) + const phase = await this.kube.waitCSVStatusPhase(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, installedCSVName) + if (phase === 'Failed') { + const csv = await this.kube.getCSV(installedCSVName, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME) + if (!csv) { + throw new Error(`Cluster service version '${installedCSVName}' not found.`) + } throw new Error(`Cluster service version resource failed for Dev Workspace operator, cause: ${csv.status.message}, reason: ${csv.status.reason}.`) } task.title = `${task.title}...[OK]` @@ -119,9 +120,8 @@ export class OLMDevWorkspaceTasks { { title: 'Delete Dev Workspace operator CSV', task: async (_ctx: any, task: any) => { - const csvs = await this.kube.getClusterServiceVersions(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME) - const csvsToDelete = csvs.items.filter(csv => csv.metadata.name!.startsWith(DEVWORKSPACE_CSV_PREFIX)) - for (const csv of csvsToDelete) { + const csvs = await this.kube.getCSVWithPrefix(DEVWORKSPACE_CSV_PREFIX, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME) + for (const csv of csvs) { await this.kube.deleteClusterServiceVersion(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, csv.metadata.name!) } task.title = `${task.title}...[OK]` @@ -200,8 +200,7 @@ export class OLMDevWorkspaceTasks { return false } - const csvAll = await this.kube.getClusterServiceVersions(DEFAULT_OPENSHIFT_OPERATORS_NS_NAME) - const devWorkspaceCSVs = csvAll.items.filter(csv => csv.metadata.name!.startsWith(DEVWORKSPACE_CSV_PREFIX)) - return devWorkspaceCSVs.length > 0 + const csvs = await this.kube.getCSVWithPrefix(DEVWORKSPACE_CSV_PREFIX, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME) + return csvs.length > 0 } } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index ef0e229b0..023707fbf 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -218,13 +218,14 @@ export class OLMTasks { { title: 'Check cluster service version resource', task: async (ctx: any, task: any) => { - const installedCSV = await this.kube.waitInstalledCSV(ctx.operatorNamespace, ctx.subscriptionName) - const csv = await this.kube.getCSV(installedCSV, ctx.operatorNamespace) - if (!csv) { - throw new Error(`cluster service version resource ${installedCSV} not found`) - } - if (csv.status.phase === 'Failed') { - throw new Error(`cluster service version resource failed. Cause: ${csv.status.message}. Reason: ${csv.status.reason}.`) + const installedCSVName = await this.kube.waitInstalledCSVInSubscription(ctx.operatorNamespace, ctx.subscriptionName) + const phase = await this.kube.waitCSVStatusPhase(ctx.operatorNamespace, installedCSVName) + if (phase === 'Failed') { + const csv = await this.kube.getCSV(installedCSVName, ctx.operatorNamespace) + if (!csv) { + throw new Error(`Cluster service version '${installedCSVName}' not found.`) + } + throw new Error(`Cluster service version resource failed, cause: ${csv.status.message}, reason: ${csv.status.reason}.`) } task.title = `${task.title}...[OK]` }, @@ -232,14 +233,13 @@ export class OLMTasks { { title: TASK_TITLE_SET_CUSTOM_OPERATOR_IMAGE, enabled: () => flags['che-operator-image'], - task: async (_ctx: any, task: any) => { - const csvList = await this.kube.getClusterServiceVersions(flags.chenamespace) - if (csvList.items.length < 1) { - throw new Error('Failed to get CSV for Che operator') + task: async (ctx: any, task: any) => { + const csvs = await this.kube.getCSVWithPrefix(CSV_PREFIX, ctx.operatorNamespace) + if (csvs.length !== 1) { + throw new Error('Eclipse Che operator CSV not found.') } - const csv = csvList.items[0] const jsonPatch = [{ op: 'replace', path: '/spec/install/spec/deployments/0/spec/template/spec/containers/0/image', value: flags['che-operator-image'] }] - await this.kube.patchClusterServiceVersion(csv.metadata.namespace!, csv.metadata.name!, jsonPatch) + await this.kube.patchClusterServiceVersion(csvs[0].metadata.namespace!, csvs[0].metadata.name!, jsonPatch) task.title = `${task.title}...[OK]` }, }, @@ -419,9 +419,8 @@ export class OLMTasks { title: 'Delete Eclipse Che cluster service versions', enabled: ctx => ctx.isPreInstalledOLM, task: async (ctx: any, task: any) => { - const csvs = await kube.getClusterServiceVersions(ctx.operatorNamespace) - const csvsToDelete = csvs.items.filter(csv => csv.metadata.name!.startsWith(CSV_PREFIX)) - for (const csv of csvsToDelete) { + const csvs = await kube.getCSVWithPrefix(CSV_PREFIX, ctx.operatorNamespace) + for (const csv of csvs) { await kube.deleteClusterServiceVersion(ctx.operatorNamespace, csv.metadata.name!) } task.title = `${task.title}...[OK]`