From 66445acfc2a1caa0f0f662fdcdebd70c4be6c433 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Thu, 3 Jun 2021 18:07:32 +0300 Subject: [PATCH 1/5] chore: Improve error handling Signed-off-by: Anatolii Bazko --- src/api/kube.ts | 14 +++++--------- src/commands/auth/login.ts | 4 ++-- src/commands/cacert/export.ts | 4 ++-- src/commands/server/debug.ts | 6 +++--- src/commands/server/delete.ts | 6 +++--- src/commands/server/deploy.ts | 8 ++++---- src/commands/server/logs.ts | 6 +++--- src/commands/server/start.ts | 8 ++++---- src/commands/server/stop.ts | 10 +++++----- src/commands/server/update.ts | 6 +++--- src/commands/workspace/inject.ts | 4 ++-- src/tasks/che.ts | 22 +++++++++++----------- src/tasks/installers/helm.ts | 4 ++-- src/tasks/platforms/api.ts | 6 +++--- src/tasks/platforms/docker-desktop.ts | 3 ++- src/tasks/platforms/k8s.ts | 3 ++- src/util.ts | 16 +++++++++++----- 17 files changed, 67 insertions(+), 63 deletions(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index a4272ab3c..19e9ec988 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -20,7 +20,7 @@ import * as net from 'net' import { Writable } from 'stream' import { CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL, DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT, DEFAULT_K8S_POD_WAIT_TIMEOUT, OLM_STABLE_CHANNEL_NAME } from '../constants' -import { getClusterClientCommand, isKubernetesPlatformFamily, safeLoadFromYamlFile } from '../util' +import { getClusterClientCommand, isKubernetesPlatformFamily, newError, safeLoadFromYamlFile } from '../util' import { V1Certificate } from './typings/cert-manager' import { CatalogSource, ClusterServiceVersion, ClusterServiceVersionList, InstallPlan, OperatorGroup, PackageManifest, Subscription } from './typings/olm' @@ -1643,13 +1643,7 @@ export class KubeHelper { } else if (crs.length !== 1) { throw new Error(`Too many resources of type ${resourcePlural}.${resourceAPIGroup} found in the namespace '${namespace}'`) } - - return crs[0] } catch (e) { - if (e.response.statusCode === 404) { - // There is no CRD - return - } throw this.wrapK8sClientError(e) } } @@ -2635,8 +2629,10 @@ export class KubeHelper { * @param e k8s error to wrap */ private wrapK8sClientError(e: any): Error { - if (e.response && e.response.body && e.response.body.message) return new Error(e.response.body.message) - else return new Error(e) + if (e.response && e.response.body) { + return newError(e.response.body, e) + } + return e } public safeLoadFromYamlFile(filePath: string): any { diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index 3a3f5a760..13afe6cf7 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -19,7 +19,7 @@ import { ChectlContext } from '../../api/context' import { KubeHelper } from '../../api/kube' import { cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, username, USERNAME_KEY } from '../../common-flags' import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' -import { getCommandErrorMessage, OPENSHIFT_CLI } from '../../util' +import { OPENSHIFT_CLI, wrapCommandError } from '../../util' const REFRESH_TOKEN_KEY = 'refresh-token' const PASSWORD_KEY = 'password' @@ -161,7 +161,7 @@ export default class Login extends Command { const username = await loginManager.setLoginContext(cheApiEndpoint, loginData) cli.info(`Successfully logged into ${cheApiEndpoint} as ${username}`) } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } } } diff --git a/src/commands/cacert/export.ts b/src/commands/cacert/export.ts index ce66fa33a..e49cb08ed 100644 --- a/src/commands/cacert/export.ts +++ b/src/commands/cacert/export.ts @@ -16,7 +16,7 @@ import { ChectlContext } from '../../api/context' import { KubeHelper } from '../../api/kube' import { cheNamespace, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' import { DEFAULT_ANALYTIC_HOOK_NAME, DEFAULT_CA_CERT_FILE_NAME } from '../../constants' -import { findWorkingNamespace, getCommandErrorMessage } from '../../util' +import { findWorkingNamespace, wrapCommandError } from '../../util' export default class Export extends Command { static description = 'Retrieves Eclipse Che self-signed certificate' @@ -62,7 +62,7 @@ export default class Export extends Command { this.log('Self signed certificate secret not found. Is commonly trusted certificate used?') } } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } } } diff --git a/src/commands/server/debug.ts b/src/commands/server/debug.ts index 8d13541f0..545ac9fae 100644 --- a/src/commands/server/debug.ts +++ b/src/commands/server/debug.ts @@ -17,7 +17,7 @@ import { cheNamespace, CHE_TELEMETRY, listrRenderer, skipKubeHealthzCheck } from import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' import { CheTasks } from '../../tasks/che' import { ApiTasks } from '../../tasks/platforms/api' -import { findWorkingNamespace, getCommandErrorMessage } from '../../util' +import { findWorkingNamespace, wrapCommandError } from '../../util' export default class Debug extends Command { static description = 'Enable local debug of Eclipse Che server' @@ -44,7 +44,7 @@ export default class Debug extends Command { const apiTasks = new ApiTasks() const tasks = new Listr([], { renderer: flags['listr-renderer'] as any }) - tasks.add(apiTasks.testApiTasks(flags, this)) + tasks.add(apiTasks.testApiTasks(flags)) tasks.add(cheTasks.verifyCheNamespaceExistsTask(flags, this)) tasks.add(cheTasks.debugTask(flags)) @@ -53,7 +53,7 @@ export default class Debug extends Command { this.log(`Eclipse Che server debug is available on localhost:${flags['debug-port']}.`) this.log('The program keeps running to enable port forwarding.') } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } } } diff --git a/src/commands/server/delete.ts b/src/commands/server/delete.ts index 90a6646b7..6f01c6112 100644 --- a/src/commands/server/delete.ts +++ b/src/commands/server/delete.ts @@ -24,7 +24,7 @@ import { HelmTasks } from '../../tasks/installers/helm' import { OLMTasks } from '../../tasks/installers/olm' import { OperatorTasks } from '../../tasks/installers/operator' import { ApiTasks } from '../../tasks/platforms/api' -import { findWorkingNamespace, getCommandErrorMessage, getCommandSuccessMessage, notifyCommandCompletedSuccessfully } from '../../util' +import { findWorkingNamespace, getCommandSuccessMessage, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Delete extends Command { static description = 'delete any Eclipse Che related resource: Kubernetes/OpenShift/Helm' @@ -71,7 +71,7 @@ export default class Delete extends Command { const devWorkspaceTasks = new DevWorkspaceTasks(flags) const tasks = new Listrq([], ctx.listrOptions) - tasks.add(apiTasks.testApiTasks(flags, this)) + tasks.add(apiTasks.testApiTasks(flags)) tasks.add(operatorTasks.deleteTasks(flags)) tasks.add(olmTasks.deleteTasks(flags)) tasks.add(cheTasks.deleteTasks(flags)) @@ -98,7 +98,7 @@ export default class Delete extends Command { await tasks.run() cli.log(getCommandSuccessMessage()) } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } } else { this.exit(0) diff --git a/src/commands/server/deploy.ts b/src/commands/server/deploy.ts index e72d80f13..a24cff4f3 100644 --- a/src/commands/server/deploy.ts +++ b/src/commands/server/deploy.ts @@ -24,7 +24,7 @@ import { checkChectlAndCheVersionCompatibility, downloadTemplates, getPrintHighl import { InstallerTasks } from '../../tasks/installers/installer' import { ApiTasks } from '../../tasks/platforms/api' import { PlatformTasks } from '../../tasks/platforms/platform' -import { askForChectlUpdateIfNeeded, getCommandErrorMessage, getCommandSuccessMessage, getEmbeddedTemplatesDirectory, getProjectName, isKubernetesPlatformFamily, isOpenshiftPlatformFamily, notifyCommandCompletedSuccessfully } from '../../util' +import { askForChectlUpdateIfNeeded, getCommandSuccessMessage, getEmbeddedTemplatesDirectory, getProjectName, isKubernetesPlatformFamily, isOpenshiftPlatformFamily, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Deploy extends Command { static description = 'Deploy Eclipse Che server' @@ -395,10 +395,10 @@ export default class Deploy extends Command { // Checks if Eclipse Che is already deployed let preInstallTasks = new Listr(undefined, ctx.listrOptions) - preInstallTasks.add(apiTasks.testApiTasks(flags, this)) + preInstallTasks.add(apiTasks.testApiTasks(flags)) preInstallTasks.add({ title: '👀 Looking for an already existing Eclipse Che instance', - task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags, this)) + task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags)) }) preInstallTasks.add(checkChectlAndCheVersionCompatibility(flags)) preInstallTasks.add(downloadTemplates(flags)) @@ -444,7 +444,7 @@ export default class Deploy extends Command { this.log(getCommandSuccessMessage()) } } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } notifyCommandCompletedSuccessfully() diff --git a/src/commands/server/logs.ts b/src/commands/server/logs.ts index cbe542a7d..225095d27 100644 --- a/src/commands/server/logs.ts +++ b/src/commands/server/logs.ts @@ -17,7 +17,7 @@ import { cheDeployment, cheNamespace, CHE_TELEMETRY, listrRenderer, skipKubeHeal import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' import { CheTasks } from '../../tasks/che' import { ApiTasks } from '../../tasks/platforms/api' -import { findWorkingNamespace, getCommandErrorMessage, getCommandSuccessMessage } from '../../util' +import { findWorkingNamespace, getCommandSuccessMessage, wrapCommandError } from '../../util' export default class Logs extends Command { static description = 'Collect Eclipse Che logs' @@ -46,7 +46,7 @@ export default class Logs extends Command { const tasks = new Listr([], { renderer: flags['listr-renderer'] as any }) await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Logs.id, flags }) - tasks.add(apiTasks.testApiTasks(flags, this)) + tasks.add(apiTasks.testApiTasks(flags)) tasks.add(cheTasks.verifyCheNamespaceExistsTask(flags, this)) tasks.add(cheTasks.serverLogsTasks(flags, false)) @@ -55,7 +55,7 @@ export default class Logs extends Command { await tasks.run(ctx) this.log(getCommandSuccessMessage()) } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } this.exit(0) diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index c65aea01e..469302dcb 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -16,7 +16,7 @@ import { ChectlContext } from '../../api/context' import { cheDeployment, cheNamespace, k8sPodDownloadImageTimeout, K8SPODDOWNLOADIMAGETIMEOUT_KEY, k8sPodErrorRecheckTimeout, K8SPODERRORRECHECKTIMEOUT_KEY, k8sPodReadyTimeout, K8SPODREADYTIMEOUT_KEY, k8sPodWaitTimeout, K8SPODWAITTIMEOUT_KEY, listrRenderer, logsDirectory, LOG_DIRECTORY_KEY, skipKubeHealthzCheck } from '../../common-flags' import { CheTasks } from '../../tasks/che' import { ApiTasks } from '../../tasks/platforms/api' -import { findWorkingNamespace, getCommandErrorMessage, getCommandSuccessMessage, notifyCommandCompletedSuccessfully } from '../../util' +import { findWorkingNamespace, getCommandSuccessMessage, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Start extends Command { static description = 'Start Eclipse Che server' @@ -44,10 +44,10 @@ export default class Start extends Command { // Checks if Eclipse Che is already deployed const preInstallTasks = new Listr([ - apiTasks.testApiTasks(flags, this), + apiTasks.testApiTasks(flags), { title: '👀 Looking for an already existing Eclipse Che instance', - task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags, this)) + task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags)) }], ctx.listrOptions) const logsTasks = new Listr([{ @@ -73,7 +73,7 @@ export default class Start extends Command { this.log(getCommandSuccessMessage()) } } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } notifyCommandCompletedSuccessfully() diff --git a/src/commands/server/stop.ts b/src/commands/server/stop.ts index 755a33c74..870c14dfd 100644 --- a/src/commands/server/stop.ts +++ b/src/commands/server/stop.ts @@ -17,7 +17,7 @@ import { accessToken, cheDeployment, cheNamespace, CHE_TELEMETRY, listrRenderer, import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' import { CheTasks } from '../../tasks/che' import { ApiTasks } from '../../tasks/platforms/api' -import { findWorkingNamespace, getCommandErrorMessage, getCommandSuccessMessage, notifyCommandCompletedSuccessfully } from '../../util' +import { findWorkingNamespace, getCommandSuccessMessage, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Stop extends Command { static description = 'stop Eclipse Che server' @@ -55,8 +55,8 @@ export default class Stop extends Command { } ) - tasks.add(apiTasks.testApiTasks(flags, this)) - tasks.add(cheTasks.checkIfCheIsInstalledTasks(flags, this)) + tasks.add(apiTasks.testApiTasks(flags)) + tasks.add(cheTasks.checkIfCheIsInstalledTasks(flags)) tasks.add([ { title: 'Deployment doesn\'t exist', @@ -68,13 +68,13 @@ export default class Stop extends Command { ], { renderer: flags['listr-renderer'] as any } ) - tasks.add(cheTasks.scaleCheDownTasks(this)) + tasks.add(cheTasks.scaleCheDownTasks()) tasks.add(cheTasks.waitPodsDeletedTasks()) try { await tasks.run() cli.log(getCommandSuccessMessage()) } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } notifyCommandCompletedSuccessfully() diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index ecfdf8d98..46970ebf7 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -22,7 +22,7 @@ import { DEFAULT_ANALYTIC_HOOK_NAME, DEFAULT_CHE_OPERATOR_IMAGE_NAME, MIN_CHE_OP import { checkChectlAndCheVersionCompatibility, downloadTemplates, getPrintHighlightedMessagesTask } from '../../tasks/installers/common-tasks' import { InstallerTasks } from '../../tasks/installers/installer' import { ApiTasks } from '../../tasks/platforms/api' -import { askForChectlUpdateIfNeeded, findWorkingNamespace, getCommandErrorMessage, getCommandSuccessMessage, getEmbeddedTemplatesDirectory, getProjectName, getProjectVersion, notifyCommandCompletedSuccessfully } from '../../util' +import { askForChectlUpdateIfNeeded, findWorkingNamespace, getCommandSuccessMessage, getEmbeddedTemplatesDirectory, getProjectName, getProjectVersion, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Update extends Command { static description = 'Update Eclipse Che server.' @@ -126,7 +126,7 @@ export default class Update extends Command { // pre update tasks const apiTasks = new ApiTasks() const preUpdateTasks = new Listr([], ctx.listrOptions) - preUpdateTasks.add(apiTasks.testApiTasks(flags, this)) + preUpdateTasks.add(apiTasks.testApiTasks(flags)) preUpdateTasks.add(checkChectlAndCheVersionCompatibility(flags)) preUpdateTasks.add(downloadTemplates(flags)) preUpdateTasks.add(installerTasks.preUpdateTasks(flags, this)) @@ -158,7 +158,7 @@ export default class Update extends Command { this.log(getCommandSuccessMessage()) } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } notifyCommandCompletedSuccessfully() diff --git a/src/commands/workspace/inject.ts b/src/commands/workspace/inject.ts index 8aeed847f..4affde198 100644 --- a/src/commands/workspace/inject.ts +++ b/src/commands/workspace/inject.ts @@ -24,7 +24,7 @@ import { ChectlContext } from '../../api/context' import { KubeHelper } from '../../api/kube' import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' -import { getClusterClientCommand, getCommandErrorMessage, OPENSHIFT_CLI } from '../../util' +import { getClusterClientCommand, OPENSHIFT_CLI, wrapCommandError } from '../../util' export default class Inject extends Command { static description = 'Inject configurations and tokens in a workspace' @@ -100,7 +100,7 @@ export default class Inject extends Command { try { await this.injectKubeconfig(flags, workspaceNamespace, workspacePodName, workspaceId!) } catch (err) { - this.error(getCommandErrorMessage(err)) + this.error(wrapCommandError(err)) } } diff --git a/src/tasks/che.ts b/src/tasks/che.ts index 5d1ab39ed..ef46ad976 100644 --- a/src/tasks/che.ts +++ b/src/tasks/che.ts @@ -17,7 +17,7 @@ import { KubeHelper } from '../api/kube' import { OpenShiftHelper } from '../api/openshift' import { VersionHelper } from '../api/version' import { CHE_OPERATOR_SELECTOR, DOC_LINK, DOC_LINK_RELEASE_NOTES, OUTPUT_SEPARATOR } from '../constants' -import { base64Decode } from '../util' +import { base64Decode, newError } from '../util' import { KubeTasks } from './kube' @@ -114,7 +114,7 @@ export class CheTasks { * is[Component]Deployed, is[Component]Stopped, is[Component]Ready * where component is one the: Che, Keycloak, Postgres, PluginRegistry, DevfileRegistry */ - checkIfCheIsInstalledTasks(_flags: any, command: Command): ReadonlyArray { + checkIfCheIsInstalledTasks(_flags: any): ReadonlyArray { return [ { title: `Verify if Eclipse Che is deployed into namespace \"${this.cheNamespace}\"`, @@ -214,7 +214,7 @@ export class CheTasks { const auth = ctx.isAuthEnabled ? '(auth enabled)' : '(auth disabled)' task.title = `${task.title}...${status} ${auth}` } catch (error) { - command.error(`E_CHECK_CHE_STATUS_FAIL - Failed to check Eclipse Che status (URL: ${cheURL}). ${error.message}`) + return newError(`Failed to check Eclipse Che status (URL: ${cheURL}).`, error) } } } @@ -287,7 +287,7 @@ export class CheTasks { * * @see [CheTasks](#checkIfCheIsInstalledTasks) */ - scaleCheDownTasks(command: Command): ReadonlyArray { + scaleCheDownTasks(): ReadonlyArray { return [{ title: 'Stop Eclipse Che Server and wait until it\'s ready to shutdown', enabled: (ctx: any) => !ctx.isCheStopped, @@ -304,7 +304,7 @@ export class CheTasks { await cheApi.waitUntilCheServerReadyToShutdown() task.title = `${task.title}...done` } catch (error) { - command.error(`E_SHUTDOWN_CHE_SERVER_FAIL - Failed to shutdown Eclipse Che server. ${error.message}`) + return newError('Failed to shutdown Eclipse Che server.', error) } } }, @@ -316,7 +316,7 @@ export class CheTasks { await this.kube.scaleDeployment(this.cheDeploymentName, this.cheNamespace, 0) task.title = await `${task.title}...done` } catch (error) { - command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale deployment. ${error.message}`) + return newError(`Failed to scale ${this.cheDeploymentName} deployment.`, error) } } }, @@ -328,7 +328,7 @@ export class CheTasks { await this.kube.scaleDeployment(this.dashboardDeploymentName, this.cheNamespace, 0) task.title = await `${task.title}...done` } catch (error) { - command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale dashboard deployment. ${error.message}`) + return newError('Failed to scale dashboard deployment.', error) } } }, @@ -340,7 +340,7 @@ export class CheTasks { await this.kube.scaleDeployment(this.keycloakDeploymentName, this.cheNamespace, 0) task.title = await `${task.title}...done` } catch (error) { - command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale keycloak deployment. ${error.message}`) + return newError('Failed to scale keycloak deployment.', error) } } }, @@ -352,7 +352,7 @@ export class CheTasks { await this.kube.scaleDeployment(this.postgresDeploymentName, this.cheNamespace, 0) task.title = await `${task.title}...done` } catch (error) { - command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale postgres deployment. ${error.message}`) + return newError('Failed to scale postgres deployment.', error) } } }, @@ -364,7 +364,7 @@ export class CheTasks { await this.kube.scaleDeployment(this.devfileRegistryDeploymentName, this.cheNamespace, 0) task.title = await `${task.title}...done` } catch (error) { - command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale devfile-registry deployment. ${error.message}`) + return newError('Failed to scale devfile registry deployment.', error) } } }, @@ -376,7 +376,7 @@ export class CheTasks { await this.kube.scaleDeployment(this.pluginRegistryDeploymentName, this.cheNamespace, 0) task.title = await `${task.title}...done` } catch (error) { - command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale plugin-registry deployment. ${error.message}`) + return newError('Failed to scale plugin registry deployment.', error) } } }] diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index 742764bc9..26eb71e39 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -21,7 +21,7 @@ import { KubeHelper } from '../../api/kube' import { VersionHelper } from '../../api/version' import { CHE_ROOT_CA_SECRET_NAME, CHE_TLS_SECRET_NAME } from '../../constants' import { CertManagerTasks } from '../../tasks/component-installers/cert-manager' -import { generatePassword, safeSaveYamlToFile } from '../../util' +import { generatePassword, newError, safeSaveYamlToFile } from '../../util' interface HelmChartDependency { name: string @@ -65,7 +65,7 @@ export class HelmTasks { task.title = `${task.title}: Found ${version}` } catch (error) { - command.error(`Unable to get helm version. ${error.message}`) + return newError('Unable to get helm version', error) } } }, diff --git a/src/tasks/platforms/api.ts b/src/tasks/platforms/api.ts index b228ab765..fe3d60c10 100644 --- a/src/tasks/platforms/api.ts +++ b/src/tasks/platforms/api.ts @@ -7,11 +7,11 @@ * * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import { Command } from '@oclif/command' import { cli } from 'cli-ux' import * as Listr from 'listr' import { KubeHelper } from '../../api/kube' +import { newError } from '../../util' export class ApiTasks { /** @@ -19,7 +19,7 @@ export class ApiTasks { * * `isOpenShift` property is provisioned into context. */ - testApiTasks(flags: any, command: Command): Listr.ListrTask { + testApiTasks(flags: any): Listr.ListrTask { let kube = new KubeHelper(flags) return { title: 'Verify Kubernetes API', @@ -37,7 +37,7 @@ export class ApiTasks { task.title = `${task.title} (it's OpenShift)` } } catch (error) { - command.error(`Failed to connect to Kubernetes API, error: ${error.message}. If you're sure that your Kubernetes cluster is healthy - you can skip this check with '--skip-kubernetes-health-check' flag.`) + return newError('Failed to connect to Kubernetes API. If you\'re sure that your Kubernetes cluster is healthy - you can skip this check with \'--skip-kubernetes-health-check\' flag.', error) } } } diff --git a/src/tasks/platforms/docker-desktop.ts b/src/tasks/platforms/docker-desktop.ts index dc892b4c4..3b66fbd39 100644 --- a/src/tasks/platforms/docker-desktop.ts +++ b/src/tasks/platforms/docker-desktop.ts @@ -16,6 +16,7 @@ import * as os from 'os' import { KubeHelper } from '../../api/kube' import { VersionHelper } from '../../api/version' +import { newError } from '../../util' export class DockerDesktopTasks { private readonly kh: KubeHelper @@ -56,7 +57,7 @@ export class DockerDesktopTasks { await this.kh.checkKubeApi() task.title = `${task.title}...done.` } catch (error) { - command.error('E_PLATFORM_NOT_READY: ' + error) + return newError('Platform not ready.', error) } } }, diff --git a/src/tasks/platforms/k8s.ts b/src/tasks/platforms/k8s.ts index f7d343413..52433d928 100644 --- a/src/tasks/platforms/k8s.ts +++ b/src/tasks/platforms/k8s.ts @@ -14,6 +14,7 @@ import * as Listr from 'listr' import { KubeHelper } from '../../api/kube' import { VersionHelper } from '../../api/version' +import { newError } from '../../util' import { CommonPlatformTasks } from './common-platform-tasks' @@ -40,7 +41,7 @@ export class K8sTasks { await kh.checkKubeApi() task.title = `${task.title}...done.` } catch (error) { - command.error('E_PLATFORM_NOT_READY: ' + error) + return newError('Platform not ready.', error) } } }, diff --git a/src/util.ts b/src/util.ts index c4ad7f011..3c2705cdd 100644 --- a/src/util.ts +++ b/src/util.ts @@ -183,18 +183,24 @@ export function getCommandSuccessMessage(): string { } /** - * Returns command error message. + * Wraps error into command error. */ -export function getCommandErrorMessage(err: Error): string { +export function wrapCommandError(error: Error): Error { const ctx = ChectlContext.get() const logDirectory = ctx[ChectlContext.LOGS_DIR] - let message = `${err}\nCommand ${ctx[ChectlContext.COMMAND_ID]} failed. Error log: ${ctx[ChectlContext.ERROR_LOG]}` + let commandErrorMessage = `Command ${ctx[ChectlContext.COMMAND_ID]} failed. Error log: ${ctx[ChectlContext.ERROR_LOG]}.` if (logDirectory && isDirEmpty(logDirectory)) { - message += ` Eclipse Che logs: ${logDirectory}` + commandErrorMessage += ` Eclipse Che logs: ${logDirectory}.` } - return message + return newError(commandErrorMessage, error) +} + +export function newError(message: string, cause: Error): Error { + const error = new Error(message) + error.stack += `\nCause: ${cause.stack}` + return error } export function notifyCommandCompletedSuccessfully(): void { From d5b01c3603bf4011714fc9de9f0bec42e669b62b Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Fri, 4 Jun 2021 09:33:08 +0300 Subject: [PATCH 2/5] Fix Signed-off-by: Anatolii Bazko --- src/api/kube.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/kube.ts b/src/api/kube.ts index 19e9ec988..a0daad687 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1644,7 +1644,9 @@ export class KubeHelper { throw new Error(`Too many resources of type ${resourcePlural}.${resourceAPIGroup} found in the namespace '${namespace}'`) } } catch (e) { - throw this.wrapK8sClientError(e) + if (e.response && e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } } } From 3d068b9d6d59f8ad5597f652d50e55a93032ff34 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Fri, 4 Jun 2021 12:07:30 +0300 Subject: [PATCH 3/5] Improvements Signed-off-by: Anatolii Bazko --- src/api/che-api-client.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/api/che-api-client.ts b/src/api/che-api-client.ts index cae2714b6..5118e4457 100644 --- a/src/api/che-api-client.ts +++ b/src/api/che-api-client.ts @@ -13,7 +13,7 @@ import axios, { AxiosInstance } from 'axios' import { cli } from 'cli-ux' import * as https from 'https' -import { sleep } from '../util' +import { newError, sleep } from '../util' /** * Singleton responsible for calls to Che API. @@ -344,28 +344,27 @@ export class CheApiClient { if (error.response) { const status = error.response.status if (status === 403) { - return new Error(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`) + return newError(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`, error) } else if (status === 401) { - return new Error(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`) + return newError(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`, error) } else if (status === 404) { - return new Error(`E_CHE_API_NOTFOUND - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`) + return newError(`E_CHE_API_NOTFOUND - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`, error) } else if (status === 503) { - return new Error(`E_CHE_API_UNAVAIL - Endpoint: ${endpoint} returned 503 code`) + return newError(`E_CHE_API_UNAVAIL - Endpoint: ${endpoint} returned 503 code`, error) } else { // The request was made and the server responded with a status code // that falls out of the range of 2xx - return new Error(`E_CHE_API_UNKNOWN_ERROR - Endpoint: ${endpoint} -Status: ${error.response.status}`) + return newError(`E_CHE_API_UNKNOWN_ERROR - Endpoint: ${endpoint} -Status: ${error.response.status}`, error) } } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js - return new Error(`E_CHE_API_NO_RESPONSE - Endpoint: ${endpoint} - Error message: ${error.message}`) + return newError(`E_CHE_API_NO_RESPONSE - Endpoint: ${endpoint} - Error message: ${error.message}`, error) } else { // Something happened in setting up the request that triggered an Error - return new Error(`E_CHECTL_UNKNOWN_ERROR - Endpoint: ${endpoint} - Message: ${error.message}`) + return newError(`E_CHECTL_UNKNOWN_ERROR - Endpoint: ${endpoint} - Message: ${error.message}`, error) } } - } From f2a5a3a90c4047b12220542e21b046b9e8e40796 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Fri, 4 Jun 2021 16:31:51 +0300 Subject: [PATCH 4/5] Fix tests Signed-off-by: Anatolii Bazko --- test/e2e/e2e.test.ts | 2 +- test/e2e/util.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/test/e2e/e2e.test.ts b/test/e2e/e2e.test.ts index 01bc53b0e..5f77419dc 100644 --- a/test/e2e/e2e.test.ts +++ b/test/e2e/e2e.test.ts @@ -37,7 +37,7 @@ const INSTALLER_OPERATOR = 'operator' const INSTALLER_HELM = 'helm' const INSTALLER_OLM = 'olm' -const DEVFILE_URL = 'https://raw.githubusercontent.com/eclipse/che-devfile-registry/master/devfiles/quarkus/devfile.yaml' +const DEVFILE_URL = 'https://raw.githubusercontent.com/eclipse-che/che-devfile-registry/master/devfiles/go/devfile.yaml' function getDeployCommand(): string { let command: string diff --git a/test/e2e/util.ts b/test/e2e/util.ts index 74ff96554..6c5052adc 100644 --- a/test/e2e/util.ts +++ b/test/e2e/util.ts @@ -25,7 +25,7 @@ interface WorkspaceInfo { status: string } -export const DEVFILE_URL = 'https://raw.githubusercontent.com/eclipse/che-devfile-registry/master/devfiles/quarkus/devfile.yaml' +export const DEVFILE_URL = 'https://raw.githubusercontent.com/eclipse-che/che-devfile-registry/master/devfiles/go/devfile.yaml' export const NAMESPACE = 'eclipse-che' export const NIGHTLY = 'nightly' @@ -111,8 +111,11 @@ export class E2eHelper { // Return id of test workspaces(e2e-tests. Please look devfile-example.yaml file) async getWorkspaceId(): Promise { const workspaces = await this.getAllWorkspaces() - const workspaceId = workspaces.filter((wks => wks.name.match(this.devfileName))).map(({ id }) => id)[0] + if (workspaces.length === 0) { + throw Error('Workspace not found') + } + const workspaceId = workspaces[0].id if (!workspaceId) { throw Error('Error getting workspaceId') } @@ -123,11 +126,13 @@ export class E2eHelper { // Return the status of test workspaces(e2e-tests. Please look devfile-example.yaml file) async getWorkspaceStatus(): Promise { const workspaces = await this.getAllWorkspaces() - const workspaceStatus = workspaces.filter((wks => wks.name.match(this.devfileName))).map(({ status }) => status)[0] + if (workspaces.length === 0) { + throw Error('Workspace not found') + } + const workspaceStatus = workspaces[0].status if (!workspaceStatus) { - throw Error('Error getting workspace_id') - + throw Error('Error getting workspace status') } return workspaceStatus From 742b648e1c818852bf117aa76cfc7d4cd150fe13 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Mon, 7 Jun 2021 12:02:30 +0300 Subject: [PATCH 5/5] Fix Signed-off-by: Anatolii Bazko --- src/api/kube.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/kube.ts b/src/api/kube.ts index a0daad687..5811e4f24 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1643,6 +1643,8 @@ export class KubeHelper { } else if (crs.length !== 1) { throw new Error(`Too many resources of type ${resourcePlural}.${resourceAPIGroup} found in the namespace '${namespace}'`) } + + return crs[0] } catch (e) { if (e.response && e.response.statusCode !== 404) { throw this.wrapK8sClientError(e)