From 30fe57b53ad5bf6e7b6223c8a967f57ee4cf1bfc Mon Sep 17 00:00:00 2001 From: Flavius Lacatusu Date: Mon, 7 Jun 2021 14:19:39 +0200 Subject: [PATCH 1/5] feat: Deprecate from chectl tslint and eslint and check license headers Signed-off-by: Flavius Lacatusu --- .eslintrc.js | 29 + .github/workflows/automation-check.yml | 9 + .gitignore | 1 + configs/base.eslint.chectl.json | 23 + configs/eslint.json | 6 + configs/eslint.license.json | 25 + package.json | 13 +- run.sh | 6 + src/api/che-api-client.ts | 10 +- src/api/che-login-manager.ts | 10 +- src/api/che.ts | 10 +- src/api/config-manager.ts | 10 +- src/api/context.ts | 10 +- src/api/devfile.ts | 11 +- src/api/github-client.ts | 10 +- src/api/kube.ts | 10 +- src/api/openshift.ts | 10 +- src/api/typings/cert-manager.d.ts | 10 +- src/api/typings/olm.d.ts | 10 +- src/api/typings/openshift.d.ts | 10 +- src/api/version.ts | 10 +- src/commands/auth/delete.ts | 10 +- src/commands/auth/get.ts | 10 +- src/commands/auth/list.ts | 10 +- src/commands/auth/login.ts | 10 +- src/commands/auth/logout.ts | 10 +- src/commands/auth/use.ts | 10 +- src/commands/cacert/export.ts | 10 +- src/commands/dashboard/open.ts | 10 +- src/commands/devfile/generate.ts | 10 +- src/commands/server/debug.ts | 10 +- src/commands/server/delete.ts | 10 +- src/commands/server/deploy.ts | 10 +- src/commands/server/logs.ts | 10 +- src/commands/server/start.ts | 10 +- src/commands/server/status.ts | 10 +- src/commands/server/stop.ts | 10 +- src/commands/server/update.ts | 10 +- src/commands/workspace/create.ts | 10 +- src/commands/workspace/delete.ts | 10 +- src/commands/workspace/inject.ts | 10 +- src/commands/workspace/list.ts | 10 +- src/commands/workspace/logs.ts | 10 +- src/commands/workspace/start.ts | 10 +- src/commands/workspace/stop.ts | 10 +- src/common-flags.ts | 10 +- src/constants.ts | 10 +- src/hooks/analytics/analytics.ts | 10 +- src/hooks/analytics/segment-adapter.ts | 10 +- src/hooks/prerun/new-version-warning.ts | 8 +- src/index.ts | 10 +- src/tasks/che.ts | 10 +- .../component-installers/cert-manager.ts | 10 +- .../devfile-workspace-operator-installer.ts | 10 +- src/tasks/installers/common-tasks.ts | 10 +- src/tasks/installers/helm.ts | 10 +- src/tasks/installers/installer.ts | 10 +- src/tasks/installers/olm.ts | 10 +- src/tasks/installers/operator.ts | 10 +- src/tasks/kube.ts | 10 +- src/tasks/platforms/api.ts | 10 +- src/tasks/platforms/common-platform-tasks.ts | 10 +- src/tasks/platforms/crc.ts | 10 +- src/tasks/platforms/docker-desktop.ts | 10 +- src/tasks/platforms/k8s.ts | 10 +- src/tasks/platforms/microk8s.ts | 10 +- src/tasks/platforms/minikube.ts | 10 +- src/tasks/platforms/minishift.ts | 10 +- src/tasks/platforms/openshift.ts | 10 +- src/tasks/platforms/platform.ts | 10 +- src/util.ts | 10 +- tslint.json | 6 - yarn.lock | 617 +++++++++++++----- 73 files changed, 952 insertions(+), 412 deletions(-) create mode 100644 .eslintrc.js create mode 100644 configs/base.eslint.chectl.json create mode 100644 configs/eslint.json create mode 100644 configs/eslint.license.json create mode 100644 run.sh delete mode 100644 tslint.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..2515de07c --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,29 @@ + /** + * + * Copyright (c) 2019-2021 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 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + + /** @type {import('eslint').Linter.Config} */ + module.exports = { + root: true, + extends: [ + './configs/eslint.json' + ], + ignorePatterns: [ + '**/{node_modules,lib}' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json', + sourceType: "module", + ecmaVersion: 2015, + } +}; diff --git a/.github/workflows/automation-check.yml b/.github/workflows/automation-check.yml index 5422b3e57..8e02825d9 100644 --- a/.github/workflows/automation-check.yml +++ b/.github/workflows/automation-check.yml @@ -24,3 +24,12 @@ jobs: echo "[ERROR] README.md it is not up to date. Please run 'yarn oclif-dev readme' to update and commit the changes." exit 1 fi + - name: Check license header year eslint template + run: | + TEMPLATE=$(jq '.rules."header/header"[2][1].pattern' configs/eslint.license.json) + CURRENT_YEAR=$(date +'%Y') + + if [[ ${TEMPLATE} != *"$CURRENT_YEAR"* ]];then + echo -e "[ERROR] The license header template doesn't contain the current year. Please change the year in configs/eslint.license.json and run yarn lint:fix" + exit 1 + fi diff --git a/.gitignore b/.gitignore index d4ff18c2b..53ea70314 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ tsconfig.tsbuildinfo oclif.manifest.json # templates are added at post-install step templates +.eslintcache diff --git a/configs/base.eslint.chectl.json b/configs/base.eslint.chectl.json new file mode 100644 index 000000000..23de9e303 --- /dev/null +++ b/configs/base.eslint.chectl.json @@ -0,0 +1,23 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 6, + "ecmaFeatures": { + "jsx": false + } + }, + "plugins": [ + "@typescript-eslint", + "no-null" + ], + "env": { + "browser": true, + "mocha": true, + "node": true + }, + "ignorePatterns": [ + "node_modules", + "lib" + ] + } diff --git a/configs/eslint.json b/configs/eslint.json new file mode 100644 index 000000000..fbef61a3b --- /dev/null +++ b/configs/eslint.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "./eslint.license.json", + "./base.eslint.chectl.json" + ] +} diff --git a/configs/eslint.license.json b/configs/eslint.license.json new file mode 100644 index 000000000..00f5af1c1 --- /dev/null +++ b/configs/eslint.license.json @@ -0,0 +1,25 @@ +{ + "plugins": ["@typescript-eslint", "header"], + "rules": { + "header/header": [ + 2, + "block", + [ + "*", + { + "pattern": "^ \\* Copyright \\(c\\) 2021 Red Hat, Inc\\.$", + "template": " * Copyright (c) 2021 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", + " *", + " * Contributors:", + " * Red Hat, Inc. - initial API and implementation", + " " + ] + ] + } + } diff --git a/package.json b/package.json index d5bcf0395..3448aa598 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "@eclipse-che/api": "latest", "@oclif/dev-cli": "^1", "@oclif/test": "^1", - "@oclif/tslint": "^3", "@types/chai": "^4", "@types/command-exists": "^1.2.0", "@types/countries-and-timezones": "^2.0.3", @@ -73,14 +72,18 @@ "@types/uuid": "^8.3.0", "@types/websocket": "^1.0.1", "@types/ws": "^7.4.0", + "@typescript-eslint/eslint-plugin": "^4.26.0", + "@typescript-eslint/parser": "^4.26.0", "chai": "^4.2.0", "cpx": "^1.5.0", + "eslint": "^7.28.0", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-no-null": "^1.0.2", "globby": "^11", "jest": "^26.6.3", "nock": "^11.7.0", "ts-jest": "^26.5.1", "ts-node": "^9", - "tslint": "^6", "typescript": "^4.1", "typescript-formatter": "7.2.2" }, @@ -154,18 +157,18 @@ "postinstall-repositories": "yarn upgrade eclipse-che-server eclipse-che-operator eclipse-che-devfile-workspace-operator", "postinstall-cleanup": "rimraf node_modules/eclipse-che-server && rimraf node_modules/eclipse-che-operator", "test": "jest --collect-coverage", - "posttest": "tslint -p test -t stylish", "test-watch": "jest --watchAll", "e2e-minikube-helm": "export PLATFORM=minikube && export INSTALLER=helm && yarn jest ./test/e2e/e2e.test.ts --testRegex='/test/(e2e)/.*.test.ts'", "e2e-minikube-operator": "export PLATFORM=minikube && export INSTALLER=operator && yarn jest ./test/e2e/e2e.test.ts --testRegex='/test/(e2e)/.*.test.ts'", "e2e-minishift": "export PLATFORM=minishift && export INSTALLER=operator && yarn jest ./test/e2e/e2e.test.ts --testRegex='/test/(e2e)/.*.test.ts'", "e2e-openshift": "export PLATFORM=openshift && export INSTALLER=operator && yarn jest ./test/e2e/e2e.test.ts --testRegex='/test/(e2e)/.*.test.ts'", "gnirts-ci": "node .ci/obfuscate/gnirts.js", - "prepack": "rm -rf lib && rm -rf tsconfig.tsbuildinfo && tsc -b && oclif-dev manifest && oclif-dev readme && yarn gnirts-ci", + "prepack": "yarn lint && rm -rf lib && rm -rf tsconfig.tsbuildinfo && tsc -b && oclif-dev manifest && oclif-dev readme && yarn gnirts-ci", "pack-binaries": "oclif-dev pack", "postpack": "rm -f oclif.manifest.json", "format": "tsfmt -r --useTsfmt tsfmt.json", - "tslint-fix": "tslint --fix -p test -t stylish", + "lint": "eslint --cache=true --no-error-on-unmatched-pattern=true '{src,tests}/**/*.ts'", + "lint:fix": "eslint --fix --cache=true --no-error-on-unmatched-pattern=true \"{src,tests}/**/*.{ts,tsx}\"", "version": "oclif-dev readme && git add README.md", "watch": "tsc --watch" }, diff --git a/run.sh b/run.sh new file mode 100644 index 000000000..0dc8f71d8 --- /dev/null +++ b/run.sh @@ -0,0 +1,6 @@ +TEMPLATE=$(jq '.rules."header/header"[2][1].pattern' configs/eslint.license.json) +CURRENT_YEAR=$(date +'%Y') + +if [[ ${TEMPLATE} != *"$CURRENT_YEAR"* ]];then + echo -e "[INFO] Your license header template doesn't contain the current year. Please change config/eslint.license.json year and run yarn lint:fix" +fi diff --git a/src/api/che-api-client.ts b/src/api/che-api-client.ts index cae2714b6..b0c96bbd5 100644 --- a/src/api/che-api-client.ts +++ b/src/api/che-api-client.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019-2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { che as chetypes } from '@eclipse-che/api' import axios, { AxiosInstance } from 'axios' diff --git a/src/api/che-login-manager.ts b/src/api/che-login-manager.ts index d63636b7b..7dcc95c6d 100644 --- a/src/api/che-login-manager.ts +++ b/src/api/che-login-manager.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import axios, { AxiosInstance } from 'axios' import * as fs from 'fs-extra' diff --git a/src/api/che.ts b/src/api/che.ts index e0ca40118..fc5474d02 100644 --- a/src/api/che.ts +++ b/src/api/che.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019-2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { che as chetypes } from '@eclipse-che/api' import { CoreV1Api, V1Pod, Watch } from '@kubernetes/client-node' diff --git a/src/api/config-manager.ts b/src/api/config-manager.ts index 964fb5b7e..3b8f919fd 100644 --- a/src/api/config-manager.ts +++ b/src/api/config-manager.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import * as fs from 'fs-extra' import { merge } from 'lodash' diff --git a/src/api/context.ts b/src/api/context.ts index b4a8e132d..1a44fe074 100644 --- a/src/api/context.ts +++ b/src/api/context.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import Command from '@oclif/command' import Listr = require('listr') diff --git a/src/api/devfile.ts b/src/api/devfile.ts index 0c2e0abf9..08b8c3219 100644 --- a/src/api/devfile.ts +++ b/src/api/devfile.ts @@ -1,6 +1,13 @@ -// Generated automatically via quicktype.io /** - * This schema describes the structure of the devfile object + * Copyright (c) 2021 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 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation */ export interface Devfile { attributes?: { [key: string]: string } diff --git a/src/api/github-client.ts b/src/api/github-client.ts index 7d3bededc..63c2e3481 100644 --- a/src/api/github-client.ts +++ b/src/api/github-client.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020-2021 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Octokit } from '@octokit/rest' diff --git a/src/api/kube.ts b/src/api/kube.ts index a4272ab3c..64250f511 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019-2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { AdmissionregistrationV1Api, ApiextensionsV1Api, ApiextensionsV1beta1Api, ApisApi, AppsV1Api, AuthorizationV1Api, BatchV1Api, CoreV1Api, CustomObjectsApi, ExtensionsV1beta1Api, ExtensionsV1beta1IngressList, KubeConfig, Log, PortForward, RbacAuthorizationV1Api, V1ClusterRole, V1ClusterRoleBinding, V1ClusterRoleBindingList, V1ConfigMap, V1ConfigMapEnvSource, V1Container, V1ContainerStateTerminated, V1ContainerStateWaiting, V1Deployment, V1DeploymentList, V1DeploymentSpec, V1EnvFromSource, V1Job, V1JobSpec, V1LabelSelector, V1MutatingWebhookConfiguration, V1Namespace, V1NamespaceList, V1ObjectMeta, V1PersistentVolumeClaimList, V1Pod, V1PodCondition, V1PodList, V1PodSpec, V1PodTemplateSpec, V1PolicyRule, V1Role, V1RoleBinding, V1RoleBindingList, V1RoleList, V1RoleRef, V1Secret, V1SelfSubjectAccessReview, V1SelfSubjectAccessReviewSpec, V1Service, V1ServiceAccount, V1ServiceList, V1Subject, Watch } from '@kubernetes/client-node' import { Cluster, Context } from '@kubernetes/client-node/dist/config_types' diff --git a/src/api/openshift.ts b/src/api/openshift.ts index 621b705a7..e13b0e191 100644 --- a/src/api/openshift.ts +++ b/src/api/openshift.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import execa = require('execa') diff --git a/src/api/typings/cert-manager.d.ts b/src/api/typings/cert-manager.d.ts index 1be12fe5f..eae8d420f 100644 --- a/src/api/typings/cert-manager.d.ts +++ b/src/api/typings/cert-manager.d.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ export interface V1Certificate { apiVersion: string diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index 32689cb3c..54677a1b5 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { V1Deployment, V1ObjectMeta } from '@kubernetes/client-node' diff --git a/src/api/typings/openshift.d.ts b/src/api/typings/openshift.d.ts index b4d35ece6..ffa1701ab 100644 --- a/src/api/typings/openshift.d.ts +++ b/src/api/typings/openshift.d.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ export interface OAuth { apiVersion: string; diff --git a/src/api/version.ts b/src/api/version.ts index f0e4e79b0..77b44ba85 100644 --- a/src/api/version.ts +++ b/src/api/version.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import axios from 'axios' import { cli } from 'cli-ux' diff --git a/src/commands/auth/delete.ts b/src/commands/auth/delete.ts index 817df3725..be34ed8bd 100644 --- a/src/commands/auth/delete.ts +++ b/src/commands/auth/delete.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/auth/get.ts b/src/commands/auth/get.ts index 66d90d306..7e7e70986 100644 --- a/src/commands/auth/get.ts +++ b/src/commands/auth/get.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/auth/list.ts b/src/commands/auth/list.ts index 9d01cb82f..b8e1cdc2e 100644 --- a/src/commands/auth/list.ts +++ b/src/commands/auth/list.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index 3a3f5a760..d59b92230 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { string } from '@oclif/parser/lib/flags' diff --git a/src/commands/auth/logout.ts b/src/commands/auth/logout.ts index 0e68dc943..5728fcdaf 100644 --- a/src/commands/auth/logout.ts +++ b/src/commands/auth/logout.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/auth/use.ts b/src/commands/auth/use.ts index d4fe72696..3ed22a01a 100644 --- a/src/commands/auth/use.ts +++ b/src/commands/auth/use.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/cacert/export.ts b/src/commands/cacert/export.ts index ce66fa33a..bc6bb4aef 100644 --- a/src/commands/cacert/export.ts +++ b/src/commands/cacert/export.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { string } from '@oclif/parser/lib/flags' diff --git a/src/commands/dashboard/open.ts b/src/commands/dashboard/open.ts index 21a6b412c..0241b68d6 100644 --- a/src/commands/dashboard/open.ts +++ b/src/commands/dashboard/open.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/devfile/generate.ts b/src/commands/devfile/generate.ts index 8f342e1c7..be1cae7e0 100644 --- a/src/commands/devfile/generate.ts +++ b/src/commands/devfile/generate.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { ExtensionsV1beta1Ingress, V1Deployment, V1DeploymentSpec, V1ObjectMeta, V1PersistentVolumeClaim, V1PersistentVolumeClaimSpec, V1PodTemplateSpec, V1Service, V1ServicePort, V1ServiceSpec } from '@kubernetes/client-node' import { Command, flags } from '@oclif/command' diff --git a/src/commands/server/debug.ts b/src/commands/server/debug.ts index 8d13541f0..beac0c310 100644 --- a/src/commands/server/debug.ts +++ b/src/commands/server/debug.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { integer } from '@oclif/parser/lib/flags' diff --git a/src/commands/server/delete.ts b/src/commands/server/delete.ts index 90a6646b7..7a4f78f1e 100644 --- a/src/commands/server/delete.ts +++ b/src/commands/server/delete.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { boolean } from '@oclif/command/lib/flags' diff --git a/src/commands/server/deploy.ts b/src/commands/server/deploy.ts index e72d80f13..94eeb5c16 100644 --- a/src/commands/server/deploy.ts +++ b/src/commands/server/deploy.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { boolean, string } from '@oclif/parser/lib/flags' diff --git a/src/commands/server/logs.ts b/src/commands/server/logs.ts index cbe542a7d..60355538a 100644 --- a/src/commands/server/logs.ts +++ b/src/commands/server/logs.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { string } from '@oclif/parser/lib/flags' diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index c65aea01e..737a68557 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/server/status.ts b/src/commands/server/status.ts index 6da700e4a..4e89dcd3d 100644 --- a/src/commands/server/status.ts +++ b/src/commands/server/status.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/server/stop.ts b/src/commands/server/stop.ts index 755a33c74..37ae5e880 100644 --- a/src/commands/server/stop.ts +++ b/src/commands/server/stop.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { string } from '@oclif/parser/lib/flags' diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index ecfdf8d98..3d63027f3 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { string } from '@oclif/parser/lib/flags' diff --git a/src/commands/workspace/create.ts b/src/commands/workspace/create.ts index 09cad7737..16f4b9dbb 100644 --- a/src/commands/workspace/create.ts +++ b/src/commands/workspace/create.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019-2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { boolean, string } from '@oclif/parser/lib/flags' diff --git a/src/commands/workspace/delete.ts b/src/commands/workspace/delete.ts index 383c5b805..743938ce8 100644 --- a/src/commands/workspace/delete.ts +++ b/src/commands/workspace/delete.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/workspace/inject.ts b/src/commands/workspace/inject.ts index 8aeed847f..c6af2da0e 100644 --- a/src/commands/workspace/inject.ts +++ b/src/commands/workspace/inject.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Context } from '@kubernetes/client-node/dist/config_types' import { Command, flags } from '@oclif/command' diff --git a/src/commands/workspace/list.ts b/src/commands/workspace/list.ts index cb217afe1..1fa91c4fa 100644 --- a/src/commands/workspace/list.ts +++ b/src/commands/workspace/list.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/workspace/logs.ts b/src/commands/workspace/logs.ts index b29fd2efe..432b37a17 100644 --- a/src/commands/workspace/logs.ts +++ b/src/commands/workspace/logs.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { string } from '@oclif/parser/lib/flags' diff --git a/src/commands/workspace/start.ts b/src/commands/workspace/start.ts index 5b2273756..7fd408686 100644 --- a/src/commands/workspace/start.ts +++ b/src/commands/workspace/start.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019-2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import Command, { flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/commands/workspace/stop.ts b/src/commands/workspace/stop.ts index aeacff122..91a226590 100644 --- a/src/commands/workspace/stop.ts +++ b/src/commands/workspace/stop.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command, flags } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/common-flags.ts b/src/common-flags.ts index 505547bf2..0f7d4a585 100644 --- a/src/common-flags.ts +++ b/src/common-flags.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { boolean, string } from '@oclif/parser/lib/flags' import { DEFAULT_CHE_NAMESPACE, DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE, DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT, DEFAULT_K8S_POD_WAIT_TIMEOUT, DOC_LINK_OBTAIN_ACCESS_TOKEN, DOC_LINK_OBTAIN_ACCESS_TOKEN_OAUTH } from './constants' diff --git a/src/constants.ts b/src/constants.ts index 095969615..1f69cad4e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ export const CHECTL_PROJECT_NAME = 'chectl' export const OPERATOR_TEMPLATE_DIR = 'che-operator' diff --git a/src/hooks/analytics/analytics.ts b/src/hooks/analytics/analytics.ts index 625c80aa2..435a69bff 100644 --- a/src/hooks/analytics/analytics.ts +++ b/src/hooks/analytics/analytics.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { IConfig } from '@oclif/config' import { cli } from 'cli-ux' diff --git a/src/hooks/analytics/segment-adapter.ts b/src/hooks/analytics/segment-adapter.ts index fcb8a6a30..e630f3c43 100644 --- a/src/hooks/analytics/segment-adapter.ts +++ b/src/hooks/analytics/segment-adapter.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { cli } from 'cli-ux' import { getTimezone } from 'countries-and-timezones' import * as fs from 'fs-extra' diff --git a/src/hooks/prerun/new-version-warning.ts b/src/hooks/prerun/new-version-warning.ts index fc1cd1998..299e4351f 100644 --- a/src/hooks/prerun/new-version-warning.ts +++ b/src/hooks/prerun/new-version-warning.ts @@ -1,12 +1,14 @@ -/********************************************************************* +/** * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Hook } from '@oclif/config' import { cli } from 'cli-ux' diff --git a/src/index.ts b/src/index.ts index 014630136..71556b35c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,13 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ export { run } from '@oclif/command' diff --git a/src/tasks/che.ts b/src/tasks/che.ts index 5d1ab39ed..a29e0516d 100644 --- a/src/tasks/che.ts +++ b/src/tasks/che.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as Listr from 'listr' diff --git a/src/tasks/component-installers/cert-manager.ts b/src/tasks/component-installers/cert-manager.ts index 068dd6f76..20157c4ab 100644 --- a/src/tasks/component-installers/cert-manager.ts +++ b/src/tasks/component-installers/cert-manager.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import * as fs from 'fs-extra' import * as Listr from 'listr' diff --git a/src/tasks/component-installers/devfile-workspace-operator-installer.ts b/src/tasks/component-installers/devfile-workspace-operator-installer.ts index 83babdf5b..904af6105 100644 --- a/src/tasks/component-installers/devfile-workspace-operator-installer.ts +++ b/src/tasks/component-installers/devfile-workspace-operator-installer.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { cli } from 'cli-ux' import * as Listr from 'listr' diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index beb14986a..16694c4b6 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import Command from '@oclif/command' import ansi = require('ansi-colors') diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index 742764bc9..9b44f5f03 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index f1d8fbb0c..cbe943e7a 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import Command from '@oclif/command' import * as Listr from 'listr' diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 66776e947..126c5254a 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import Command from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 72c1fc477..46744fd31 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { V1ClusterRole, V1ClusterRoleBinding, V1Deployment, V1Role, V1RoleBinding } from '@kubernetes/client-node' import { Command } from '@oclif/command' import { cli } from 'cli-ux' diff --git a/src/tasks/kube.ts b/src/tasks/kube.ts index f7e17eb1a..f25a584dc 100644 --- a/src/tasks/kube.ts +++ b/src/tasks/kube.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { cli } from 'cli-ux' import * as Listr from 'listr' diff --git a/src/tasks/platforms/api.ts b/src/tasks/platforms/api.ts index b228ab765..58c359abd 100644 --- a/src/tasks/platforms/api.ts +++ b/src/tasks/platforms/api.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import { cli } from 'cli-ux' import * as Listr from 'listr' diff --git a/src/tasks/platforms/common-platform-tasks.ts b/src/tasks/platforms/common-platform-tasks.ts index f2fba7bf3..89e4c8245 100644 --- a/src/tasks/platforms/common-platform-tasks.ts +++ b/src/tasks/platforms/common-platform-tasks.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import * as http from 'http' import * as https from 'https' diff --git a/src/tasks/platforms/crc.ts b/src/tasks/platforms/crc.ts index 6896e7b94..da389620f 100644 --- a/src/tasks/platforms/crc.ts +++ b/src/tasks/platforms/crc.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/docker-desktop.ts b/src/tasks/platforms/docker-desktop.ts index dc892b4c4..5bfaf33ac 100644 --- a/src/tasks/platforms/docker-desktop.ts +++ b/src/tasks/platforms/docker-desktop.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/k8s.ts b/src/tasks/platforms/k8s.ts index f7d343413..167ff8140 100644 --- a/src/tasks/platforms/k8s.ts +++ b/src/tasks/platforms/k8s.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/microk8s.ts b/src/tasks/platforms/microk8s.ts index d66949c40..1d4787fad 100644 --- a/src/tasks/platforms/microk8s.ts +++ b/src/tasks/platforms/microk8s.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/minikube.ts b/src/tasks/platforms/minikube.ts index b4e71b16e..94c3d61d2 100644 --- a/src/tasks/platforms/minikube.ts +++ b/src/tasks/platforms/minikube.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/minishift.ts b/src/tasks/platforms/minishift.ts index 097d82c0a..1ba08232b 100644 --- a/src/tasks/platforms/minishift.ts +++ b/src/tasks/platforms/minishift.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/openshift.ts b/src/tasks/platforms/openshift.ts index 963888195..e799a01b2 100644 --- a/src/tasks/platforms/openshift.ts +++ b/src/tasks/platforms/openshift.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import { Command } from '@oclif/command' import * as commandExists from 'command-exists' diff --git a/src/tasks/platforms/platform.ts b/src/tasks/platforms/platform.ts index 865640cb7..66303d76c 100644 --- a/src/tasks/platforms/platform.ts +++ b/src/tasks/platforms/platform.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import Command from '@oclif/command' import * as Listr from 'listr' diff --git a/src/util.ts b/src/util.ts index c4ad7f011..73f88d8e3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,12 +1,14 @@ -/********************************************************************* - * Copyright (c) 2020 Red Hat, Inc. - * +/** + * Copyright (c) 2021 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 - **********************************************************************/ + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ import UpdateCommand from '@oclif/plugin-update/lib/commands/update' import axios from 'axios' diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 6afc81835..000000000 --- a/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "@oclif/tslint", - "rules": { - "object-curly-spacing": "always" - } -} diff --git a/yarn.lock b/yarn.lock index 56b6f5b5e..cd3133b4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" @@ -288,24 +295,20 @@ resolved "https://registry.yarnpkg.com/@eclipse-che/api/-/api-7.18.1.tgz#1beae9ebe694e4b58d0edfefcde07995ce6cd0b2" integrity sha512-KnnnDpnxxK0TBgR0Ux3oOYCvmCv6d4cRA+1j9wiLkaJighCqgCUaxnHWXEfolYbRq1+o/sfsxN+BxRDuF+H8wQ== -"@fimbul/bifrost@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.21.0.tgz#d0fafa25938fda475657a6a1e407a21bbe02c74e" - integrity sha512-ou8VU+nTmOW1jeg+FT+sn+an/M0Xb9G16RucrfhjXGWv1Q97kCoM5CG9Qj7GYOSdu7km72k7nY83Eyr53Bkakg== - dependencies: - "@fimbul/ymir" "^0.21.0" - get-caller-file "^2.0.0" - tslib "^1.8.1" - tsutils "^3.5.0" - -"@fimbul/ymir@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/ymir/-/ymir-0.21.0.tgz#8525726787aceeafd4e199472c0d795160b5d4a1" - integrity sha512-T/y7WqPsm4n3zhT08EpB5sfdm2Kvw3gurAxr2Lr5dQeLi8ZsMlNT/Jby+ZmuuAAd1PnXYzKp+2SXgIkQIIMCUg== +"@eslint/eslintrc@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" + integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== dependencies: - inversify "^5.0.0" - reflect-metadata "^0.1.12" - tslib "^1.8.1" + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -695,14 +698,6 @@ dependencies: fancy-test "^1.4.3" -"@oclif/tslint@^3": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@oclif/tslint/-/tslint-3.1.1.tgz#e055cdf158630862fd44546f6b8fb1266c9778e7" - integrity sha512-B1ZWbgzwxDhNZLzVnn+JjyFf9u+J9wNwsz/ZX9YvA9edRYcdiJz9JikCttGPi35V0NU0TUV4UqTqo/q/wQ06jQ== - dependencies: - tslint-eslint-rules "^5.4.0" - tslint-xo "^0.9.0" - "@octokit/auth-token@^2.4.4": version "2.4.5" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" @@ -972,6 +967,11 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb" integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww== +"@types/json-schema@^7.0.7": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/listr@^0.14.2": version "0.14.2" resolved "https://registry.yarnpkg.com/@types/listr/-/listr-0.14.2.tgz#2e5f80fbc3ca8dceb9940ce9bf8e3113ab452545" @@ -1151,6 +1151,76 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.0.tgz#12bbd6ebd5e7fabd32e48e1e60efa1f3554a3242" + integrity sha512-yA7IWp+5Qqf+TLbd8b35ySFOFzUfL7i+4If50EqvjT6w35X8Lv0eBHb6rATeWmucks37w+zV+tWnOXI9JlG6Eg== + dependencies: + "@typescript-eslint/experimental-utils" "4.26.0" + "@typescript-eslint/scope-manager" "4.26.0" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + lodash "^4.17.21" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.0.tgz#ba7848b3f088659cdf71bce22454795fc55be99a" + integrity sha512-TH2FO2rdDm7AWfAVRB5RSlbUhWxGVuxPNzGT7W65zVfl8H/WeXTk1e69IrcEVsBslrQSTDKQSaJD89hwKrhdkw== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/typescript-estree" "4.26.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.0.tgz#31b6b732c9454f757b020dab9b6754112aa5eeaf" + integrity sha512-b4jekVJG9FfmjUfmM4VoOItQhPlnt6MPOBUL0AQbiTmm+SSpSdhHYlwayOm4IW9KLI/4/cRKtQCmDl1oE2OlPg== + dependencies: + "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/typescript-estree" "4.26.0" + debug "^4.3.1" + +"@typescript-eslint/scope-manager@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.0.tgz#60d1a71df162404e954b9d1c6343ff3bee496194" + integrity sha512-G6xB6mMo4xVxwMt5lEsNTz3x4qGDt0NSGmTBNBPJxNsrTXJSm21c6raeYroS2OwQsOyIXqKZv266L/Gln1BWqg== + dependencies: + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/visitor-keys" "4.26.0" + +"@typescript-eslint/types@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.0.tgz#7c6732c0414f0a69595f4f846ebe12616243d546" + integrity sha512-rADNgXl1kS/EKnDr3G+m7fB9yeJNnR9kF7xMiXL6mSIWpr3Wg5MhxyfEXy/IlYthsqwBqHOr22boFbf/u6O88A== + +"@typescript-eslint/typescript-estree@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.0.tgz#aea17a40e62dc31c63d5b1bbe9a75783f2ce7109" + integrity sha512-GHUgahPcm9GfBuy3TzdsizCcPjKOAauG9xkz9TR8kOdssz2Iz9jRCSQm6+aVFa23d5NcSpo1GdHGSQKe0tlcbg== + dependencies: + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/visitor-keys" "4.26.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.0.tgz#26d2583169222815be4dcd1da4fe5459bc3bcc23" + integrity sha512-cw4j8lH38V1ycGBbF+aFiLUls9Z0Bw8QschP3mkth50BbWzgFS33ISIgBzUMuQ2IdahoEv/rXstr8Zhlz4B1Zg== + dependencies: + "@typescript-eslint/types" "4.26.0" + eslint-visitor-keys "^2.0.0" + abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -1164,6 +1234,11 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-jsx@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" @@ -1174,6 +1249,11 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== +acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + acorn@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" @@ -1187,6 +1267,16 @@ aggregate-error@^1.0.0: clean-stack "^1.0.0" indent-string "^3.0.0" +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.12.3: version "6.12.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" @@ -1197,6 +1287,16 @@ ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.6.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.0.tgz#60cc45d9c46a477d80d92c48076d972c342e5720" + integrity sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + analytics-node@^3.4.0-beta.3: version "3.4.0-beta.3" resolved "https://registry.yarnpkg.com/analytics-node/-/analytics-node-3.4.0-beta.3.tgz#5eb0694598cff493c64faf5efc1225533a253f13" @@ -1211,7 +1311,7 @@ analytics-node@^3.4.0-beta.3: remove-trailing-slash "^0.1.0" uuid "^3.2.1" -ansi-colors@4.1.1: +ansi-colors@4.1.1, ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== @@ -1373,6 +1473,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -1692,11 +1797,6 @@ buffers@~0.1.1: resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - byline@5.x, byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -1795,7 +1895,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2077,7 +2177,7 @@ command-exists@^1.2.9: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@^2.12.1, commander@^2.19.0: +commander@^2.19.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2162,7 +2262,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2233,7 +2333,7 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -2269,7 +2369,7 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" -deep-is@~0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -2338,13 +2438,12 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523" - integrity sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM= +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: - esutils "^1.1.6" - isarray "0.0.1" + esutils "^2.0.2" domexception@^2.0.1: version "2.0.1" @@ -2380,15 +2479,15 @@ ecc-jsbn@~0.1.1: "eclipse-che-devfile-workspace-operator@git://github.com/devfile/devworkspace-operator#main": version "0.0.0" - resolved "git://github.com/devfile/devworkspace-operator#698baff5f24938a74db97490a564451e80091c36" + resolved "git://github.com/devfile/devworkspace-operator#ee228b8da7499229592d8b553606a3a56609eec4" "eclipse-che-operator@git://github.com/eclipse-che/che-operator#main": version "0.0.0" - resolved "git://github.com/eclipse-che/che-operator#72f8d81cc7fe15d3ace93d5272a4443f8421b9d9" + resolved "git://github.com/eclipse-che/che-operator#b2f4f44f4519898e9e7a17152b2197b5cd4bd4ac" "eclipse-che-server@git://github.com/eclipse-che/che-server#main": version "0.0.0" - resolved "git://github.com/eclipse-che/che-server#578e36ecf3cf05512362b1a93729d79568c32482" + resolved "git://github.com/eclipse-che/che-server#a4f454b518fb327c2a51ab3ee22f458bb923ff06" editorconfig@^0.15.0: version "0.15.3" @@ -2432,6 +2531,13 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -2444,7 +2550,7 @@ es6-promise@^4.2.8: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== -escape-string-regexp@4.0.0: +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== @@ -2471,21 +2577,131 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-plugin-header@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" + integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== + +eslint-plugin-no-null@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz#1236a812391390a1877ad4007c26e745341c951f" + integrity sha1-EjaoEjkTkKGHetQAfCbnRTQclR8= + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint@^7.28.0: + version "7.28.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.28.0.tgz#435aa17a0b82c13bb2be9d51408b617e49c1e820" + integrity sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.1.2" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.9" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^5.2.0: +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== -esutils@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.1.6.tgz#c01ccaa9ae4b897c6d0c3e210ae52f3c7a844375" - integrity sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U= - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2664,7 +2880,7 @@ fancy-test@^1.4.3, fancy-test@^1.4.9: mock-stdin "^1.0.0" stdout-stderr "^0.1.9" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -2686,7 +2902,7 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -2727,6 +2943,13 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -2783,6 +3006,19 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + follow-redirects@1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" @@ -2918,12 +3154,17 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.0, get-caller-file@^2.0.1: +get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -3005,6 +3246,13 @@ glob-parent@^5.1.0: dependencies: is-glob "^4.0.1" +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob2base@^0.0.12: version "0.0.12" resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" @@ -3029,6 +3277,13 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^13.6.0, globals@^13.9.0: + version "13.9.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" + integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== + dependencies: + type-fest "^0.20.2" + globby@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" @@ -3043,7 +3298,7 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^11, globby@^11.0.1: +globby@^11, globby@^11.0.1, globby@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== @@ -3243,11 +3498,24 @@ ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + ignore@^5.1.1, ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -3316,11 +3584,6 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -inversify@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.0.1.tgz#500d709b1434896ce5a0d58915c4a4210e34fb6e" - integrity sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ== - invert-kv@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523" @@ -3471,7 +3734,7 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" -is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -3593,11 +3856,6 @@ is-wsl@^2.1.1, is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -4132,11 +4390,21 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -4228,6 +4496,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -4322,11 +4598,21 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -4347,6 +4633,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + lodash@4.x, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -4601,7 +4892,7 @@ mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -4897,6 +5188,18 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + os-locale@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-5.0.0.tgz#6d26c1d95b6597c5d5317bf5fba37eccec3672e0" @@ -4991,6 +5294,13 @@ pako@~1.0.5: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -5106,6 +5416,11 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -5141,6 +5456,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + prompts@^2.0.1: version "2.3.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" @@ -5306,11 +5626,6 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -reflect-metadata@^0.1.12: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" @@ -5331,6 +5646,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -5398,6 +5718,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -5410,6 +5735,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -5553,7 +5883,7 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -5570,6 +5900,13 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.2.1, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -5658,6 +5995,15 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -5932,6 +6278,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + subarg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" @@ -5984,6 +6335,18 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +table@^6.0.9: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + tar-fs@^1.16.3: version "1.16.3" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" @@ -6057,6 +6420,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -6209,12 +6577,7 @@ ts-node@^9: source-map-support "^0.5.17" yn "3.1.1" -tslib@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" - integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== - -tslib@^1, tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== @@ -6224,77 +6587,10 @@ tslib@^2, tslib@^2.0.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== -tslint-consistent-codestyle@^1.11.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.16.0.tgz#52348ea899a7e025b37cc6545751c6a566a19077" - integrity sha512-ebR/xHyMEuU36hGNOgCfjGBNYxBPixf0yU1Yoo6s3BrpBRFccjPOmIVaVvQsWAUAMdmfzHOCihVkcaMfimqvHw== - dependencies: - "@fimbul/bifrost" "^0.21.0" - tslib "^1.7.1" - tsutils "^2.29.0" - -tslint-eslint-rules@^5.3.1, tslint-eslint-rules@^5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5" - integrity sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w== - dependencies: - doctrine "0.7.2" - tslib "1.9.0" - tsutils "^3.0.0" - -tslint-microsoft-contrib@^5.0.2: - version "5.2.1" - resolved "https://registry.yarnpkg.com/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz#a6286839f800e2591d041ea2800c77487844ad81" - integrity sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA== - dependencies: - tsutils "^2.27.2 <2.29.0" - -tslint-xo@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/tslint-xo/-/tslint-xo-0.9.0.tgz#e1faa505fecd5ac705460fc9f5ca417c3036c14f" - integrity sha512-Zk5jBdQVUaHEmR9TUoh1TJOjjCr7/nRplA+jDZBvucyBMx65pt0unTr6H/0HvrtSlucFvOMYsyBZE1W8b4AOig== - dependencies: - tslint-consistent-codestyle "^1.11.0" - tslint-eslint-rules "^5.3.1" - tslint-microsoft-contrib "^5.0.2" - -tslint@^6: - version "6.1.3" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" - integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.3" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.13.0" - tsutils "^2.29.0" - -"tsutils@^2.27.2 <2.29.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.28.0.tgz#6bd71e160828f9d019b6f4e844742228f85169a1" - integrity sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA== - dependencies: - tslib "^1.8.1" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - -tsutils@^3.0.0, tsutils@^3.5.0: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -6310,6 +6606,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -6327,6 +6630,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -6460,6 +6768,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + v8-to-istanbul@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz#04bfd1026ba4577de5472df4f5e89af49de5edda" @@ -6580,7 +6893,7 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== From 6530e416fd7ad2ac5179d47b22d53c9ae981e444 Mon Sep 17 00:00:00 2001 From: Flavius Lacatusu Date: Tue, 8 Jun 2021 10:46:08 +0200 Subject: [PATCH 2/5] feat: Add eslint rules Signed-off-by: Flavius Lacatusu --- configs/disabled.rules.chectl.json | 27 ++ configs/eslint.json | 5 +- package.json | 6 +- run.sh | 6 - src/api/che-api-client.ts | 39 +-- src/api/che-login-manager.ts | 41 ++- src/api/che.ts | 18 +- src/api/config-manager.ts | 2 + src/api/github-client.ts | 45 ++- src/api/kube.ts | 278 +++++++++--------- src/api/openshift.ts | 8 +- src/api/typings/openshift.d.ts | 2 +- src/api/version.ts | 10 +- src/commands/auth/delete.ts | 8 +- src/commands/auth/get.ts | 4 +- src/commands/auth/list.ts | 3 +- src/commands/auth/login.ts | 11 +- src/commands/auth/logout.ts | 2 +- src/commands/auth/use.ts | 14 +- src/commands/cacert/export.ts | 4 +- src/commands/dashboard/open.ts | 2 +- src/commands/devfile/generate.ts | 53 ++-- src/commands/server/debug.ts | 4 +- src/commands/server/delete.ts | 4 +- src/commands/server/deploy.ts | 52 ++-- src/commands/server/logs.ts | 4 +- src/commands/server/start.ts | 10 +- src/commands/server/status.ts | 3 +- src/commands/server/stop.ts | 14 +- src/commands/server/update.ts | 19 +- src/commands/workspace/create.ts | 7 +- src/commands/workspace/delete.ts | 9 +- src/commands/workspace/inject.ts | 19 +- src/commands/workspace/list.ts | 4 +- src/commands/workspace/logs.ts | 8 +- src/commands/workspace/start.ts | 8 +- src/commands/workspace/stop.ts | 6 +- src/common-flags.ts | 22 +- src/hooks/analytics/analytics.ts | 1 - src/hooks/analytics/segment-adapter.ts | 15 +- src/tasks/che.ts | 146 ++++----- .../component-installers/cert-manager.ts | 24 +- .../devfile-workspace-operator-installer.ts | 55 ++-- src/tasks/installers/common-tasks.ts | 21 +- src/tasks/installers/helm.ts | 64 ++-- src/tasks/installers/installer.ts | 18 +- src/tasks/installers/olm.ts | 77 ++--- src/tasks/installers/operator.ts | 87 +++--- src/tasks/kube.ts | 12 +- src/tasks/platforms/api.ts | 4 +- src/tasks/platforms/common-platform-tasks.ts | 4 +- src/tasks/platforms/crc.ts | 12 +- src/tasks/platforms/docker-desktop.ts | 15 +- src/tasks/platforms/k8s.ts | 10 +- src/tasks/platforms/microk8s.ts | 24 +- src/tasks/platforms/minikube.ts | 37 +-- src/tasks/platforms/minishift.ts | 10 +- src/tasks/platforms/openshift.ts | 4 +- src/tasks/platforms/platform.ts | 22 +- src/util.ts | 13 +- yarn.lock | 222 +++++++++++++- 61 files changed, 978 insertions(+), 700 deletions(-) create mode 100644 configs/disabled.rules.chectl.json delete mode 100644 run.sh diff --git a/configs/disabled.rules.chectl.json b/configs/disabled.rules.chectl.json new file mode 100644 index 000000000..b5c8ff798 --- /dev/null +++ b/configs/disabled.rules.chectl.json @@ -0,0 +1,27 @@ +{ + "plugins": ["@typescript-eslint"], + "rules": { + "no-use-before-define": 0, + "valid-jsdoc": 0, + "camelcase": 0, + "unicorn/catch-error-name": 0, + "node/no-unsupported-features/node-builtins": 0, + "no-await-in-loop": 0, + "no-mixed-operators": 0, + "max-statements-per-line": 0, + "no-negated-condition": 0, + "unicorn/explicit-length-check": 0, + "new-cap": 0, + "require-atomic-updates": 0, + "@typescript-eslint/no-empty-function": 0, + "no-useless-escape": 0, + "object-curly-spacing": 0, + "@typescript-eslint/explicit-module-boundary-types": 0, + "no-inner-declarations": 0, + "@typescript-eslint/ban-types": 0, + "no-multi-assign": 0, + "no-lonely-if": 0, + "no-async-promise-executor": 0, + "prefer-promise-reject-errors": 0 + } +} diff --git a/configs/eslint.json b/configs/eslint.json index fbef61a3b..5ab264cd0 100644 --- a/configs/eslint.json +++ b/configs/eslint.json @@ -1,6 +1,9 @@ { "extends": [ + "oclif", + "oclif-typescript", "./eslint.license.json", - "./base.eslint.chectl.json" + "./base.eslint.chectl.json", + "./disabled.rules.chectl.json" ] } diff --git a/package.json b/package.json index 3448aa598..edd6f0b91 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,8 @@ "chai": "^4.2.0", "cpx": "^1.5.0", "eslint": "^7.28.0", + "eslint-config-oclif": "^3.1.0", + "eslint-config-oclif-typescript": "^0.2.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-no-null": "^1.0.2", "globby": "^11", @@ -88,7 +90,7 @@ "typescript-formatter": "7.2.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" }, "files": [ "/bin", @@ -149,7 +151,7 @@ }, "repository": "che-incubator/chectl", "scripts": { - "postinstall": "npm run -s postinstall-repositories && npm run -s postinstall-helm && npm run -s postinstall-cert-manager && npm run -s postinstall-operator && npm run -s postinstall-dev-workspace && npm run -s postinstall-cleanup", + "postinstall": "yarn lint && npm run -s postinstall-repositories && npm run -s postinstall-helm && npm run -s postinstall-cert-manager && npm run -s postinstall-operator && npm run -s postinstall-dev-workspace && npm run -s postinstall-cleanup", "postinstall-helm": "rimraf templates/kubernetes && cpx 'node_modules/eclipse-che-server/deploy/kubernetes/**' 'templates/kubernetes'", "postinstall-cert-manager": "rimraf templates/cert-manager && cpx 'node_modules/eclipse-che-server/deploy/cert-manager/**' 'templates/cert-manager'", "postinstall-dev-workspace": "rimraf templates/devworkspace && cpx 'node_modules/eclipse-che-devfile-workspace-operator/deploy/**' 'templates/devworkspace'", diff --git a/run.sh b/run.sh deleted file mode 100644 index 0dc8f71d8..000000000 --- a/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -TEMPLATE=$(jq '.rules."header/header"[2][1].pattern' configs/eslint.license.json) -CURRENT_YEAR=$(date +'%Y') - -if [[ ${TEMPLATE} != *"$CURRENT_YEAR"* ]];then - echo -e "[INFO] Your license header template doesn't contain the current year. Please change config/eslint.license.json year and run yarn lint:fix" -fi diff --git a/src/api/che-api-client.ts b/src/api/che-api-client.ts index b0c96bbd5..1d94a7185 100644 --- a/src/api/che-api-client.ts +++ b/src/api/che-api-client.ts @@ -23,6 +23,7 @@ import { sleep } from '../util' let instance: CheApiClient | undefined export class CheApiClient { public defaultCheResponseTimeoutMs = 3000 + public readonly cheApiEndpoint: string private readonly axios: AxiosInstance @@ -34,7 +35,7 @@ export class CheApiClient { const httpsAgent = new https.Agent({ rejectUnauthorized: false }) this.axios = axios.create({ - httpsAgent + httpsAgent, }) } @@ -114,9 +115,8 @@ export class CheApiClient { } catch (error) { if (error.response && error.response.status === 409) { return - } else { - throw this.getCheApiError(error, endpoint) } + throw this.getCheApiError(error, endpoint) } if (!response || response.status !== 204) { throw new Error('E_BAD_RESP_CHE_API') @@ -126,7 +126,7 @@ export class CheApiClient { async waitUntilCheServerReadyToShutdown(intervalMs = 500, timeoutMs = 60000): Promise { const iterations = timeoutMs / intervalMs for (let index = 0; index < iterations; index++) { - let status = await this.getCheServerStatus() + const status = await this.getCheServerStatus() if (status === 'READY_TO_SHUTDOWN') { return } @@ -168,9 +168,8 @@ export class CheApiClient { const response = await this.axios.get(endpoint, { headers }) if (response && response.data) { return response.data - } else { - throw new Error('E_BAD_RESP_CHE_SERVER') } + throw new Error('E_BAD_RESP_CHE_SERVER') } catch (error) { throw this.getCheApiError(error, endpoint) } @@ -289,9 +288,8 @@ export class CheApiClient { if (response && response.data) { return response.data as chetypes.workspace.Workspace - } else { - throw new Error('E_BAD_RESP_CHE_SERVER') } + throw new Error('E_BAD_RESP_CHE_SERVER') } /** @@ -324,9 +322,8 @@ export class CheApiClient { } catch (error) { if (error.response && (error.response.status === 404 || error.response.status === 503)) { return false - } else { - throw this.getCheApiError(error, endpoint) } + throw this.getCheApiError(error, endpoint) } this.checkResponse(response, endpoint) if (!response.data['che.keycloak.token.endpoint']) { @@ -347,27 +344,23 @@ export class CheApiClient { const status = error.response.status if (status === 403) { return new Error(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`) - } else if (status === 401) { + } if (status === 401) { return new Error(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`) - } else if (status === 404) { + } if (status === 404) { return new Error(`E_CHE_API_NOTFOUND - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`) - } else if (status === 503) { + } if (status === 503) { return new Error(`E_CHE_API_UNAVAIL - Endpoint: ${endpoint} returned 503 code`) - } 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}`) } - - } else if (error.request) { + // 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}`) + } 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}`) - } else { - // Something happened in setting up the request that triggered an Error - return new Error(`E_CHECTL_UNKNOWN_ERROR - Endpoint: ${endpoint} - Message: ${error.message}`) } + // Something happened in setting up the request that triggered an Error + return new Error(`E_CHECTL_UNKNOWN_ERROR - Endpoint: ${endpoint} - Message: ${error.message}`) } - } diff --git a/src/api/che-login-manager.ts b/src/api/che-login-manager.ts index 7dcc95c6d..c355182c6 100644 --- a/src/api/che-login-manager.ts +++ b/src/api/che-login-manager.ts @@ -65,15 +65,15 @@ export interface PasswordLoginRecord { } export function isRefreshTokenLoginData(loginData: LoginRecord): loginData is RefreshTokenLoginRecord { - return !!(loginData as RefreshTokenLoginRecord).refreshToken + return Boolean((loginData as RefreshTokenLoginRecord).refreshToken) } export function isOcUserTokenLoginData(loginData: LoginRecord): loginData is OcUserTokenLoginRecord { - return !!(loginData as OcUserTokenLoginRecord).subjectToken + return Boolean((loginData as OcUserTokenLoginRecord).subjectToken) } export function isPasswordLoginData(loginData: LoginRecord): loginData is PasswordLoginRecord { - return !!(loginData as PasswordLoginRecord).password + return Boolean((loginData as PasswordLoginRecord).password) } // Response structure from /api/keycloak/settings @@ -117,10 +117,13 @@ let loginContext: CheServerLoginManager | undefined */ export class CheServerLoginManager { private loginData: CheServerLoginConfig + private apiUrl: string + private username: string private readonly dataFilePath: string + private readonly axios: AxiosInstance private constructor(dataFilePath: string) { @@ -137,7 +140,7 @@ export class CheServerLoginManager { // Make axios ignore untrusted certificate error for self-signed certificate case. const httpsAgent = new https.Agent({ rejectUnauthorized: false }) this.axios = axios.create({ - httpsAgent + httpsAgent, }) } @@ -168,10 +171,9 @@ export class CheServerLoginManager { public hasLoginFor(apiUrl: string, username?: string): boolean { apiUrl = CheApiClient.normalizeCheApiEndpointUrl(apiUrl) if (username) { - return !!this.getLoginRecord(apiUrl, username) - } else { - return !!this.loginData.logins![apiUrl] + return Boolean(this.getLoginRecord(apiUrl, username)) } + return Boolean(this.loginData.logins![apiUrl]) } public getCurrentLoginInfo(): { cheApiEndpoint: string, username: string } { @@ -187,7 +189,7 @@ export class CheServerLoginManager { const allLogins = new Map() for (const [apiUrl, serverLogins] of Object.entries(this.loginData.logins!)) { - allLogins.set(apiUrl, Array.from(Object.keys(serverLogins))) + allLogins.set(apiUrl, [...Object.keys(serverLogins)]) } return allLogins } @@ -214,7 +216,7 @@ export class CheServerLoginManager { } const refreshTokenLoginRecord: RefreshTokenLoginRecord = { refreshToken: keycloakAuthData.refresh_token, - expires: now + refreshTokenExpiresIn + expires: now + refreshTokenExpiresIn, } const username = isPasswordLoginData(loginRecord) ? loginRecord.username : @@ -430,16 +432,14 @@ export class CheServerLoginManager { } if (isPasswordLoginData(loginRecord)) { return this.getKeycloakAuthDataByUserNameAndPassword(cheKeycloakSettings, loginRecord.username, loginRecord.password) - } else { - if (isRefreshTokenLoginData(loginRecord)) { - return this.getKeycloakAuthDataByRefreshToken(cheKeycloakSettings, loginRecord.refreshToken) - } else if (isOcUserTokenLoginData(loginRecord)) { - return this.getKeycloakAuthDataByOcToken(cheKeycloakSettings, loginRecord.subjectToken, loginRecord.subjectIssuer) - } else { - // Should never happen - throw new Error('Token is not provided') - } } + if (isRefreshTokenLoginData(loginRecord)) { + return this.getKeycloakAuthDataByRefreshToken(cheKeycloakSettings, loginRecord.refreshToken) + } if (isOcUserTokenLoginData(loginRecord)) { + return this.getKeycloakAuthDataByOcToken(cheKeycloakSettings, loginRecord.subjectToken, loginRecord.subjectIssuer) + } + // Should never happen + throw new Error('Token is not provided') } private async getKeycloakAuthDataByUserNameAndPassword(cheKeycloakSettings: CheKeycloakSettings, username: string, password: string): Promise { @@ -451,7 +451,7 @@ export class CheServerLoginManager { password, } const headers = { - 'Content-Type': 'application/x-www-form-urlencoded' + 'Content-Type': 'application/x-www-form-urlencoded', } try { const response = await this.axios.post(keycloakTokenUrl, querystring.stringify(data), { headers, timeout: REQUEST_TIMEOUT_MS }) @@ -489,7 +489,7 @@ export class CheServerLoginManager { private async requestKeycloakAuth(keycloakTokenUrl: string, requestData: any): Promise { const headers = { - 'Content-Type': 'application/x-www-form-urlencoded' + 'Content-Type': 'application/x-www-form-urlencoded', } try { const response = await this.axios.post(keycloakTokenUrl, querystring.stringify(requestData), { headers, timeout: REQUEST_TIMEOUT_MS }) @@ -522,7 +522,6 @@ export class CheServerLoginManager { throw new Error(`Failed to get userdata from ${endpoint}. Cause: ${error.message}`) } } - } /** diff --git a/src/api/che.ts b/src/api/che.ts index fc5474d02..3b2e66ee1 100644 --- a/src/api/che.ts +++ b/src/api/che.ts @@ -35,7 +35,9 @@ import { KubeHelper } from './kube' export class CheHelper { defaultCheResponseTimeoutMs = 3000 + kube: KubeHelper + oc = new OpenShiftHelper() private readonly axios: AxiosInstance @@ -47,7 +49,7 @@ export class CheHelper { const httpsAgent = new https.Agent({ rejectUnauthorized: false }) this.axios = axios.create({ - httpsAgent + httpsAgent, }) } @@ -111,9 +113,8 @@ export class CheHelper { if (await this.kube.isOpenShift()) { return this.cheOpenShiftURL(namespace) - } else { - return this.cheK8sURL(namespace) } + return this.cheK8sURL(namespace) } async chePluginRegistryURL(namespace = ''): Promise { @@ -129,14 +130,13 @@ export class CheHelper { // grab URL if (await this.kube.isOpenShift()) { return this.chePluginRegistryOpenShiftURL(namespace) - } else { - return this.chePluginRegistryK8sURL(namespace) } + return this.chePluginRegistryK8sURL(namespace) } async isSelfSignedCertificateSecretExist(namespace: string): Promise { const selfSignedCertSecret = await this.kube.getSecret(CHE_ROOT_CA_SECRET_NAME, namespace) - return !!selfSignedCertSecret + return Boolean(selfSignedCertSecret) } /** @@ -285,7 +285,7 @@ export class CheHelper { try { devfile = await this.parseDevfile(devfilePath) if (workspaceName) { - let json: Devfile = yaml.load(devfile) + const json: Devfile = yaml.load(devfile) json.metadata.name = workspaceName devfile = yaml.dump(json) } @@ -303,9 +303,8 @@ export class CheHelper { if (devfilePath.startsWith('http')) { const response = await this.axios.get(devfilePath) return response.data - } else { - return fs.readFileSync(devfilePath, 'utf8') } + return fs.readFileSync(devfilePath, 'utf8') } async buildDashboardURL(ideURL: string): Promise { @@ -533,5 +532,4 @@ export class CheHelper { } } } - } diff --git a/src/api/config-manager.ts b/src/api/config-manager.ts index 3b8f919fd..4eafefc58 100644 --- a/src/api/config-manager.ts +++ b/src/api/config-manager.ts @@ -21,9 +21,11 @@ import { ChectlContext } from './context' */ export class ConfigManager { private static configManager: ConfigManager + private static readonly CHECTL_CONFIG_FILE_NAME = 'config.json' private data: any + private readonly configPath: string private constructor(configDir: string) { diff --git a/src/api/github-client.ts b/src/api/github-client.ts index 63c2e3481..47f7a6e2c 100644 --- a/src/api/github-client.ts +++ b/src/api/github-client.ts @@ -42,7 +42,7 @@ export class CheGithubClient { async getTemplatesTagInfo(installer: string, version?: string): Promise { if (installer === 'operator' || installer === 'olm') { return this.getTagInfoByVersion(CHE_OPERATOR_REPO, version) - } else if (installer === 'helm') { + } if (installer === 'helm') { return this.getTagInfoByVersion(CHE_REPO, version) } throw new Error(`Unsupported installer: ${installer}`) @@ -54,7 +54,7 @@ export class CheGithubClient { * @param prefix return only tags that starts with given prefix */ private async listLatestTags(repo: string, prefix = ''): Promise { - let response = await this.octokit.repos.listTags({ owner: OWNER, repo, per_page: 50 }) + const response = await this.octokit.repos.listTags({ owner: OWNER, repo, per_page: 50 }) const tags = response.data if (prefix) { return tags.filter(tag => tag.name.startsWith(prefix)) @@ -72,7 +72,7 @@ export class CheGithubClient { const tagRefResp = await this.octokit.git.getRef({ owner: OWNER, repo, ref: `tags/${tagName}` }) const tagRef = tagRefResp.data const downloadUrlResp = await this.octokit.repos.downloadZipballArchive({ owner: OWNER, repo, ref: tagRef.object.sha }) - // Simulate tag info + // Simulate tag info return { name: tagName, commit: { @@ -129,22 +129,21 @@ export class CheGithubClient { if (!version || version === 'latest' || version === 'stable') { const tags = await this.listLatestTags(repo) return this.getLatestTag(tags) - } else if (version === 'next' || version === 'nightly') { + } if (version === 'next' || version === 'nightly') { return this.getLastCommitInfo(repo) - } else { - // User might provide a version directly or only version prefix, e.g. 7.15 - // Some old tags might have 'v' prefix - if (version.startsWith('v')) { - // Remove 'v' prefix - version = version.substr(1) - } - let tagInfo = await this.getTagInfoByVersionPrefix(repo, version) - if (!tagInfo) { - // Try to add 'v' prefix - tagInfo = tagInfo = await this.getTagInfoByVersionPrefix(repo, 'v' + version) - } - return tagInfo } + // User might provide a version directly or only version prefix, e.g. 7.15 + // Some old tags might have 'v' prefix + if (version.startsWith('v')) { + // Remove 'v' prefix + version = version.substr(1) + } + let tagInfo = await this.getTagInfoByVersionPrefix(repo, version) + if (!tagInfo) { + // Try to add 'v' prefix + tagInfo = tagInfo = await this.getTagInfoByVersionPrefix(repo, 'v' + version) + } + return tagInfo } /** @@ -154,7 +153,7 @@ export class CheGithubClient { * @param versionPrefix version or version prefix, e.g. 7.22.0 or 7.18 */ private async getTagInfoByVersionPrefix(repo: string, versionPrefix: string): Promise { - let tagInfo = await this.getTag(repo, versionPrefix) + const tagInfo = await this.getTag(repo, versionPrefix) if (tagInfo) { // Exact match found return tagInfo @@ -163,7 +162,7 @@ export class CheGithubClient { const tags = await this.listLatestTags(repo, versionPrefix) if (tags.length === 0) { // Wrong version is given - return + } else if (tags.length === 1) { return tags[0] } else { @@ -226,16 +225,14 @@ export class CheGithubClient { const sortedSemanticTags = semanticTags.sort((semTagA: SemanticTagData, semTagB: SemanticTagData) => { if (semTagA.major !== semTagB.major) { return semTagB.major - semTagA.major - } else if (semTagA.minor !== semTagB.minor) { + } if (semTagA.minor !== semTagB.minor) { return semTagB.minor - semTagA.minor - } else if (semTagA.patch !== semTagB.patch) { + } if (semTagA.patch !== semTagB.patch) { return semTagB.patch - semTagA.patch - } else { - return 0 } + return 0 }) return sortedSemanticTags.map(tag => tag.data) } - } diff --git a/src/api/kube.ts b/src/api/kube.ts index 64250f511..c9ca1e6c1 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -33,11 +33,15 @@ const AWAIT_TIMEOUT_S = 30 export class KubeHelper { public readonly kubeConfig + readonly API_EXTENSIONS_V1BETA1 = 'apiextensions.k8s.io/v1beta1' podWaitTimeout: number + podDownloadImageTimeout: number + podReadyTimeout: number + podErrorRecheckTimeout: number constructor(flags?: any) { @@ -47,7 +51,6 @@ export class KubeHelper { this.podErrorRecheckTimeout = (flags && flags.spoderrorrechecktimeout) ? parseInt(flags.spoderrorrechecktimeout, 10) : DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT this.kubeConfig = new KubeConfig() this.kubeConfig.loadFromDefault() - } async createNamespace(namespaceName: string, labels: any): Promise { @@ -57,13 +60,12 @@ export class KubeHelper { kind: 'Namespace', metadata: { labels, - name: namespaceName - } + name: namespaceName, + }, } try { await k8sCoreApi.createNamespace(namespaceObject) - } catch (e) { throw this.wrapK8sClientError(e) } @@ -111,7 +113,7 @@ export class KubeHelper { async waitForService(selector: string, namespace = '', intervalMs = 500, timeoutMs = 30000) { const iterations = timeoutMs / intervalMs for (let index = 0; index < iterations; index++) { - let currentServices = await this.getServicesBySelector(selector, namespace) + const currentServices = await this.getServicesBySelector(selector, namespace) if (currentServices && currentServices.items.length > 0) { return } @@ -132,7 +134,7 @@ export class KubeHelper { async createServiceAccount(name = '', namespace = '') { const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - let sa = new V1ServiceAccount() + const sa = new V1ServiceAccount() sa.metadata = new V1ObjectMeta() sa.metadata.name = name sa.metadata.namespace = namespace @@ -148,25 +150,25 @@ export class KubeHelper { // Set up watcher const watcher = new Watch(this.kubeConfig) const request = await watcher - .watch(`/api/v1/namespaces/${namespace}/serviceaccounts`, {}, - (_phase: string, obj: any) => { - const serviceAccount = obj as V1ServiceAccount + .watch(`/api/v1/namespaces/${namespace}/serviceaccounts`, {}, + (_phase: string, obj: any) => { + const serviceAccount = obj as V1ServiceAccount - // Filter other service accounts in the given namespace - if (serviceAccount && serviceAccount.metadata && serviceAccount.metadata.name === name) { - // The service account is present, stop watching - if (request) { - request.abort() - } - // Release awaiter - resolve() - } - }, - error => { - if (error) { - reject(error) + // Filter other service accounts in the given namespace + if (serviceAccount && serviceAccount.metadata && serviceAccount.metadata.name === name) { + // The service account is present, stop watching + if (request) { + request.abort() } - }) + // Release awaiter + resolve() + } + }, + error => { + if (error) { + reject(error) + } + }) // Automatically stop watching after timeout const timeoutHandler = setTimeout(() => { @@ -247,7 +249,7 @@ export class KubeHelper { const { body } = await k8sRbacAuthApi.readClusterRole(name) return body } catch { - return + } } @@ -387,7 +389,7 @@ export class KubeHelper { const { body } = await k8sRbacAuthApi.replaceClusterRole(name, clusterRole) return body } catch { - return + } } } @@ -531,14 +533,14 @@ export class KubeHelper { async createAdminRoleBinding(name = '', serviceAccount = '', namespace = '') { const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - let rb = new V1RoleBinding() + const rb = new V1RoleBinding() rb.metadata = new V1ObjectMeta() rb.metadata.name = name rb.metadata.namespace = namespace rb.roleRef = new V1RoleRef() rb.roleRef.kind = 'ClusterRole' rb.roleRef.name = 'admin' - let subject = new V1Subject() + const subject = new V1Subject() subject.kind = 'ServiceAccount' subject.name = serviceAccount subject.namespace = namespace @@ -595,27 +597,26 @@ export class KubeHelper { } catch (e) { throw this.wrapK8sClientError(e) } - } async createClusterRoleBinding(name: string, saName: string, saNamespace = '', roleName = '') { const clusterRoleBinding = { apiVersion: 'rbac.authorization.k8s.io/v1', metadata: { - name: `${name}` + name: `${name}`, }, subjects: [ { kind: 'ServiceAccount', name: `${saName}`, - namespace: `${saNamespace}` - } + namespace: `${saNamespace}`, + }, ], roleRef: { kind: 'ClusterRole', name: `${roleName}`, - apiGroup: 'rbac.authorization.k8s.io' - } + apiGroup: 'rbac.authorization.k8s.io', + }, } as V1ClusterRoleBinding return this.createClusterRoleBindingFrom(clusterRoleBinding) } @@ -637,20 +638,20 @@ export class KubeHelper { const clusterRoleBinding = { apiVersion: 'rbac.authorization.k8s.io/v1', metadata: { - name: `${name}` + name: `${name}`, }, subjects: [ { kind: 'ServiceAccount', name: `${saName}`, - namespace: `${saNamespace}` - } + namespace: `${saNamespace}`, + }, ], roleRef: { kind: 'ClusterRole', name: `${roleName}`, - apiGroup: 'rbac.authorization.k8s.io' - } + apiGroup: 'rbac.authorization.k8s.io', + }, } as V1ClusterRoleBinding return this.replaceClusterRoleBindingFrom(clusterRoleBinding) } @@ -683,7 +684,7 @@ export class KubeHelper { const { body } = await k8sCoreApi.readNamespacedConfigMap(name, namespace) return this.compare(body, name) && body } catch { - return + } } @@ -695,7 +696,7 @@ export class KubeHelper { return body.data[key] } } catch { - return + } } @@ -764,7 +765,7 @@ export class KubeHelper { name: 'access-to-che-namespace', namespace, resource: 'namespaces', - verb: 'get' + verb: 'get', } try { @@ -788,7 +789,7 @@ export class KubeHelper { return res.body } } catch { - return + } } @@ -798,8 +799,8 @@ export class KubeHelper { // It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error. const requestOptions = { headers: { - 'content-type': 'application/merge-patch+json' - } + 'content-type': 'application/merge-patch+json', + }, } try { @@ -818,8 +819,8 @@ export class KubeHelper { // It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error. const requestOptions = { headers: { - 'content-type': 'application/strategic-merge-patch+json' - } + 'content-type': 'application/strategic-merge-patch+json', + }, } try { @@ -828,7 +829,7 @@ export class KubeHelper { return res.body } } catch { - return + } } @@ -943,7 +944,7 @@ export class KubeHelper { } const conditions = res.body.items[0].status.conditions - for (let condition of conditions) { + for (const condition of conditions) { if (condition.type === 'Ready') { return condition.status } @@ -953,7 +954,7 @@ export class KubeHelper { async waitForPodReady(selector: string, namespace = '', intervalMs = 500, timeoutMs = this.podReadyTimeout) { const iterations = timeoutMs / intervalMs for (let index = 0; index < iterations; index++) { - let readyStatus = await this.getPodReadyConditionStatus(selector, namespace) + const readyStatus = await this.getPodReadyConditionStatus(selector, namespace) if (readyStatus === 'True') { return } @@ -1037,8 +1038,8 @@ export class KubeHelper { try { const res = await k8sApi.readNamespacedDeployment(name, namespace) return ((res && res.body && - res.body.status && res.body.status.readyReplicas - && res.body.status.readyReplicas > 0) as boolean) + res.body.status && res.body.status.readyReplicas && + res.body.status.readyReplicas > 0) as boolean) } catch { return false } @@ -1075,8 +1076,8 @@ export class KubeHelper { try { const patch = { spec: { - paused: true - } + paused: true, + }, } await k8sApi.patchNamespacedDeployment(name, namespace, patch) } catch (e) { @@ -1089,8 +1090,8 @@ export class KubeHelper { try { const patch = { spec: { - paused: false - } + paused: false, + }, } await k8sApi.patchNamespacedDeployment(name, namespace, patch) } catch (e) { @@ -1102,8 +1103,8 @@ export class KubeHelper { const k8sAppsApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) const patch = { spec: { - replicas - } + replicas, + }, } let res try { @@ -1124,7 +1125,7 @@ export class KubeHelper { configMapEnvSource: string, namespace: string) { const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - let deployment = new V1Deployment() + const deployment = new V1Deployment() deployment.metadata = new V1ObjectMeta() deployment.metadata.name = name deployment.metadata.namespace = namespace @@ -1137,11 +1138,11 @@ export class KubeHelper { deployment.spec.template.metadata.labels = { app: name } deployment.spec.template.spec = new V1PodSpec() deployment.spec.template.spec.serviceAccountName = serviceAccount - let opContainer = new V1Container() + const opContainer = new V1Container() opContainer.name = name opContainer.image = image opContainer.imagePullPolicy = pullPolicy - let envFromSource = new V1EnvFromSource() + const envFromSource = new V1EnvFromSource() envFromSource.configMapRef = new V1ConfigMapEnvSource() envFromSource.configMapRef.name = configMapEnvSource opContainer.envFrom = [envFromSource] @@ -1243,7 +1244,7 @@ export class KubeHelper { configMapEnvSource: string, namespace: string) { const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - let pod = new V1Pod() + const pod = new V1Pod() pod.metadata = new V1ObjectMeta() pod.metadata.name = name pod.metadata.labels = { app: name } @@ -1251,11 +1252,11 @@ export class KubeHelper { pod.spec = new V1PodSpec() pod.spec.restartPolicy = restartPolicy pod.spec.serviceAccountName = serviceAccount - let opContainer = new V1Container() + const opContainer = new V1Container() opContainer.name = name opContainer.image = image opContainer.imagePullPolicy = pullPolicy - let envFromSource = new V1EnvFromSource() + const envFromSource = new V1EnvFromSource() envFromSource.configMapRef = new V1ConfigMapEnvSource() envFromSource.configMapRef.name = configMapEnvSource opContainer.envFrom = [envFromSource] @@ -1316,28 +1317,28 @@ export class KubeHelper { // Set up watcher const watcher = new Watch(this.kubeConfig) const request = await watcher - .watch(`/apis/batch/v1/namespaces/${namespace}/jobs/`, {}, - (_phase: string, obj: any) => { - const job = obj as V1Job - - // Filter other jobs in the given namespace - if (job && job.metadata && job.metadata.name === jobName) { - // Check job status - if (job.status && job.status.succeeded && job.status.succeeded >= 1) { - // Job is finished, stop watching - if (request) { - request.abort() - } - // Release awaiter - resolve() + .watch(`/apis/batch/v1/namespaces/${namespace}/jobs/`, {}, + (_phase: string, obj: any) => { + const job = obj as V1Job + + // Filter other jobs in the given namespace + if (job && job.metadata && job.metadata.name === jobName) { + // Check job status + if (job.status && job.status.succeeded && job.status.succeeded >= 1) { + // Job is finished, stop watching + if (request) { + request.abort() } + // Release awaiter + resolve() } - }, - error => { - if (error) { - reject(error) - } - }) + } + }, + error => { + if (error) { + reject(error) + } + }) // Automatically stop watching after timeout const timeoutHandler = setTimeout(() => { @@ -1372,9 +1373,8 @@ export class KubeHelper { async compare(body: any, name: string): Promise { if (body && body.metadata && body.metadata.name && body.metadata.name === name) { return true - } else { - return false } + return false } async ingressExist(name = '', namespace = ''): Promise { @@ -1642,7 +1642,7 @@ export class KubeHelper { const crs = (body as any).items as any[] if (crs.length === 0) { return - } else if (crs.length !== 1) { + } if (crs.length !== 1) { throw new Error(`Too many resources of type ${resourcePlural}.${resourceAPIGroup} found in the namespace '${namespace}'`) } @@ -1716,7 +1716,7 @@ export class KubeHelper { try { const { body } = await apiApi.getAPIVersions() const OLMAPIGroup = body.groups.find(apiGroup => apiGroup.name === 'operators.coreos.com') - return !!OLMAPIGroup + return Boolean(OLMAPIGroup) } catch { return false } @@ -1792,7 +1792,7 @@ export class KubeHelper { const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) try { const filetOauthAuthorizations = oAuthClientAuthorizations.filter((e => e.metadata && e.metadata.name)) - for (let oauthAuthorization of filetOauthAuthorizations) { + for (const oauthAuthorization of filetOauthAuthorizations) { await customObjectsApi.deleteClusterCustomObject('oauth.openshift.io', 'v1', 'oauthclientauthorizations', oauthAuthorization.metadata.name) } } catch (e) { @@ -1909,8 +1909,8 @@ export class KubeHelper { namespace, }, spec: { - targetNamespaces: [namespace] - } + targetNamespaces: [namespace], + }, } const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) @@ -2009,8 +2009,8 @@ export class KubeHelper { try { const patch: InstallPlan = { spec: { - approved: true - } + approved: true, + }, } await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'installplans', name, patch, undefined, undefined, undefined, { headers: { 'Content-Type': 'application/merge-patch+json' } }) } catch (e) { @@ -2026,7 +2026,7 @@ export class KubeHelper { (_phase: string, obj: any) => { const installPlan = obj as InstallPlan if (installPlan.status && installPlan.status.phase === 'Failed') { - const errorMessage = new Array() + const errorMessage = [] for (const condition of installPlan.status.conditions) { if (!condition.reason) { errorMessage.push(`Reason: ${condition.reason}`) @@ -2076,8 +2076,8 @@ export class KubeHelper { const requestOptions = { headers: { - 'content-type': 'application/json-patch+json' - } + 'content-type': 'application/json-patch+json', + }, } try { const response = await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', name, jsonPatch, undefined, undefined, undefined, requestOptions) @@ -2278,9 +2278,9 @@ export class KubeHelper { throw new Error(`Unable to get default service account token since there is no secret in '${namespaceName}' namespace`) } - let v1DefaultSATokenSecret = v1SecretList.items.find(secret => secret.metadata!.annotations - && secret.metadata!.annotations['kubernetes.io/service-account.name'] === saName - && secret.type === 'kubernetes.io/service-account-token') + const v1DefaultSATokenSecret = v1SecretList.items.find(secret => secret.metadata!.annotations && + secret.metadata!.annotations['kubernetes.io/service-account.name'] === saName && + secret.type === 'kubernetes.io/service-account-token') if (!v1DefaultSATokenSecret) { throw new Error(`Secret for '${saName}' service account is not found in namespace '${namespaceName}'`) @@ -2314,9 +2314,9 @@ export class KubeHelper { const config: AxiosRequestConfig = { httpsAgent: new https.Agent({ rejectUnauthorized: false, - requestCert: true + requestCert: true, }), - headers: token && { Authorization: 'bearer ' + token } + headers: token && { Authorization: 'bearer ' + token }, } const response = await axios.get(`${endpoint}`, config) @@ -2349,6 +2349,7 @@ export class KubeHelper { async isOpenShift(): Promise { return this.IsAPIGroupSupported('apps.openshift.io') } + async isOpenShift3(): Promise { const isAppsAPISupported = await this.IsAPIGroupSupported('apps.openshift.io') const isConfigAPISupported = await this.IsAPIGroupSupported('config.openshift.io') @@ -2379,10 +2380,9 @@ export class KubeHelper { } if (version) { - return !!group.versions.find(v => v.version === version) - } else { - return !!group + return Boolean(group.versions.find(v => v.version === version)) } + return Boolean(group) } catch (e) { throw this.wrapK8sClientError(e) } @@ -2413,9 +2413,8 @@ export class KubeHelper { } if (res.body.spec.tls && res.body.spec.tls.length > 0) { return 'https' - } else { - return 'http' } + return 'http' } catch (e) { throw this.wrapK8sClientError(e) } @@ -2442,11 +2441,9 @@ export class KubeHelper { const res = await k8sCoreApi.readNamespacedSecret(name, namespace) if (res && res.body && res.body) { return res.body - } else { - return } } catch { - return + } } @@ -2466,7 +2463,7 @@ export class KubeHelper { try { return (await k8sCoreApi.createNamespacedSecret(namespace, secret)).body } catch { - return + } } @@ -2478,32 +2475,32 @@ export class KubeHelper { // Set up watcher const watcher = new Watch(this.kubeConfig) const request = await watcher - .watch(`/api/v1/namespaces/${namespace}/secrets/`, { fieldSelector: `metadata.name=${secretName}` }, - (_phase: string, obj: any) => { - const secret = obj as V1Secret - - // Check all required data fields to be present - if (dataKeys.length > 0 && secret.data) { - for (const key of dataKeys) { - if (!secret.data[key]) { - // Key is missing or empty - return - } + .watch(`/api/v1/namespaces/${namespace}/secrets/`, { fieldSelector: `metadata.name=${secretName}` }, + (_phase: string, obj: any) => { + const secret = obj as V1Secret + + // Check all required data fields to be present + if (dataKeys.length > 0 && secret.data) { + for (const key of dataKeys) { + if (!secret.data[key]) { + // Key is missing or empty + return } } + } - // The secret with all specified fields is present, stop watching - if (request) { - request.abort() - } - // Release awaiter - resolve() - }, - error => { - if (error) { - reject(error) - } - }) + // The secret with all specified fields is present, stop watching + if (request) { + request.abort() + } + // Release awaiter + resolve() + }, + error => { + if (error) { + reject(error) + } + }) // Automatically stop watching after timeout const timeoutHandler = setTimeout(() => { @@ -2564,10 +2561,9 @@ export class KubeHelper { const res = await k8sApi.listNamespace() if (res && res.body) { return res.body - } else { - return { - items: [] - } + } + return { + items: [], } } catch (e) { throw this.wrapK8sClientError(e) @@ -2580,10 +2576,9 @@ export class KubeHelper { const res = await k8sApi.listNamespacedPod(namespace, undefined, undefined, undefined, fieldSelector, labelSelector) if (res && res.body) { return res.body - } else { - return { - items: [] - } + } + return { + items: [], } } catch (e) { throw this.wrapK8sClientError(e) @@ -2638,7 +2633,7 @@ export class KubeHelper { */ 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) + return new Error(e) } public safeLoadFromYamlFile(filePath: string): any { @@ -2670,6 +2665,7 @@ class PatchedK8sAppsApi extends AppsV1Api { this.defaultHeaders = oldDefaultHeaders return returnValue } + patchNamespacedDeploymentScale(...args: any) { const oldDefaultHeaders = this.defaultHeaders this.defaultHeaders = { diff --git a/src/api/openshift.ts b/src/api/openshift.ts index e13b0e191..74d0b09e9 100644 --- a/src/api/openshift.ts +++ b/src/api/openshift.ts @@ -20,12 +20,14 @@ export class OpenShiftHelper { const { exitCode } = await execa('oc', ['status', '--namespace', 'default'], { timeout: 60000, reject: false }) return exitCode === 0 } + async getRouteHost(name: string, namespace = ''): Promise { const command = 'oc' const args = ['get', 'route', '--namespace', namespace, '-o', `jsonpath={range.items[?(.metadata.name=='${name}')]}{.spec.host}{end}`] const { stdout } = await execa(command, args, { timeout: 60000 }) return stdout.trim() } + async getRouteProtocol(name: string, namespace = ''): Promise { const command = 'oc' const args = ['get', 'route', '--namespace', namespace, '-o', `jsonpath={range.items[?(.metadata.name=='${name}')]}{.spec.tls.termination}{end}`] @@ -33,21 +35,23 @@ export class OpenShiftHelper { const termination = stdout.trim() if (termination && termination.includes('edge') || termination.includes('passthrough') || termination.includes('reencrypt')) { return 'https' - } else { - return 'http' } + return 'http' } + async routeExist(name: string, namespace = ''): Promise { const command = 'oc' const args = ['get', 'route', '--namespace', namespace, '-o', `jsonpath={range.items[?(.metadata.name=='${name}')]}{.metadata.name}{end}`] const { stdout } = await execa(command, args, { timeout: 60000 }) return stdout.trim().includes(name) } + async deleteAllRoutes(namespace = '') { const command = 'oc' const args = ['delete', 'route', '--all', '--namespace', namespace] await execa(command, args, { timeout: 60000 }) } + async deleteAllDeploymentConfigs(namespace = '') { const command = 'oc' const args = ['delete', 'deploymentconfig', '--all', '--namespace', namespace] diff --git a/src/api/typings/openshift.d.ts b/src/api/typings/openshift.d.ts index ffa1701ab..f062081ac 100644 --- a/src/api/typings/openshift.d.ts +++ b/src/api/typings/openshift.d.ts @@ -14,7 +14,7 @@ export interface OAuth { apiVersion: string; kind: string; metadata: V1ObjectMeta; - + spec: OAuthSpec; } diff --git a/src/api/version.ts b/src/api/version.ts index 77b44ba85..184db09ea 100644 --- a/src/api/version.ts +++ b/src/api/version.ts @@ -63,7 +63,7 @@ export namespace VersionHelper { throw getMinimalVersionError(actualVersion, MINIMAL_OPENSHIFT_VERSION, 'OpenShift') } } - } + }, } } export function getK8sCheckVersionTask(flags: any): Listr.ListrTask { @@ -93,7 +93,7 @@ export namespace VersionHelper { throw getMinimalVersionError(actualVersion, MINIMAL_K8S_VERSION, 'Kubernetes') } } - } + }, } } @@ -193,14 +193,14 @@ export namespace VersionHelper { } const axiosInstance = axios.create({ - httpsAgent: new https.Agent({}) + httpsAgent: new https.Agent({}), }) try { const { data } = await axiosInstance.get(`https://che-incubator.github.io/chectl/channels/${channel}/linux-x64`) return data.version } catch { - return + } } @@ -274,7 +274,7 @@ export namespace VersionHelper { * Indicates if stable version of Eclipse Che is specified or meant implicitly. */ export function isDeployingStableVersion(flags: any): boolean { - return !!flags.version || !ChectlContext.get().isNightly + return Boolean(flags.version) || !ChectlContext.get().isNightly } /** diff --git a/src/commands/auth/delete.ts b/src/commands/auth/delete.ts index be34ed8bd..2eb673711 100644 --- a/src/commands/auth/delete.ts +++ b/src/commands/auth/delete.ts @@ -26,13 +26,14 @@ export default class Delete extends Command { { name: CHE_API_ENDPOINT_KEY, description: 'Eclipse Che server API endpoint', - required: true - } + required: true, + }, ] + static flags: flags.Input = { help: flags.help({ char: 'h' }), [USERNAME_KEY]: username, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } static examples = [ @@ -76,5 +77,4 @@ export default class Delete extends Command { cli.info(`Successfully logged out all users on ${cheApiEndpoint}`) } } - } diff --git a/src/commands/auth/get.ts b/src/commands/auth/get.ts index 7e7e70986..ff04956ac 100644 --- a/src/commands/auth/get.ts +++ b/src/commands/auth/get.ts @@ -23,8 +23,9 @@ export default class Get extends Command { static flags: flags.Input = { help: flags.help({ char: 'h' }), - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } + async run() { const { flags } = this.parse(Get) await ChectlContext.init(flags, this) @@ -39,5 +40,4 @@ export default class Get extends Command { cli.info('There is no active login session') } } - } diff --git a/src/commands/auth/list.ts b/src/commands/auth/list.ts index b8e1cdc2e..fdfb16fb9 100644 --- a/src/commands/auth/list.ts +++ b/src/commands/auth/list.ts @@ -23,7 +23,7 @@ export default class List extends Command { static flags: flags.Input = { help: flags.help({ char: 'h' }), - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { @@ -58,5 +58,4 @@ export default class List extends Command { cli.info(output) } - } diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index d59b92230..a6a335105 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -34,9 +34,10 @@ export default class Login extends Command { name: CHE_API_ENDPOINT_KEY, description: 'Eclipse Che server API endpoint', env: 'CHE_API_ENDPOINT', - required: false // In case of login via oc token with admin rights - } + required: false, // In case of login via oc token with admin rights + }, ] + static flags: flags.Input = { help: flags.help({ char: 'h' }), chenamespace: cheNamespace, @@ -45,7 +46,7 @@ export default class Login extends Command { description: 'Keycloak refresh token', env: 'CHE_KEYCLOAK_REFRESH_TOKEN', required: false, - exclusive: [USERNAME_KEY, PASSWORD_KEY] + exclusive: [USERNAME_KEY, PASSWORD_KEY], }), [USERNAME_KEY]: username, [PASSWORD_KEY]: string({ @@ -53,9 +54,9 @@ export default class Login extends Command { description: 'Eclipse Che user password', env: 'CHE_USER_PASSWORD', required: false, - exclusive: [REFRESH_TOKEN_KEY] + exclusive: [REFRESH_TOKEN_KEY], }), - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } static examples = [ diff --git a/src/commands/auth/logout.ts b/src/commands/auth/logout.ts index 5728fcdaf..4ecce90fb 100644 --- a/src/commands/auth/logout.ts +++ b/src/commands/auth/logout.ts @@ -23,7 +23,7 @@ export default class Logout extends Command { static flags: flags.Input = { help: flags.help({ char: 'h' }), - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { diff --git a/src/commands/auth/use.ts b/src/commands/auth/use.ts index 3ed22a01a..4dbe299ab 100644 --- a/src/commands/auth/use.ts +++ b/src/commands/auth/use.ts @@ -27,9 +27,10 @@ export default class Use extends Command { { name: CHE_API_ENDPOINT_KEY, description: 'Eclipse Che server API endpoint', - required: false - } + required: false, + }, ] + static flags: flags.Input = { help: flags.help({ char: 'h' }), [USERNAME_KEY]: username, @@ -37,9 +38,9 @@ export default class Use extends Command { char: 'i', description: 'Select an active login session in interactive mode', required: false, - exclusive: [USERNAME_KEY] + exclusive: [USERNAME_KEY], }), - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } static examples = [ @@ -129,7 +130,7 @@ export default class Use extends Command { if (allLogins.size === 0) { cli.info('No login session exists') return - } else if (allLogins.size === 1) { + } if (allLogins.size === 1) { // Retrieve the only login info cheApiEndpoint = allLogins.keys().next().value username = allLogins.get(cheApiEndpoint)![0] @@ -142,7 +143,7 @@ export default class Use extends Command { for (const login of serverLogins) { const choise = { name: ` ${login}`, - value: { cheApiEndpoint: serverUrl, username: login } + value: { cheApiEndpoint: serverUrl, username: login }, } choices.push(choise) if (currentLogin.cheApiEndpoint === serverUrl && currentLogin.username === login) { @@ -176,5 +177,4 @@ export default class Use extends Command { cli.info('Nothing to change') } } - } diff --git a/src/commands/cacert/export.ts b/src/commands/cacert/export.ts index bc6bb4aef..9c1439d72 100644 --- a/src/commands/cacert/export.ts +++ b/src/commands/cacert/export.ts @@ -33,10 +33,10 @@ export default class Export extends Command { If the destination is a directory, then ${DEFAULT_CA_CERT_FILE_NAME} file will be created there with Che certificate in PEM format. If this option is omitted, then Che certificate will be stored in a user's temporary directory as ${DEFAULT_CA_CERT_FILE_NAME}.`, env: 'CHE_CA_CERT_LOCATION', - default: '' + default: '', }), 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { diff --git a/src/commands/dashboard/open.ts b/src/commands/dashboard/open.ts index 0241b68d6..012037071 100644 --- a/src/commands/dashboard/open.ts +++ b/src/commands/dashboard/open.ts @@ -25,7 +25,7 @@ export default class Open extends Command { static flags: flags.Input = { help: flags.help({ char: 'h' }), chenamespace: cheNamespace, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { diff --git a/src/commands/devfile/generate.ts b/src/commands/devfile/generate.ts index be1cae7e0..79cb45ee5 100644 --- a/src/commands/devfile/generate.ts +++ b/src/commands/devfile/generate.ts @@ -38,7 +38,7 @@ const LanguagesComponents = new Map([ const EditorComponents = new Map([ ['theia-next', { type: TheEndpointName.CheEditor, alias: 'theia-editor', id: 'eclipse/che-theia/next' }], - ['theia-1.0.0', { type: TheEndpointName.CheEditor, alias: 'theia-editor', id: 'eclipse/che-theia/1.0.0' }] + ['theia-1.0.0', { type: TheEndpointName.CheEditor, alias: 'theia-editor', id: 'eclipse/che-theia/1.0.0' }], ]) export default class Generate extends Command { @@ -93,7 +93,7 @@ export default class Generate extends Command { env: 'COMMAND', required: false, }), - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { @@ -103,24 +103,24 @@ export default class Generate extends Command { kube = new KubeHelper(flags) await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Generate.id, flags }) - let name = flags.name || 'chectl-generated' + const name = flags.name || 'chectl-generated' - let devfile: Devfile = { + const devfile: Devfile = { apiVersion: '1.0.0', metadata: { - name - } + name, + }, } if (flags['git-repo'] !== undefined) { const repo: ProjectSource = { type: 'git', - location: flags['git-repo'] + location: flags['git-repo'], } const project: DevfileProject = { source: repo, - name: flags['git-repo'].split('/').pop() || 'git-project' + name: flags['git-repo'].split('/').pop() || 'git-project', } if (devfile.projects) { @@ -138,7 +138,7 @@ export default class Generate extends Command { memoryLimit: '512M', mountSources: true, command: ['tail'], - args: ['-f', '/dev/null'] + args: ['-f', '/dev/null'], } if (devfile.components) { devfile.components.push(component) @@ -148,13 +148,13 @@ export default class Generate extends Command { } if (flags.selector !== undefined) { - let k8sList = { + const k8sList = { kind: 'List', apiVersion: 'v1', metadata: { - name: `${flags.selector}` + name: `${flags.selector}`, }, - items: new Array() + items: new Array(), } const deployments = await this.getDeploymentsBySelector(flags.selector, flags.namespace) @@ -178,7 +178,7 @@ export default class Generate extends Command { const component: DevfileComponent = { type: TheEndpointName.Kubernetes, alias: `${flags.selector}`, - referenceContent: `${yaml.safeDump(k8sList, { skipInvalid: true })}` + referenceContent: `${yaml.safeDump(k8sList, { skipInvalid: true })}`, } if (devfile.components) { devfile.components.push(component) @@ -232,9 +232,9 @@ export default class Generate extends Command { type: 'exec', command: `${flags.command}`, component: `${devfile.components[0].alias}`, - workdir - } - ] + workdir, + }, + ], } if (devfile.commands) { @@ -251,9 +251,8 @@ export default class Generate extends Command { const updatedArgs = process.argv.slice(index).map(arg => { if (arg.indexOf(' ') >= 0) { return arg.replace(/(.*?)=(.*)/g, '$1=\"$2\"') - } else { - return arg } + return arg }) this.log(`# chectl ${updatedArgs.join(' ')}`) this.log(yaml.safeDump(devfile)) @@ -266,11 +265,11 @@ export default class Generate extends Command { } private async getDeploymentsBySelector(labelSelector: string, namespace = ''): Promise> { - let items = new Array() + const items = new Array() const k8sDeployList = await kube.getDeploymentsBySelector(labelSelector, namespace) k8sDeployList.items.forEach(async item => { - let deployment = new V1Deployment() + const deployment = new V1Deployment() deployment.apiVersion = 'apps/v1' deployment.kind = 'Deployment' deployment.metadata = new V1ObjectMeta() @@ -290,11 +289,11 @@ export default class Generate extends Command { } private async getServicesBySelector(labelSelector: string, namespace = ''): Promise> { - let items = new Array() + const items = new Array() const k8sServicesList = await kube.getServicesBySelector(labelSelector, namespace) k8sServicesList.items.forEach(async item => { - let service = new V1Service() + const service = new V1Service() service.kind = 'Service' service.apiVersion = 'v1' service.metadata = new V1ObjectMeta() @@ -305,7 +304,7 @@ export default class Generate extends Command { service.spec.selector = item.spec!.selector service.spec.ports = new Array() item.spec!.ports!.forEach(port => { - let svcPort = new V1ServicePort() + const svcPort = new V1ServicePort() svcPort.port = port.port service.spec!.ports!.push(svcPort) }) @@ -316,11 +315,11 @@ export default class Generate extends Command { } private async getIngressesBySelector(labelSelector: string, namespace = ''): Promise> { - let items = new Array() + const items = new Array() const k8sIngressesList = await kube.getIngressesBySelector(labelSelector, namespace) k8sIngressesList.items.forEach(async item => { - let ingress = new ExtensionsV1beta1Ingress() + const ingress = new ExtensionsV1beta1Ingress() ingress.kind = 'Ingress' ingress.apiVersion = 'extensions/v1beta1' ingress.metadata = new V1ObjectMeta() @@ -334,11 +333,11 @@ export default class Generate extends Command { } private async getPersistentVolumeClaimsBySelector(labelSelector: string, namespace = ''): Promise> { - let items = new Array() + const items = new Array() const k8sPVCsList = await kube.getPersistentVolumeClaimsBySelector(labelSelector, namespace) k8sPVCsList.items.forEach(async item => { - let pvc = new V1PersistentVolumeClaim() + const pvc = new V1PersistentVolumeClaim() pvc.kind = 'PersistentVolumeClaim' pvc.apiVersion = 'v1' pvc.metadata = new V1ObjectMeta() diff --git a/src/commands/server/debug.ts b/src/commands/server/debug.ts index beac0c310..4bc60bedd 100644 --- a/src/commands/server/debug.ts +++ b/src/commands/server/debug.ts @@ -30,10 +30,10 @@ export default class Debug extends Command { 'listr-renderer': listrRenderer, 'debug-port': integer({ description: 'Eclipse Che server debug port', - default: 8000 + default: 8000, }), 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { diff --git a/src/commands/server/delete.ts b/src/commands/server/delete.ts index 7a4f78f1e..a217f760d 100644 --- a/src/commands/server/delete.ts +++ b/src/commands/server/delete.ts @@ -37,7 +37,7 @@ export default class Delete extends Command { batch, 'delete-namespace': boolean({ description: 'Indicates that a Eclipse Che namespace will be deleted as well', - default: false + default: false, }), 'deployment-name': cheDeployment, 'listr-renderer': listrRenderer, @@ -48,7 +48,7 @@ export default class Delete extends Command { }), 'skip-kubernetes-health-check': skipKubeHealthzCheck, yes: assumeYes, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { diff --git a/src/commands/server/deploy.ts b/src/commands/server/deploy.ts index 94eeb5c16..8834cd2c8 100644 --- a/src/commands/server/deploy.ts +++ b/src/commands/server/deploy.ts @@ -40,7 +40,7 @@ export default class Deploy extends Command { cheimage: string({ char: 'i', description: 'Eclipse Che server container image', - env: 'CHE_CONTAINER_IMAGE' + env: 'CHE_CONTAINER_IMAGE', }), templates: string({ char: 't', @@ -50,18 +50,18 @@ export default class Deploy extends Command { }), 'devfile-registry-url': string({ description: 'The URL of the external Devfile registry.', - env: 'CHE_WORKSPACE_DEVFILE__REGISTRY__URL' + env: 'CHE_WORKSPACE_DEVFILE__REGISTRY__URL', }), 'plugin-registry-url': string({ description: 'The URL of the external plugin registry.', - env: 'CHE_WORKSPACE_PLUGIN__REGISTRY__URL' + env: 'CHE_WORKSPACE_PLUGIN__REGISTRY__URL', }), cheboottimeout: string({ char: 'o', description: 'Eclipse Che server bootstrap timeout (in milliseconds)', default: '40000', required: true, - env: 'CHE_SERVER_BOOT_TIMEOUT' + env: 'CHE_SERVER_BOOT_TIMEOUT', }), [K8SPODWAITTIMEOUT_KEY]: k8sPodWaitTimeout, [K8SPODREADYTIMEOUT_KEY]: k8sPodReadyTimeout, @@ -71,7 +71,7 @@ export default class Deploy extends Command { multiuser: flags.boolean({ char: 'm', description: 'Starts Eclipse Che in multi-user mode', - default: false + default: false, }), tls: flags.boolean({ char: 's', @@ -81,12 +81,12 @@ export default class Deploy extends Command { In case of providing own self-signed certificate 'self-signed-certificate' secret should be also created. For OpenShift, router will use default cluster certificates. Please see the docs how to deploy Eclipse Che on different infrastructures: ${DOCS_LINK_INSTALL_RUNNING_CHE_LOCALLY}`, - hidden: true + hidden: true, }), 'self-signed-cert': flags.boolean({ description: 'Deprecated. The flag is ignored. Self signed certificates usage is autodetected now.', default: false, - hidden: true + hidden: true, }), platform: string({ char: 'p', @@ -106,11 +106,11 @@ export default class Deploy extends Command { Please note, that just setting this flag will not likely work out of the box. According changes should be done in Kubernetes cluster configuration as well. In case of Openshift, domain adjustment should be done on the cluster configuration level.`, - default: '' + default: '', }), debug: boolean({ description: 'Enables the debug mode for Eclipse Che server. To debug Eclipse Che server from localhost use \'server:debug\' command.', - default: false + default: false, }), 'che-operator-image': string({ description: 'Container image of the operator. This parameter is used only when the installer is the operator', @@ -126,19 +126,19 @@ export default class Deploy extends Command { 'workspace-pvc-storage-class-name': string({ description: 'persistent volume(s) storage class name to use to store Eclipse Che workspaces data', env: 'CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME', - default: '' + default: '', }), 'postgres-pvc-storage-class-name': string({ description: 'persistent volume storage class name to use to store Eclipse Che postgres database', - default: '' + default: '', }), 'skip-version-check': flags.boolean({ description: 'Skip minimal versions check.', - default: false + default: false, }), '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 + default: false, }), 'auto-update': flags.boolean({ description: `Auto update approval strategy for installation Eclipse Che. @@ -146,7 +146,7 @@ export default class Deploy extends Command { By default this flag is enabled. This parameter is used only when the installer is 'olm'.`, allowNo: true, - exclusive: ['starting-csv'] + exclusive: ['starting-csv'], }), 'starting-csv': flags.string({ description: `Starting cluster service version(CSV) for installation Eclipse Che. @@ -154,7 +154,7 @@ export default class Deploy extends Command { 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'.` + This parameter is used only when the installer is 'olm'.`, }), 'olm-channel': string({ description: `Olm channel to install Eclipse Che, f.e. stable. @@ -173,24 +173,24 @@ export default class Deploy extends Command { }), 'catalog-source-name': string({ description: `OLM catalog source to install Eclipse Che operator. - This parameter is used only when the installer is the 'olm'.` + This parameter is used only when the installer is the 'olm'.`, }), 'catalog-source-namespace': string({ description: `Namespace for OLM catalog source to install Eclipse Che operator. - This parameter is used only when the installer is the 'olm'.` + This parameter is used only when the installer is the 'olm'.`, }), 'cluster-monitoring': boolean({ default: false, hidden: true, description: `Enable cluster monitoring to scrape Eclipse Che metrics in Prometheus. - This parameter is used only when the platform is 'openshift'.` + This parameter is used only when the platform is 'openshift'.`, }), 'olm-suggested-namespace': boolean({ default: true, allowNo: true, description: `Indicate to deploy Eclipse Che in OLM suggested namespace: '${DEFAULT_OLM_SUGGESTED_NAMESPACE}'. Flag 'chenamespace' is ignored in this case - This parameter is used only when the installer is 'olm'.` + This parameter is used only when the installer is 'olm'.`, }), 'skip-kubernetes-health-check': skipK8sHealthCheck, 'workspace-engine': string({ @@ -393,29 +393,29 @@ export default class Deploy extends Command { const devWorkspaceTasks = new DevWorkspaceTasks(flags) // Platform Checks - let platformCheckTasks = new Listr(platformTasks.preflightCheckTasks(flags, this), ctx.listrOptions) + const platformCheckTasks = new Listr(platformTasks.preflightCheckTasks(flags, this), ctx.listrOptions) // Checks if Eclipse Che is already deployed - let preInstallTasks = new Listr(undefined, ctx.listrOptions) + const preInstallTasks = new Listr(undefined, ctx.listrOptions) preInstallTasks.add(apiTasks.testApiTasks(flags, this)) preInstallTasks.add({ title: '๐Ÿ‘€ Looking for an already existing Eclipse Che instance', - task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags, this)) + task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags, this)), }) preInstallTasks.add(checkChectlAndCheVersionCompatibility(flags)) preInstallTasks.add(downloadTemplates(flags)) preInstallTasks.add({ title: '๐Ÿงช DevWorkspace engine (experimental / technology preview) ๐Ÿšจ', enabled: () => (this.isDevWorkspaceEnabled(ctx) || flags['workspace-engine'] === 'dev-workspace') && !ctx.isOpenShift, - task: () => new Listr(devWorkspaceTasks.getInstallTasks(flags)) + task: () => new Listr(devWorkspaceTasks.getInstallTasks(flags)), }) - let installTasks = new Listr(installerTasks.installTasks(flags, this), ctx.listrOptions) + const installTasks = new Listr(installerTasks.installTasks(flags, this), ctx.listrOptions) // Post Install Checks const postInstallTasks = new Listr([ { title: 'โœ… Post installation checklist', - task: () => new Listr(cheTasks.waitDeployedChe()) + task: () => new Listr(cheTasks.waitDeployedChe()), }, getRetrieveKeycloakCredentialsTask(flags), retrieveCheCaCertificateTask(flags), @@ -425,7 +425,7 @@ export default class Deploy extends Command { const logsTasks = new Listr([{ title: 'Following Eclipse Che logs', - task: () => new Listr(cheTasks.serverLogsTasks(flags, true)) + task: () => new Listr(cheTasks.serverLogsTasks(flags, true)), }], ctx.listrOptions) try { diff --git a/src/commands/server/logs.ts b/src/commands/server/logs.ts index 60355538a..594e3d30e 100644 --- a/src/commands/server/logs.ts +++ b/src/commands/server/logs.ts @@ -32,10 +32,10 @@ export default class Logs extends Command { directory: string({ char: 'd', description: 'Directory to store logs into', - env: 'CHE_LOGS' + env: 'CHE_LOGS', }), 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 737a68557..9119f11ce 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -49,17 +49,18 @@ export default class Start extends Command { apiTasks.testApiTasks(flags, this), { title: '๐Ÿ‘€ Looking for an already existing Eclipse Che instance', - task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags, this)) - }], ctx.listrOptions) + task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags, this)), + }, + ], ctx.listrOptions) const logsTasks = new Listr([{ title: 'Following Eclipse Che logs', - task: () => new Listr(cheTasks.serverLogsTasks(flags, true)) + task: () => new Listr(cheTasks.serverLogsTasks(flags, true)), }], ctx.listrOptions) const startCheTasks = new Listr([{ title: 'Starting Eclipse Che', - task: () => new Listr(cheTasks.scaleCheUpTasks()) + task: () => new Listr(cheTasks.scaleCheUpTasks()), }], ctx.listrOptions) try { @@ -81,5 +82,4 @@ export default class Start extends Command { notifyCommandCompletedSuccessfully() this.exit(0) } - } diff --git a/src/commands/server/status.ts b/src/commands/server/status.ts index 4e89dcd3d..eb80e9594 100644 --- a/src/commands/server/status.ts +++ b/src/commands/server/status.ts @@ -28,7 +28,7 @@ export default class Status extends Command { static flags: flags.Input = { help: flags.help({ char: 'h' }), chenamespace: cheNamespace, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { @@ -51,6 +51,5 @@ export default class Status extends Command { cli.log(`Eclipse Che Version : ${cheVersion}`) cli.log(`Eclipse Che Url : ${await che.cheURL(flags.chenamespace)}`) cli.log(`OpenShift OAuth enabled: ${openshiftOauth}\n`) - } } diff --git a/src/commands/server/stop.ts b/src/commands/server/stop.ts index 37ae5e880..fd3edf3f5 100644 --- a/src/commands/server/stop.ts +++ b/src/commands/server/stop.ts @@ -31,12 +31,12 @@ export default class Stop extends Command { 'che-selector': string({ description: 'Selector for Eclipse Che server resources', default: 'app=che,component=che', - env: 'CHE_SELECTOR' + env: 'CHE_SELECTOR', }), 'access-token': accessToken, 'listr-renderer': listrRenderer, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { @@ -50,10 +50,10 @@ export default class Stop extends Command { await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Stop.id, flags }) - let tasks = new Listr(undefined, + const tasks = new Listr(undefined, { renderer: flags['listr-renderer'] as any, - collapse: false + collapse: false, } ) @@ -65,10 +65,10 @@ export default class Stop extends Command { enabled: (ctx: any) => !ctx.isCheDeployed, task: async () => { await this.error(`E_BAD_DEPLOY - Deployment do not exist.\nA Deployment named "${flags['deployment-name']}" exist in namespace \"${flags.chenamespace}\", Eclipse Che server cannot be stopped.\nFix with: verify the namespace where Eclipse Che is running (oc get projects)\nhttps://github.com/eclipse/che`, { code: 'E_BAD_DEPLOY' }) - } - } + }, + }, ], - { renderer: flags['listr-renderer'] as any } + { renderer: flags['listr-renderer'] as any } ) tasks.add(cheTasks.scaleCheDownTasks(this)) tasks.add(cheTasks.waitPodsDeletedTasks()) diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index 3d63027f3..16e19918e 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -137,7 +137,7 @@ export default class Update extends Command { const updateTasks = new Listr([], ctx.listrOptions) updateTasks.add({ title: 'โ†บ Updating...', - task: () => new Listr(installerTasks.updateTasks(flags, this)) + task: () => new Listr(installerTasks.updateTasks(flags, this)), }) // post update tasks @@ -173,12 +173,12 @@ export default class Update extends Command { private async checkComponentImages(flags: any): Promise { const kubeHelper = new KubeHelper(flags) const cheCluster = await kubeHelper.getCheCluster(flags.chenamespace) - if (cheCluster.spec.server.cheImage - || cheCluster.spec.server.cheImageTag - || cheCluster.spec.server.devfileRegistryImage - || cheCluster.spec.database.postgresImage - || cheCluster.spec.server.pluginRegistryImage - || cheCluster.spec.auth.identityProviderImage) { + if (cheCluster.spec.server.cheImage || + cheCluster.spec.server.cheImageTag || + cheCluster.spec.server.devfileRegistryImage || + cheCluster.spec.database.postgresImage || + cheCluster.spec.server.pluginRegistryImage || + cheCluster.spec.auth.identityProviderImage) { let imagesListMsg = '' const resetImagesCrPatch: { [key: string]: any } = {} @@ -252,10 +252,9 @@ export default class Update extends Command { // Despite the operator image is the same, CR patch might contain some changes. cli.info('Patching existing Eclipse Che installation.') return true - } else { - cli.info('Eclipse Che is already up to date.') - return false } + cli.info('Eclipse Che is already up to date.') + return false } if (this.isUpgrade(ctx.deployedCheOperatorImageTag, ctx.newCheOperatorImageTag)) { diff --git a/src/commands/workspace/create.ts b/src/commands/workspace/create.ts index 16f4b9dbb..05bb071a3 100644 --- a/src/commands/workspace/create.ts +++ b/src/commands/workspace/create.ts @@ -41,17 +41,17 @@ export default class Create extends Command { start: boolean({ char: 's', description: 'Starts the workspace after creation', - default: false + default: false, }), debug: boolean({ char: 'd', description: 'Debug workspace start. It is useful when workspace start fails and it is needed to print more logs on startup. This flag is used in conjunction with --start flag.', - default: false + default: false, }), [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, [ACCESS_TOKEN_KEY]: accessToken, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { @@ -97,5 +97,4 @@ export default class Create extends Command { } return devfilePath } - } diff --git a/src/commands/workspace/delete.ts b/src/commands/workspace/delete.ts index 743938ce8..f8c5c023c 100644 --- a/src/commands/workspace/delete.ts +++ b/src/commands/workspace/delete.ts @@ -28,19 +28,20 @@ export default class Delete extends Command { chenamespace: cheNamespace, 'delete-namespace': flags.boolean({ description: 'Indicates that a Kubernetes namespace where workspace was created will be deleted as well', - default: false + default: false, }), [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, [ACCESS_TOKEN_KEY]: accessToken, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } + static args = [ { name: 'workspace', description: 'The workspace id to delete', - required: true - } + required: true, + }, ] async run() { diff --git a/src/commands/workspace/inject.ts b/src/commands/workspace/inject.ts index c6af2da0e..e9245870d 100644 --- a/src/commands/workspace/inject.ts +++ b/src/commands/workspace/inject.ts @@ -36,27 +36,27 @@ export default class Inject extends Command { kubeconfig: flags.boolean({ char: 'k', description: 'Inject the local Kubernetes configuration', - required: true + required: true, }), workspace: string({ char: 'w', description: `The workspace id to inject configuration into. It can be omitted if the only one running workspace exists. - Use workspace:list command to get all workspaces and their statuses.` + Use workspace:list command to get all workspaces and their statuses.`, }), container: string({ char: 'c', description: 'The container name. If not specified, configuration files will be injected in all containers of the workspace pod', - required: false + required: false, }), 'kube-context': string({ description: 'Kubeconfig context to inject', - required: false + required: false, }), [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, [ACCESS_TOKEN_KEY]: accessToken, chenamespace: cheNamespace, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } // Holds cluster CLI tool name: kubectl or oc @@ -145,7 +145,9 @@ export default class Inject extends Command { */ private async canInject(namespace: string, pod: string, container: string): Promise { const { exitCode } = await execa(`${this.command} exec ${pod} -n ${namespace} -c ${container} -- tar --version `, { timeout: 10000, reject: false, shell: true }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } /** @@ -209,12 +211,13 @@ export default class Inject extends Command { await execa(this.command, ['config', configPathFlag, kubeConfigPath, 'use-context', contextToInject.name], { timeout: 10000 }) await execa(this.command, ['cp', kubeConfigPath, `${namespace}/${workspacePod}:${containerHomeDir}.kube/config`, '-c', container], { timeout: 10000 }) - return } private async fileExists(namespace: string, pod: string, container: string, file: string): Promise { const { exitCode } = await execa(`${this.command} exec ${pod} -n ${namespace} -c ${container} -- test -e ${file}`, { timeout: 10000, reject: false, shell: true }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } private async containerExists(namespace: string, pod: string, container: string): Promise { diff --git a/src/commands/workspace/list.ts b/src/commands/workspace/list.ts index 1fa91c4fa..996ac492b 100644 --- a/src/commands/workspace/list.ts +++ b/src/commands/workspace/list.ts @@ -28,7 +28,7 @@ export default class List extends Command { [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, [ACCESS_TOKEN_KEY]: accessToken, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } async run() { @@ -52,7 +52,7 @@ export default class List extends Command { namespace: workspace.attributes.infrastructureNamespace, status: workspace.status, created: new Date(parseInt(workspace.attributes.created, 10)).toISOString(), - updated: workspace.attributes.updated ? new Date(parseInt(workspace.attributes.updated, 10)).toISOString() : '' + updated: workspace.attributes.updated ? new Date(parseInt(workspace.attributes.updated, 10)).toISOString() : '', }) }) cli.table(data, { id: {}, name: {}, namespace: {}, status: {}, created: {}, updated: {} }) diff --git a/src/commands/workspace/logs.ts b/src/commands/workspace/logs.ts index 432b37a17..6b5be2895 100644 --- a/src/commands/workspace/logs.ts +++ b/src/commands/workspace/logs.ts @@ -28,21 +28,21 @@ export default class Logs extends Command { workspace: string({ char: 'w', description: 'Target workspace id. Can be found in workspace configuration \'id\' field.', - required: true + required: true, }), namespace: string({ char: 'n', description: 'The namespace where workspace is located. Can be found in workspace configuration \'attributes.infrastructureNamespace\' field.', - required: true + required: true, }), directory: string({ char: 'd', description: 'Directory to store logs into', - env: 'CHE_LOGS' + env: 'CHE_LOGS', }), 'skip-kubernetes-health-check': skipKubeHealthzCheck, telemetry: CHE_TELEMETRY, - follow: FOLLOW_LOGS + follow: FOLLOW_LOGS, } async run() { diff --git a/src/commands/workspace/start.ts b/src/commands/workspace/start.ts index 7fd408686..3550f7eb7 100644 --- a/src/commands/workspace/start.ts +++ b/src/commands/workspace/start.ts @@ -28,21 +28,21 @@ export default class Start extends Command { debug: flags.boolean({ char: 'd', description: 'Debug workspace start. It is useful when workspace start fails and it is needed to print more logs on startup.', - default: false + default: false, }), [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, [ACCESS_TOKEN_KEY]: accessToken, chenamespace: cheNamespace, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } static args = [ { name: 'workspace', description: 'The workspace id to start', - required: true - } + required: true, + }, ] async run() { diff --git a/src/commands/workspace/stop.ts b/src/commands/workspace/stop.ts index 91a226590..5fdda649e 100644 --- a/src/commands/workspace/stop.ts +++ b/src/commands/workspace/stop.ts @@ -28,15 +28,15 @@ export default class Stop extends Command { [ACCESS_TOKEN_KEY]: accessToken, chenamespace: cheNamespace, 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY + telemetry: CHE_TELEMETRY, } static args = [ { name: 'workspace', description: 'The workspace id to stop', - required: true - } + required: true, + }, ] async run() { diff --git a/src/common-flags.ts b/src/common-flags.ts index 0f7d4a585..47ebd8474 100644 --- a/src/common-flags.ts +++ b/src/common-flags.ts @@ -16,7 +16,7 @@ import { DEFAULT_CHE_NAMESPACE, DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE, DEFA export const cheNamespace = string({ char: 'n', description: `Eclipse Che Kubernetes namespace. Default to '${DEFAULT_CHE_NAMESPACE}'`, - env: 'CHE_NAMESPACE' + env: 'CHE_NAMESPACE', }) export const devWorkspaceControllerNamespace = string({ @@ -34,7 +34,7 @@ export const batch = boolean({ export const cheDeployment = string({ description: 'Eclipse Che deployment name', default: 'che', - env: 'CHE_DEPLOYMENT' + env: 'CHE_DEPLOYMENT', }) export const listrRenderer = string({ @@ -47,12 +47,12 @@ export const listrRenderer = string({ export const ACCESS_TOKEN_KEY = 'access-token' export const accessToken = string({ description: `Eclipse Che OIDC Access Token. See the documentation how to obtain token: ${DOC_LINK_OBTAIN_ACCESS_TOKEN} and ${DOC_LINK_OBTAIN_ACCESS_TOKEN_OAUTH}.`, - env: 'CHE_ACCESS_TOKEN' + env: 'CHE_ACCESS_TOKEN', }) export const skipKubeHealthzCheck = boolean({ description: 'Skip Kubernetes health check', - default: false + default: false, }) export const CHE_API_ENDPOINT_KEY = 'che-api-endpoint' @@ -79,7 +79,7 @@ export const assumeYes = boolean({ export const CHE_OPERATOR_CR_YAML_KEY = 'che-operator-cr-yaml' export const cheOperatorCRYaml = string({ description: 'Path to a yaml file that defines a CheCluster used by the operator. This parameter is used only when the installer is the \'operator\' or the \'olm\'.', - default: '' + default: '', }) export const USERNAME_KEY = 'username' @@ -93,37 +93,37 @@ export const username = string({ export const K8SPODWAITTIMEOUT_KEY = 'k8spodwaittimeout' export const k8sPodWaitTimeout = string({ description: 'Waiting time for Pod scheduled condition (in milliseconds)', - default: `${DEFAULT_K8S_POD_WAIT_TIMEOUT}` + default: `${DEFAULT_K8S_POD_WAIT_TIMEOUT}`, }) export const K8SPODDOWNLOADIMAGETIMEOUT_KEY = 'k8spoddownloadimagetimeout' export const k8sPodDownloadImageTimeout = string({ description: 'Waiting time for Pod downloading image (in milliseconds)', - default: `${DEFAULT_K8S_POD_WAIT_TIMEOUT}` + default: `${DEFAULT_K8S_POD_WAIT_TIMEOUT}`, }) export const K8SPODREADYTIMEOUT_KEY = 'k8spodreadytimeout' export const k8sPodReadyTimeout = string({ description: 'Waiting time for Pod Ready condition (in milliseconds)', - default: `${DEFAULT_K8S_POD_WAIT_TIMEOUT}` + default: `${DEFAULT_K8S_POD_WAIT_TIMEOUT}`, }) export const K8SPODERRORRECHECKTIMEOUT_KEY = 'k8spoderrorrechecktimeout' export const k8sPodErrorRecheckTimeout = string({ description: 'Waiting time for Pod rechecking error (in milliseconds)', - default: `${DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT}` + default: `${DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT}`, }) export const LOG_DIRECTORY_KEY = 'directory' export const logsDirectory = string({ char: 'd', description: 'Directory to store logs into', - env: 'CHE_LOGS' + env: 'CHE_LOGS', }) export const CHE_TELEMETRY = string({ description: 'Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry', - options: ['on', 'off'] + options: ['on', 'off'], }) export const DEPLOY_VERSION_KEY = 'version' diff --git a/src/hooks/analytics/analytics.ts b/src/hooks/analytics/analytics.ts index 435a69bff..362384090 100644 --- a/src/hooks/analytics/analytics.ts +++ b/src/hooks/analytics/analytics.ts @@ -52,7 +52,6 @@ export const hook = async (options: { command: string, flags: any, config: IConf await segment.identifySegmentEvent(segmentId) await segment.trackSegmentEvent(options) - } catch { return this } diff --git a/src/hooks/analytics/segment-adapter.ts b/src/hooks/analytics/segment-adapter.ts index e630f3c43..65d3656e0 100644 --- a/src/hooks/analytics/segment-adapter.ts +++ b/src/hooks/analytics/segment-adapter.ts @@ -20,7 +20,7 @@ import { v4 } from 'uuid' import { getDistribution, getProjectName, getProjectVersion } from '../../util' -let Analytics = require('analytics-node') +const Analytics = require('analytics-node') export interface SegmentConfig { segmentWriteKey: string @@ -42,6 +42,7 @@ export namespace SegmentProperties { */ export class SegmentAdapter { private readonly segment: typeof Analytics + private readonly id: string constructor(segmentConfig: SegmentConfig, segmentId: string) { @@ -98,12 +99,12 @@ export class SegmentAdapter { properties: { ...pick(options.flags, ['platform', 'installer']), command: options.command, - version: getProjectVersion() + version: getProjectVersion(), }, // Property which indicate segment will integrate with all configured destinations. integrations: { - All: true - } + All: true, + }, }) } @@ -114,7 +115,7 @@ export class SegmentAdapter { os_name: os.platform(), os_version: os.release(), os_distribution: await getDistribution(), - locale: osLocale.sync().replace('_', '-') + locale: osLocale.sync().replace('_', '-'), } } @@ -131,10 +132,10 @@ export class SegmentAdapter { }, os: { name: os.platform(), - version: os.release() + version: os.release(), }, location: { - country: getTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone)?.country || 'XX' + country: getTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone)?.country || 'XX', }, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, } diff --git a/src/tasks/che.ts b/src/tasks/che.ts index a29e0516d..33d5f04d9 100644 --- a/src/tasks/che.ts +++ b/src/tasks/che.ts @@ -28,29 +28,39 @@ import { KubeTasks } from './kube' */ export class CheTasks { kube: KubeHelper + kubeTasks: KubeTasks + oc = new OpenShiftHelper() + che: CheHelper cheNamespace: string cheAccessToken: string | undefined + cheSelector = 'app=che,component=che' + cheDeploymentName: string dashboardDeploymentName = 'che-dashboard' + dashboardSelector = 'app=che,component=che-dashboard' keycloakDeploymentName = 'keycloak' + keycloakSelector = 'app=che,component=keycloak' postgresDeploymentName = 'postgres' + postgresSelector = 'app=che,component=postgres' devfileRegistryDeploymentName = 'devfile-registry' + devfileRegistrySelector = 'app=che,component=devfile-registry' pluginRegistryDeploymentName = 'plugin-registry' + pluginRegistrySelector = 'app=che,component=plugin-registry' cheConsoleLinkName = 'che' @@ -78,34 +88,34 @@ export class CheTasks { { title: 'PostgreSQL pod bootstrap', enabled: ctx => ctx.isPostgresDeployed && !ctx.isPostgresReady, - task: () => this.kubeTasks.podStartTasks(this.postgresSelector, this.cheNamespace) + task: () => this.kubeTasks.podStartTasks(this.postgresSelector, this.cheNamespace), }, { title: 'Keycloak pod bootstrap', enabled: ctx => ctx.isKeycloakDeployed && !ctx.isKeycloakReady, - task: () => this.kubeTasks.podStartTasks(this.keycloakSelector, this.cheNamespace) + task: () => this.kubeTasks.podStartTasks(this.keycloakSelector, this.cheNamespace), }, { title: 'Devfile Registry pod bootstrap', enabled: ctx => ctx.isDevfileRegistryDeployed && !ctx.isDevfileRegistryReady, - task: () => this.kubeTasks.podStartTasks(this.devfileRegistrySelector, this.cheNamespace) + task: () => this.kubeTasks.podStartTasks(this.devfileRegistrySelector, this.cheNamespace), }, { title: 'Plug-in Registry pod bootstrap', enabled: ctx => ctx.isPluginRegistryDeployed && !ctx.isPluginRegistryReady, - task: () => this.kubeTasks.podStartTasks(this.pluginRegistrySelector, this.cheNamespace) + task: () => this.kubeTasks.podStartTasks(this.pluginRegistrySelector, this.cheNamespace), }, { title: 'Eclipse Che Dashboard pod bootstrap', enabled: ctx => ctx.isDashboardDeployed && !ctx.isDashboardReady, - task: () => this.kubeTasks.podStartTasks(this.dashboardSelector, this.cheNamespace) + task: () => this.kubeTasks.podStartTasks(this.dashboardSelector, this.cheNamespace), }, { title: 'Eclipse Che Server pod bootstrap', enabled: ctx => !ctx.isCheReady, - task: () => this.kubeTasks.podStartTasks(this.cheSelector, this.cheNamespace) + task: () => this.kubeTasks.podStartTasks(this.cheSelector, this.cheNamespace), }, - ...this.checkEclipseCheStatus() + ...this.checkEclipseCheStatus(), ] } @@ -177,31 +187,31 @@ export class CheTasks { { enabled: () => ctx.isCheDeployed, title: `Found ${ctx.isCheStopped ? 'stopped' : 'running'} Eclipse Che deployment`, - task: () => { } + task: () => { }, }, { enabled: () => ctx.isPostgresDeployed, title: `Found ${ctx.isPostgresStopped ? 'stopped' : 'running'} postgres deployment`, - task: () => { } + task: () => { }, }, { enabled: () => ctx.isKeycloakDeployed, title: `Found ${ctx.isKeycloakStopped ? 'stopped' : 'running'} keycloak deployment`, - task: () => { } + task: () => { }, }, { enabled: () => ctx.isPluginRegistryDeployed, title: `Found ${ctx.isPluginRegistryStopped ? 'stopped' : 'running'} plugin registry deployment`, - task: () => { } + task: () => { }, }, { enabled: () => ctx.isDevfileRegistryDeployed, title: `Found ${ctx.isDevfileRegistryStopped ? 'stopped' : 'running'} devfile registry deployment`, - task: () => { } - } + task: () => { }, + }, ]) } - } + }, }, { title: 'Check Eclipse Che server status', @@ -218,8 +228,8 @@ export class CheTasks { } catch (error) { command.error(`E_CHECK_CHE_STATUS_FAIL - Failed to check Eclipse Che status (URL: ${cheURL}). ${error.message}`) } - } - } + }, + }, ] } @@ -237,7 +247,7 @@ export class CheTasks { task: async () => { await this.kube.scaleDeployment(this.postgresDeploymentName, this.cheNamespace, 1) return this.kubeTasks.podStartTasks(this.postgresSelector, this.cheNamespace) - } + }, }, { title: 'Keycloak pod bootstrap', @@ -245,7 +255,7 @@ export class CheTasks { task: async () => { await this.kube.scaleDeployment(this.keycloakDeploymentName, this.cheNamespace, 1) return this.kubeTasks.podStartTasks(this.keycloakSelector, this.cheNamespace) - } + }, }, { title: 'Devfile registry pod bootstrap', @@ -253,7 +263,7 @@ export class CheTasks { task: async () => { await this.kube.scaleDeployment(this.devfileRegistryDeploymentName, this.cheNamespace, 1) return this.kubeTasks.podStartTasks(this.devfileRegistrySelector, this.cheNamespace) - } + }, }, { title: 'Plug-in Registry pod bootstrap', @@ -261,7 +271,7 @@ export class CheTasks { task: async () => { await this.kube.scaleDeployment(this.pluginRegistryDeploymentName, this.cheNamespace, 1) return this.kubeTasks.podStartTasks(this.pluginRegistrySelector, this.cheNamespace) - } + }, }, { title: 'Eclipse Che Dashboard pod bootstrap', @@ -269,7 +279,7 @@ export class CheTasks { task: async () => { await this.kube.scaleDeployment(this.dashboardDeploymentName, this.cheNamespace, 1) return this.kubeTasks.podStartTasks(this.dashboardSelector, this.cheNamespace) - } + }, }, { title: 'Eclipse Che Server pod bootstrap', @@ -277,9 +287,9 @@ export class CheTasks { task: async () => { await this.kube.scaleDeployment(this.cheDeploymentName, this.cheNamespace, 1) return this.kubeTasks.podStartTasks(this.cheSelector, this.cheNamespace) - } + }, }, - ...this.checkEclipseCheStatus() + ...this.checkEclipseCheStatus(), ] } @@ -308,7 +318,7 @@ export class CheTasks { } catch (error) { command.error(`E_SHUTDOWN_CHE_SERVER_FAIL - Failed to shutdown Eclipse Che server. ${error.message}`) } - } + }, }, { title: `Scale \"${this.cheDeploymentName}\" deployment to zero`, @@ -320,7 +330,7 @@ export class CheTasks { } catch (error) { command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale deployment. ${error.message}`) } - } + }, }, { title: 'Scale \"dashboard\" deployment to zero', @@ -332,7 +342,7 @@ export class CheTasks { } catch (error) { command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale dashboard deployment. ${error.message}`) } - } + }, }, { title: 'Scale \"keycloak\" deployment to zero', @@ -344,7 +354,7 @@ export class CheTasks { } catch (error) { command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale keycloak deployment. ${error.message}`) } - } + }, }, { title: 'Scale \"postgres\" deployment to zero', @@ -356,7 +366,7 @@ export class CheTasks { } catch (error) { command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale postgres deployment. ${error.message}`) } - } + }, }, { title: 'Scale \"devfile registry\" deployment to zero', @@ -368,7 +378,7 @@ export class CheTasks { } catch (error) { command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale devfile-registry deployment. ${error.message}`) } - } + }, }, { title: 'Scale \"plugin registry\" deployment to zero', @@ -380,7 +390,7 @@ export class CheTasks { } catch (error) { command.error(`E_SCALE_DEPLOY_FAIL - Failed to scale plugin-registry deployment. ${error.message}`) } - } + }, }] } @@ -394,14 +404,14 @@ export class CheTasks { task: async (_ctx: any, task: any) => { await this.kube.deleteAllDeployments(flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete all services', task: async (_ctx: any, task: any) => { await this.kube.deleteAllServices(flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete all ingresses', @@ -409,7 +419,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { await this.kube.deleteAllIngresses(flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete all routes', @@ -417,7 +427,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { await this.oc.deleteAllRoutes(flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete configmaps for Eclipse Che server and operator', @@ -425,7 +435,7 @@ export class CheTasks { await this.kube.deleteConfigMap('che', flags.chenamespace) await this.kube.deleteConfigMap('che-operator', flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete rolebindings che, che-workspace-exec and che-workspace-view', @@ -435,7 +445,7 @@ export class CheTasks { await this.kube.deleteRoleBinding('che-workspace-exec', flags.chenamespace) await this.kube.deleteRoleBinding('che-workspace-view', flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete service accounts che, che-workspace', @@ -443,7 +453,7 @@ export class CheTasks { await this.kube.deleteServiceAccount('che', flags.chenamespace) await this.kube.deleteServiceAccount('che-workspace', flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete PVCs', @@ -453,7 +463,7 @@ export class CheTasks { await this.kube.deletePersistentVolumeClaim('keycloak-data', flags.chenamespace) await this.kube.deletePersistentVolumeClaim('keycloak-log', flags.chenamespace) task.title = `${task.title}...OK` - } + }, }, { title: `Delete consoleLink ${this.cheConsoleLinkName}`, @@ -464,8 +474,9 @@ export class CheTasks { await this.kube.deleteConsoleLink(this.cheConsoleLinkName) } task.title = `${task.title}...OK` - } - }] + }, + }, + ] } /** @@ -478,43 +489,43 @@ export class CheTasks { task: async (_ctx: any, task: any) => { await this.kube.waitUntilPodIsDeleted(this.cheSelector, this.cheNamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait until Eclipse Che Dashboard pod is deleted', task: async (_ctx: any, task: any) => { await this.kube.waitUntilPodIsDeleted(this.dashboardSelector, this.cheNamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait until Keycloak pod is deleted', task: async (_ctx: any, task: any) => { await this.kube.waitUntilPodIsDeleted(this.keycloakSelector, this.cheNamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait until PostgreSQL pod is deleted', task: async (_ctx: any, task: any) => { await this.kube.waitUntilPodIsDeleted(this.postgresSelector, this.cheNamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait until Devfile Registry pod is deleted', task: async (_ctx: any, task: any) => { await this.kube.waitUntilPodIsDeleted(this.devfileRegistrySelector, this.cheNamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait until Plug-in Registry pod is deleted', task: async (_ctx: any, task: any) => { await this.kube.waitUntilPodIsDeleted(this.pluginRegistrySelector, this.cheNamespace) task.title = `${task.title}...done.` - } - } + }, + }, ] } @@ -527,7 +538,7 @@ export class CheTasks { await this.kube.deleteNamespace(flags.chenamespace) } task.title = `${task.title}...OK` - } + }, }] } @@ -538,7 +549,7 @@ export class CheTasks { if (!await this.kube.getNamespace(flags.chenamespace)) { command.error(`E_BAD_NS - Namespace does not exist.\nThe Kubernetes Namespace "${flags.chenamespace}" doesn't exist.`, { code: 'EBADNS' }) } - } + }, }] } @@ -550,7 +561,7 @@ export class CheTasks { title: 'Verify if the workspaces is running', task: async (ctx: any) => { ctx.pod = await this.che.getWorkspacePodName(flags.chenamespace!, flags.workspace).catch(e => command.error(e.message)) - } + }, }] } @@ -565,57 +576,57 @@ export class CheTasks { task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, CHE_OPERATOR_SELECTOR, ctx.directory, follow) task.title = `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} Eclipse Che Server logs`, task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, this.cheSelector, ctx.directory, follow) task.title = await `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} PostgreSQL logs`, task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, this.postgresSelector, ctx.directory, follow) task.title = await `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} Keycloak logs`, task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, this.keycloakSelector, ctx.directory, follow) task.title = await `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} Plug-in Registry logs`, task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, this.pluginRegistrySelector, ctx.directory, follow) task.title = await `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} Devfile Registry logs`, task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, this.devfileRegistrySelector, ctx.directory, follow) task.title = await `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} Eclipse Che Dashboard logs`, task: async (ctx: any, task: any) => { await this.che.readPodLog(flags.chenamespace, this.dashboardSelector, ctx.directory, follow) task.title = await `${task.title}...done` - } + }, }, { title: `${follow ? 'Start following' : 'Read'} namespace events`, task: async (ctx: any, task: any) => { await this.che.readNamespaceEvents(flags.chenamespace, ctx.directory, follow) task.title = await `${task.title}...done` - } - } + }, + }, ] } @@ -630,7 +641,7 @@ export class CheTasks { } ctx.podName = chePods.items[0].metadata!.name! task.title = `${task.title}...done` - } + }, }, { title: 'Check if debug mode is enabled', @@ -641,15 +652,15 @@ export class CheTasks { } task.title = `${task.title}...done` - } + }, }, { title: `Forward port '${flags['debug-port']}'`, task: async (ctx: any, task: any) => { await this.kube.portForward(ctx.podName, flags.chenamespace, flags['debug-port']) task.title = `${task.title}...done` - } - } + }, + }, ] } @@ -720,8 +731,8 @@ export class CheTasks { } ctx.highlightedMessages = messages.concat(ctx.highlightedMessages) task.title = `${task.title}...done` - } - } + }, + }, ] } @@ -733,9 +744,8 @@ export class CheTasks { const cheApi = CheApiClient.getInstance(ctx.cheURL + '/api') task.title = `${task.title}...done` return cheApi.isCheServerReady() - } - } + }, + }, ] } - } diff --git a/src/tasks/component-installers/cert-manager.ts b/src/tasks/component-installers/cert-manager.ts index 20157c4ab..432e529c4 100644 --- a/src/tasks/component-installers/cert-manager.ts +++ b/src/tasks/component-installers/cert-manager.ts @@ -26,12 +26,14 @@ export const DEFAULT_CHE_CLUSTER_ISSUER_NAME = 'che-cluster-issuer' export class CertManagerTasks { protected kubeHelper: KubeHelper + protected cheHelper: CheHelper constructor(flags: any) { this.kubeHelper = new KubeHelper(flags) this.cheHelper = new CheHelper(flags) } + /** * Verify if cert-manager is installed in cluster */ @@ -47,14 +49,14 @@ export class CertManagerTasks { } else { task.title = `${task.title}...not deployed` } - } + }, }, { title: 'Deploy cert-manager', enabled: ctx => !ctx.certManagerInstalled, task: async (ctx: any, task: any) => { let yamlPath = path.join(flags.templates, 'cert-manager', 'cert-manager.yaml') - if (! await fs.pathExists(yamlPath)) { + if (!await fs.pathExists(yamlPath)) { // Older Che versions don't have Cert Manager install yaml in templates // Try to use embedded in chectl version yamlPath = path.join(__dirname, '../../../installers/cert-manager.yml') @@ -64,7 +66,7 @@ export class CertManagerTasks { ctx.certManagerInstalled = true task.title = `${task.title}...done` - } + }, }, { title: 'Wait for cert-manager', @@ -82,10 +84,11 @@ export class CertManagerTasks { await this.kubeHelper.waitForPodReady('app.kubernetes.io/name=cainjector', CERT_MANAGER_NAMESPACE_NAME, 1000, timeout) task.title = `${task.title}...ready` - } + }, }, ] } + /** * Returns list of tasks which perform cert-manager checks and requests self-signed certificate for Che. */ @@ -143,7 +146,7 @@ export class CertManagerTasks { } else { task.title = `${task.title}...already exists` } - } + }, }, { title: 'Set up Eclipse Che certificates issuer', @@ -177,7 +180,7 @@ export class CertManagerTasks { } else { task.title = `${task.title}...already exists` } - } + }, }, { title: 'Request certificate', @@ -205,7 +208,7 @@ export class CertManagerTasks { ctx.cheCertificateExists = true task.title = `${task.title}...done` - } + }, }, { title: 'Wait for certificate', @@ -217,7 +220,7 @@ export class CertManagerTasks { await this.kubeHelper.waitSecret(CHE_TLS_SECRET_NAME, flags.chenamespace, ['tls.key', 'tls.crt', 'ca.crt']) task.title = `${task.title}...ready` - } + }, }, { title: 'Retrieving Che CA certificate', @@ -242,9 +245,8 @@ export class CertManagerTasks { } else { throw new Error('Failed to get Cert Manager CA secret') } - } - } + }, + }, ] } - } diff --git a/src/tasks/component-installers/devfile-workspace-operator-installer.ts b/src/tasks/component-installers/devfile-workspace-operator-installer.ts index 904af6105..933720ac9 100644 --- a/src/tasks/component-installers/devfile-workspace-operator-installer.ts +++ b/src/tasks/component-installers/devfile-workspace-operator-installer.ts @@ -27,8 +27,11 @@ import { createNamespaceTask } from '../installers/common-tasks' */ export class DevWorkspaceTasks { protected kubeHelper: KubeHelper + protected cheHelper: CheHelper + protected openShiftHelper: OpenShiftHelper + protected certManagerTask: CertManagerTasks protected devWorkspaceServiceAccount = 'devworkspace-controller-serviceaccount' @@ -41,13 +44,18 @@ export class DevWorkspaceTasks { // DevWorkspace Controller Cluster Roles protected devWorkspaceEditWorkspaceClusterRole = 'devworkspace-controller-edit-workspaces' + protected devworkspaceProxyClusterRole = 'devworkspace-controller-proxy-role' + protected devworkspaceClusterRole = 'devworkspace-controller-role' + protected devWorkspaceViewWorkspaceClusterRole = 'devworkspace-controller-view-workspaces' + protected devWorkspaceClusterRoleWebhook = 'devworkspace-webhook-server' // DevWorkspace Controller ClusterRole Bindings protected devworkspaceProxyClusterRoleBinding = 'devworkspace-controller-proxy-rolebinding' + protected devWorkspaceRoleBinding = 'devworkspace-controller-rolebinding' // DevWorkspace Che Roles @@ -58,12 +66,16 @@ export class DevWorkspaceTasks { // DevWorkspace Che Cluster Roles protected devWorkspaceCheMetricsReader = 'devworkspace-che-metrics-reader' + protected devWorkspaceCheProxyRole = 'devworkspace-che-proxy-role' + protected devWorkspaceCheRole = 'devworkspace-che-role' // ClusterRoleBindings DevWorkspaceChe protected devWorkspaceCheProxyClusterRolebinding = 'devworkspace-che-proxy-rolebinding' + protected devWorkspaceCheClusterRolebinding = 'devworkspace-che-rolebinding' + protected devWorkspaceWebhookServerClusterRolebinding = 'devworkspace-webhook-server' // Deployment names @@ -71,20 +83,25 @@ export class DevWorkspaceTasks { // ConfigMap names protected devWorkspaceConfigMap = 'devworkspace-controller-configmap' + protected devworkspaceCheConfigmap = 'devworkspace-che-configmap' protected devWorkspaceCertificate = 'devworkspace-controller-serving-cert' + protected devWorkspaceCertIssuer = 'devworkspace-controller-selfsigned-issuer' // DevWorkspace CRD Names protected devWorkspacesCrdName = 'devworkspaces.workspace.devfile.io' + protected devWorkspaceTemplatesCrdName = 'devworkspacetemplates.workspace.devfile.io' + protected workspaceRoutingsCrdName = 'devworkspaceroutings.controller.devfile.io' protected webhooksName = 'controller.devfile.io' // Web Terminal Operator constants protected WTOSubscriptionName = 'web-terminal' + protected WTONamespace = 'openshift-operators' // Devworkspace Che operator namespace @@ -92,9 +109,13 @@ export class DevWorkspaceTasks { // chemanager k8s object info protected cheManagerApiGroupName = 'che.eclipse.org' + protected cheManagerApiVersionName = 'v1alpha1' + protected cheManagerCRDName = 'chemanagers.che.eclipse.org' + protected cheManagersKindPlural = 'chemanagers' + constructor(private readonly flags: any) { this.kubeHelper = new KubeHelper(flags) this.cheHelper = new CheHelper(flags) @@ -120,7 +141,7 @@ export class DevWorkspaceTasks { enabled: (ctx: any) => !ctx.isOpenShift, task: async (ctx: any, _task: any) => { return new Listr(this.certManagerTask.getDeployCertManagerTasks(flags), ctx.listrOptions) - } + }, }, // WARNING: Issuer and Certificate should be moved to che-operator side. Depends on issue: https://github.com/eclipse/che/issues/19502 { @@ -136,7 +157,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.createCertificateIssuer(devWorkspaceIssuerCertFilePath, ctx.certManagerK8sApiVersion, DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) task.title = `${task.title}...Done.` - } + }, }, { title: `Create self signed certificate ${this.devWorkspaceCertificate}`, @@ -152,7 +173,7 @@ export class DevWorkspaceTasks { const certifiateYaml = this.kubeHelper.safeLoadFromYamlFile(certificateTemplatePath) as V1Certificate await this.kubeHelper.createCheClusterCertificate(certifiateYaml, ctx.certManagerK8sApiVersion) task.title = `${task.title}...Done.` - } + }, }, ] } @@ -168,7 +189,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteAllDeployments(DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) await this.kubeHelper.deleteAllDeployments(DEFAULT_DEV_WORKSPACE_CHE_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete all DevWorkspace Controller and DevWorkspace Che services', @@ -176,7 +197,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteAllServices(DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) await this.kubeHelper.deleteAllServices(DEFAULT_DEV_WORKSPACE_CHE_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete all DevWorkspace Controller and DevWorkspace Che routes', @@ -184,7 +205,7 @@ export class DevWorkspaceTasks { task: async (_ctx: any, task: any) => { await this.kubeHelper.deleteAllIngresses(DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete all DevWorkspace Controller and DevWorkspace Che routes', @@ -192,7 +213,7 @@ export class DevWorkspaceTasks { task: async (_ctx: any, task: any) => { await this.openShiftHelper.deleteAllRoutes(DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller and DevWorkspace Che configmaps', @@ -201,7 +222,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteConfigMap(this.devworkspaceCheConfigmap, DEFAULT_DEV_WORKSPACE_CHE_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller and DevWorkspace Che ClusterRoleBindings', @@ -213,7 +234,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteClusterRoleBinding(this.devWorkspaceWebhookServerClusterRolebinding) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller and DevWorkspace Che role', @@ -222,7 +243,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteRole(this.devWorkspaceCheLeaderElectionRole, DEFAULT_DEV_WORKSPACE_CHE_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller and DevWorkspace Che roleBinding', @@ -231,7 +252,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteRoleBinding(this.devWorkspaceCheLeaderElectionRoleBinding, DEFAULT_DEV_WORKSPACE_CHE_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller and DevWorkspace Che cluster roles', @@ -246,7 +267,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteClusterRole(this.devWorkspaceCheRole) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller service account', @@ -254,7 +275,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteServiceAccount(this.devWorkspaceServiceAccount, DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller self-signed certificates', @@ -264,7 +285,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteNamespacedIssuer(this.devWorkspaceCertIssuer, 'v1', DEFAULT_DEV_WORKSPACE_CONTROLLER_NAMESPACE) task.title = await `${task.title}...OK` - } + }, }, { title: 'Delete DevWorkspace Controller we', @@ -272,7 +293,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteMutatingWebhookConfiguration(this.webhooksName) task.title = await `${task.title} ...OK` - } + }, }, { title: 'Delete DevWorkspace Controller CRDs', @@ -282,7 +303,7 @@ export class DevWorkspaceTasks { await this.kubeHelper.deleteCrd(this.workspaceRoutingsCrdName) task.title = await `${task.title}...OK` - } + }, }, { title: `Delete the Custom Resource of type ${this.cheManagerCRDName}`, @@ -317,7 +338,7 @@ export class DevWorkspaceTasks { } else { task.title = `${task.title}...Failed` } - } + }, }, ] } diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index 16694c4b6..6a63c9625 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -42,7 +42,7 @@ export function createNamespaceTask(namespaceName: string, labels: {}): Listr.Li await che.waitNamespaceActive(namespaceName) task.title = `${task.title}...Done.` } - } + }, } } @@ -64,7 +64,7 @@ export function checkChectlAndCheVersionCompatibility(flags: any): Listr.ListrTa } task.title = `${task.title}... OK` - } + }, } } @@ -111,14 +111,14 @@ export function downloadTemplates(flags: any): Listr.ListrTask { const cheHelper = new CheHelper(flags) await cheHelper.downloadAndUnpackTemplates(flags.installer, ctx.versionInfo.zipball_url, versionTemplatesDirPath) task.title = `${task.title} ... OK` - } + }, } } export function createEclipseCheCluster(flags: any, kube: KubeHelper): Listr.ListrTask { return { title: `Create the Custom Resource of type ${CHE_CLUSTER_CRD} in the namespace ${flags.chenamespace}`, - enabled: ctx => !!ctx.customCR || !!ctx.defaultCR, + enabled: ctx => Boolean(ctx.customCR) || Boolean(ctx.defaultCR), task: async (ctx: any, task: any) => { ctx.isCheDeployed = true ctx.isPostgresDeployed = true @@ -151,7 +151,7 @@ export function createEclipseCheCluster(flags: any, kube: KubeHelper): Listr.Lis } task.title = `${task.title}...done.` - } + }, } } @@ -173,7 +173,7 @@ export function patchingEclipseCheCluster(flags: any, kube: KubeHelper, command: } await kube.patchCheCluster(cheCluster.metadata.name, flags.chenamespace, ctx[ChectlContext.CR_PATCH]) task.title = `${task.title}...done.` - } + }, } } @@ -197,8 +197,7 @@ export function retrieveCheCaCertificateTask(flags: any): Listr.ListrTask { } else { task.title = `${task.title}... commonly trusted certificate is used.` } - - } + }, } } @@ -222,7 +221,7 @@ export function getRetrieveKeycloakCredentialsTask(flags: any): Listr.ListrTask } else { task.title = `${task.title}...failed.` } - } + }, } } @@ -239,10 +238,10 @@ export function getPrintHighlightedMessagesTask(): Listr.ListrTask { for (const message of ctx.highlightedMessages) { printMessageTasks.add({ title: message, - task: () => { } + task: () => { }, }) } return printMessageTasks - } + }, } } diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index 9b44f5f03..73773a4da 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -49,7 +49,11 @@ export class HelmTasks { return new Listr([ { title: 'Verify if helm is installed', - task: () => { if (!commandExists.sync('helm')) { command.error('E_REQUISITE_NOT_FOUND') } } + task: () => { + if (!commandExists.sync('helm')) { + command.error('E_REQUISITE_NOT_FOUND') + } + }, }, { title: 'Check Helm Version', @@ -69,7 +73,7 @@ export class HelmTasks { } catch (error) { command.error(`Unable to get helm version. ${error.message}`) } - } + }, }, { title: `Create Namespace (${flags.chenamespace})`, @@ -81,7 +85,7 @@ export class HelmTasks { await execa(`kubectl create namespace ${flags.chenamespace}`, { shell: true }) task.title = `${task.title}...done.` } - } + }, }, { title: 'Check Eclipse Che TLS certificate', @@ -118,7 +122,7 @@ export class HelmTasks { return certManagerListTasks } - } + }, }, { title: 'Create Tiller Role Binding', @@ -132,7 +136,7 @@ export class HelmTasks { await this.createTillerRoleBinding() task.title = `${task.title}...done.` } - } + }, }, { title: 'Check Cluster Role Binding', @@ -146,7 +150,7 @@ export class HelmTasks { await this.removeClusterRoleBinding(flags.chenamespace) task.title = `${task.title}...done.` } - } + }, }, { title: 'Create Tiller Service Account', @@ -160,13 +164,13 @@ export class HelmTasks { await this.createTillerServiceAccount() task.title = `${task.title}...done.` } - } + }, }, { title: 'Create Tiller RBAC', // Tiller is not used anymore in helm v3 enabled: (ctx: any) => !ctx.isHelmV3, - task: async () => this.createTillerRBAC(flags.templates) + task: async () => this.createTillerRBAC(flags.templates), }, { // Tiller is not used anymore in helm v3 @@ -180,7 +184,7 @@ export class HelmTasks { await this.createTillerService() task.title = `${task.title}...done.` } - } + }, }, { title: 'Updating Helm Chart dependencies', @@ -200,14 +204,14 @@ export class HelmTasks { } await this.updateCheHelmChartDependencies(flags) task.title = `${task.title}...done.` - } + }, }, { title: 'Deploying Eclipse Che Helm Chart', task: async (ctx: any, task: any) => { await this.upgradeCheHelmChart(ctx, flags) task.title = `${task.title}...done.` - } + }, }, ], { renderer: flags['listr-renderer'] as any }) } @@ -226,23 +230,29 @@ export class HelmTasks { await this.purgeHelmChart('che', flags.chenamespace) task.title = `${task.title} ... OK` } - } + }, }] } async clusterRoleBindingExist(cheNamespace: string, execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['get', 'clusterrolebinding', `${cheNamespace}-che-clusterrole-binding`], { timeout: execTimeout, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async removeClusterRoleBinding(cheNamespace: string, execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['delete', 'clusterrolebinding', `${cheNamespace}-che-clusterrole-binding`], { timeout: execTimeout, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async tillerRoleBindingExist(execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['get', 'clusterrolebinding', 'add-on-cluster-admin'], { timeout: execTimeout, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async createTillerRoleBinding(execTimeout = 30000) { @@ -251,7 +261,9 @@ export class HelmTasks { async tillerServiceAccountExist(execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['get', 'serviceaccounts', 'tiller', '--namespace', 'kube-system'], { timeout: execTimeout, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async createTillerServiceAccount(execTimeout = 120000) { @@ -267,16 +279,21 @@ export class HelmTasks { async tillerServiceExist(execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['get', 'services', 'tiller-deploy', '-n', 'kube-system'], { timeout: execTimeout, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async getVersion(execTimeout = 10000): Promise { + // eslint-disable-next-line prefer-const let { stdout, exitCode } = await execa('helm', ['version', '-c', '--short'], { timeout: execTimeout, reject: false }) const CLIENT_PREFIX = 'Client: ' if (stdout.startsWith(CLIENT_PREFIX)) { stdout = stdout.substring(CLIENT_PREFIX.length) } - if (exitCode === 0) { return stdout } + if (exitCode === 0) { + return stdout + } throw new Error('Unable to get version') } @@ -330,7 +347,7 @@ error: E_COMMAND_FAILED`) let multiUserFlag = '' let tlsFlag = '' - let setOptions = [] + const setOptions = [] ctx.isCheDeployed = true ctx.isDashboardDeployed = true @@ -345,7 +362,7 @@ error: E_COMMAND_FAILED`) tlsFlag = `-f ${destDir}values/tls.yaml` } - const selfSignedCertSecretExists = !! await this.kubeHelper.getSecret(CHE_ROOT_CA_SECRET_NAME, flags.chenamespace) + const selfSignedCertSecretExists = Boolean(await this.kubeHelper.getSecret(CHE_ROOT_CA_SECRET_NAME, flags.chenamespace)) setOptions.push(`--set global.tls.useSelfSignedCerts=${selfSignedCertSecretExists}`) if (flags['plugin-registry-url']) { @@ -384,9 +401,9 @@ error: E_COMMAND_FAILED`) const patchFlags = flags['helm-patch-yaml'] ? '-f ' + flags['helm-patch-yaml'] : '' - let command = `helm upgrade --install che --force --namespace ${flags.chenamespace} ${setOptions.join(' ')} ${multiUserFlag} ${tlsFlag} ${patchFlags} ${destDir}` + const command = `helm upgrade --install che --force --namespace ${flags.chenamespace} ${setOptions.join(' ')} ${multiUserFlag} ${tlsFlag} ${patchFlags} ${destDir}` - let { exitCode, stderr } = await execa(command, { timeout: execTimeout, reject: false, shell: true }) + const { exitCode, stderr } = await execa(command, { timeout: execTimeout, reject: false, shell: true }) // if process failed, check the following // if revision=1, purge and retry command else rollback if (exitCode !== 0) { @@ -407,11 +424,8 @@ error: E_COMMAND_FAILED`) await this.purgeHelmChart('che', flags.chenamespace) } else { await execa('helm', ['rollback', flags.chenamespace, revision], { timeout: execTimeout }) - } await execa(command, { timeout: execTimeout, shell: true }) - } } - } diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index cbe943e7a..298519c9f 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -40,12 +40,14 @@ export class InstallerTasks { } } else { title = '๐Ÿƒโ€ Installer preflight check' - task = () => { command.error(`Installer ${flags.installer} does not support update ยฏ\\_(ใƒ„)_/ยฏ`) } + task = () => { + command.error(`Installer ${flags.installer} does not support update ยฏ\\_(ใƒ„)_/ยฏ`) + } } return [{ title, - task + task, }] } @@ -68,12 +70,14 @@ export class InstallerTasks { } } else { title = '๐Ÿƒโ€ Installer preflight check' - task = () => { command.error(`Installer ${flags.installer} does not support update ยฏ\\_(ใƒ„)_/ยฏ`) } + task = () => { + command.error(`Installer ${flags.installer} does not support update ยฏ\\_(ใƒ„)_/ยฏ`) + } } return [{ title, - task + task, }] } @@ -109,12 +113,14 @@ export class InstallerTasks { // installer.ts END CHE ONLY } else { title = '๐Ÿƒโ€ Installer preflight check' - task = () => { command.error(`Installer ${flags.installer} is not supported ยฏ\\_(ใƒ„)_/ยฏ`) } + task = () => { + command.error(`Installer ${flags.installer} is not supported ยฏ\\_(ใƒ„)_/ยฏ`) + } } return [{ title, - task + task, }] } } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index 126c5254a..d89f4980b 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -26,7 +26,9 @@ import { createEclipseCheCluster, createNamespaceTask, patchingEclipseCheCluster export class OLMTasks { prometheusRoleName = 'prometheus-k8s' + prometheusRoleBindingName = 'prometheus-k8s' + /** * Returns list of tasks which perform preflight platform checks. */ @@ -47,7 +49,7 @@ export class OLMTasks { await kube.createRoleFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...done.` } - } + }, }, { enabled: () => flags['cluster-monitoring'] && flags.platform === 'openshift', @@ -62,7 +64,7 @@ export class OLMTasks { await kube.createRoleBindingFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...done.` } - } + }, }, { title: 'Create operator group', @@ -73,7 +75,7 @@ export class OLMTasks { await kube.createOperatorGroup(OPERATOR_GROUP_NAME, flags.chenamespace) task.title = `${task.title}...created new one.` } - } + }, }, { title: 'Configure context information', @@ -105,7 +107,7 @@ export class OLMTasks { } task.title = `${task.title}...done.` - } + }, }, { enabled: () => !VersionHelper.isDeployingStableVersion(flags) && !flags['catalog-source-name'] && !flags['catalog-source-yaml'] && flags['olm-channel'] !== OLM_STABLE_CHANNEL_NAME, @@ -119,7 +121,7 @@ export class OLMTasks { } else { task.title = `${task.title}...It already exists.` } - } + }, }, { enabled: () => flags['catalog-source-yaml'], @@ -135,7 +137,7 @@ export class OLMTasks { } else { task.title = `${task.title}...It already exists.` } - } + }, }, { title: 'Create operator subscription', @@ -158,7 +160,7 @@ export class OLMTasks { await kube.createOperatorSubscription(subscription) task.title = `${task.title}...created new one.` } - } + }, }, { title: 'Wait while subscription is ready', @@ -166,7 +168,7 @@ export class OLMTasks { const installPlan = await kube.waitOperatorSubscriptionReadyForApproval(flags.chenamespace, SUBSCRIPTION_NAME, 600) ctx.installPlanName = installPlan.name task.title = `${task.title}...done.` - } + }, }, { title: 'Approve installation', @@ -174,14 +176,14 @@ export class OLMTasks { task: async (ctx: any, task: any) => { await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait while operator installed', task: async (ctx: any, task: any) => { await kube.waitUntilOperatorIsInstalled(ctx.installPlanName, flags.chenamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Set custom operator image', @@ -195,7 +197,7 @@ export class OLMTasks { const jsonPatch = [{ op: 'replace', path: '/spec/install/spec/deployments/0/spec/template/spec/containers/0/image', value: flags['che-operator-image'] }] await kube.patchClusterServiceVersion(csv.metadata.namespace!, csv.metadata.name!, jsonPatch) task.title = `${task.title}... changed to ${flags['che-operator-image']}.` - } + }, }, { title: 'Prepare Eclipse Che cluster CR', @@ -211,9 +213,9 @@ export class OLMTasks { } task.title = `${task.title}...Done.` - } + }, }, - createEclipseCheCluster(flags, kube) + createEclipseCheCluster(flags, kube), ], { renderer: flags['listr-renderer'] as any }) } @@ -228,7 +230,7 @@ export class OLMTasks { command.error(`Unable to find operator group ${OPERATOR_GROUP_NAME}`) } task.title = `${task.title}...done.` - } + }, }, { title: 'Check if operator subscription exists', @@ -237,7 +239,7 @@ export class OLMTasks { command.error(`Unable to find operator subscription ${SUBSCRIPTION_NAME}`) } task.title = `${task.title}...done.` - } + }, }, ], { renderer: flags['listr-renderer'] as any }) } @@ -278,7 +280,7 @@ export class OLMTasks { } } command.error('Unable to find installation plan to update.') - } + }, }, { title: 'Approve installation', @@ -286,7 +288,7 @@ export class OLMTasks { task: async (ctx: any, task: any) => { await kube.approveOperatorInstallationPlan(ctx.installPlanName, flags.chenamespace) task.title = `${task.title}...done.` - } + }, }, { title: 'Wait while newer operator installed', @@ -295,9 +297,9 @@ export class OLMTasks { await kube.waitUntilOperatorIsInstalled(ctx.installPlanName, flags.chenamespace, 60) ctx.highlightedMessages.push(`Operator is updated from ${ctx.currentVersion} to ${ctx.nextVersion} version`) task.title = `${task.title}...done.` - } + }, }, - patchingEclipseCheCluster(flags, kube, command) + patchingEclipseCheCluster(flags, kube, command), ], { renderer: flags['listr-renderer'] as any }) } @@ -307,9 +309,9 @@ 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 + ctx.isPreInstalledOLM = Boolean(await kube.isPreInstalledOLM()) task.title = `${task.title}: ${ctx.isPreInstalledOLM}...OK` - } + }, }, { title: `Delete(OLM) operator subscription ${SUBSCRIPTION_NAME}`, @@ -317,7 +319,7 @@ export class OLMTasks { task: async (_ctx: any, task: any) => { await kube.deleteOperatorSubscription(SUBSCRIPTION_NAME, flags.chenamespace) task.title = `${task.title}...OK` - } + }, }, { title: 'Delete(OLM) Eclipse Che cluster service versions', @@ -327,7 +329,7 @@ export class OLMTasks { const csvsToDelete = csvs.items.filter(csv => csv.metadata.name!.startsWith(CVS_PREFIX)) csvsToDelete.forEach(csv => kube.deleteClusterServiceVersion(flags.chenamespace, csv.metadata.name!)) task.title = `${task.title}...OK` - } + }, }, { title: `Delete(OLM) operator group ${OPERATOR_GROUP_NAME}`, @@ -335,35 +337,35 @@ export class OLMTasks { task: async (_ctx: any, task: any) => { await kube.deleteOperatorGroup(OPERATOR_GROUP_NAME, flags.chenamespace) task.title = `${task.title}...OK` - } + }, }, { title: `Delete(OLM) custom catalog source ${CUSTOM_CATALOG_SOURCE_NAME}`, task: async (_ctx: any, task: any) => { await kube.deleteCatalogSource(flags.chenamespace, CUSTOM_CATALOG_SOURCE_NAME) task.title = `${task.title}...OK` - } + }, }, { title: `Delete(OLM) nigthly catalog source ${NIGHTLY_CATALOG_SOURCE_NAME}`, task: async (_ctx: any, task: any) => { await kube.deleteCatalogSource(flags.chenamespace, NIGHTLY_CATALOG_SOURCE_NAME) task.title = `${task.title}...OK` - } + }, }, { title: `Delete role ${this.prometheusRoleName}`, task: async (_ctx: any, task: any) => { await kube.deleteRole(this.prometheusRoleName, flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, { title: `Delete role binding ${this.prometheusRoleName}`, task: async (_ctx: any, task: any) => { await kube.deleteRoleBinding(this.prometheusRoleName, flags.chenamespace) task.title = await `${task.title}...OK` - } + }, }, ] } @@ -378,7 +380,7 @@ export class OLMTasks { command.error('OLM is required for installation Eclipse Che with installer flag \'olm\'') } task.title = `${task.title}...done.` - } + }, } } @@ -388,7 +390,7 @@ export class OLMTasks { kind: 'Subscription', metadata: { name, - namespace + namespace, }, spec: { channel, @@ -397,7 +399,7 @@ export class OLMTasks { source: sourceName, sourceNamespace, startingCSV, - } + }, } } @@ -414,10 +416,10 @@ export class OLMTasks { sourceType: 'grpc', updateStrategy: { registryPoll: { - interval: '15m' - } - } - } + interval: '15m', + }, + }, + }, } } @@ -428,13 +430,12 @@ export class OLMTasks { if (csv && csv.metadata.annotations) { const CRRaw = csv.metadata.annotations!['alm-examples'] return (yaml.safeLoad(CRRaw) as Array)[0] - } else { - throw new Error(`Unable to retrieve Che cluster CR definition from CSV: ${currentCSV}`) } + throw new Error(`Unable to retrieve Che cluster CR definition from CSV: ${currentCSV}`) } private getOlmNamespaceLabels(flags: any): any { - //The label values must be strings + // The label values must be strings if (flags['cluster-monitoring'] && flags.platform === 'openshift') { return { 'openshift.io/cluster-monitoring': 'true' } } diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 46744fd31..5d583fb27 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -27,11 +27,17 @@ import { createEclipseCheCluster, createNamespaceTask, patchingEclipseCheCluster export class OperatorTasks { operatorServiceAccount = 'che-operator' + cheClusterCrd = 'checlusters.org.eclipse.che' + cheClusterBackupCrd = 'checlusterbackups.org.eclipse.che' + cheClusterRestoreCrd = 'checlusterrestores.org.eclipse.che' + cheManagerCRD = 'chemanagers.che.eclipse.org' + dwRoutingCRD = 'devworkspaceroutings.controller.devfile.io' + legacyClusterResourcesName = 'che-operator' private getReadRolesAndBindingsTask(kube: KubeHelper): Listr.ListrTask { @@ -79,7 +85,7 @@ export class OperatorTasks { } task.title = `${task.title}...done.` - } + }, } } @@ -143,7 +149,7 @@ export class OperatorTasks { } task.title = `${task.title}...done.` - } + }, } } @@ -155,7 +161,7 @@ export class OperatorTasks { const kubeTasks = new KubeTasks(flags) const ctx = ChectlContext.get() ctx.resourcesPath = path.join(flags.templates, OPERATOR_TEMPLATE_DIR) - if (VersionHelper.isDeployingStableVersion(flags) && ! await kube.isOpenShift3()) { + if (VersionHelper.isDeployingStableVersion(flags) && !await kube.isOpenShift3()) { command.warn('Consider using the more reliable \'OLM\' installer when deploying a stable release of Eclipse Che (--installer=olm).') } return new Listr([ @@ -171,7 +177,7 @@ export class OperatorTasks { await kube.createServiceAccountFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...done.` } - } + }, }, this.getReadRolesAndBindingsTask(kube), this.getCreateOrUpdateRolesAndBindingsTask(flags, 'Creating Roles and Bindings', false), @@ -186,7 +192,7 @@ export class OperatorTasks { await kube.createCrdFromFile(newCRDPath) task.title = `${task.title}...done.` } - } + }, }, { title: 'Create backup and restore CRDs', @@ -217,13 +223,12 @@ export class OperatorTasks { } else { task.title = `${task.title}...skipped.` } - - } + }, }, { title: `Create CRD ${this.cheManagerCRD}`, task: async (ctx: any, task: any) => { - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { task.title = `${task.title}...Skipped.` return } @@ -241,12 +246,12 @@ export class OperatorTasks { await kube.createCrdFromFile(newCRDPath) task.title = `${task.title}...done.` } - } + }, }, { title: `Create CRD ${this.dwRoutingCRD}`, task: async (ctx: any, task: any) => { - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { task.title = `${task.title}...Skipped.` return } @@ -264,14 +269,14 @@ export class OperatorTasks { await kube.createCrdFromFile(newCRDPath) task.title = `${task.title}...done.` } - } + }, }, { title: 'Waiting 5 seconds for the new Kubernetes resources to get flushed', task: async (_ctx: any, task: any) => { await cli.wait(5000) task.title = `${task.title}...done.` - } + }, }, { title: `Create deployment ${OPERATOR_DEPLOYMENT_NAME} in namespace ${flags.chenamespace}`, @@ -285,11 +290,11 @@ export class OperatorTasks { await kube.createDeploymentFrom(operatorDeployment) task.title = `${task.title}...done.` } - } + }, }, { title: 'Operator pod bootstrap', - task: () => kubeTasks.podStartTasks(CHE_OPERATOR_SELECTOR, flags.chenamespace) + task: () => kubeTasks.podStartTasks(CHE_OPERATOR_SELECTOR, flags.chenamespace), }, { title: 'Prepare Eclipse Che cluster CR', @@ -306,9 +311,9 @@ export class OperatorTasks { } task.title = `${task.title}...Done.` - } + }, }, - createEclipseCheCluster(flags, kube) + createEclipseCheCluster(flags, kube), ], { renderer: flags['listr-renderer'] as any }) } @@ -324,7 +329,7 @@ export class OperatorTasks { } ctx.deployedCheOperatorYaml = operatorDeployment task.title = `${task.title}...done` - } + }, }, { title: 'Detecting existing version...', @@ -346,8 +351,9 @@ export class OperatorTasks { ctx.newCheOperatorImageTag = newTag task.title = `${task.title} ${ctx.deployedCheOperatorImageTag} -> ${ctx.newCheOperatorImageTag}` - } - }]) + }, + }, + ]) } updateTasks(flags: any, command: Command): Listr { @@ -367,7 +373,7 @@ export class OperatorTasks { await kube.createServiceAccountFromFile(yamlFilePath, flags.chenamespace) task.title = `${task.title}...created new one.` } - } + }, }, this.getReadRolesAndBindingsTask(kube), this.getCreateOrUpdateRolesAndBindingsTask(flags, 'Updating Roles and Bindings', true), @@ -388,7 +394,7 @@ export class OperatorTasks { await kube.createCrdFromFile(newCRDPath) task.title = `${task.title}...created new one.` } - } + }, }, { title: 'Updating backup and restore CRDs', @@ -423,12 +429,12 @@ export class OperatorTasks { } else { task.title = `${task.title}...skipped.` } - } + }, }, { title: `Updating CRD ${this.cheManagerCRD}`, task: async (ctx: any, task: any) => { - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { task.title = `${task.title}...Skipped.` return } @@ -447,12 +453,12 @@ export class OperatorTasks { await kube.createCrdFromFile(newCRDPath) task.title = `${task.title}...created new one.` } - } + }, }, { title: `Updating CRD ${this.dwRoutingCRD}`, task: async (ctx: any, task: any) => { - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { task.title = `${task.title}...Skipped.` return } @@ -471,14 +477,14 @@ export class OperatorTasks { await kube.createCrdFromFile(newCRDPath) task.title = `${task.title}...created new one.` } - } + }, }, { title: 'Waiting 5 seconds for the new Kubernetes resources to get flushed', task: async (_ctx: any, task: any) => { await cli.wait(5000) task.title = `${task.title}...done.` - } + }, }, { title: `Updating deployment ${OPERATOR_DEPLOYMENT_NAME} in namespace ${flags.chenamespace}`, @@ -493,14 +499,14 @@ export class OperatorTasks { await kube.createDeploymentFrom(operatorDeployment) task.title = `${task.title}...created new one.` } - } + }, }, { title: 'Waiting newer operator to be run', task: async (_ctx: any, _task: any) => { await cli.wait(1000) await kube.waitLatestReplica(OPERATOR_DEPLOYMENT_NAME, flags.chenamespace) - } + }, }, patchingEclipseCheCluster(flags, kube, command), ], { renderer: flags['listr-renderer'] as any }) @@ -510,7 +516,7 @@ export class OperatorTasks { * Returns list of tasks which remove Eclipse Che operator related resources */ deleteTasks(flags: any): ReadonlyArray { - let kh = new KubeHelper(flags) + const kh = new KubeHelper(flags) return [{ title: 'Delete oauthClientAuthorizations', task: async (_ctx: any, task: any) => { @@ -520,7 +526,7 @@ export class OperatorTasks { await kh.deleteOAuthClientAuthorizations(oAuthClientAuthorizations) } task.title = `${task.title}...OK` - } + }, }, { title: `Delete the Custom Resource of type ${CHE_CLUSTER_CRD}`, @@ -558,7 +564,7 @@ export class OperatorTasks { } else { task.title = `${task.title}...Failed` } - } + }, }, { title: 'Delete CRDs', @@ -572,7 +578,7 @@ export class OperatorTasks { await kh.deleteCrd(this.cheClusterRestoreCrd) task.title = `${task.title}...OK` } - } + }, }, { title: 'Delete Roles and Bindings', @@ -614,23 +620,22 @@ export class OperatorTasks { } task.title = `${task.title}...OK` - } + }, }, { title: `Delete service accounts ${this.operatorServiceAccount}`, task: async (_ctx: any, task: any) => { await kh.deleteServiceAccount(this.operatorServiceAccount, flags.chenamespace) task.title = `${task.title}...OK` - } + }, }, { title: 'Delete PVC che-operator', task: async (_ctx: any, task: any) => { await kh.deletePersistentVolumeClaim('che-operator', flags.chenamespace) task.title = `${task.title}...OK` - } - }, - ] + }, + }] } retrieveContainerImage(deployment: V1Deployment) { @@ -653,7 +658,7 @@ export class OperatorTasks { let newCRDFilePath: string const kube = new KubeHelper(flags) - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { // try to get CRD v1beta1 if platform doesn't support v1 newCRDFilePath = path.join(ctx.resourcesPath, 'crds', 'org_v1_che_crd-v1beta1.yaml') if (fs.existsSync(newCRDFilePath)) { @@ -668,7 +673,7 @@ export class OperatorTasks { private async getBackupRestoreCrdFilesNames(kube: KubeHelper): Promise<[string, string]> { let backupCrdFileName: string let restoreCrdFileName: string - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { // Needed for Openshift 3.x backupCrdFileName = 'org.eclipse.che_checlusterbackups_crd-v1beta1.yaml' restoreCrdFileName = 'org.eclipse.che_checlusterrestores_crd-v1beta1.yaml' @@ -706,7 +711,7 @@ export class OperatorTasks { } const kube = new KubeHelper(flags) - if (! await kube.IsAPIExtensionSupported('v1')) { + if (!await kube.IsAPIExtensionSupported('v1')) { const containers = operatorDeployment.spec!.template.spec!.containers || [] operatorDeployment.spec!.template.spec!.containers = containers.filter(c => c.name === 'che-operator') } diff --git a/src/tasks/kube.ts b/src/tasks/kube.ts index f25a584dc..50d63380c 100644 --- a/src/tasks/kube.ts +++ b/src/tasks/kube.ts @@ -21,7 +21,9 @@ interface FailState { export class KubeTasks { private readonly interval = 500 + private readonly kubeHelper: KubeHelper + constructor(flags: any) { this.kubeHelper = new KubeHelper(flags) } @@ -63,7 +65,7 @@ export class KubeTasks { } throw new Error(`Failed to schedule a pod: ${await this.getTimeOutErrorMessage(namespace, selector)}`) - } + }, }, { title: 'Downloading images', @@ -92,7 +94,7 @@ export class KubeTasks { } throw new Error(`Failed to download image: ${await this.getTimeOutErrorMessage(namespace, selector)}`) - } + }, }, { title: 'Starting', @@ -137,8 +139,8 @@ export class KubeTasks { } throw new Error(`Failed to start a pod: ${await this.getTimeOutErrorMessage(namespace, selector)}`) - } - } + }, + }, ]) } @@ -150,7 +152,7 @@ export class KubeTasks { private async isPodConditionStatusPassed(namespace: string, selector: string, conditionType: string): Promise { const status = await this.kubeHelper.getPodCondition(namespace, selector, conditionType) const allScheduled = !status.some(s => s.status !== 'True') - return !!status.length && allScheduled + return Boolean(status.length) && allScheduled } /** diff --git a/src/tasks/platforms/api.ts b/src/tasks/platforms/api.ts index 58c359abd..ab14a5ee1 100644 --- a/src/tasks/platforms/api.ts +++ b/src/tasks/platforms/api.ts @@ -22,7 +22,7 @@ export class ApiTasks { * `isOpenShift` property is provisioned into context. */ testApiTasks(flags: any, command: Command): Listr.ListrTask { - let kube = new KubeHelper(flags) + const kube = new KubeHelper(flags) return { title: 'Verify Kubernetes API', task: async (ctx: any, task: any) => { @@ -41,7 +41,7 @@ export class ApiTasks { } 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.`) } - } + }, } } } diff --git a/src/tasks/platforms/common-platform-tasks.ts b/src/tasks/platforms/common-platform-tasks.ts index 89e4c8245..d6a122e83 100644 --- a/src/tasks/platforms/common-platform-tasks.ts +++ b/src/tasks/platforms/common-platform-tasks.ts @@ -30,12 +30,12 @@ export namespace CommonPlatformTasks { return } - if (! await checkHttpServer(domain, 80) && ! await checkHttpsServer(domain, 443)) { + if (!await checkHttpServer(domain, 80) && !await checkHttpsServer(domain, 443)) { throw new Error(`Cannot reach cluster at "${domain}". To skip this check add "--skip-cluster-availability-check" flag.`) } task.title = `${task.title}... ok` - } + }, } } diff --git a/src/tasks/platforms/crc.ts b/src/tasks/platforms/crc.ts index da389620f..57076dd70 100644 --- a/src/tasks/platforms/crc.ts +++ b/src/tasks/platforms/crc.ts @@ -31,7 +31,7 @@ export class CRCHelper { } else { task.title = `${task.title}...done.` } - } + }, }, { title: 'Verify if crc is installed', @@ -41,7 +41,7 @@ export class CRCHelper { } else { task.title = `${task.title}...done.` } - } + }, }, { title: 'Verify if CodeReady Containers is running', @@ -52,7 +52,7 @@ export class CRCHelper { } else { task.title = `${task.title}...done.` } - } + }, }, VersionHelper.getOpenShiftCheckVersionTask(flags), VersionHelper.getK8sCheckVersionTask(flags), @@ -63,7 +63,7 @@ export class CRCHelper { const ip = await this.getCRCIP() flags.domain = ip + '.nip.io' task.title = `${task.title}...${flags.domain}.` - } + }, }, ], { renderer: flags['listr-renderer'] as any }) } @@ -74,14 +74,12 @@ export class CRCHelper { stdout.includes('CRC VM: Running') && stdout.includes('OpenShift: Running')) { return true - } else { - return false } + return false } async getCRCIP(): Promise { const { stdout } = await execa('crc', ['ip'], { timeout: 10000 }) return stdout } - } diff --git a/src/tasks/platforms/docker-desktop.ts b/src/tasks/platforms/docker-desktop.ts index 5bfaf33ac..a8a9f1aee 100644 --- a/src/tasks/platforms/docker-desktop.ts +++ b/src/tasks/platforms/docker-desktop.ts @@ -37,7 +37,7 @@ export class DockerDesktopTasks { if (!commandExists.sync('kubectl')) { command.error('E_REQUISITE_NOT_FOUND') } - } + }, }, { title: 'Verify if kubectl context is Docker Desktop', @@ -48,7 +48,7 @@ export class DockerDesktopTasks { } else { task.title = `${task.title}: Found ${context}.` } - } + }, }, { title: 'Verify remote kubernetes status', @@ -60,14 +60,14 @@ export class DockerDesktopTasks { } catch (error) { command.error('E_PLATFORM_NOT_READY: ' + error) } - } + }, }, VersionHelper.getK8sCheckVersionTask(flags), { title: 'Verify if nginx ingress is installed', task: async (ctx: any) => { ctx.isNginxIngressInstalled = await this.isNginxIngressEnabled() - } + }, }, { title: 'Installing nginx ingress', @@ -76,7 +76,7 @@ export class DockerDesktopTasks { return 'Ngninx ingress is already setup.' } }, - task: () => this.enableNginxIngress() + task: () => this.enableNginxIngress(), }, // Should automatically compute route if missing @@ -93,7 +93,7 @@ export class DockerDesktopTasks { task.title = `${task.title}... auto-assigning domain to ${flags.domain}.` } task.title = `${task.title}...set to ${flags.domain}.` - } + }, }, ], { renderer: flags['listr-renderer'] as any }) } @@ -117,7 +117,7 @@ export class DockerDesktopTasks { } grabIps(): string[] { - let networkInterfaces = os.networkInterfaces() + const networkInterfaces = os.networkInterfaces() const allIps: string[] = [] Object.keys(networkInterfaces).forEach(interfaceName => { networkInterfaces[interfaceName].forEach(iface => { @@ -128,5 +128,4 @@ export class DockerDesktopTasks { }) return allIps } - } diff --git a/src/tasks/platforms/k8s.ts b/src/tasks/platforms/k8s.ts index 167ff8140..80d9804d2 100644 --- a/src/tasks/platforms/k8s.ts +++ b/src/tasks/platforms/k8s.ts @@ -31,7 +31,7 @@ export class K8sTasks { if (!commandExists.sync('kubectl')) { command.error('E_REQUISITE_NOT_FOUND') } - } + }, }, { title: 'Verify remote kubernetes status', @@ -44,7 +44,7 @@ export class K8sTasks { } catch (error) { command.error('E_PLATFORM_NOT_READY: ' + error) } - } + }, }, VersionHelper.getK8sCheckVersionTask(flags), // Should automatically compute route if missing @@ -55,11 +55,11 @@ export class K8sTasks { command.error('E_MISSING_ARGUMENT: the domain parameter needs to be defined.') } task.title = `${task.title}...set to ${flags.domain}.` - } + }, }, - CommonPlatformTasks.getPingClusterTask(flags) + CommonPlatformTasks.getPingClusterTask(flags), ], - { renderer: flags['listr-renderer'] as any } + { renderer: flags['listr-renderer'] as any } ) } } diff --git a/src/tasks/platforms/microk8s.ts b/src/tasks/platforms/microk8s.ts index 1d4787fad..2ce059de3 100644 --- a/src/tasks/platforms/microk8s.ts +++ b/src/tasks/platforms/microk8s.ts @@ -31,7 +31,7 @@ export class MicroK8sTasks { if (!commandExists.sync('kubectl')) { command.error('E_REQUISITE_NOT_FOUND') } - } + }, }, { title: 'Verify if microk8s is installed', @@ -39,13 +39,13 @@ export class MicroK8sTasks { if (!commandExists.sync('microk8s.status')) { command.error('E_REQUISITE_NOT_FOUND', { code: 'E_REQUISITE_NOT_FOUND' }) } - } + }, }, { title: 'Verify if microk8s is running', task: async (ctx: any) => { ctx.isMicroK8sRunning = await this.isMicroK8sRunning() - } + }, }, { title: 'Start microk8s', @@ -58,14 +58,14 @@ export class MicroK8sTasks { // microk8s.start requires sudo permissions // this.startMicroK8s() command.error('MicroK8s is not running.', { code: 'E_REQUISITE_NOT_RUNNING' }) - } + }, }, VersionHelper.getK8sCheckVersionTask(flags), { title: 'Verify if microk8s ingress and storage addons is enabled', task: async (ctx: any) => { ctx.enabledAddons = await this.enabledAddons() - } + }, }, { title: 'Enable microk8s ingress addon', @@ -74,7 +74,7 @@ export class MicroK8sTasks { return 'Ingress addon is already enabled.' } }, - task: () => this.enableIngressAddon() + task: () => this.enableIngressAddon(), }, { title: 'Enable microk8s storage addon', @@ -87,7 +87,7 @@ export class MicroK8sTasks { // Enabling storage requires sudo permissions // this.enableStorageAddon() return command.error('The storage addon hasn\'t been enabled in microk8s', { code: 'E_REQUISITE_NOT_FOUND' }) - } + }, }, { title: 'Retrieving microk8s IP and domain for ingress URLs', @@ -96,15 +96,17 @@ export class MicroK8sTasks { const ip = await this.getMicroK8sIP() flags.domain = ip + '.nip.io' task.title = `${task.title}...${flags.domain}.` - } + }, }, - CommonPlatformTasks.getPingClusterTask(flags) + CommonPlatformTasks.getPingClusterTask(flags), ], { renderer: flags['listr-renderer'] as any }) } async isMicroK8sRunning(): Promise { const { exitCode } = await execa('microk8s.status', { timeout: 10000, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async startMicroK8s() { @@ -115,7 +117,7 @@ export class MicroK8sTasks { const { stdout } = await execa('microk8s.status', ['--format', 'short'], { timeout: 10000 }) return { ingress: stdout.includes('ingress: enabled'), - storage: stdout.includes('storage: enabled') + storage: stdout.includes('storage: enabled'), } } diff --git a/src/tasks/platforms/minikube.ts b/src/tasks/platforms/minikube.ts index 94c3d61d2..7f62984bb 100644 --- a/src/tasks/platforms/minikube.ts +++ b/src/tasks/platforms/minikube.ts @@ -33,7 +33,7 @@ export class MinikubeTasks { if (!commandExists.sync('kubectl')) { command.error('E_REQUISITE_NOT_FOUND') } - } + }, }, { title: 'Verify if minikube is installed', @@ -41,13 +41,13 @@ export class MinikubeTasks { if (!commandExists.sync('minikube')) { command.error('E_REQUISITE_NOT_FOUND', { code: 'E_REQUISITE_NOT_FOUND' }) } - } + }, }, { title: 'Verify if minikube is running', task: async (ctx: any) => { ctx.isMinikubeRunning = await this.isMinikubeRunning() - } + }, }, { title: 'Start minikube', @@ -56,14 +56,14 @@ export class MinikubeTasks { return 'Minikube is already running.' } }, - task: () => this.startMinikube() + task: () => this.startMinikube(), }, VersionHelper.getK8sCheckVersionTask(flags), { title: 'Verify if minikube ingress addon is enabled', task: async (ctx: any) => { ctx.isIngressAddonEnabled = await this.isIngressAddonEnabled() - } + }, }, { title: 'Enable minikube ingress addon', @@ -72,7 +72,7 @@ export class MinikubeTasks { return 'Ingress addon is already enabled.' } }, - task: () => this.enableIngressAddon() + task: () => this.enableIngressAddon(), }, { title: 'Retrieving minikube IP and domain for ingress URLs', @@ -81,7 +81,7 @@ export class MinikubeTasks { const ip = await this.getMinikubeIP() flags.domain = ip + '.nip.io' task.title = `${task.title}...${flags.domain}.` - } + }, }, { title: 'Checking minikube version', @@ -93,7 +93,7 @@ export class MinikubeTasks { ctx.minikubeVersionPatch = parseInt(versionComponents[2], 10) task.title = `${task.title}... ${version}` - } + }, }, { // Starting from Minikube 1.9 there is a bug with storage provisioner which prevents Che from successful deployment. @@ -110,30 +110,32 @@ export class MinikubeTasks { kind: 'Pod', spec: { containers: [ - { name: 'storage-provisioner', image: storageProvisionerImage } - ] - } + { name: 'storage-provisioner', image: storageProvisionerImage }, + ], + }, } - if (! await kube.patchNamespacedPod('storage-provisioner', 'kube-system', storageProvisionerImagePatch)) { + if (!await kube.patchNamespacedPod('storage-provisioner', 'kube-system', storageProvisionerImagePatch)) { throw new Error('Failed to patch storage provisioner image') } // Set required permissions for cluster role of persistent volume provisioner - if (! await kube.addClusterRoleRule('system:persistent-volume-provisioner', + if (!await kube.addClusterRoleRule('system:persistent-volume-provisioner', [''], ['endpoints'], ['get', 'list', 'watch', 'create', 'patch', 'update'])) { throw new Error('Failed to patch permissions for persistent-volume-provisioner') } task.title = `${task.title}... done` - } + }, }, - CommonPlatformTasks.getPingClusterTask(flags) + CommonPlatformTasks.getPingClusterTask(flags), ], { renderer: flags['listr-renderer'] as any }) } async isMinikubeRunning(): Promise { const { exitCode } = await execa('minikube', ['status'], { timeout: 10000, reject: false }) - if (exitCode === 0) { return true } else { return false } + if (exitCode === 0) { + return true + } return false } async startMinikube() { @@ -147,12 +149,12 @@ export class MinikubeTasks { // grab json const json = JSON.parse(stdout) return json.ingress && json.ingress.Status === 'enabled' + // eslint-disable-next-line no-else-return } else { // probably with old minikube, let's try with classic output const { stdout } = await execa('minikube', ['addons', 'list'], { timeout: 10000 }) return stdout.includes('ingress: enabled') } - } async enableIngressAddon() { @@ -170,5 +172,4 @@ export class MinikubeTasks { const versionString = versionLine.trim().split(' ')[2].substr(1) return versionString } - } diff --git a/src/tasks/platforms/minishift.ts b/src/tasks/platforms/minishift.ts index 1ba08232b..71b171493 100644 --- a/src/tasks/platforms/minishift.ts +++ b/src/tasks/platforms/minishift.ts @@ -31,7 +31,7 @@ export class MinishiftTasks { } else { task.title = `${task.title}...done.` } - } + }, }, { title: 'Verify if minishift is installed', @@ -41,7 +41,7 @@ export class MinishiftTasks { } else { task.title = `${task.title}...done.` } - } + }, }, { title: 'Verify if minishift is running', @@ -52,7 +52,7 @@ export class MinishiftTasks { } else { task.title = `${task.title}...done.` } - } + }, }, VersionHelper.getOpenShiftCheckVersionTask(flags), VersionHelper.getK8sCheckVersionTask(flags), @@ -65,14 +65,12 @@ export class MinishiftTasks { stdout.includes('Minishift: Running') && stdout.includes('OpenShift: Running')) { return true - } else { - return false } + return false } async getMinishiftIP(): Promise { const { stdout } = await execa('minishift', ['ip'], { timeout: 10000 }) return stdout } - } diff --git a/src/tasks/platforms/openshift.ts b/src/tasks/platforms/openshift.ts index e799a01b2..0b2231585 100644 --- a/src/tasks/platforms/openshift.ts +++ b/src/tasks/platforms/openshift.ts @@ -31,7 +31,7 @@ export class OpenshiftTasks { } else { task.title = `${task.title}...done.` } - } + }, }, { title: 'Verify if openshift is running', @@ -42,7 +42,7 @@ export class OpenshiftTasks { } else { task.title = `${task.title}...done.` } - } + }, }, VersionHelper.getOpenShiftCheckVersionTask(flags), VersionHelper.getK8sCheckVersionTask(flags), diff --git a/src/tasks/platforms/platform.ts b/src/tasks/platforms/platform.ts index 66303d76c..6819f4c8c 100644 --- a/src/tasks/platforms/platform.ts +++ b/src/tasks/platforms/platform.ts @@ -38,49 +38,53 @@ export class PlatformTasks { if (!flags.platform) { task = { title: 'โœˆ๏ธ Platform preflight checklist', - task: () => { command.error('Platform is required ยฏ\\_(ใƒ„)_/ยฏ') } + task: () => { + command.error('Platform is required ยฏ\\_(ใƒ„)_/ยฏ') + }, } } else if (flags.platform === 'openshift') { task = { title: 'โœˆ๏ธ Openshift preflight checklist', - task: () => openshiftTasks.preflightCheckTasks(flags, command) + task: () => openshiftTasks.preflightCheckTasks(flags, command), } } else if (flags.platform === 'crc') { task = { title: 'โœˆ๏ธ CodeReady Containers preflight checklist', - task: () => crc.preflightCheckTasks(flags, command) + task: () => crc.preflightCheckTasks(flags, command), } // platform.ts BEGIN CHE ONLY } else if (flags.platform === 'minikube') { task = { title: 'โœˆ๏ธ Minikube preflight checklist', - task: () => minikubeTasks.preflightCheckTasks(flags, command) + task: () => minikubeTasks.preflightCheckTasks(flags, command), } } else if (flags.platform === 'minishift') { task = { title: 'โœˆ๏ธ Minishift preflight checklist', - task: () => minishiftTasks.preflightCheckTasks(flags, command) + task: () => minishiftTasks.preflightCheckTasks(flags, command), } } else if (flags.platform === 'microk8s') { task = { title: 'โœˆ๏ธ MicroK8s preflight checklist', - task: () => microk8sTasks.preflightCheckTasks(flags, command) + task: () => microk8sTasks.preflightCheckTasks(flags, command), } } else if (flags.platform === 'k8s') { task = { title: 'โœˆ๏ธ Kubernetes preflight checklist', - task: () => k8sTasks.preflightCheckTasks(flags, command) + task: () => k8sTasks.preflightCheckTasks(flags, command), } } else if (flags.platform === 'docker-desktop') { task = { title: 'โœˆ๏ธ Docker Desktop preflight checklist', - task: () => dockerDesktopTasks.preflightCheckTasks(flags, command) + task: () => dockerDesktopTasks.preflightCheckTasks(flags, command), } // platform.ts END CHE ONLY } else { task = { title: 'โœˆ๏ธ Platform preflight checklist', - task: () => { command.error(`Platform ${flags.platform} is not supported yet ยฏ\\_(ใƒ„)_/ยฏ`) } + task: () => { + command.error(`Platform ${flags.platform} is not supported yet ยฏ\\_(ใƒ„)_/ยฏ`) + }, } } diff --git a/src/util.ts b/src/util.ts index 73f88d8e3..468788013 100644 --- a/src/util.ts +++ b/src/util.ts @@ -64,7 +64,7 @@ export function generatePassword(passwodLength: number, charactersSet = '') { const ranges = [[ZERO_CHAR_CODE, NINE_CHAR_CODE], [A_CHAR_CODE, Z_CHAR_CODE], [a_CHAR_CODE, z_CHAR_CODE]] dictionary = [] - for (let range of ranges) { + for (const range of ranges) { for (let charCode = range[0]; charCode <= range[1]; charCode++) { dictionary.push(String.fromCharCode(charCode)) } @@ -107,8 +107,8 @@ export function getImageNameAndTag(image: string): [string, string] { deployedCheOperatorImageName = image deployedCheOperatorImageTag = 'latest' } else { - let beforeLastColon = image.substring(0, lastColonIndex) - let afterLastColon = image.substring(lastColonIndex + 1) + const beforeLastColon = image.substring(0, lastColonIndex) + const afterLastColon = image.substring(lastColonIndex + 1) if (afterLastColon.includes('/')) { // The colon is for registry port and not for a tag deployedCheOperatorImageName = image @@ -202,7 +202,7 @@ export function getCommandErrorMessage(err: Error): string { export function notifyCommandCompletedSuccessfully(): void { notifier.notify({ title: 'chectl', - message: getCommandSuccessMessage() + message: getCommandSuccessMessage(), }) } @@ -272,7 +272,7 @@ export async function downloadFile(url: string, dest: string): Promise { */ export async function downloadYaml(url: string): Promise { const axiosInstance = axios.create({ - httpsAgent: new https.Agent({}) + httpsAgent: new https.Agent({}), }) const response = await axiosInstance.get(url) return yaml.safeLoad(response.data) @@ -327,8 +327,7 @@ export async function getDistribution(): Promise { const platorm = await promisify(getos)() as getos.LinuxOs return platorm.dist } catch { - return + } } - return } diff --git a/yarn.lock b/yarn.lock index cd3133b4a..857466625 100644 --- a/yarn.lock +++ b/yarn.lock @@ -900,6 +900,11 @@ resolved "https://registry.yarnpkg.com/@types/countries-and-timezones/-/countries-and-timezones-2.0.3.tgz#0b889d6105ad33c4995b5fc3b84636156aa3acfd" integrity sha512-P1LCigalHJA3fOsMNqXAzjkiaK5VCFaXUpHqIwUoc19clJoD/2VlC9JuRWizP3YCPeNekJBANEY9IFSIp0prBw== +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + "@types/fs-extra@^9.0.1": version "9.0.1" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918" @@ -967,7 +972,7 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb" integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww== -"@types/json-schema@^7.0.7": +"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.7": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== @@ -1151,6 +1156,16 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^2.6.1": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" + integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== + dependencies: + "@typescript-eslint/experimental-utils" "2.34.0" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + tsutils "^3.17.1" + "@typescript-eslint/eslint-plugin@^4.26.0": version "4.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.0.tgz#12bbd6ebd5e7fabd32e48e1e60efa1f3554a3242" @@ -1165,6 +1180,16 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/experimental-utils@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" + integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + "@typescript-eslint/experimental-utils@4.26.0": version "4.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.0.tgz#ba7848b3f088659cdf71bce22454795fc55be99a" @@ -1177,6 +1202,16 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/parser@^2.6.1": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" + integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-visitor-keys "^1.1.0" + "@typescript-eslint/parser@^4.26.0": version "4.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.0.tgz#31b6b732c9454f757b020dab9b6754112aa5eeaf" @@ -1200,6 +1235,19 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.0.tgz#7c6732c0414f0a69595f4f846ebe12616243d546" integrity sha512-rADNgXl1kS/EKnDr3G+m7fB9yeJNnR9kF7xMiXL6mSIWpr3Wg5MhxyfEXy/IlYthsqwBqHOr22boFbf/u6O88A== +"@typescript-eslint/typescript-estree@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/typescript-estree@4.26.0": version "4.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.0.tgz#aea17a40e62dc31c63d5b1bbe9a75783f2ce7109" @@ -1986,6 +2034,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" + integrity sha1-jffHquUf02h06PjQW5GAvBGj/tc= + dependencies: + escape-string-regexp "^1.0.5" + clean-stack@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" @@ -2479,15 +2534,15 @@ ecc-jsbn@~0.1.1: "eclipse-che-devfile-workspace-operator@git://github.com/devfile/devworkspace-operator#main": version "0.0.0" - resolved "git://github.com/devfile/devworkspace-operator#ee228b8da7499229592d8b553606a3a56609eec4" + resolved "git://github.com/devfile/devworkspace-operator#c1be0903944bef33592a2cc6089600727269fea8" "eclipse-che-operator@git://github.com/eclipse-che/che-operator#main": version "0.0.0" - resolved "git://github.com/eclipse-che/che-operator#b2f4f44f4519898e9e7a17152b2197b5cd4bd4ac" + resolved "git://github.com/eclipse-che/che-operator#cad643a6e5af2f85b9eebe8e5e7dbc5af50a0853" "eclipse-che-server@git://github.com/eclipse-che/che-server#main": version "0.0.0" - resolved "git://github.com/eclipse-che/che-server#a4f454b518fb327c2a51ab3ee22f458bb923ff06" + resolved "git://github.com/eclipse-che/che-server#10d437437fce462263021ff3c63bd6dd37ac1c11" editorconfig@^0.15.0: version "0.15.3" @@ -2577,17 +2632,100 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-ast-utils@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz#3d58ba557801cfb1c941d68131ee9f8c34bd1586" + integrity sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA== + dependencies: + lodash.get "^4.4.2" + lodash.zip "^4.2.0" + +eslint-config-oclif-typescript@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-0.2.0.tgz#8e2a1c788ce47af73d5bf8a3a0b5b2b6a80f05d6" + integrity sha512-BdP5FgjxwqV9LmVnfd4TaxhwdQipY7GJVVuP2AKm+EkKoupSCflSiRqnd2cj8qvVq/IXYPK2eHpRDk5ts8hP9g== + dependencies: + "@typescript-eslint/eslint-plugin" "^2.6.1" + "@typescript-eslint/parser" "^2.6.1" + eslint-config-xo-space "^0.20.0" + eslint-plugin-mocha "^5.2.0" + eslint-plugin-node "^7.0.1" + eslint-plugin-unicorn "^6.0.1" + +eslint-config-oclif@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-oclif/-/eslint-config-oclif-3.1.0.tgz#cbc207ced09e31676dcee2f724fc509cd20eb0bd" + integrity sha512-Tqgy43cNXsSdhTLWW4RuDYGFhV240sC4ISSv/ZiUEg/zFxExSEUpRE6J+AGnkKY9dYwIW4C9b2YSUVv8z/miMA== + dependencies: + eslint-config-xo-space "^0.20.0" + eslint-plugin-mocha "^5.2.0" + eslint-plugin-node "^7.0.1" + eslint-plugin-unicorn "^6.0.1" + +eslint-config-xo-space@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/eslint-config-xo-space/-/eslint-config-xo-space-0.20.0.tgz#75e1fb86d1b052fc1cc3036ca2fa441fa92b85e4" + integrity sha512-bOsoZA8M6v1HviDUIGVq1fLVnSu3mMZzn85m2tqKb73tSzu4GKD4Jd2Py4ZKjCgvCbRRByEB5HPC3fTMnnJ1uw== + dependencies: + eslint-config-xo "^0.24.0" + +eslint-config-xo@^0.24.0: + version "0.24.2" + resolved "https://registry.yarnpkg.com/eslint-config-xo/-/eslint-config-xo-0.24.2.tgz#f61b8ce692e9f9519bdb6edc4ed7ebcd5be48f48" + integrity sha512-ivQ7qISScW6gfBp+p31nQntz1rg34UCybd3uvlngcxt5Utsf4PMMi9QoAluLFcPUM5Tvqk4JGraR9qu3msKPKQ== + +eslint-plugin-es@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" + integrity sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA== + dependencies: + eslint-utils "^1.4.2" + regexpp "^2.0.1" + eslint-plugin-header@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== +eslint-plugin-mocha@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-5.3.0.tgz#cf3eb18ae0e44e433aef7159637095a7cb19b15b" + integrity sha512-3uwlJVLijjEmBeNyH60nzqgA1gacUWLUmcKV8PIGNvj1kwP/CTgAWQHn2ayyJVwziX+KETkr9opNwT1qD/RZ5A== + dependencies: + ramda "^0.26.1" + eslint-plugin-no-null@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz#1236a812391390a1877ad4007c26e745341c951f" integrity sha1-EjaoEjkTkKGHetQAfCbnRTQclR8= -eslint-scope@^5.1.1: +eslint-plugin-node@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz#a6e054e50199b2edd85518b89b4e7b323c9f36db" + integrity sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw== + dependencies: + eslint-plugin-es "^1.3.1" + eslint-utils "^1.3.1" + ignore "^4.0.2" + minimatch "^3.0.4" + resolve "^1.8.1" + semver "^5.5.0" + +eslint-plugin-unicorn@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-6.0.1.tgz#4a97f0bc9449e20b82848dad12094ee2ba72347e" + integrity sha512-hjy9LhTdtL7pz8WTrzS0CGXRkWK3VAPLDjihofj8JC+uxQLfXm0WwZPPPB7xKmcjRyoH+jruPHOCrHNEINpG/Q== + dependencies: + clean-regexp "^1.0.0" + eslint-ast-utils "^1.0.0" + import-modules "^1.1.0" + lodash.camelcase "^4.1.1" + lodash.kebabcase "^4.0.1" + lodash.snakecase "^4.0.1" + lodash.upperfirst "^4.2.0" + safe-regex "^1.1.0" + +eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -2595,7 +2733,14 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: +eslint-utils@^1.3.1, eslint-utils@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -3272,6 +3417,18 @@ glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3498,7 +3655,7 @@ ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ignore@^4.0.6: +ignore@^4.0.2, ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== @@ -3524,6 +3681,11 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +import-modules@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/import-modules/-/import-modules-1.1.0.tgz#748db79c5cc42bb9701efab424f894e72600e9dc" + integrity sha1-dI23nFzEK7lwHvq0JPiU5yYA6dw= + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4598,21 +4760,41 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.camelcase@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.kebabcase@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.snakecase@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -4638,6 +4820,16 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= +lodash.upperfirst@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" + integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984= + +lodash.zip@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= + lodash@4.x, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -5543,6 +5735,11 @@ querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +ramda@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + randomatic@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" @@ -5646,7 +5843,12 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.1.0: +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -5757,7 +5959,7 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2: dependencies: path-parse "^1.0.6" -resolve@^1.18.1: +resolve@^1.18.1, resolve@^1.8.1: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -6587,7 +6789,7 @@ tslib@^2, tslib@^2.0.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== -tsutils@^3.21.0: +tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== From 44263be3871e7d768dee164013ba849c9923f6ec Mon Sep 17 00:00:00 2001 From: Flavius Lacatusu Date: Tue, 8 Jun 2021 13:51:18 +0200 Subject: [PATCH 3/5] fix readme Signed-off-by: Flavius Lacatusu --- .github/workflows/automation-check.yml | 9 - configs/disabled.rules.chectl.json | 5 +- configs/eslint.license.json | 4 +- src/api/che-api-client.ts | 694 +-- src/api/che-login-manager.ts | 21 +- src/api/che.ts | 11 +- src/api/config-manager.ts | 2 +- src/api/context.ts | 2 +- src/api/devfile.ts | 2 +- src/api/github-client.ts | 40 +- src/api/kube.ts | 5225 +++++++++-------- src/api/openshift.ts | 5 +- src/api/typings/cert-manager.d.ts | 2 +- src/api/typings/olm.d.ts | 2 +- src/api/typings/openshift.d.ts | 2 +- src/api/version.ts | 4 +- src/commands/auth/delete.ts | 2 +- src/commands/auth/get.ts | 2 +- src/commands/auth/list.ts | 2 +- src/commands/auth/login.ts | 2 +- src/commands/auth/logout.ts | 2 +- src/commands/auth/use.ts | 4 +- src/commands/cacert/export.ts | 2 +- src/commands/dashboard/open.ts | 2 +- src/commands/devfile/generate.ts | 5 +- src/commands/server/debug.ts | 2 +- src/commands/server/delete.ts | 2 +- src/commands/server/deploy.ts | 888 +-- src/commands/server/logs.ts | 2 +- src/commands/server/start.ts | 106 +- src/commands/server/status.ts | 2 +- src/commands/server/stop.ts | 2 +- src/commands/server/update.ts | 7 +- src/commands/workspace/create.ts | 2 +- src/commands/workspace/delete.ts | 2 +- src/commands/workspace/inject.ts | 11 +- src/commands/workspace/list.ts | 2 +- src/commands/workspace/logs.ts | 2 +- src/commands/workspace/start.ts | 2 +- src/commands/workspace/stop.ts | 2 +- src/common-flags.ts | 2 +- src/constants.ts | 2 +- src/hooks/analytics/analytics.ts | 2 +- src/hooks/analytics/segment-adapter.ts | 2 +- src/hooks/prerun/new-version-warning.ts | 2 +- src/index.ts | 2 +- src/tasks/che.ts | 2 +- .../component-installers/cert-manager.ts | 2 +- .../devfile-workspace-operator-installer.ts | 2 +- src/tasks/installers/common-tasks.ts | 2 +- src/tasks/installers/helm.ts | 22 +- src/tasks/installers/installer.ts | 2 +- src/tasks/installers/olm.ts | 5 +- src/tasks/installers/operator.ts | 2 +- src/tasks/kube.ts | 2 +- src/tasks/platforms/api.ts | 10 +- src/tasks/platforms/common-platform-tasks.ts | 2 +- src/tasks/platforms/crc.ts | 5 +- src/tasks/platforms/docker-desktop.ts | 2 +- src/tasks/platforms/k8s.ts | 2 +- src/tasks/platforms/microk8s.ts | 6 +- src/tasks/platforms/minikube.ts | 7 +- src/tasks/platforms/minishift.ts | 5 +- src/tasks/platforms/openshift.ts | 2 +- src/tasks/platforms/platform.ts | 2 +- src/util.ts | 5 +- 66 files changed, 3614 insertions(+), 3574 deletions(-) diff --git a/.github/workflows/automation-check.yml b/.github/workflows/automation-check.yml index 8e02825d9..5422b3e57 100644 --- a/.github/workflows/automation-check.yml +++ b/.github/workflows/automation-check.yml @@ -24,12 +24,3 @@ jobs: echo "[ERROR] README.md it is not up to date. Please run 'yarn oclif-dev readme' to update and commit the changes." exit 1 fi - - name: Check license header year eslint template - run: | - TEMPLATE=$(jq '.rules."header/header"[2][1].pattern' configs/eslint.license.json) - CURRENT_YEAR=$(date +'%Y') - - if [[ ${TEMPLATE} != *"$CURRENT_YEAR"* ]];then - echo -e "[ERROR] The license header template doesn't contain the current year. Please change the year in configs/eslint.license.json and run yarn lint:fix" - exit 1 - fi diff --git a/configs/disabled.rules.chectl.json b/configs/disabled.rules.chectl.json index b5c8ff798..3703e713c 100644 --- a/configs/disabled.rules.chectl.json +++ b/configs/disabled.rules.chectl.json @@ -22,6 +22,9 @@ "no-multi-assign": 0, "no-lonely-if": 0, "no-async-promise-executor": 0, - "prefer-promise-reject-errors": 0 + "prefer-promise-reject-errors": 0, + "no-else-return": 0, + "no-useless-return": 0, + "complexity": 0 } } diff --git a/configs/eslint.license.json b/configs/eslint.license.json index 00f5af1c1..162e76971 100644 --- a/configs/eslint.license.json +++ b/configs/eslint.license.json @@ -7,8 +7,8 @@ [ "*", { - "pattern": "^ \\* Copyright \\(c\\) 2021 Red Hat, Inc\\.$", - "template": " * Copyright (c) 2021 Red Hat, Inc." + "pattern": "^ \\* Copyright \\(c\\) \\d{4}(-\\d{4})* Red Hat, Inc\\.$", + "template": " * Copyright (c) 2019-2021 Red Hat, Inc." }, " * This program and the accompanying materials are made", " * available under the terms of the Eclipse Public License 2.0", diff --git a/src/api/che-api-client.ts b/src/api/che-api-client.ts index 3e19534f6..0d0303942 100644 --- a/src/api/che-api-client.ts +++ b/src/api/che-api-client.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -18,349 +18,355 @@ import * as https from 'https' import { newError, sleep } from '../util' /** - * Singleton responsible for calls to Che API. - */ + * Singleton responsible for calls to Che API. + */ let instance: CheApiClient | undefined export class CheApiClient { - public defaultCheResponseTimeoutMs = 3000 - - public readonly cheApiEndpoint: string - - private readonly axios: AxiosInstance - - private constructor(cheApiEndpoint: string) { - this.cheApiEndpoint = cheApiEndpoint - - // Make axios ignore untrusted certificate error for self-signed certificate case. - const httpsAgent = new https.Agent({ rejectUnauthorized: false }) - - this.axios = axios.create({ - httpsAgent, - }) - } - - public static getInstance(cheApiEndpoint: string): CheApiClient { - cheApiEndpoint = this.normalizeCheApiEndpointUrl(cheApiEndpoint)! - if (!instance || instance.cheApiEndpoint !== cheApiEndpoint) { - instance = new CheApiClient(cheApiEndpoint) - } - return instance - } - - public static normalizeCheApiEndpointUrl(url: string) { - if (!url.includes('://')) { - url = 'https://' + url - } - const u = new URL(url) - url = 'https://' + u.host + u.pathname - if (url.endsWith('/')) { - url = url.slice(0, -1) - } - return url - } - - /** - * Checks whether provided url really points to Che server API. - * Throws an exception if it's not. - */ - async checkCheApiEndpointUrl(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { - try { - const response = await this.axios.get(`${this.cheApiEndpoint}/system/state`, { timeout: responseTimeoutMs }) - if (response.data && response.data.status) { - return - } - } catch { - throw new Error(`E_CHE_API_URL_NO_RESPONSE - Failed to connect to "${this.cheApiEndpoint}". Is it the right url?`) - } - throw new Error(`E_CHE_API_WRONG_URL - Provided url "${this.cheApiEndpoint}" is not Che API url`) - } - - async isCheServerReady(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { - const id = this.axios.interceptors.response.use(response => response, async (error: any) => { - if (error.config && error.response && (error.response.status === 404 || error.response.status === 503)) { - await sleep(500) - return this.axios.request(error.config) - } - return Promise.reject(error) - }) - - try { - await this.axios.get(`${this.cheApiEndpoint}/system/state`, { timeout: responseTimeoutMs }) - return true - } catch { - return false - } finally { - this.axios.interceptors.response.eject(id) - } - } - - async getCheServerStatus(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { - const endpoint = `${this.cheApiEndpoint}/system/state` - let response = null - try { - response = await this.axios.get(endpoint, { timeout: responseTimeoutMs }) - } catch (error) { - throw this.getCheApiError(error, endpoint) - } - this.checkResponse(response, endpoint) - return response.data.status - } - - async startCheServerShutdown(accessToken = '', responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { - const endpoint = `${this.cheApiEndpoint}/system/stop?shutdown=true` - const headers = accessToken ? { Authorization: accessToken } : null - let response = null - try { - response = await this.axios.post(endpoint, null, { headers, timeout: responseTimeoutMs }) - } catch (error) { - if (error.response && error.response.status === 409) { - return - } - throw this.getCheApiError(error, endpoint) - } - if (!response || response.status !== 204) { - throw new Error('E_BAD_RESP_CHE_API') - } - } - - async waitUntilCheServerReadyToShutdown(intervalMs = 500, timeoutMs = 60000): Promise { - const iterations = timeoutMs / intervalMs - for (let index = 0; index < iterations; index++) { - const status = await this.getCheServerStatus() - if (status === 'READY_TO_SHUTDOWN') { - return - } - await cli.wait(intervalMs) - } - throw new Error('ERR_TIMEOUT') - } - - /** - * Returns list of all workspaces of the user. - */ - async getAllWorkspaces(accessToken?: string): Promise { - const all: chetypes.workspace.Workspace[] = [] - const itemsPerPage = 30 - - let skipCount = 0 - let workspaces: chetypes.workspace.Workspace[] - do { - workspaces = await this.getWorkspaces(skipCount, itemsPerPage, accessToken) - all.push(...workspaces) - skipCount += workspaces.length - } while (workspaces.length === itemsPerPage) - - return all - } - - /** - * Returns list of workspaces in given range. - * If lst of all workspaces is needed, getAllWorkspaces should be used insted. - */ - async getWorkspaces(skipCount = 0, maxItems = 30, accessToken?: string): Promise { - const endpoint = `${this.cheApiEndpoint}/workspace?skipCount=${skipCount}&maxItems=${maxItems}` - const headers: any = { 'Content-Type': 'text/yaml' } - if (accessToken && accessToken.length > 0) { - headers.Authorization = accessToken - } - - try { - const response = await this.axios.get(endpoint, { headers }) - if (response && response.data) { - return response.data - } - throw new Error('E_BAD_RESP_CHE_SERVER') - } catch (error) { - throw this.getCheApiError(error, endpoint) - } - } - - async getWorkspaceById(workspaceId: string, accessToken?: string): Promise { - const endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}` - const headers: any = { 'Content-Type': 'text/yaml' } - if (accessToken) { - headers.Authorization = accessToken - } - - try { - const response = await this.axios.get(endpoint, { headers }) - return response.data - } catch (error) { - if (error.response.status === 404) { - throw new Error(`Workspace ${workspaceId} not found. Please use the command workspace:list to get list of the existed workspaces.`) - } - throw this.getCheApiError(error, endpoint) - } - } - - async deleteWorkspaceById(workspaceId: string, accessToken?: string): Promise { - const endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}` - const headers: any = {} - if (accessToken) { - headers.Authorization = accessToken - } - - try { - await this.axios.delete(endpoint, { headers }) - } catch (error) { - if (error.response.status === 404) { - throw new Error(`Workspace ${workspaceId} not found. Please use the command workspace:list to get list of the existed workspaces.`) - } else if (error.response.status === 409) { - throw new Error('Cannot delete a running workspace. Please stop it using the command workspace:stop and try again') - } - throw this.getCheApiError(error, endpoint) - } - } - - async startWorkspace(workspaceId: string, debug: boolean, accessToken?: string): Promise { - let endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}/runtime` - if (debug) { - endpoint += '?debug-workspace-start=true' - } - let response - - const headers: { [key: string]: string } = {} - if (accessToken) { - headers.Authorization = accessToken - } - try { - response = await this.axios.post(endpoint, undefined, { headers }) - } catch (error) { - if (error.response && error.response.status === 404) { - throw new Error(`E_WORKSPACE_NOT_EXIST - workspace with "${workspaceId}" id doesn't exist`) - } else { - throw this.getCheApiError(error, endpoint) - } - } - - this.checkResponse(response, endpoint) - } - - async stopWorkspace(workspaceId: string, accessToken?: string): Promise { - const endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}/runtime` - let response - - const headers: { [key: string]: string } = {} - if (accessToken) { - headers.Authorization = accessToken - } - try { - response = await this.axios.delete(endpoint, { headers }) - } catch (error) { - if (error.response && error.response.status === 404) { - throw new Error(`E_WORKSPACE_NOT_EXIST - workspace with "${workspaceId}" id doesn't exist`) - } else { - throw this.getCheApiError(error, endpoint) - } - } - - if (!response || response.status !== 204) { - throw new Error('E_BAD_RESP_CHE_API') - } - } - - async createWorkspaceFromDevfile(devfileContent: string, accessToken?: string): Promise { - const endpoint = `${this.cheApiEndpoint}/workspace/devfile` - const headers: any = { 'Content-Type': 'text/yaml' } - if (accessToken) { - headers.Authorization = accessToken - } - - let response: any - try { - response = await this.axios.post(endpoint, devfileContent, { headers }) - } catch (error) { - if (error.response) { - if (error.response.status === 400) { - throw new Error(`E_BAD_DEVFILE_FORMAT - Message: ${error.response.data.message}`) - } - if (error.response.status === 409) { - let message = '' - if (error.response.data) { - message = error.response.data.message - } - throw new Error(`E_CONFLICT - Message: ${message}`) - } - } - - throw this.getCheApiError(error, endpoint) - } - - if (response && response.data) { - return response.data as chetypes.workspace.Workspace - } - throw new Error('E_BAD_RESP_CHE_SERVER') - } - - /** - * Returns Keycloak settings or undefined for single user mode. - */ - async getKeycloakSettings(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { - const endpoint = `${this.cheApiEndpoint}/keycloak/settings` - let response - try { - response = await this.axios.get(endpoint, { timeout: responseTimeoutMs }) - } catch (error) { - if (error.response && error.response.status === 404) { - return - } - throw this.getCheApiError(error, endpoint) - } - this.checkResponse(response, endpoint) - if (!response.data['che.keycloak.token.endpoint']) { - // The response is not keycloak response, but a default fallback - throw new Error('E_BAD_CHE_API_URL') - } - return response.data - } - - async isAuthenticationEnabled(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { - const endpoint = `${this.cheApiEndpoint}/keycloak/settings` - let response - try { - response = await this.axios.get(endpoint, { timeout: responseTimeoutMs }) - } catch (error) { - if (error.response && (error.response.status === 404 || error.response.status === 503)) { - return false - } - throw this.getCheApiError(error, endpoint) - } - this.checkResponse(response, endpoint) - if (!response.data['che.keycloak.token.endpoint']) { - // The response is not keycloak response, but a default fallback - return false - } - return true - } - - private checkResponse(response: any, endpoint?: string): void { - if (!response || response.status !== 200 || !response.data) { - throw new Error(`E_BAD_RESP_CHE_API - Response code: ${response.status}` + endpoint ? `, endpoint: ${endpoint}` : '') - } - } - - private getCheApiError(error: any, endpoint: string): Error { - if (error.response) { - const status = error.response.status - if (status === 403) { - return newError(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`, error) - } if (status === 401) { - return newError(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`, error) - } if (status === 404) { - return newError(`E_CHE_API_NOTFOUND - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`, error) - } if (status === 503) { - return newError(`E_CHE_API_UNAVAIL - Endpoint: ${endpoint} returned 503 code`, error) - } - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - return newError(`E_CHE_API_UNKNOWN_ERROR - Endpoint: ${endpoint} -Status: ${error.response.status}`, error) - } 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 newError(`E_CHE_API_NO_RESPONSE - Endpoint: ${endpoint} - Error message: ${error.message}`, error) - } - // Something happened in setting up the request that triggered an Error - return newError(`E_CHECTL_UNKNOWN_ERROR - Endpoint: ${endpoint} - Message: ${error.message}`, error) - } + public defaultCheResponseTimeoutMs = 3000 + + public readonly cheApiEndpoint: string + + private readonly axios: AxiosInstance + + private constructor(cheApiEndpoint: string) { + this.cheApiEndpoint = cheApiEndpoint + + // Make axios ignore untrusted certificate error for self-signed certificate case. + const httpsAgent = new https.Agent({ rejectUnauthorized: false }) + + this.axios = axios.create({ + httpsAgent, + }) + } + + public static getInstance(cheApiEndpoint: string): CheApiClient { + cheApiEndpoint = this.normalizeCheApiEndpointUrl(cheApiEndpoint)! + if (!instance || instance.cheApiEndpoint !== cheApiEndpoint) { + instance = new CheApiClient(cheApiEndpoint) + } + return instance + } + + public static normalizeCheApiEndpointUrl(url: string) { + if (!url.includes('://')) { + url = 'https://' + url + } + const u = new URL(url) + url = 'https://' + u.host + u.pathname + if (url.endsWith('/')) { + url = url.slice(0, -1) + } + return url + } + + /** + * Checks whether provided url really points to Che server API. + * Throws an exception if it's not. + */ + async checkCheApiEndpointUrl(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { + try { + const response = await this.axios.get(`${this.cheApiEndpoint}/system/state`, { timeout: responseTimeoutMs }) + if (response.data && response.data.status) { + return + } + } catch { + throw new Error(`E_CHE_API_URL_NO_RESPONSE - Failed to connect to "${this.cheApiEndpoint}". Is it the right url?`) + } + throw new Error(`E_CHE_API_WRONG_URL - Provided url "${this.cheApiEndpoint}" is not Che API url`) + } + + async isCheServerReady(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { + const id = this.axios.interceptors.response.use(response => response, async (error: any) => { + if (error.config && error.response && (error.response.status === 404 || error.response.status === 503)) { + await sleep(500) + return this.axios.request(error.config) + } + return Promise.reject(error) + }) + + try { + await this.axios.get(`${this.cheApiEndpoint}/system/state`, { timeout: responseTimeoutMs }) + return true + } catch { + return false + } finally { + this.axios.interceptors.response.eject(id) + } + } + + async getCheServerStatus(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { + const endpoint = `${this.cheApiEndpoint}/system/state` + let response = null + try { + response = await this.axios.get(endpoint, { timeout: responseTimeoutMs }) + } catch (error) { + throw this.getCheApiError(error, endpoint) + } + this.checkResponse(response, endpoint) + return response.data.status + } + + async startCheServerShutdown(accessToken = '', responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { + const endpoint = `${this.cheApiEndpoint}/system/stop?shutdown=true` + const headers = accessToken ? { Authorization: accessToken } : null + let response = null + try { + response = await this.axios.post(endpoint, null, { headers, timeout: responseTimeoutMs }) + } catch (error) { + if (error.response && error.response.status === 409) { + return + } else { + throw this.getCheApiError(error, endpoint) + } + } + if (!response || response.status !== 204) { + throw new Error('E_BAD_RESP_CHE_API') + } + } + + async waitUntilCheServerReadyToShutdown(intervalMs = 500, timeoutMs = 60000): Promise { + const iterations = timeoutMs / intervalMs + for (let index = 0; index < iterations; index++) { + const status = await this.getCheServerStatus() + if (status === 'READY_TO_SHUTDOWN') { + return + } + await cli.wait(intervalMs) + } + throw new Error('ERR_TIMEOUT') + } + + /** + * Returns list of all workspaces of the user. + */ + async getAllWorkspaces(accessToken?: string): Promise { + const all: chetypes.workspace.Workspace[] = [] + const itemsPerPage = 30 + + let skipCount = 0 + let workspaces: chetypes.workspace.Workspace[] + do { + workspaces = await this.getWorkspaces(skipCount, itemsPerPage, accessToken) + all.push(...workspaces) + skipCount += workspaces.length + } while (workspaces.length === itemsPerPage) + + return all + } + + /** + * Returns list of workspaces in given range. + * If lst of all workspaces is needed, getAllWorkspaces should be used insted. + */ + async getWorkspaces(skipCount = 0, maxItems = 30, accessToken?: string): Promise { + const endpoint = `${this.cheApiEndpoint}/workspace?skipCount=${skipCount}&maxItems=${maxItems}` + const headers: any = { 'Content-Type': 'text/yaml' } + if (accessToken && accessToken.length > 0) { + headers.Authorization = accessToken + } + + try { + const response = await this.axios.get(endpoint, { headers }) + if (response && response.data) { + return response.data + } else { + throw new Error('E_BAD_RESP_CHE_SERVER') + } + } catch (error) { + throw this.getCheApiError(error, endpoint) + } + } + + async getWorkspaceById(workspaceId: string, accessToken?: string): Promise { + const endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}` + const headers: any = { 'Content-Type': 'text/yaml' } + if (accessToken) { + headers.Authorization = accessToken + } + + try { + const response = await this.axios.get(endpoint, { headers }) + return response.data + } catch (error) { + if (error.response.status === 404) { + throw new Error(`Workspace ${workspaceId} not found. Please use the command workspace:list to get list of the existed workspaces.`) + } + throw this.getCheApiError(error, endpoint) + } + } + + async deleteWorkspaceById(workspaceId: string, accessToken?: string): Promise { + const endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}` + const headers: any = {} + if (accessToken) { + headers.Authorization = accessToken + } + + try { + await this.axios.delete(endpoint, { headers }) + } catch (error) { + if (error.response.status === 404) { + throw new Error(`Workspace ${workspaceId} not found. Please use the command workspace:list to get list of the existed workspaces.`) + } else if (error.response.status === 409) { + throw new Error('Cannot delete a running workspace. Please stop it using the command workspace:stop and try again') + } + throw this.getCheApiError(error, endpoint) + } + } + + async startWorkspace(workspaceId: string, debug: boolean, accessToken?: string): Promise { + let endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}/runtime` + if (debug) { + endpoint += '?debug-workspace-start=true' + } + let response + + const headers: { [key: string]: string } = {} + if (accessToken) { + headers.Authorization = accessToken + } + try { + response = await this.axios.post(endpoint, undefined, { headers }) + } catch (error) { + if (error.response && error.response.status === 404) { + throw new Error(`E_WORKSPACE_NOT_EXIST - workspace with "${workspaceId}" id doesn't exist`) + } else { + throw this.getCheApiError(error, endpoint) + } + } + + this.checkResponse(response, endpoint) + } + + async stopWorkspace(workspaceId: string, accessToken?: string): Promise { + const endpoint = `${this.cheApiEndpoint}/workspace/${workspaceId}/runtime` + let response + + const headers: { [key: string]: string } = {} + if (accessToken) { + headers.Authorization = accessToken + } + try { + response = await this.axios.delete(endpoint, { headers }) + } catch (error) { + if (error.response && error.response.status === 404) { + throw new Error(`E_WORKSPACE_NOT_EXIST - workspace with "${workspaceId}" id doesn't exist`) + } else { + throw this.getCheApiError(error, endpoint) + } + } + + if (!response || response.status !== 204) { + throw new Error('E_BAD_RESP_CHE_API') + } + } + + async createWorkspaceFromDevfile(devfileContent: string, accessToken?: string): Promise { + const endpoint = `${this.cheApiEndpoint}/workspace/devfile` + const headers: any = { 'Content-Type': 'text/yaml' } + if (accessToken) { + headers.Authorization = accessToken + } + + let response: any + try { + response = await this.axios.post(endpoint, devfileContent, { headers }) + } catch (error) { + if (error.response) { + if (error.response.status === 400) { + throw new Error(`E_BAD_DEVFILE_FORMAT - Message: ${error.response.data.message}`) + } + if (error.response.status === 409) { + let message = '' + if (error.response.data) { + message = error.response.data.message + } + throw new Error(`E_CONFLICT - Message: ${message}`) + } + } + + throw this.getCheApiError(error, endpoint) + } + + if (response && response.data) { + return response.data as chetypes.workspace.Workspace + } else { + throw new Error('E_BAD_RESP_CHE_SERVER') + } + } + + /** + * Returns Keycloak settings or undefined for single user mode. + */ + async getKeycloakSettings(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { + const endpoint = `${this.cheApiEndpoint}/keycloak/settings` + let response + try { + response = await this.axios.get(endpoint, { timeout: responseTimeoutMs }) + } catch (error) { + if (error.response && error.response.status === 404) { + return + } + throw this.getCheApiError(error, endpoint) + } + this.checkResponse(response, endpoint) + if (!response.data['che.keycloak.token.endpoint']) { + // The response is not keycloak response, but a default fallback + throw new Error('E_BAD_CHE_API_URL') + } + return response.data + } + + async isAuthenticationEnabled(responseTimeoutMs = this.defaultCheResponseTimeoutMs): Promise { + const endpoint = `${this.cheApiEndpoint}/keycloak/settings` + let response + try { + response = await this.axios.get(endpoint, { timeout: responseTimeoutMs }) + } catch (error) { + if (error.response && (error.response.status === 404 || error.response.status === 503)) { + return false + } else { + throw this.getCheApiError(error, endpoint) + } + } + this.checkResponse(response, endpoint) + if (!response.data['che.keycloak.token.endpoint']) { + // The response is not keycloak response, but a default fallback + return false + } + return true + } + + private checkResponse(response: any, endpoint?: string): void { + if (!response || response.status !== 200 || !response.data) { + throw new Error(`E_BAD_RESP_CHE_API - Response code: ${response.status}` + endpoint ? `, endpoint: ${endpoint}` : '') + } + } + + private getCheApiError(error: any, endpoint: string): Error { + if (error.response) { + const status = error.response.status + if (status === 403) { + return newError(`E_CHE_API_FORBIDDEN - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data.message)}`, error) + } else if (status === 401) { + return newError(`E_CHE_API_UNAUTHORIZED - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`, error) + } else if (status === 404) { + return newError(`E_CHE_API_NOTFOUND - Endpoint: ${endpoint} - Message: ${JSON.stringify(error.response.data)}`, error) + } else if (status === 503) { + 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 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 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 newError(`E_CHECTL_UNKNOWN_ERROR - Endpoint: ${endpoint} - Message: ${error.message}`, error) + } + } } diff --git a/src/api/che-login-manager.ts b/src/api/che-login-manager.ts index c355182c6..2ce4c620b 100644 --- a/src/api/che-login-manager.ts +++ b/src/api/che-login-manager.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -172,8 +172,9 @@ export class CheServerLoginManager { apiUrl = CheApiClient.normalizeCheApiEndpointUrl(apiUrl) if (username) { return Boolean(this.getLoginRecord(apiUrl, username)) + } else { + return Boolean(this.loginData.logins![apiUrl]) } - return Boolean(this.loginData.logins![apiUrl]) } public getCurrentLoginInfo(): { cheApiEndpoint: string, username: string } { @@ -432,14 +433,16 @@ export class CheServerLoginManager { } if (isPasswordLoginData(loginRecord)) { return this.getKeycloakAuthDataByUserNameAndPassword(cheKeycloakSettings, loginRecord.username, loginRecord.password) + } else { + if (isRefreshTokenLoginData(loginRecord)) { + return this.getKeycloakAuthDataByRefreshToken(cheKeycloakSettings, loginRecord.refreshToken) + } else if (isOcUserTokenLoginData(loginRecord)) { + return this.getKeycloakAuthDataByOcToken(cheKeycloakSettings, loginRecord.subjectToken, loginRecord.subjectIssuer) + } else { + // Should never happen + throw new Error('Token is not provided') + } } - if (isRefreshTokenLoginData(loginRecord)) { - return this.getKeycloakAuthDataByRefreshToken(cheKeycloakSettings, loginRecord.refreshToken) - } if (isOcUserTokenLoginData(loginRecord)) { - return this.getKeycloakAuthDataByOcToken(cheKeycloakSettings, loginRecord.subjectToken, loginRecord.subjectIssuer) - } - // Should never happen - throw new Error('Token is not provided') } private async getKeycloakAuthDataByUserNameAndPassword(cheKeycloakSettings: CheKeycloakSettings, username: string, password: string): Promise { diff --git a/src/api/che.ts b/src/api/che.ts index 3b2e66ee1..9995b90ff 100644 --- a/src/api/che.ts +++ b/src/api/che.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -113,8 +113,9 @@ export class CheHelper { if (await this.kube.isOpenShift()) { return this.cheOpenShiftURL(namespace) + } else { + return this.cheK8sURL(namespace) } - return this.cheK8sURL(namespace) } async chePluginRegistryURL(namespace = ''): Promise { @@ -130,8 +131,9 @@ export class CheHelper { // grab URL if (await this.kube.isOpenShift()) { return this.chePluginRegistryOpenShiftURL(namespace) + } else { + return this.chePluginRegistryK8sURL(namespace) } - return this.chePluginRegistryK8sURL(namespace) } async isSelfSignedCertificateSecretExist(namespace: string): Promise { @@ -303,8 +305,9 @@ export class CheHelper { if (devfilePath.startsWith('http')) { const response = await this.axios.get(devfilePath) return response.data + } else { + return fs.readFileSync(devfilePath, 'utf8') } - return fs.readFileSync(devfilePath, 'utf8') } async buildDashboardURL(ideURL: string): Promise { diff --git a/src/api/config-manager.ts b/src/api/config-manager.ts index 4eafefc58..25ce94bf5 100644 --- a/src/api/config-manager.ts +++ b/src/api/config-manager.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/api/context.ts b/src/api/context.ts index 1a44fe074..a9f70691d 100644 --- a/src/api/context.ts +++ b/src/api/context.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/api/devfile.ts b/src/api/devfile.ts index 08b8c3219..a74e9d33c 100644 --- a/src/api/devfile.ts +++ b/src/api/devfile.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/api/github-client.ts b/src/api/github-client.ts index 47f7a6e2c..9fead1c67 100644 --- a/src/api/github-client.ts +++ b/src/api/github-client.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -42,7 +42,7 @@ export class CheGithubClient { async getTemplatesTagInfo(installer: string, version?: string): Promise { if (installer === 'operator' || installer === 'olm') { return this.getTagInfoByVersion(CHE_OPERATOR_REPO, version) - } if (installer === 'helm') { + } else if (installer === 'helm') { return this.getTagInfoByVersion(CHE_REPO, version) } throw new Error(`Unsupported installer: ${installer}`) @@ -129,21 +129,22 @@ export class CheGithubClient { if (!version || version === 'latest' || version === 'stable') { const tags = await this.listLatestTags(repo) return this.getLatestTag(tags) - } if (version === 'next' || version === 'nightly') { + } else if (version === 'next' || version === 'nightly') { return this.getLastCommitInfo(repo) + } else { + // User might provide a version directly or only version prefix, e.g. 7.15 + // Some old tags might have 'v' prefix + if (version.startsWith('v')) { + // Remove 'v' prefix + version = version.substr(1) + } + let tagInfo = await this.getTagInfoByVersionPrefix(repo, version) + if (!tagInfo) { + // Try to add 'v' prefix + tagInfo = tagInfo = await this.getTagInfoByVersionPrefix(repo, 'v' + version) + } + return tagInfo } - // User might provide a version directly or only version prefix, e.g. 7.15 - // Some old tags might have 'v' prefix - if (version.startsWith('v')) { - // Remove 'v' prefix - version = version.substr(1) - } - let tagInfo = await this.getTagInfoByVersionPrefix(repo, version) - if (!tagInfo) { - // Try to add 'v' prefix - tagInfo = tagInfo = await this.getTagInfoByVersionPrefix(repo, 'v' + version) - } - return tagInfo } /** @@ -162,7 +163,7 @@ export class CheGithubClient { const tags = await this.listLatestTags(repo, versionPrefix) if (tags.length === 0) { // Wrong version is given - + return } else if (tags.length === 1) { return tags[0] } else { @@ -225,12 +226,13 @@ export class CheGithubClient { const sortedSemanticTags = semanticTags.sort((semTagA: SemanticTagData, semTagB: SemanticTagData) => { if (semTagA.major !== semTagB.major) { return semTagB.major - semTagA.major - } if (semTagA.minor !== semTagB.minor) { + } else if (semTagA.minor !== semTagB.minor) { return semTagB.minor - semTagA.minor - } if (semTagA.patch !== semTagB.patch) { + } else if (semTagA.patch !== semTagB.patch) { return semTagB.patch - semTagA.patch + } else { + return 0 } - return 0 }) return sortedSemanticTags.map(tag => tag.data) diff --git a/src/api/kube.ts b/src/api/kube.ts index 90028773f..d1de2f1ca 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -16,7 +16,7 @@ import axios, { AxiosRequestConfig } from 'axios' import { cli } from 'cli-ux' import * as execa from 'execa' import * as fs from 'fs' - import https = require('https') +import https = require('https') import { merge } from 'lodash' import * as net from 'net' import { Writable } from 'stream' @@ -32,2613 +32,2620 @@ import { VersionHelper } from './version' const AWAIT_TIMEOUT_S = 30 export class KubeHelper { - public readonly kubeConfig - - readonly API_EXTENSIONS_V1BETA1 = 'apiextensions.k8s.io/v1beta1' - - podWaitTimeout: number - - podDownloadImageTimeout: number - - podReadyTimeout: number - - podErrorRecheckTimeout: number - - constructor(flags?: any) { - this.podWaitTimeout = (flags && flags.k8spodwaittimeout) ? parseInt(flags.k8spodwaittimeout, 10) : DEFAULT_K8S_POD_WAIT_TIMEOUT - this.podReadyTimeout = (flags && flags.k8spodreadytimeout) ? parseInt(flags.k8spodreadytimeout, 10) : DEFAULT_K8S_POD_WAIT_TIMEOUT - this.podDownloadImageTimeout = (flags && flags.k8spoddownloadimagetimeout) ? parseInt(flags.k8spoddownloadimagetimeout, 10) : DEFAULT_K8S_POD_WAIT_TIMEOUT - this.podErrorRecheckTimeout = (flags && flags.spoderrorrechecktimeout) ? parseInt(flags.spoderrorrechecktimeout, 10) : DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT - this.kubeConfig = new KubeConfig() - this.kubeConfig.loadFromDefault() - } - - async createNamespace(namespaceName: string, labels: any): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - const namespaceObject = { - apiVersion: 'v1', - kind: 'Namespace', - metadata: { - labels, - name: namespaceName, - }, - } - - try { - await k8sCoreApi.createNamespace(namespaceObject) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async deleteAllServices(namespace: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const res = await k8sApi.listNamespacedService(namespace) - if (res && res.response && res.response.statusCode === 200) { - const serviceList = res.body - await serviceList.items.forEach(async service => { - try { - await k8sApi.deleteNamespacedService(service.metadata!.name!, namespace) - } catch (error) { - if (error.response.statusCode !== 404) { - throw error - } - } - }) - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async applyResource(yamlPath: string, opts = ''): Promise { - const command = `kubectl apply -f ${yamlPath} ${opts}` - await execa(command, { timeout: 30000, shell: true }) - } - - async getServicesBySelector(labelSelector = '', namespace = ''): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const res = await k8sCoreApi.listNamespacedService(namespace, 'true', undefined, undefined, undefined, labelSelector) - if (res && res.body) { - return res.body - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - throw new Error('ERR_LIST_SERVICES') - } - - async waitForService(selector: string, namespace = '', intervalMs = 500, timeoutMs = 30000) { - const iterations = timeoutMs / intervalMs - for (let index = 0; index < iterations; index++) { - const currentServices = await this.getServicesBySelector(selector, namespace) - if (currentServices && currentServices.items.length > 0) { - return - } - await cli.wait(intervalMs) - } - throw new Error(`ERR_TIMEOUT: Timeout set to waiting for service ${timeoutMs}`) - } - - async serviceAccountExist(name = '', namespace = ''): Promise { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const { body } = await k8sApi.readNamespacedServiceAccount(name, namespace) - return this.compare(body, name) - } catch { - return false - } - } - - async createServiceAccount(name = '', namespace = '') { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - const sa = new V1ServiceAccount() - sa.metadata = new V1ObjectMeta() - sa.metadata.name = name - sa.metadata.namespace = namespace - try { - return await k8sCoreApi.createNamespacedServiceAccount(namespace, sa) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async waitServiceAccount(name: string, namespace: string, timeout = AWAIT_TIMEOUT_S): Promise { - return new Promise(async (resolve, reject) => { - // Set up watcher - const watcher = new Watch(this.kubeConfig) - const request = await watcher - .watch(`/api/v1/namespaces/${namespace}/serviceaccounts`, {}, - (_phase: string, obj: any) => { - const serviceAccount = obj as V1ServiceAccount - - // Filter other service accounts in the given namespace - if (serviceAccount && serviceAccount.metadata && serviceAccount.metadata.name === name) { - // The service account is present, stop watching - if (request) { - request.abort() - } - // Release awaiter - resolve() - } - }, - error => { - if (error) { - reject(error) - } - }) - - // Automatically stop watching after timeout - const timeoutHandler = setTimeout(() => { - request.abort() - reject(`Timeout reached while waiting for "${name}" service account.`) - }, timeout * 1000) - - // Request service account, for case if it is already exist - const serviceAccount = await this.getSecret(name, namespace) - if (serviceAccount) { - // Stop watching - request.abort() - clearTimeout(timeoutHandler) - - // Relese awaiter - resolve() - } - }) - } - - async deleteServiceAccount(name: string, namespace: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - await k8sCoreApi.deleteNamespacedServiceAccount(name, namespace) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async createServiceAccountFromFile(filePath: string, namespace = '') { - const yamlServiceAccount = this.safeLoadFromYamlFile(filePath) as V1ServiceAccount - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - return await k8sCoreApi.createNamespacedServiceAccount(namespace, yamlServiceAccount) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceServiceAccountFromFile(filePath: string, namespace = '') { - const yamlServiceAccount = this.safeLoadFromYamlFile(filePath) as V1ServiceAccount - if (!yamlServiceAccount || !yamlServiceAccount.metadata || !yamlServiceAccount.metadata.name) { - throw new Error(`Service account read from ${filePath} must have name specified.`) - } - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - return await k8sCoreApi.replaceNamespacedServiceAccount(yamlServiceAccount.metadata.name, namespace, yamlServiceAccount) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async roleExist(name = '', namespace = ''): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const { body } = await k8sRbacAuthApi.readNamespacedRole(name, namespace) - return this.compare(body, name) - } catch { - return false - } - } - - async clusterRoleExist(name = ''): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const { body } = await k8sRbacAuthApi.readClusterRole(name) - return this.compare(body, name) - } catch { - return false - } - } - - async getClusterRole(name: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const { body } = await k8sRbacAuthApi.readClusterRole(name) - return body - } catch { - - } - } - - async getRole(name: string, namespace: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const res = await k8sRbacAuthApi.readNamespacedRole(name, namespace) - return res.body - } catch (e) { - if (e.statusCode === 404) { - return - } - throw this.wrapK8sClientError(e) - } - } - - async listRoles(namespace: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const res = await k8sRbacAuthApi.listNamespacedRole(namespace) - return res.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createRoleFrom(yamlRole: V1Role, namespace: string) { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const res = await k8sRbacAuthApi.createNamespacedRole(namespace, yamlRole) - return res.response.statusCode - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createRoleFromFile(filePath: string, namespace: string) { - const yamlRole = this.safeLoadFromYamlFile(filePath) as V1Role - return this.createRoleFrom(yamlRole, namespace) - } - - async replaceRoleFrom(yamlRole: V1Role, namespace: string) { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - - if (!yamlRole.metadata || !yamlRole.metadata.name) { - throw new Error('Role object requires name') - } - try { - const res = await k8sRbacAuthApi.replaceNamespacedRole(yamlRole.metadata.name, namespace, yamlRole) - return res.response.statusCode - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceRoleFromFile(filePath: string, namespace: string) { - const yamlRole = this.safeLoadFromYamlFile(filePath) as V1Role - return this.replaceRoleFrom(yamlRole, namespace) - } - - async listClusterRoles(): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const res = await k8sRbacAuthApi.listClusterRole() - return res.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createClusterRoleFrom(yamlClusterRole: V1ClusterRole, clusterRoleName?: string) { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - if (!yamlClusterRole.metadata) { - yamlClusterRole.metadata = {} - } - - if (clusterRoleName) { - yamlClusterRole.metadata.name = clusterRoleName - } else if (!yamlClusterRole.metadata.name) { - throw new Error('Role name is not specified') - } - try { - const res = await k8sRbacAuthApi.createClusterRole(yamlClusterRole) - return res.response.statusCode - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createClusterRoleFromFile(filePath: string, clusterRoleName?: string) { - const yamlClusterRole = this.safeLoadFromYamlFile(filePath) as V1ClusterRole - return this.createClusterRoleFrom(yamlClusterRole, clusterRoleName) - } - - async replaceClusterRoleFrom(yamlClusterRole: V1ClusterRole, clusterRoleName?: string) { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - if (!yamlClusterRole.metadata) { - yamlClusterRole.metadata = {} - } - - if (clusterRoleName) { - yamlClusterRole.metadata.name = clusterRoleName - } else if (!yamlClusterRole.metadata.name) { - throw new Error('Role name is not specified') - } - try { - const res = await k8sRbacAuthApi.replaceClusterRole(yamlClusterRole.metadata.name, yamlClusterRole) - return res.response.statusCode - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceClusterRoleFromFile(filePath: string, clusterRoleName?: string) { - const yamlClusterRole = this.safeLoadFromYamlFile(filePath) as V1ClusterRole - return this.replaceClusterRoleFrom(yamlClusterRole, clusterRoleName) - } - - async addClusterRoleRule(name: string, apiGroups: string[], resources: string[], verbs: string[]): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - const clusterRole = await this.getClusterRole(name) - if (clusterRole) { - // Clean up metadata, otherwise replace role call will fail - clusterRole.metadata = {} - clusterRole.metadata.name = name - - // Add new policy - const additionaRule = new V1PolicyRule() - additionaRule.apiGroups = apiGroups - additionaRule.resources = resources - additionaRule.verbs = verbs - if (clusterRole.rules) { - clusterRole.rules.push(additionaRule) - } - - try { - const { body } = await k8sRbacAuthApi.replaceClusterRole(name, clusterRole) - return body - } catch { - - } - } - } - - async deleteRole(name: string, namespace: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - await k8sCoreApi.deleteNamespacedRole(name, namespace) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async getPodListByLabel(namespace: string, labelSelector: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const { body: podList } = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, labelSelector) - - return podList.items - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async deleteClusterRole(name: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - await k8sCoreApi.deleteClusterRole(name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async listRoleBindings(namespace: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const res = await k8sRbacAuthApi.listNamespacedRoleBinding(namespace) - return res.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async roleBindingExist(name = '', namespace = ''): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - await k8sRbacAuthApi.readNamespacedRoleBinding(name, namespace) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async isMutatingWebhookConfigurationExists(name: string): Promise { - const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) - try { - await k8sAdmissionApi.readMutatingWebhookConfiguration(name) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async getMutatingWebhookConfiguration(name: string): Promise { - const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) - try { - const res = await k8sAdmissionApi.readMutatingWebhookConfiguration(name) - return res.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async isValidatingWebhookConfigurationExists(name: string): Promise { - const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) - try { - await k8sAdmissionApi.readValidatingWebhookConfiguration(name) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async deleteValidatingWebhookConfiguration(name: string): Promise { - const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) - try { - await k8sAdmissionApi.deleteValidatingWebhookConfiguration(name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async deleteMutatingWebhookConfiguration(name: string): Promise { - const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) - try { - await k8sAdmissionApi.deleteMutatingWebhookConfiguration(name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async listClusterRoleBindings(labelSelector?: string, fieldSelector?: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const res = await k8sRbacAuthApi.listClusterRoleBinding(undefined, undefined, undefined, fieldSelector, labelSelector) - return res.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async clusterRoleBindingExist(name: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const { body } = await k8sRbacAuthApi.readClusterRoleBinding(name) - return this.compare(body, name) - } catch { - return false - } - } - - async createAdminRoleBinding(name = '', serviceAccount = '', namespace = '') { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - const rb = new V1RoleBinding() - rb.metadata = new V1ObjectMeta() - rb.metadata.name = name - rb.metadata.namespace = namespace - rb.roleRef = new V1RoleRef() - rb.roleRef.kind = 'ClusterRole' - rb.roleRef.name = 'admin' - const subject = new V1Subject() - subject.kind = 'ServiceAccount' - subject.name = serviceAccount - subject.namespace = namespace - rb.subjects = [subject] - try { - return await k8sRbacAuthApi.createNamespacedRoleBinding(namespace, rb) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createRoleBindingFrom(yamlRoleBinding: V1RoleBinding, namespace: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const response = await k8sRbacAuthApi.createNamespacedRoleBinding(namespace, yamlRoleBinding) - return response.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createRoleBindingFromFile(filePath: string, namespace: string): Promise { - const yamlRoleBinding = this.safeLoadFromYamlFile(filePath) as V1RoleBinding - return this.createRoleBindingFrom(yamlRoleBinding, namespace) - } - - async replaceRoleBindingFrom(yamlRoleBinding: V1RoleBinding, namespace: string): Promise { - if (!yamlRoleBinding.metadata || !yamlRoleBinding.metadata.name) { - throw new Error('RoleBinding object requires name') - } - - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - const response = await k8sRbacAuthApi.replaceNamespacedRoleBinding(yamlRoleBinding.metadata.name, namespace, yamlRoleBinding) - return response.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceRoleBindingFromFile(filePath: string, namespace: string): Promise { - const yamlRoleBinding = this.safeLoadFromYamlFile(filePath) as V1RoleBinding - return this.replaceRoleBindingFrom(yamlRoleBinding, namespace) - } - - async createClusterRoleBindingFrom(yamlClusterRoleBinding: V1ClusterRoleBinding) { - if (!yamlClusterRoleBinding.metadata || !yamlClusterRoleBinding.metadata.name) { - throw new Error('ClusterRoleBinding object requires name') - } - - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - return await k8sRbacAuthApi.createClusterRoleBinding(yamlClusterRoleBinding) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createClusterRoleBinding(name: string, saName: string, saNamespace = '', roleName = '') { - const clusterRoleBinding = { - apiVersion: 'rbac.authorization.k8s.io/v1', - metadata: { - name: `${name}`, - }, - subjects: [ - { - kind: 'ServiceAccount', - name: `${saName}`, - namespace: `${saNamespace}`, - }, - ], - roleRef: { - kind: 'ClusterRole', - name: `${roleName}`, - apiGroup: 'rbac.authorization.k8s.io', - }, - } as V1ClusterRoleBinding - return this.createClusterRoleBindingFrom(clusterRoleBinding) - } - - async replaceClusterRoleBindingFrom(clusterRoleBinding: V1ClusterRoleBinding) { - if (!clusterRoleBinding.metadata || !clusterRoleBinding.metadata.name) { - throw new Error('Cluster Role Binding must have name specified') - } - - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - return await k8sRbacAuthApi.replaceClusterRoleBinding(clusterRoleBinding.metadata.name, clusterRoleBinding) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceClusterRoleBinding(name: string, saName: string, saNamespace = '', roleName = '') { - const clusterRoleBinding = { - apiVersion: 'rbac.authorization.k8s.io/v1', - metadata: { - name: `${name}`, - }, - subjects: [ - { - kind: 'ServiceAccount', - name: `${saName}`, - namespace: `${saNamespace}`, - }, - ], - roleRef: { - kind: 'ClusterRole', - name: `${roleName}`, - apiGroup: 'rbac.authorization.k8s.io', - }, - } as V1ClusterRoleBinding - return this.replaceClusterRoleBindingFrom(clusterRoleBinding) - } - - async deleteRoleBinding(name: string, namespace: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - await k8sRbacAuthApi.deleteNamespacedRoleBinding(name, namespace) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async deleteClusterRoleBinding(name: string): Promise { - const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) - try { - await k8sRbacAuthApi.deleteClusterRoleBinding(name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async getConfigMap(name = '', namespace = ''): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const { body } = await k8sCoreApi.readNamespacedConfigMap(name, namespace) - return this.compare(body, name) && body - } catch { - - } - } - - async getConfigMapValue(name: string, namespace: string, key: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const { body } = await k8sCoreApi.readNamespacedConfigMap(name, namespace) - if (body.data) { - return body.data[key] - } - } catch { - - } - } - - async createConfigMapFromFile(filePath: string, namespace = '') { - const yamlConfigMap = this.safeLoadFromYamlFile(filePath) as V1ConfigMap - return this.createNamespacedConfigMap(namespace, yamlConfigMap) - } - - public async createNamespacedConfigMap(namespace: string, configMap: V1ConfigMap) { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - - try { - const { body } = await k8sCoreApi.createNamespacedConfigMap(namespace, configMap) - return body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async patchConfigMap(name: string, patch: any, namespace = '') { - const k8sCoreApi = this.kubeConfig.makeApiClient(PatchedK8sApi) - try { - return await k8sCoreApi.patchNamespacedConfigMap(name, namespace, patch) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async deleteConfigMap(name: string, namespace: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - await k8sCoreApi.deleteNamespacedConfigMap(name, namespace) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - public async replaceNamespacedConfigMap(name: string, namespace: string, configMap: V1ConfigMap) { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - - try { - const { body } = await k8sCoreApi.replaceNamespacedConfigMap(name, namespace, configMap) - return body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getNamespace(namespace: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const { body } = await k8sApi.readNamespace(namespace) - return body - } catch { - } - } - - async hasReadPermissionsForNamespace(namespace: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(AuthorizationV1Api) - const accessReview = new V1SelfSubjectAccessReview() - accessReview.spec = new V1SelfSubjectAccessReviewSpec() - accessReview.spec.resourceAttributes = { - group: '', - name: 'access-to-che-namespace', - namespace, - resource: 'namespaces', - verb: 'get', - } - - try { - const { body } = await k8sApi.createSelfSubjectAccessReview(accessReview) - return body.status!.allowed - } catch (error) { - if (error.response && error.response.body) { - if (error.response.body.code === 403) { - return false - } - } - throw this.wrapK8sClientError(error) - } - } - - async readNamespacedPod(podName: string, namespace: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const res = await k8sCoreApi.readNamespacedPod(podName, namespace) - if (res && res.body) { - return res.body - } - } catch { - - } - } - - async patchCustomResource(name: string, namespace: string, patch: any, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - // It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error. - const requestOptions = { - headers: { - 'content-type': 'application/merge-patch+json', - }, - } - - try { - const res = await k8sCoreApi.patchNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural, name, patch, undefined, undefined, undefined, requestOptions) - if (res && res.body) { - return res.body - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async patchNamespacedPod(name: string, namespace: string, patch: any): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - - // It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error. - const requestOptions = { - headers: { - 'content-type': 'application/strategic-merge-patch+json', - }, - } - - try { - const res = await k8sCoreApi.patchNamespacedPod(name, namespace, patch, undefined, undefined, undefined, undefined, requestOptions) - if (res && res.body) { - return res.body - } - } catch { - - } - } - - async podsExistBySelector(selector: string, namespace = ''): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - let res - try { - res = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector) - } catch (e) { - throw this.wrapK8sClientError(e) - } - - if (!res || !res.body || !res.body.items) { - throw new Error(`Get pods by selector "${selector}" returned an invalid response`) - } - - return (res.body.items.length > 0) - } - - /** - * Returns pod waiting state. - */ - async getPodWaitingState(namespace: string, selector: string, desiredPhase: string): Promise { - const pods = await this.getPodListByLabel(namespace, selector) - if (!pods.length) { - return - } - - for (const pod of pods) { - if (pod.status && pod.status.phase === desiredPhase && pod.status.containerStatuses) { - for (const status of pod.status.containerStatuses) { - if (status.state && status.state.waiting && status.state.waiting.message && status.state.waiting.reason) { - return status.state.waiting - } - } - } - } - } - - /** - * Returns pod last terminated state. - */ - async getPodLastTerminatedState(namespace: string, selector: string): Promise { - const pods = await this.getPodListByLabel(namespace, selector) - if (!pods.length) { - return - } - - for (const pod of pods) { - if (pod.status && pod.status.containerStatuses) { - for (const status of pod.status.containerStatuses) { - if (status.lastState) { - return status.lastState.terminated - } - } - } - } - } - - async getPodCondition(namespace: string, selector: string, conditionType: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - let res - try { - res = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector) - } catch (e) { - throw this.wrapK8sClientError(e) - } - - if (!res || !res.body || !res.body.items) { - return [] - } - - const conditions: V1PodCondition[] = [] - for (const pod of res.body.items) { - if (pod.status && pod.status.conditions) { - for (const condition of pod.status.conditions) { - if (condition.type === conditionType) { - conditions.push(condition) - } - } - } - } - - return conditions - } - - async getPodReadyConditionStatus(selector: string, namespace = ''): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - let res - try { - res = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector) - } catch (e) { - throw this.wrapK8sClientError(e) - } - - if (!res || !res.body || !res.body.items) { - throw new Error(`Get pods by selector "${selector}" returned an invalid response.`) - } - - if (res.body.items.length < 1) { - // No pods found by the specified selector. So, it's not ready. - return 'False' - } - - if (res.body.items.length > 1) { - // Several pods found, rolling update? - return - } - - if (!res.body.items[0].status || !res.body.items[0].status.conditions || !(res.body.items[0].status.conditions.length > 0)) { - return - } - - const conditions = res.body.items[0].status.conditions - for (const condition of conditions) { - if (condition.type === 'Ready') { - return condition.status - } - } - } - - async waitForPodReady(selector: string, namespace = '', intervalMs = 500, timeoutMs = this.podReadyTimeout) { - const iterations = timeoutMs / intervalMs - for (let index = 0; index < iterations; index++) { - const readyStatus = await this.getPodReadyConditionStatus(selector, namespace) - if (readyStatus === 'True') { - return - } - await cli.wait(intervalMs) - } - throw new Error(`ERR_TIMEOUT: Timeout set to pod ready timeout ${this.podReadyTimeout}`) - } - - async waitUntilPodIsDeleted(selector: string, namespace = '', intervalMs = 500, timeoutMs = this.podReadyTimeout) { - const iterations = timeoutMs / intervalMs - for (let index = 0; index < iterations; index++) { - const pods = await this.listNamespacedPod(namespace, undefined, selector) - if (!pods.items.length) { - return - } - await cli.wait(intervalMs) - } - throw new Error('ERR_TIMEOUT: Waiting until pod is deleted took too long.') - } - - async deletePod(name: string, namespace = '') { - this.kubeConfig.loadFromDefault() - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - return await k8sCoreApi.deleteNamespacedPod(name, namespace) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - // make sure that flag is specified for command that it's invoked - async waitLatestReplica(deploymentName: string, namespace = '', intervalMs = 500, timeoutMs = this.podWaitTimeout) { - const iterations = timeoutMs / intervalMs - for (let index = 0; index < iterations; index++) { - const deployment = await this.getDeployment(deploymentName, namespace) - if (!deployment) { - throw new Error(`Deployment ${namespace}/${deploymentName} is not found.`) - } - - const deploymentStatus = deployment.status - if (!deploymentStatus) { - throw new Error(`Deployment ${namespace}/${deploymentName} does not have any status`) - } - - if (deploymentStatus.unavailableReplicas && deploymentStatus.unavailableReplicas > 0) { - await cli.wait(intervalMs) - } else { - return - } - } - - throw new Error(`ERR_TIMEOUT: Timeout set to pod wait timeout ${this.podWaitTimeout}`) - } - - async deploymentExist(name = '', namespace = ''): Promise { - const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - const { body } = await k8sApi.readNamespacedDeployment(name, namespace) - return this.compare(body, name) - } catch { - return false - } - } - - async isConfigMapExists(name: string, namespace: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - await k8sApi.readNamespacedConfigMap(name, namespace) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async deploymentReady(name = '', namespace = ''): Promise { - const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - const res = await k8sApi.readNamespacedDeployment(name, namespace) - return ((res && res.body && - res.body.status && res.body.status.readyReplicas && - res.body.status.readyReplicas > 0) as boolean) - } catch { - return false - } - } - - async deploymentStopped(name = '', namespace = ''): Promise { - const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - const res = await k8sApi.readNamespacedDeployment(name, namespace) - if (res && res.body && res.body.spec && res.body.spec.replicas) { - throw new Error(`Deployment '${name}' without replicas in spec is fetched`) - } - return res.body!.spec!.replicas === 0 - } catch { - return false - } - } - - async isDeploymentPaused(name = '', namespace = ''): Promise { - const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - const res = await k8sApi.readNamespacedDeployment(name, namespace) - if (!res || !res.body || !res.body.spec) { - throw new Error('E_BAD_DEPLOY_RESPONSE') - } - return res.body.spec.paused || false - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async pauseDeployment(name = '', namespace = '') { - const k8sApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) - try { - const patch = { - spec: { - paused: true, - }, - } - await k8sApi.patchNamespacedDeployment(name, namespace, patch) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async resumeDeployment(name = '', namespace = '') { - const k8sApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) - try { - const patch = { - spec: { - paused: false, - }, - } - await k8sApi.patchNamespacedDeployment(name, namespace, patch) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async scaleDeployment(name = '', namespace = '', replicas: number) { - const k8sAppsApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) - const patch = { - spec: { - replicas, - }, - } - let res - try { - res = await k8sAppsApi.patchNamespacedDeploymentScale(name, namespace, patch) - } catch (e) { - throw this.wrapK8sClientError(e) - } - - if (!res || !res.body) { - throw new Error('Patch deployment scale returned an invalid response') - } - } - - async createDeployment(name: string, - image: string, - serviceAccount: string, - pullPolicy: string, - configMapEnvSource: string, - namespace: string) { - const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - const deployment = new V1Deployment() - deployment.metadata = new V1ObjectMeta() - deployment.metadata.name = name - deployment.metadata.namespace = namespace - deployment.spec = new V1DeploymentSpec() - deployment.spec.selector = new V1LabelSelector() - deployment.spec.selector.matchLabels = { app: name } - deployment.spec.template = new V1PodTemplateSpec() - deployment.spec.template.metadata = new V1ObjectMeta() - deployment.spec.template.metadata.name = name - deployment.spec.template.metadata.labels = { app: name } - deployment.spec.template.spec = new V1PodSpec() - deployment.spec.template.spec.serviceAccountName = serviceAccount - const opContainer = new V1Container() - opContainer.name = name - opContainer.image = image - opContainer.imagePullPolicy = pullPolicy - const envFromSource = new V1EnvFromSource() - envFromSource.configMapRef = new V1ConfigMapEnvSource() - envFromSource.configMapRef.name = configMapEnvSource - opContainer.envFrom = [envFromSource] - deployment.spec.template.spec.containers = [opContainer] - - try { - return await k8sAppsApi.createNamespacedDeployment(namespace, deployment) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createDeploymentFrom(yamlDeployment: V1Deployment): Promise { - const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - await k8sAppsApi.createNamespacedDeployment(yamlDeployment.metadata!.namespace!, yamlDeployment) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createServiceFrom(yamlService: V1Service, namespace = '') { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - return await k8sApi.createNamespacedService(namespace, yamlService) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceDeploymentFrom(yamlDeployment: V1Deployment): Promise { - // updating restartedAt to make sure that rollout will be restarted - let annotations = yamlDeployment.spec!.template!.metadata!.annotations - if (!annotations) { - annotations = {} - yamlDeployment.spec!.template!.metadata!.annotations = annotations - } - annotations['kubectl.kubernetes.io/restartedAt'] = new Date().toISOString() - - const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - await k8sAppsApi.replaceNamespacedDeployment(yamlDeployment.metadata!.name!, yamlDeployment.metadata!.namespace!, yamlDeployment) - } catch (e) { - if (e.response && e.response.body && e.response.body.message && e.response.body.message.toString().endsWith('field is immutable')) { - try { - await k8sAppsApi.deleteNamespacedDeployment(yamlDeployment.metadata!.name!, yamlDeployment.metadata!.namespace!) - await k8sAppsApi.createNamespacedDeployment(yamlDeployment.metadata!.namespace!, yamlDeployment) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - throw this.wrapK8sClientError(e) - } - } - - async deleteAllDeployments(namespace: string): Promise { - const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - await k8sAppsApi.deleteCollectionNamespacedDeployment(namespace) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getDeploymentsBySelector(labelSelector = '', namespace = ''): Promise { - const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - const res = await k8sAppsApi.listNamespacedDeployment(namespace, 'true', undefined, undefined, undefined, labelSelector) - if (res && res.body) { - return res.body - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - throw new Error('ERR_LIST_NAMESPACES') - } - - async getDeployment(name: string, namespace: string): Promise { - const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) - try { - const res = await k8sAppsApi.readNamespacedDeployment(name, namespace) - if (res && res.body) { - return res.body! - } - } catch (error) { - if (error.response && error.response.statusCode === 404) { - return - } - throw this.wrapK8sClientError(error) - } - throw new Error('ERR_GET_DEPLOYMENT') - } - - async createPod(name: string, - image: string, - serviceAccount: string, - restartPolicy: string, - pullPolicy: string, - configMapEnvSource: string, - namespace: string) { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - const pod = new V1Pod() - pod.metadata = new V1ObjectMeta() - pod.metadata.name = name - pod.metadata.labels = { app: name } - pod.metadata.namespace = namespace - pod.spec = new V1PodSpec() - pod.spec.restartPolicy = restartPolicy - pod.spec.serviceAccountName = serviceAccount - const opContainer = new V1Container() - opContainer.name = name - opContainer.image = image - opContainer.imagePullPolicy = pullPolicy - const envFromSource = new V1EnvFromSource() - envFromSource.configMapRef = new V1ConfigMapEnvSource() - envFromSource.configMapRef.name = configMapEnvSource - opContainer.envFrom = [envFromSource] - pod.spec.containers = [opContainer] - - try { - return await k8sCoreApi.createNamespacedPod(namespace, pod) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createJob(name: string, - image: string, - serviceAccount: string, - namespace: string, - backoffLimit = 0, - restartPolicy = 'Never') { - const k8sBatchApi = this.kubeConfig.makeApiClient(BatchV1Api) - - const job = new V1Job() - job.metadata = new V1ObjectMeta() - job.metadata.name = name - job.metadata.labels = { app: name } - job.metadata.namespace = namespace - job.spec = new V1JobSpec() - job.spec.ttlSecondsAfterFinished = 10 - job.spec.backoffLimit = backoffLimit - job.spec.template = new V1PodTemplateSpec() - job.spec.template.spec = new V1PodSpec() - job.spec.template.spec.serviceAccountName = serviceAccount - const jobContainer = new V1Container() - jobContainer.name = name - jobContainer.image = image - job.spec.template.spec.restartPolicy = restartPolicy - job.spec.template.spec.containers = [jobContainer] - - try { - return await k8sBatchApi.createNamespacedJob(namespace, job) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getJob(jobName: string, namespace: string): Promise { - const k8sBatchApi = this.kubeConfig.makeApiClient(BatchV1Api) - - try { - const result = await k8sBatchApi.readNamespacedJob(jobName, namespace) - return result.body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async waitJob(jobName: string, namespace: string, timeout = AWAIT_TIMEOUT_S): Promise { - return new Promise(async (resolve, reject) => { - // Set up watcher - const watcher = new Watch(this.kubeConfig) - const request = await watcher - .watch(`/apis/batch/v1/namespaces/${namespace}/jobs/`, {}, - (_phase: string, obj: any) => { - const job = obj as V1Job - - // Filter other jobs in the given namespace - if (job && job.metadata && job.metadata.name === jobName) { - // Check job status - if (job.status && job.status.succeeded && job.status.succeeded >= 1) { - // Job is finished, stop watching - if (request) { - request.abort() - } - // Release awaiter - resolve() - } - } - }, - error => { - if (error) { - reject(error) - } - }) - - // Automatically stop watching after timeout - const timeoutHandler = setTimeout(() => { - request.abort() - reject(`Timeout reached while waiting for "${jobName}" job.`) - }, timeout * 1000) - - // Request job, for case if it is already ready - const job = await this.getJob(jobName, namespace) - if (job.status && job.status.succeeded && job.status.succeeded >= 1) { - // Stop watching - request.abort() - clearTimeout(timeoutHandler) - - // Relese awaiter - resolve() - } - }) - } - - async deleteJob(jobName: string, namespace: string): Promise { - const k8sBatchApi = this.kubeConfig.makeApiClient(BatchV1Api) - - try { - const result = await k8sBatchApi.deleteNamespacedJob(jobName, namespace) - return result.body.status === 'Success' - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async compare(body: any, name: string): Promise { - if (body && body.metadata && body.metadata.name && body.metadata.name === name) { - return true - } - return false - } - - async ingressExist(name = '', namespace = ''): Promise { - const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) - try { - const { body } = await k8sExtensionsApi.readNamespacedIngress(name, namespace) - return this.compare(body, name) - } catch { - return false - } - } - - async deleteAllIngresses(namespace: string): Promise { - const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) - try { - await k8sExtensionsApi.deleteCollectionNamespacedIngress(namespace) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createCrdFromFile(filePath: string): Promise { - const yaml = this.safeLoadFromYamlFile(filePath) - if (yaml.apiVersion === this.API_EXTENSIONS_V1BETA1) { - return this.createCrdV1Beta1(yaml) - } - return this.createCrdV1(yaml) - } - - private async createCrdV1Beta1(yaml: any): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) - try { - await k8sApi.createCustomResourceDefinition(yaml) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - private async createCrdV1(yaml: any): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) - try { - await k8sApi.createCustomResourceDefinition(yaml) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async replaceCrdFromFile(filePath: string, resourceVersion: string): Promise { - const yaml = this.safeLoadFromYamlFile(filePath) - if (!yaml.metadata || !yaml.metadata.name) { - throw new Error(`Name is not defined in: ${filePath}`) - } - - yaml.metadata.resourceVersion = resourceVersion - if (yaml.apiVersion === this.API_EXTENSIONS_V1BETA1) { - return this.replaceCrdV1Beta1(yaml) - } - return this.replaceCrdV1(yaml) - } - - private async replaceCrdV1Beta1(yaml: any): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) - try { - await k8sApi.replaceCustomResourceDefinition(yaml.metadata.name, yaml) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - private async replaceCrdV1(yaml: any): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) - try { - await k8sApi.replaceCustomResourceDefinition(yaml.metadata.name, yaml) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getCrd(name: string): Promise { - if (await this.IsAPIExtensionSupported('v1')) { - return this.getCrdV1(name) - } - return this.getCrdV1beta1(name) - } - - private async getCrdV1(name: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) - try { - const { body } = await k8sApi.readCustomResourceDefinition(name) - return body - } catch (e) { - if (e.response.statusCode === 404) { - return - } - - throw this.wrapK8sClientError(e) - } - } - - private async getCrdV1beta1(name: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) - try { - const { body } = await k8sApi.readCustomResourceDefinition(name) - return body - } catch (e) { - if (e.response.statusCode === 404) { - return - } - - throw this.wrapK8sClientError(e) - } - } - - async getCrdStorageVersion(name: string): Promise { - const crd = await this.getCrd(name) - if (!crd.spec.versions) { - // Should never happen - return 'v1' - } - - const version = crd.spec.versions.find((v: any) => v.storage) - return version ? version.name : 'v1' - } - - async deleteCrd(name: string): Promise { - if (await this.IsAPIExtensionSupported('v1')) { - return this.deleteCrdV1(name) - } - return this.deleteCrdV1Beta1(name) - } - - private async deleteCrdV1Beta1(name: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) - try { - await k8sApi.deleteCustomResourceDefinition(name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - private async deleteCrdV1(name: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) - try { - await k8sApi.deleteCustomResourceDefinition(name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async createCheCluster(cheClusterCR: any, flags: any, ctx: any, useDefaultCR: boolean): Promise { - const cheNamespace = flags.chenamespace - if (useDefaultCR) { - // If CheCluster CR is not explicitly provided, then modify the default example CR - // with values derived from the other parameters - - if (VersionHelper.isDeployingStableVersion(flags)) { - // Use images from operator defaults in case of a stable version - cheClusterCR.spec.server.cheImage = '' - cheClusterCR.spec.server.cheImageTag = '' - cheClusterCR.spec.server.pluginRegistryImage = '' - cheClusterCR.spec.server.devfileRegistryImage = '' - cheClusterCR.spec.auth.identityProviderImage = '' - } - const cheImage = flags.cheimage - if (cheImage) { - const imageAndTag = cheImage.split(':', 2) - cheClusterCR.spec.server.cheImage = imageAndTag[0] - cheClusterCR.spec.server.cheImageTag = imageAndTag.length === 2 ? imageAndTag[1] : 'latest' - } - - if ((flags.installer === 'olm' && !flags['catalog-source-yaml']) || (flags['catalog-source-yaml'] && flags['olm-channel'] === OLM_STABLE_CHANNEL_NAME)) { - // use default image tag for `olm` to install stable Che, because we don't have nightly channel for OLM catalog. - cheClusterCR.spec.server.cheImageTag = '' - } - cheClusterCR.spec.server.cheDebug = flags.debug ? flags.debug.toString() : 'false' - - if (isKubernetesPlatformFamily(flags.platform) || !cheClusterCR.spec.auth.openShiftoAuth) { - cheClusterCR.spec.auth.updateAdminPassword = true - } - - if (!cheClusterCR.spec.k8s) { - cheClusterCR.spec.k8s = {} - } - if (flags.tls) { - cheClusterCR.spec.server.tlsSupport = flags.tls - if (!cheClusterCR.spec.k8s.tlsSecretName) { - cheClusterCR.spec.k8s.tlsSecretName = 'che-tls' - } - } - if (flags.domain) { - cheClusterCR.spec.k8s.ingressDomain = flags.domain - } - const pluginRegistryUrl = flags['plugin-registry-url'] - if (pluginRegistryUrl) { - cheClusterCR.spec.server.pluginRegistryUrl = pluginRegistryUrl - cheClusterCR.spec.server.externalPluginRegistry = true - } - const devfileRegistryUrl = flags['devfile-registry-url'] - if (devfileRegistryUrl) { - cheClusterCR.spec.server.devfileRegistryUrl = devfileRegistryUrl - cheClusterCR.spec.server.externalDevfileRegistry = true - } - - cheClusterCR.spec.storage.postgresPVCStorageClassName = flags['postgres-pvc-storage-class-name'] - cheClusterCR.spec.storage.workspacePVCStorageClassName = flags['workspace-pvc-storage-class-name'] - - if (flags['workspace-engine'] === 'dev-workspace') { - cheClusterCR.spec.devWorkspace.enable = true - } - - // Use self-signed TLS certificate by default (for versions before 7.14.3). - // In modern versions of Che this field is ignored. - cheClusterCR.spec.server.selfSignedCert = true - } - - cheClusterCR.spec.server.cheClusterRoles = ctx.namespaceEditorClusterRoleName - - // override default values - if (ctx.crPatch) { - merge(cheClusterCR, ctx.crPatch) - } - - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.createNamespacedCustomObject('org.eclipse.che', 'v1', cheNamespace, 'checlusters', cheClusterCR) - return body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async patchCheCluster(name: string, namespace: string, patch: any): Promise { - try { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - const { body } = await customObjectsApi.patchNamespacedCustomObject('org.eclipse.che', 'v1', namespace, 'checlusters', name, patch, undefined, undefined, undefined, { headers: { 'Content-Type': 'application/merge-patch+json' } }) - return body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - /** - * Returns `checlusters.org.eclipse.che' in the given namespace. - */ - async getCheCluster(cheNamespace: string): Promise { - return this.getCustomResource(cheNamespace, CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL) - } - - /** - * Returns custom resource in the given namespace. - */ - async getCustomResource(namespace: string, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.listNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural) - if (!(body as any).items) { - return - } - - const crs = (body as any).items as any[] - if (crs.length === 0) { - return - } 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) - } - } - } - - /** - * Deletes `checlusters.org.eclipse.che' resources in the given namespace. - */ - async getAllCheClusters(): Promise { - return this.getAllCustomResources(CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL) - } - - /** - * Returns all custom resources - */ - async getAllCustomResources(resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.listClusterCustomObject(resourceAPIGroup, resourceAPIVersion, resourcePlural) - return (body as any).items ? (body as any).items : [] - } catch (e) { - if (e.response.statusCode === 404) { - // There is no CRD - return [] - } - throw this.wrapK8sClientError(e) - } - } - - /** - * Deletes `checlusters.org.eclipse.che' resources in the given namespace. - */ - async deleteCheCluster(namespace: string): Promise { - return this.deleteCustomResource(namespace, CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL) - } - - /** - * Deletes custom resources in the given namespace. - */ - async deleteCustomResource(namespace: string, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.listNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural) - if (!(body as any).items) { - return - } - - const crs = (body as any).items as any[] - for (const cr of crs) { - await customObjectsApi.deleteNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural, cr.metadata.name) - } - } catch (e) { - if (e.response.statusCode === 404) { - // There is no CRD - return - } - throw this.wrapK8sClientError(e) - } - } - - async isPreInstalledOLM(): Promise { - const apiApi = this.kubeConfig.makeApiClient(ApisApi) - try { - const { body } = await apiApi.getAPIVersions() - const OLMAPIGroup = body.groups.find(apiGroup => apiGroup.name === 'operators.coreos.com') - return Boolean(OLMAPIGroup) - } catch { - return false - } - } - - async getUsersNumber(): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - let amountOfUsers: number - try { - const { body } = await customObjectsApi.listClusterCustomObject('user.openshift.io', 'v1', 'users') - if (!(body as any).items) { - throw new Error('Unable to get list users.') - } - amountOfUsers = (body as any).items.length - } catch (e) { - throw this.wrapK8sClientError(e) - } - return amountOfUsers - } - - async getOpenshiftAuthProviders(): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - const oAuthName = 'cluster' - const { body } = await customObjectsApi.getClusterCustomObject('config.openshift.io', 'v1', 'oauths', oAuthName) - return (body as OAuth).spec.identityProviders - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async operatorSourceExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', name) - return this.compare(body, name) - } catch { - return false - } - } - - async catalogSourceExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) - return this.compare(body, name) - } catch { - return false - } - } - - async getOAuthClientAuthorizations(clientName: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.listClusterCustomObject('oauth.openshift.io', 'v1', 'oauthclientauthorizations') - - if (!(body as any).items) { - return [] - } - const oauthClientAuthorizations = (body as any).items as any[] - return oauthClientAuthorizations.filter(o => o.clientName === clientName) - } catch (e) { - if (e.response.statusCode === 404) { - // There is no 'oauthclientauthorizations` - return [] - } - throw this.wrapK8sClientError(e) - } - } - - async deleteOAuthClientAuthorizations(oAuthClientAuthorizations: any[]): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const filetOauthAuthorizations = oAuthClientAuthorizations.filter((e => e.metadata && e.metadata.name)) - for (const oauthAuthorization of filetOauthAuthorizations) { - await customObjectsApi.deleteClusterCustomObject('oauth.openshift.io', 'v1', 'oauthclientauthorizations', oauthAuthorization.metadata.name) - } - } catch (e) { - if (e.response.statusCode === 404) { - return - } - throw this.wrapK8sClientError(e) - } - } - - async consoleLinkExists(name: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - await customObjectsApi.getClusterCustomObject('console.openshift.io', 'v1', 'consolelinks', name) - return true - } catch (e) { - if (e.response.statusCode === 404) { - // There are no consoleLink - return false - } - throw this.wrapK8sClientError(e) - } - } - - async deleteConsoleLink(name: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - await customObjectsApi.deleteClusterCustomObject('console.openshift.io', 'v1', 'consolelinks', name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async getCatalogSource(name: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) - return body as CatalogSource - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - readCatalogSourceFromFile(filePath: string): CatalogSource { - const catalogSource = this.safeLoadFromYamlFile(filePath) as CatalogSource - if (!catalogSource.metadata || !catalogSource.metadata.name) { - throw new Error(`CatalogSource from ${filePath} must have specified metadata and name`) - } - return catalogSource - } - - async createCatalogSource(catalogSource: CatalogSource) { - const customObjectsApi = this.kubeConfig.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.kubeConfig) - const request = await 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 deleteCatalogSource(namespace: string, catalogSourceName: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', catalogSourceName) - } catch (e) { - if (e.response.statusCode === 404) { - return - } - throw this.wrapK8sClientError(e) - } - } - - async operatorGroupExists(name: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.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.kubeConfig.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): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', operatorGroupName) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async createOperatorSubscription(subscription: Subscription) { - const namespace: string = subscription.metadata.namespace! - - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', subscription) - return body - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getOperatorSubscription(name: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.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.kubeConfig.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): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', operatorSubscriptionName) - } catch (e) { - if (e.response.statusCode !== 404) { - 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.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.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) - }) - } - - async approveOperatorInstallationPlan(name = '', namespace = '') { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - const patch: InstallPlan = { - spec: { - approved: true, - }, - } - await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'installplans', name, patch, undefined, undefined, undefined, { headers: { 'Content-Type': 'application/merge-patch+json' } }) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async waitUntilOperatorIsInstalled(installPlanName: string, namespace: string, timeout = 240) { - return new Promise(async (resolve, reject) => { - const watcher = new Watch(this.kubeConfig) - const request = await 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.phase === 'Failed') { - const errorMessage = [] - for (const condition of installPlan.status.conditions) { - if (!condition.reason) { - errorMessage.push(`Reason: ${condition.reason}`) - errorMessage.push(!condition.message ? `Message: ${condition.message}` : '') - } - } - reject(errorMessage.join(' ')) - } - if (installPlan.status && installPlan.status.conditions) { - for (const condition of installPlan.status.conditions) { - if (condition.type === 'Installed' && condition.status === 'True') { - resolve(installPlan) - } - } - } - }, - error => { - if (error) { - reject(error) - } - }) - - setTimeout(() => { - request.abort() - reject(`Timeout reached while waiting for "${installPlanName}" has go status 'Installed'.`) - }, timeout * 1000) - }) - } - - async getCSV(csvName: string, namespace: string): Promise { - const csvs = await this.getClusterServiceVersions(namespace) - return csvs.items.find(item => item.metadata.name === csvName) - } - - async getClusterServiceVersions(namespace: string): Promise { - const customObjectsApi = this.kubeConfig.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 patchClusterServiceVersion(namespace: string, name: string, jsonPatch: any[]): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - const requestOptions = { - headers: { - 'content-type': 'application/json-patch+json', - }, - } - try { - const response = await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', name, jsonPatch, undefined, undefined, undefined, requestOptions) - return response.body as ClusterServiceVersion - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async deleteClusterServiceVersion(namespace: string, csvName: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - try { - await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', csvName) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async getPackageManifect(name: string): Promise { - const customObjectsApi = this.kubeConfig.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.kubeConfig.makeApiClient(CoreV1Api) - try { - await k8sCoreApi.deleteNamespace(namespace) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - /** - * Returns CRD version of Cert Manager - */ - async getCertManagerK8sApiVersion(): Promise { - return this.getCrdStorageVersion('certificates.cert-manager.io') - } - - async clusterIssuerExists(name: string, version: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - // If cluster issuers doesn't exist an exception will be thrown - await customObjectsApi.getClusterCustomObject('cert-manager.io', version, 'clusterissuers', name) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async isNamespacedCertificateExists(name: string, version: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - // If cluster issuers doesn't exist an exception will be thrown - await customObjectsApi.getNamespacedCustomObject('cert-manager.io', version, namespace, 'certificates', name) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async deleteNamespacedCertificate(name: string, version: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - // If cluster certificates doesn't exist an exception will be thrown - await customObjectsApi.deleteNamespacedCustomObject('cert-manager.io', version, namespace, 'certificates', name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async deleteNamespacedIssuer(name: string, version: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - await customObjectsApi.deleteNamespacedCustomObject('cert-manager.io', version, namespace, 'issuers', name) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async listClusterIssuers(version: string, labelSelector?: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - let res - try { - res = await customObjectsApi.listClusterCustomObject('cert-manager.io', version, 'clusterissuers', undefined, undefined, undefined, labelSelector) - } catch (e) { - throw this.wrapK8sClientError(e) - } - - if (!res || !res.body) { - throw new Error('Unable to get cluster issuers list') - } - const clusterIssuersList: { items?: any[] } = res.body - - return clusterIssuersList.items || [] - } - - async createCheClusterIssuer(cheClusterIssuerYamlPath: string, version: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - const cheClusterIssuer = this.safeLoadFromYamlFile(cheClusterIssuerYamlPath) - try { - await customObjectsApi.createClusterCustomObject('cert-manager.io', version, 'clusterissuers', cheClusterIssuer) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async createCertificateIssuer(cheClusterIssuerYamlPath: string, version: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - const certificateIssuer = this.safeLoadFromYamlFile(cheClusterIssuerYamlPath) - try { - await customObjectsApi.createNamespacedCustomObject('cert-manager.io', version, namespace, 'issuers', certificateIssuer) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async isCertificateIssuerExists(name: string, version: string, namespace: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - // If issuers doesn't exist an exception will be thrown - await customObjectsApi.getNamespacedCustomObject('cert-manager.io', version, namespace, 'issuers', name) - return true - } catch (e) { - if (e.response.statusCode === 404) { - return false - } - - throw this.wrapK8sClientError(e) - } - } - - async createCheClusterCertificate(certificate: V1Certificate, version: string): Promise { - const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) - - try { - await customObjectsApi.createNamespacedCustomObject('cert-manager.io', version, certificate.metadata.namespace, 'certificates', certificate) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async currentContext(): Promise { - return this.kubeConfig.getCurrentContext() - } - - getContext(name: string): Context | null { - return this.kubeConfig.getContextObject(name) - } - - /** - * Retrieve the default token from the default serviceAccount. - */ - async getDefaultServiceAccountToken(): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - const namespaceName = 'default' - const saName = 'default' - let res - // now get the matching secrets - try { - res = await k8sCoreApi.listNamespacedSecret(namespaceName) - } catch (e) { - throw this.wrapK8sClientError(e) - } - if (!res || !res.body) { - throw new Error('Unable to get default service account') - } - const v1SecretList = res.body - - if (!v1SecretList.items || v1SecretList.items.length === 0) { - throw new Error(`Unable to get default service account token since there is no secret in '${namespaceName}' namespace`) - } - - const v1DefaultSATokenSecret = v1SecretList.items.find(secret => secret.metadata!.annotations && - secret.metadata!.annotations['kubernetes.io/service-account.name'] === saName && - secret.type === 'kubernetes.io/service-account-token') - - if (!v1DefaultSATokenSecret) { - throw new Error(`Secret for '${saName}' service account is not found in namespace '${namespaceName}'`) - } - - return Buffer.from(v1DefaultSATokenSecret.data!.token, 'base64').toString() - } - - async checkKubeApi() { - const currentCluster = this.kubeConfig.getCurrentCluster() - if (!currentCluster) { - throw new Error(`The current context is unknown. It should be set using '${getClusterClientCommand()} config use-context ' or in another way.`) - } - - try { - await this.requestKubeHealthz(currentCluster) - } catch (error) { - if (error.message && (error.message as string).includes('E_K8S_API_UNAUTHORIZED')) { - const token = await this.getDefaultServiceAccountToken() - await this.requestKubeHealthz(currentCluster, token) - } else { - throw error - } - } - } - - async requestKubeHealthz(currentCluster: Cluster, token?: string) { - const endpoint = `${currentCluster.server}/healthz` - - try { - const config: AxiosRequestConfig = { - httpsAgent: new https.Agent({ - rejectUnauthorized: false, - requestCert: true, - }), - headers: token && { Authorization: 'bearer ' + token }, - } - - const response = await axios.get(`${endpoint}`, config) - if (!response || response.status !== 200 || response.data !== 'ok') { - throw new Error('E_BAD_RESP_K8S_API') - } - } catch (error) { - if (error.response && error.response.status === 403) { - throw new Error(`E_K8S_API_FORBIDDEN - Message: ${error.response.data.message}`) - } - if (error.response && error.response.status === 401) { - throw new Error(`E_K8S_API_UNAUTHORIZED - Message: ${error.response.data.message}`) - } - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - throw new Error(`E_K8S_API_UNKNOWN_ERROR - Status: ${error.response.status}`) - } 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 - throw new Error(`E_K8S_API_NO_RESPONSE - Endpoint: ${endpoint} - Error message: ${error.message}`) - } else { - // Something happened in setting up the request that triggered an Error - throw new Error(`E_CHECTL_UNKNOWN_ERROR - Message: ${error.message}`) - } - } - } - - async isOpenShift(): Promise { - return this.IsAPIGroupSupported('apps.openshift.io') - } - - async isOpenShift3(): Promise { - const isAppsAPISupported = await this.IsAPIGroupSupported('apps.openshift.io') - const isConfigAPISupported = await this.IsAPIGroupSupported('config.openshift.io') - return isAppsAPISupported && !isConfigAPISupported - } - - async isOpenShift4(): Promise { - const isRouteAPISupported = await this.IsAPIGroupSupported('route.openshift.io') - const isConfigAPISupported = await this.IsAPIGroupSupported('config.openshift.io') - return isRouteAPISupported && isConfigAPISupported - } - - async IsAPIExtensionSupported(version: string): Promise { - return this.IsAPIGroupSupported('apiextensions.k8s.io', version) - } - - async IsAPIGroupSupported(name: string, version?: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(ApisApi) - try { - const res = await k8sCoreApi.getAPIVersions() - if (!res || !res.body || !res.body.groups) { - return false - } - - const group = res.body.groups.find(g => g.name === name) - if (!group) { - return false - } - - if (version) { - return Boolean(group.versions.find(v => v.version === version)) - } - return Boolean(group) - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getIngressHost(name = '', namespace = ''): Promise { - const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) - try { - const res = await k8sExtensionsApi.readNamespacedIngress(name, namespace) - if (res && res.body && - res.body.spec && - res.body.spec.rules && - res.body.spec.rules.length > 0) { - return res.body.spec.rules[0].host || '' - } - throw new Error('ERR_INGRESS_NO_HOST') - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getIngressProtocol(name = '', namespace = ''): Promise { - const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) - try { - const res = await k8sExtensionsApi.readNamespacedIngress(name, namespace) - if (!res || !res.body || !res.body.spec) { - throw new Error('ERR_INGRESS_NO_HOST') - } - if (res.body.spec.tls && res.body.spec.tls.length > 0) { - return 'https' - } - return 'http' - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async getIngressesBySelector(labelSelector = '', namespace = ''): Promise { - const k8sV1Beta = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) - try { - const res = await k8sV1Beta.listNamespacedIngress(namespace, 'true', undefined, undefined, undefined, labelSelector) - if (res && res.body) { - return res.body - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - throw new Error('ERR_LIST_INGRESSES') - } - - async getSecret(name = '', namespace = 'default'): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - - // now get the matching secrets - try { - const res = await k8sCoreApi.readNamespacedSecret(name, namespace) - if (res && res.body && res.body) { - return res.body - } - } catch { - - } - } - - /** - * Creates a secret with given name and data. - * Data should not be base64 encoded. - */ - async createSecret(name: string, data: { [key: string]: string }, namespace: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - - const secret = new V1Secret() - secret.metadata = new V1ObjectMeta() - secret.metadata.name = name - secret.metadata.namespace = namespace - secret.stringData = data - - try { - return (await k8sCoreApi.createNamespacedSecret(namespace, secret)).body - } catch { - - } - } - - /** - * Awaits secret to be present and contain non-empty data fields specified in dataKeys parameter. - */ - async waitSecret(secretName: string, namespace: string, dataKeys: string[] = [], timeout = AWAIT_TIMEOUT_S): Promise { - return new Promise(async (resolve, reject) => { - // Set up watcher - const watcher = new Watch(this.kubeConfig) - const request = await watcher - .watch(`/api/v1/namespaces/${namespace}/secrets/`, { fieldSelector: `metadata.name=${secretName}` }, - (_phase: string, obj: any) => { - const secret = obj as V1Secret - - // Check all required data fields to be present - if (dataKeys.length > 0 && secret.data) { - for (const key of dataKeys) { - if (!secret.data[key]) { - // Key is missing or empty - return - } - } - } - - // The secret with all specified fields is present, stop watching - if (request) { - request.abort() - } - // Release awaiter - resolve() - }, - error => { - if (error) { - reject(error) - } - }) - - // Automatically stop watching after timeout - const timeoutHandler = setTimeout(() => { - request.abort() - reject(`Timeout reached while waiting for "${secretName}" secret.`) - }, timeout * 1000) - - // Request secret, for case if it is already exist - const secret = await this.getSecret(secretName, namespace) - if (secret) { - // Stop watching - request.abort() - clearTimeout(timeoutHandler) - - // Relese awaiter - resolve() - } - }) - } - - async persistentVolumeClaimExist(name = '', namespace = ''): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const { body } = await k8sCoreApi.readNamespacedPersistentVolumeClaim(name, namespace) - return this.compare(body, name) - } catch { - return false - } - } - - async deletePersistentVolumeClaim(name: string, namespace: string): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - await k8sCoreApi.deleteNamespacedPersistentVolumeClaim(name, namespace) - } catch (e) { - if (e.response.statusCode !== 404) { - throw this.wrapK8sClientError(e) - } - } - } - - async getPersistentVolumeClaimsBySelector(labelSelector = '', namespace = ''): Promise { - const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const res = await k8sCoreApi.listNamespacedPersistentVolumeClaim(namespace, 'true', undefined, undefined, undefined, labelSelector) - if (res && res.body) { - return res.body - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - throw new Error('ERR_LIST_PVCS') - } - - async listNamespace(): Promise { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const res = await k8sApi.listNamespace() - if (res && res.body) { - return res.body - } - return { - items: [], - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - async listNamespacedPod(namespace: string, fieldSelector?: string, labelSelector?: string): Promise { - const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) - try { - const res = await k8sApi.listNamespacedPod(namespace, undefined, undefined, undefined, fieldSelector, labelSelector) - if (res && res.body) { - return res.body - } - return { - items: [], - } - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - /** - * Reads log by chunk and writes into a file. - */ - async readNamespacedPodLog(pod: string, namespace: string, container: string, filename: string, follow: boolean): Promise { - return new Promise(async (resolve, reject) => { - const logHelper = new Log(this.kubeConfig) - const stream = new Writable() - stream._write = function (chunk, encoding, done) { - fs.appendFileSync(filename, chunk, { encoding }) - done() - } - - await logHelper.log(namespace, pod, container, stream, error => { - stream.end() - if (error) { - reject(error) - } else { - resolve() - } - }, { follow }) - }) - } - - /** - * Forwards port, based on the example - * https://github.com/kubernetes-client/javascript/blob/master/examples/typescript/port-forward/port-forward.ts - */ - async portForward(podName: string, namespace: string, port: number): Promise { - const portForwardHelper = new PortForward(this.kubeConfig, true) - try { - const server = net.createServer(async socket => { - await portForwardHelper.portForward(namespace, podName, [port], socket, null, socket) - }) - server.listen(port, 'localhost') - return - } catch (e) { - throw this.wrapK8sClientError(e) - } - } - - /** - * Checks if message is present and returns error with it - * or returns error with the specified error if message is not found. - * - * @param e k8s error to wrap - */ - private wrapK8sClientError(e: any): Error { - if (e.response && e.response.body) { - return newError(e.response.body, e) - } - return e - } - - public safeLoadFromYamlFile(filePath: string): any { - return safeLoadFromYamlFile(filePath) - } + public readonly kubeConfig + + readonly API_EXTENSIONS_V1BETA1 = 'apiextensions.k8s.io/v1beta1' + + podWaitTimeout: number + + podDownloadImageTimeout: number + + podReadyTimeout: number + + podErrorRecheckTimeout: number + + constructor(flags?: any) { + this.podWaitTimeout = (flags && flags.k8spodwaittimeout) ? parseInt(flags.k8spodwaittimeout, 10) : DEFAULT_K8S_POD_WAIT_TIMEOUT + this.podReadyTimeout = (flags && flags.k8spodreadytimeout) ? parseInt(flags.k8spodreadytimeout, 10) : DEFAULT_K8S_POD_WAIT_TIMEOUT + this.podDownloadImageTimeout = (flags && flags.k8spoddownloadimagetimeout) ? parseInt(flags.k8spoddownloadimagetimeout, 10) : DEFAULT_K8S_POD_WAIT_TIMEOUT + this.podErrorRecheckTimeout = (flags && flags.spoderrorrechecktimeout) ? parseInt(flags.spoderrorrechecktimeout, 10) : DEFAULT_K8S_POD_ERROR_RECHECK_TIMEOUT + this.kubeConfig = new KubeConfig() + this.kubeConfig.loadFromDefault() + } + + async createNamespace(namespaceName: string, labels: any): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + const namespaceObject = { + apiVersion: 'v1', + kind: 'Namespace', + metadata: { + labels, + name: namespaceName, + }, + } + + try { + await k8sCoreApi.createNamespace(namespaceObject) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteAllServices(namespace: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const res = await k8sApi.listNamespacedService(namespace) + if (res && res.response && res.response.statusCode === 200) { + const serviceList = res.body + await serviceList.items.forEach(async service => { + try { + await k8sApi.deleteNamespacedService(service.metadata!.name!, namespace) + } catch (error) { + if (error.response.statusCode !== 404) { + throw error + } + } + }) + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async applyResource(yamlPath: string, opts = ''): Promise { + const command = `kubectl apply -f ${yamlPath} ${opts}` + await execa(command, { timeout: 30000, shell: true }) + } + + async getServicesBySelector(labelSelector = '', namespace = ''): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const res = await k8sCoreApi.listNamespacedService(namespace, 'true', undefined, undefined, undefined, labelSelector) + if (res && res.body) { + return res.body + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + throw new Error('ERR_LIST_SERVICES') + } + + async waitForService(selector: string, namespace = '', intervalMs = 500, timeoutMs = 30000) { + const iterations = timeoutMs / intervalMs + for (let index = 0; index < iterations; index++) { + const currentServices = await this.getServicesBySelector(selector, namespace) + if (currentServices && currentServices.items.length > 0) { + return + } + await cli.wait(intervalMs) + } + throw new Error(`ERR_TIMEOUT: Timeout set to waiting for service ${timeoutMs}`) + } + + async serviceAccountExist(name = '', namespace = ''): Promise { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const { body } = await k8sApi.readNamespacedServiceAccount(name, namespace) + return this.compare(body, name) + } catch { + return false + } + } + + async createServiceAccount(name = '', namespace = '') { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + const sa = new V1ServiceAccount() + sa.metadata = new V1ObjectMeta() + sa.metadata.name = name + sa.metadata.namespace = namespace + try { + return await k8sCoreApi.createNamespacedServiceAccount(namespace, sa) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async waitServiceAccount(name: string, namespace: string, timeout = AWAIT_TIMEOUT_S): Promise { + return new Promise(async (resolve, reject) => { + // Set up watcher + const watcher = new Watch(this.kubeConfig) + const request = await watcher + .watch(`/api/v1/namespaces/${namespace}/serviceaccounts`, {}, + (_phase: string, obj: any) => { + const serviceAccount = obj as V1ServiceAccount + + // Filter other service accounts in the given namespace + if (serviceAccount && serviceAccount.metadata && serviceAccount.metadata.name === name) { + // The service account is present, stop watching + if (request) { + request.abort() + } + // Release awaiter + resolve() + } + }, + error => { + if (error) { + reject(error) + } + }) + + // Automatically stop watching after timeout + const timeoutHandler = setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${name}" service account.`) + }, timeout * 1000) + + // Request service account, for case if it is already exist + const serviceAccount = await this.getSecret(name, namespace) + if (serviceAccount) { + // Stop watching + request.abort() + clearTimeout(timeoutHandler) + + // Relese awaiter + resolve() + } + }) + } + + async deleteServiceAccount(name: string, namespace: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + await k8sCoreApi.deleteNamespacedServiceAccount(name, namespace) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async createServiceAccountFromFile(filePath: string, namespace = '') { + const yamlServiceAccount = this.safeLoadFromYamlFile(filePath) as V1ServiceAccount + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + return await k8sCoreApi.createNamespacedServiceAccount(namespace, yamlServiceAccount) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceServiceAccountFromFile(filePath: string, namespace = '') { + const yamlServiceAccount = this.safeLoadFromYamlFile(filePath) as V1ServiceAccount + if (!yamlServiceAccount || !yamlServiceAccount.metadata || !yamlServiceAccount.metadata.name) { + throw new Error(`Service account read from ${filePath} must have name specified.`) + } + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + return await k8sCoreApi.replaceNamespacedServiceAccount(yamlServiceAccount.metadata.name, namespace, yamlServiceAccount) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async roleExist(name = '', namespace = ''): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const { body } = await k8sRbacAuthApi.readNamespacedRole(name, namespace) + return this.compare(body, name) + } catch { + return false + } + } + + async clusterRoleExist(name = ''): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const { body } = await k8sRbacAuthApi.readClusterRole(name) + return this.compare(body, name) + } catch { + return false + } + } + + async getClusterRole(name: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const { body } = await k8sRbacAuthApi.readClusterRole(name) + return body + } catch { + return + } + } + + async getRole(name: string, namespace: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const res = await k8sRbacAuthApi.readNamespacedRole(name, namespace) + return res.body + } catch (e) { + if (e.statusCode === 404) { + return + } + throw this.wrapK8sClientError(e) + } + } + + async listRoles(namespace: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const res = await k8sRbacAuthApi.listNamespacedRole(namespace) + return res.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createRoleFrom(yamlRole: V1Role, namespace: string) { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const res = await k8sRbacAuthApi.createNamespacedRole(namespace, yamlRole) + return res.response.statusCode + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createRoleFromFile(filePath: string, namespace: string) { + const yamlRole = this.safeLoadFromYamlFile(filePath) as V1Role + return this.createRoleFrom(yamlRole, namespace) + } + + async replaceRoleFrom(yamlRole: V1Role, namespace: string) { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + + if (!yamlRole.metadata || !yamlRole.metadata.name) { + throw new Error('Role object requires name') + } + try { + const res = await k8sRbacAuthApi.replaceNamespacedRole(yamlRole.metadata.name, namespace, yamlRole) + return res.response.statusCode + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceRoleFromFile(filePath: string, namespace: string) { + const yamlRole = this.safeLoadFromYamlFile(filePath) as V1Role + return this.replaceRoleFrom(yamlRole, namespace) + } + + async listClusterRoles(): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const res = await k8sRbacAuthApi.listClusterRole() + return res.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createClusterRoleFrom(yamlClusterRole: V1ClusterRole, clusterRoleName?: string) { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + if (!yamlClusterRole.metadata) { + yamlClusterRole.metadata = {} + } + + if (clusterRoleName) { + yamlClusterRole.metadata.name = clusterRoleName + } else if (!yamlClusterRole.metadata.name) { + throw new Error('Role name is not specified') + } + try { + const res = await k8sRbacAuthApi.createClusterRole(yamlClusterRole) + return res.response.statusCode + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createClusterRoleFromFile(filePath: string, clusterRoleName?: string) { + const yamlClusterRole = this.safeLoadFromYamlFile(filePath) as V1ClusterRole + return this.createClusterRoleFrom(yamlClusterRole, clusterRoleName) + } + + async replaceClusterRoleFrom(yamlClusterRole: V1ClusterRole, clusterRoleName?: string) { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + if (!yamlClusterRole.metadata) { + yamlClusterRole.metadata = {} + } + + if (clusterRoleName) { + yamlClusterRole.metadata.name = clusterRoleName + } else if (!yamlClusterRole.metadata.name) { + throw new Error('Role name is not specified') + } + try { + const res = await k8sRbacAuthApi.replaceClusterRole(yamlClusterRole.metadata.name, yamlClusterRole) + return res.response.statusCode + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceClusterRoleFromFile(filePath: string, clusterRoleName?: string) { + const yamlClusterRole = this.safeLoadFromYamlFile(filePath) as V1ClusterRole + return this.replaceClusterRoleFrom(yamlClusterRole, clusterRoleName) + } + + async addClusterRoleRule(name: string, apiGroups: string[], resources: string[], verbs: string[]): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + const clusterRole = await this.getClusterRole(name) + if (clusterRole) { + // Clean up metadata, otherwise replace role call will fail + clusterRole.metadata = {} + clusterRole.metadata.name = name + + // Add new policy + const additionaRule = new V1PolicyRule() + additionaRule.apiGroups = apiGroups + additionaRule.resources = resources + additionaRule.verbs = verbs + if (clusterRole.rules) { + clusterRole.rules.push(additionaRule) + } + + try { + const { body } = await k8sRbacAuthApi.replaceClusterRole(name, clusterRole) + return body + } catch { + return + } + } + } + + async deleteRole(name: string, namespace: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + await k8sCoreApi.deleteNamespacedRole(name, namespace) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async getPodListByLabel(namespace: string, labelSelector: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const { body: podList } = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, labelSelector) + + return podList.items + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteClusterRole(name: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + await k8sCoreApi.deleteClusterRole(name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async listRoleBindings(namespace: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const res = await k8sRbacAuthApi.listNamespacedRoleBinding(namespace) + return res.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async roleBindingExist(name = '', namespace = ''): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + await k8sRbacAuthApi.readNamespacedRoleBinding(name, namespace) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async isMutatingWebhookConfigurationExists(name: string): Promise { + const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) + try { + await k8sAdmissionApi.readMutatingWebhookConfiguration(name) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async getMutatingWebhookConfiguration(name: string): Promise { + const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) + try { + const res = await k8sAdmissionApi.readMutatingWebhookConfiguration(name) + return res.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async isValidatingWebhookConfigurationExists(name: string): Promise { + const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) + try { + await k8sAdmissionApi.readValidatingWebhookConfiguration(name) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async deleteValidatingWebhookConfiguration(name: string): Promise { + const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) + try { + await k8sAdmissionApi.deleteValidatingWebhookConfiguration(name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async deleteMutatingWebhookConfiguration(name: string): Promise { + const k8sAdmissionApi = this.kubeConfig.makeApiClient(AdmissionregistrationV1Api) + try { + await k8sAdmissionApi.deleteMutatingWebhookConfiguration(name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async listClusterRoleBindings(labelSelector?: string, fieldSelector?: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const res = await k8sRbacAuthApi.listClusterRoleBinding(undefined, undefined, undefined, fieldSelector, labelSelector) + return res.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async clusterRoleBindingExist(name: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const { body } = await k8sRbacAuthApi.readClusterRoleBinding(name) + return this.compare(body, name) + } catch { + return false + } + } + + async createAdminRoleBinding(name = '', serviceAccount = '', namespace = '') { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + const rb = new V1RoleBinding() + rb.metadata = new V1ObjectMeta() + rb.metadata.name = name + rb.metadata.namespace = namespace + rb.roleRef = new V1RoleRef() + rb.roleRef.kind = 'ClusterRole' + rb.roleRef.name = 'admin' + const subject = new V1Subject() + subject.kind = 'ServiceAccount' + subject.name = serviceAccount + subject.namespace = namespace + rb.subjects = [subject] + try { + return await k8sRbacAuthApi.createNamespacedRoleBinding(namespace, rb) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createRoleBindingFrom(yamlRoleBinding: V1RoleBinding, namespace: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const response = await k8sRbacAuthApi.createNamespacedRoleBinding(namespace, yamlRoleBinding) + return response.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createRoleBindingFromFile(filePath: string, namespace: string): Promise { + const yamlRoleBinding = this.safeLoadFromYamlFile(filePath) as V1RoleBinding + return this.createRoleBindingFrom(yamlRoleBinding, namespace) + } + + async replaceRoleBindingFrom(yamlRoleBinding: V1RoleBinding, namespace: string): Promise { + if (!yamlRoleBinding.metadata || !yamlRoleBinding.metadata.name) { + throw new Error('RoleBinding object requires name') + } + + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + const response = await k8sRbacAuthApi.replaceNamespacedRoleBinding(yamlRoleBinding.metadata.name, namespace, yamlRoleBinding) + return response.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceRoleBindingFromFile(filePath: string, namespace: string): Promise { + const yamlRoleBinding = this.safeLoadFromYamlFile(filePath) as V1RoleBinding + return this.replaceRoleBindingFrom(yamlRoleBinding, namespace) + } + + async createClusterRoleBindingFrom(yamlClusterRoleBinding: V1ClusterRoleBinding) { + if (!yamlClusterRoleBinding.metadata || !yamlClusterRoleBinding.metadata.name) { + throw new Error('ClusterRoleBinding object requires name') + } + + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + return await k8sRbacAuthApi.createClusterRoleBinding(yamlClusterRoleBinding) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createClusterRoleBinding(name: string, saName: string, saNamespace = '', roleName = '') { + const clusterRoleBinding = { + apiVersion: 'rbac.authorization.k8s.io/v1', + metadata: { + name: `${name}`, + }, + subjects: [ + { + kind: 'ServiceAccount', + name: `${saName}`, + namespace: `${saNamespace}`, + }, + ], + roleRef: { + kind: 'ClusterRole', + name: `${roleName}`, + apiGroup: 'rbac.authorization.k8s.io', + }, + } as V1ClusterRoleBinding + return this.createClusterRoleBindingFrom(clusterRoleBinding) + } + + async replaceClusterRoleBindingFrom(clusterRoleBinding: V1ClusterRoleBinding) { + if (!clusterRoleBinding.metadata || !clusterRoleBinding.metadata.name) { + throw new Error('Cluster Role Binding must have name specified') + } + + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + return await k8sRbacAuthApi.replaceClusterRoleBinding(clusterRoleBinding.metadata.name, clusterRoleBinding) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceClusterRoleBinding(name: string, saName: string, saNamespace = '', roleName = '') { + const clusterRoleBinding = { + apiVersion: 'rbac.authorization.k8s.io/v1', + metadata: { + name: `${name}`, + }, + subjects: [ + { + kind: 'ServiceAccount', + name: `${saName}`, + namespace: `${saNamespace}`, + }, + ], + roleRef: { + kind: 'ClusterRole', + name: `${roleName}`, + apiGroup: 'rbac.authorization.k8s.io', + }, + } as V1ClusterRoleBinding + return this.replaceClusterRoleBindingFrom(clusterRoleBinding) + } + + async deleteRoleBinding(name: string, namespace: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + await k8sRbacAuthApi.deleteNamespacedRoleBinding(name, namespace) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async deleteClusterRoleBinding(name: string): Promise { + const k8sRbacAuthApi = this.kubeConfig.makeApiClient(RbacAuthorizationV1Api) + try { + await k8sRbacAuthApi.deleteClusterRoleBinding(name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async getConfigMap(name = '', namespace = ''): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const { body } = await k8sCoreApi.readNamespacedConfigMap(name, namespace) + return this.compare(body, name) && body + } catch { + return + } + } + + async getConfigMapValue(name: string, namespace: string, key: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const { body } = await k8sCoreApi.readNamespacedConfigMap(name, namespace) + if (body.data) { + return body.data[key] + } + } catch { + return + } + } + + async createConfigMapFromFile(filePath: string, namespace = '') { + const yamlConfigMap = this.safeLoadFromYamlFile(filePath) as V1ConfigMap + return this.createNamespacedConfigMap(namespace, yamlConfigMap) + } + + public async createNamespacedConfigMap(namespace: string, configMap: V1ConfigMap) { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + + try { + const { body } = await k8sCoreApi.createNamespacedConfigMap(namespace, configMap) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async patchConfigMap(name: string, patch: any, namespace = '') { + const k8sCoreApi = this.kubeConfig.makeApiClient(PatchedK8sApi) + try { + return await k8sCoreApi.patchNamespacedConfigMap(name, namespace, patch) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteConfigMap(name: string, namespace: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + await k8sCoreApi.deleteNamespacedConfigMap(name, namespace) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + public async replaceNamespacedConfigMap(name: string, namespace: string, configMap: V1ConfigMap) { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + + try { + const { body } = await k8sCoreApi.replaceNamespacedConfigMap(name, namespace, configMap) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getNamespace(namespace: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const { body } = await k8sApi.readNamespace(namespace) + return body + } catch { + } + } + + async hasReadPermissionsForNamespace(namespace: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(AuthorizationV1Api) + const accessReview = new V1SelfSubjectAccessReview() + accessReview.spec = new V1SelfSubjectAccessReviewSpec() + accessReview.spec.resourceAttributes = { + group: '', + name: 'access-to-che-namespace', + namespace, + resource: 'namespaces', + verb: 'get', + } + + try { + const { body } = await k8sApi.createSelfSubjectAccessReview(accessReview) + return body.status!.allowed + } catch (error) { + if (error.response && error.response.body) { + if (error.response.body.code === 403) { + return false + } + } + throw this.wrapK8sClientError(error) + } + } + + async readNamespacedPod(podName: string, namespace: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const res = await k8sCoreApi.readNamespacedPod(podName, namespace) + if (res && res.body) { + return res.body + } + } catch { + return + } + } + + async patchCustomResource(name: string, namespace: string, patch: any, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + // It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error. + const requestOptions = { + headers: { + 'content-type': 'application/merge-patch+json', + }, + } + + try { + const res = await k8sCoreApi.patchNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural, name, patch, undefined, undefined, undefined, requestOptions) + if (res && res.body) { + return res.body + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async patchNamespacedPod(name: string, namespace: string, patch: any): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + + // It is required to patch content-type, otherwise request will be rejected with 415 (Unsupported media type) error. + const requestOptions = { + headers: { + 'content-type': 'application/strategic-merge-patch+json', + }, + } + + try { + const res = await k8sCoreApi.patchNamespacedPod(name, namespace, patch, undefined, undefined, undefined, undefined, requestOptions) + if (res && res.body) { + return res.body + } + } catch { + return + } + } + + async podsExistBySelector(selector: string, namespace = ''): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + let res + try { + res = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector) + } catch (e) { + throw this.wrapK8sClientError(e) + } + + if (!res || !res.body || !res.body.items) { + throw new Error(`Get pods by selector "${selector}" returned an invalid response`) + } + + return (res.body.items.length > 0) + } + + /** + * Returns pod waiting state. + */ + async getPodWaitingState(namespace: string, selector: string, desiredPhase: string): Promise { + const pods = await this.getPodListByLabel(namespace, selector) + if (!pods.length) { + return + } + + for (const pod of pods) { + if (pod.status && pod.status.phase === desiredPhase && pod.status.containerStatuses) { + for (const status of pod.status.containerStatuses) { + if (status.state && status.state.waiting && status.state.waiting.message && status.state.waiting.reason) { + return status.state.waiting + } + } + } + } + } + + /** + * Returns pod last terminated state. + */ + async getPodLastTerminatedState(namespace: string, selector: string): Promise { + const pods = await this.getPodListByLabel(namespace, selector) + if (!pods.length) { + return + } + + for (const pod of pods) { + if (pod.status && pod.status.containerStatuses) { + for (const status of pod.status.containerStatuses) { + if (status.lastState) { + return status.lastState.terminated + } + } + } + } + } + + async getPodCondition(namespace: string, selector: string, conditionType: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + let res + try { + res = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector) + } catch (e) { + throw this.wrapK8sClientError(e) + } + + if (!res || !res.body || !res.body.items) { + return [] + } + + const conditions: V1PodCondition[] = [] + for (const pod of res.body.items) { + if (pod.status && pod.status.conditions) { + for (const condition of pod.status.conditions) { + if (condition.type === conditionType) { + conditions.push(condition) + } + } + } + } + + return conditions + } + + async getPodReadyConditionStatus(selector: string, namespace = ''): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + let res + try { + res = await k8sCoreApi.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector) + } catch (e) { + throw this.wrapK8sClientError(e) + } + + if (!res || !res.body || !res.body.items) { + throw new Error(`Get pods by selector "${selector}" returned an invalid response.`) + } + + if (res.body.items.length < 1) { + // No pods found by the specified selector. So, it's not ready. + return 'False' + } + + if (res.body.items.length > 1) { + // Several pods found, rolling update? + return + } + + if (!res.body.items[0].status || !res.body.items[0].status.conditions || !(res.body.items[0].status.conditions.length > 0)) { + return + } + + const conditions = res.body.items[0].status.conditions + for (const condition of conditions) { + if (condition.type === 'Ready') { + return condition.status + } + } + } + + async waitForPodReady(selector: string, namespace = '', intervalMs = 500, timeoutMs = this.podReadyTimeout) { + const iterations = timeoutMs / intervalMs + for (let index = 0; index < iterations; index++) { + const readyStatus = await this.getPodReadyConditionStatus(selector, namespace) + if (readyStatus === 'True') { + return + } + await cli.wait(intervalMs) + } + throw new Error(`ERR_TIMEOUT: Timeout set to pod ready timeout ${this.podReadyTimeout}`) + } + + async waitUntilPodIsDeleted(selector: string, namespace = '', intervalMs = 500, timeoutMs = this.podReadyTimeout) { + const iterations = timeoutMs / intervalMs + for (let index = 0; index < iterations; index++) { + const pods = await this.listNamespacedPod(namespace, undefined, selector) + if (!pods.items.length) { + return + } + await cli.wait(intervalMs) + } + throw new Error('ERR_TIMEOUT: Waiting until pod is deleted took too long.') + } + + async deletePod(name: string, namespace = '') { + this.kubeConfig.loadFromDefault() + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + return await k8sCoreApi.deleteNamespacedPod(name, namespace) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + // make sure that flag is specified for command that it's invoked + async waitLatestReplica(deploymentName: string, namespace = '', intervalMs = 500, timeoutMs = this.podWaitTimeout) { + const iterations = timeoutMs / intervalMs + for (let index = 0; index < iterations; index++) { + const deployment = await this.getDeployment(deploymentName, namespace) + if (!deployment) { + throw new Error(`Deployment ${namespace}/${deploymentName} is not found.`) + } + + const deploymentStatus = deployment.status + if (!deploymentStatus) { + throw new Error(`Deployment ${namespace}/${deploymentName} does not have any status`) + } + + if (deploymentStatus.unavailableReplicas && deploymentStatus.unavailableReplicas > 0) { + await cli.wait(intervalMs) + } else { + return + } + } + + throw new Error(`ERR_TIMEOUT: Timeout set to pod wait timeout ${this.podWaitTimeout}`) + } + + async deploymentExist(name = '', namespace = ''): Promise { + const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + const { body } = await k8sApi.readNamespacedDeployment(name, namespace) + return this.compare(body, name) + } catch { + return false + } + } + + async isConfigMapExists(name: string, namespace: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + await k8sApi.readNamespacedConfigMap(name, namespace) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async deploymentReady(name = '', namespace = ''): Promise { + const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + const res = await k8sApi.readNamespacedDeployment(name, namespace) + return ((res && res.body && + res.body.status && res.body.status.readyReplicas && + res.body.status.readyReplicas > 0) as boolean) + } catch { + return false + } + } + + async deploymentStopped(name = '', namespace = ''): Promise { + const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + const res = await k8sApi.readNamespacedDeployment(name, namespace) + if (res && res.body && res.body.spec && res.body.spec.replicas) { + throw new Error(`Deployment '${name}' without replicas in spec is fetched`) + } + return res.body!.spec!.replicas === 0 + } catch { + return false + } + } + + async isDeploymentPaused(name = '', namespace = ''): Promise { + const k8sApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + const res = await k8sApi.readNamespacedDeployment(name, namespace) + if (!res || !res.body || !res.body.spec) { + throw new Error('E_BAD_DEPLOY_RESPONSE') + } + return res.body.spec.paused || false + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async pauseDeployment(name = '', namespace = '') { + const k8sApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) + try { + const patch = { + spec: { + paused: true, + }, + } + await k8sApi.patchNamespacedDeployment(name, namespace, patch) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async resumeDeployment(name = '', namespace = '') { + const k8sApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) + try { + const patch = { + spec: { + paused: false, + }, + } + await k8sApi.patchNamespacedDeployment(name, namespace, patch) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async scaleDeployment(name = '', namespace = '', replicas: number) { + const k8sAppsApi = this.kubeConfig.makeApiClient(PatchedK8sAppsApi) + const patch = { + spec: { + replicas, + }, + } + let res + try { + res = await k8sAppsApi.patchNamespacedDeploymentScale(name, namespace, patch) + } catch (e) { + throw this.wrapK8sClientError(e) + } + + if (!res || !res.body) { + throw new Error('Patch deployment scale returned an invalid response') + } + } + + async createDeployment(name: string, + image: string, + serviceAccount: string, + pullPolicy: string, + configMapEnvSource: string, + namespace: string) { + const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) + const deployment = new V1Deployment() + deployment.metadata = new V1ObjectMeta() + deployment.metadata.name = name + deployment.metadata.namespace = namespace + deployment.spec = new V1DeploymentSpec() + deployment.spec.selector = new V1LabelSelector() + deployment.spec.selector.matchLabels = { app: name } + deployment.spec.template = new V1PodTemplateSpec() + deployment.spec.template.metadata = new V1ObjectMeta() + deployment.spec.template.metadata.name = name + deployment.spec.template.metadata.labels = { app: name } + deployment.spec.template.spec = new V1PodSpec() + deployment.spec.template.spec.serviceAccountName = serviceAccount + const opContainer = new V1Container() + opContainer.name = name + opContainer.image = image + opContainer.imagePullPolicy = pullPolicy + const envFromSource = new V1EnvFromSource() + envFromSource.configMapRef = new V1ConfigMapEnvSource() + envFromSource.configMapRef.name = configMapEnvSource + opContainer.envFrom = [envFromSource] + deployment.spec.template.spec.containers = [opContainer] + + try { + return await k8sAppsApi.createNamespacedDeployment(namespace, deployment) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createDeploymentFrom(yamlDeployment: V1Deployment): Promise { + const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + await k8sAppsApi.createNamespacedDeployment(yamlDeployment.metadata!.namespace!, yamlDeployment) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createServiceFrom(yamlService: V1Service, namespace = '') { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + return await k8sApi.createNamespacedService(namespace, yamlService) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceDeploymentFrom(yamlDeployment: V1Deployment): Promise { + // updating restartedAt to make sure that rollout will be restarted + let annotations = yamlDeployment.spec!.template!.metadata!.annotations + if (!annotations) { + annotations = {} + yamlDeployment.spec!.template!.metadata!.annotations = annotations + } + annotations['kubectl.kubernetes.io/restartedAt'] = new Date().toISOString() + + const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + await k8sAppsApi.replaceNamespacedDeployment(yamlDeployment.metadata!.name!, yamlDeployment.metadata!.namespace!, yamlDeployment) + } catch (e) { + if (e.response && e.response.body && e.response.body.message && e.response.body.message.toString().endsWith('field is immutable')) { + try { + await k8sAppsApi.deleteNamespacedDeployment(yamlDeployment.metadata!.name!, yamlDeployment.metadata!.namespace!) + await k8sAppsApi.createNamespacedDeployment(yamlDeployment.metadata!.namespace!, yamlDeployment) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + throw this.wrapK8sClientError(e) + } + } + + async deleteAllDeployments(namespace: string): Promise { + const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + await k8sAppsApi.deleteCollectionNamespacedDeployment(namespace) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getDeploymentsBySelector(labelSelector = '', namespace = ''): Promise { + const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + const res = await k8sAppsApi.listNamespacedDeployment(namespace, 'true', undefined, undefined, undefined, labelSelector) + if (res && res.body) { + return res.body + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + throw new Error('ERR_LIST_NAMESPACES') + } + + async getDeployment(name: string, namespace: string): Promise { + const k8sAppsApi = this.kubeConfig.makeApiClient(AppsV1Api) + try { + const res = await k8sAppsApi.readNamespacedDeployment(name, namespace) + if (res && res.body) { + return res.body! + } + } catch (error) { + if (error.response && error.response.statusCode === 404) { + return + } + throw this.wrapK8sClientError(error) + } + throw new Error('ERR_GET_DEPLOYMENT') + } + + async createPod(name: string, + image: string, + serviceAccount: string, + restartPolicy: string, + pullPolicy: string, + configMapEnvSource: string, + namespace: string) { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + const pod = new V1Pod() + pod.metadata = new V1ObjectMeta() + pod.metadata.name = name + pod.metadata.labels = { app: name } + pod.metadata.namespace = namespace + pod.spec = new V1PodSpec() + pod.spec.restartPolicy = restartPolicy + pod.spec.serviceAccountName = serviceAccount + const opContainer = new V1Container() + opContainer.name = name + opContainer.image = image + opContainer.imagePullPolicy = pullPolicy + const envFromSource = new V1EnvFromSource() + envFromSource.configMapRef = new V1ConfigMapEnvSource() + envFromSource.configMapRef.name = configMapEnvSource + opContainer.envFrom = [envFromSource] + pod.spec.containers = [opContainer] + + try { + return await k8sCoreApi.createNamespacedPod(namespace, pod) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createJob(name: string, + image: string, + serviceAccount: string, + namespace: string, + backoffLimit = 0, + restartPolicy = 'Never') { + const k8sBatchApi = this.kubeConfig.makeApiClient(BatchV1Api) + + const job = new V1Job() + job.metadata = new V1ObjectMeta() + job.metadata.name = name + job.metadata.labels = { app: name } + job.metadata.namespace = namespace + job.spec = new V1JobSpec() + job.spec.ttlSecondsAfterFinished = 10 + job.spec.backoffLimit = backoffLimit + job.spec.template = new V1PodTemplateSpec() + job.spec.template.spec = new V1PodSpec() + job.spec.template.spec.serviceAccountName = serviceAccount + const jobContainer = new V1Container() + jobContainer.name = name + jobContainer.image = image + job.spec.template.spec.restartPolicy = restartPolicy + job.spec.template.spec.containers = [jobContainer] + + try { + return await k8sBatchApi.createNamespacedJob(namespace, job) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getJob(jobName: string, namespace: string): Promise { + const k8sBatchApi = this.kubeConfig.makeApiClient(BatchV1Api) + + try { + const result = await k8sBatchApi.readNamespacedJob(jobName, namespace) + return result.body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async waitJob(jobName: string, namespace: string, timeout = AWAIT_TIMEOUT_S): Promise { + return new Promise(async (resolve, reject) => { + // Set up watcher + const watcher = new Watch(this.kubeConfig) + const request = await watcher + .watch(`/apis/batch/v1/namespaces/${namespace}/jobs/`, {}, + (_phase: string, obj: any) => { + const job = obj as V1Job + + // Filter other jobs in the given namespace + if (job && job.metadata && job.metadata.name === jobName) { + // Check job status + if (job.status && job.status.succeeded && job.status.succeeded >= 1) { + // Job is finished, stop watching + if (request) { + request.abort() + } + // Release awaiter + resolve() + } + } + }, + error => { + if (error) { + reject(error) + } + }) + + // Automatically stop watching after timeout + const timeoutHandler = setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${jobName}" job.`) + }, timeout * 1000) + + // Request job, for case if it is already ready + const job = await this.getJob(jobName, namespace) + if (job.status && job.status.succeeded && job.status.succeeded >= 1) { + // Stop watching + request.abort() + clearTimeout(timeoutHandler) + + // Relese awaiter + resolve() + } + }) + } + + async deleteJob(jobName: string, namespace: string): Promise { + const k8sBatchApi = this.kubeConfig.makeApiClient(BatchV1Api) + + try { + const result = await k8sBatchApi.deleteNamespacedJob(jobName, namespace) + return result.body.status === 'Success' + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async compare(body: any, name: string): Promise { + if (body && body.metadata && body.metadata.name && body.metadata.name === name) { + return true + } else { + return false + } + } + + async ingressExist(name = '', namespace = ''): Promise { + const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) + try { + const { body } = await k8sExtensionsApi.readNamespacedIngress(name, namespace) + return this.compare(body, name) + } catch { + return false + } + } + + async deleteAllIngresses(namespace: string): Promise { + const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) + try { + await k8sExtensionsApi.deleteCollectionNamespacedIngress(namespace) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createCrdFromFile(filePath: string): Promise { + const yaml = this.safeLoadFromYamlFile(filePath) + if (yaml.apiVersion === this.API_EXTENSIONS_V1BETA1) { + return this.createCrdV1Beta1(yaml) + } + return this.createCrdV1(yaml) + } + + private async createCrdV1Beta1(yaml: any): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) + try { + await k8sApi.createCustomResourceDefinition(yaml) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + private async createCrdV1(yaml: any): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) + try { + await k8sApi.createCustomResourceDefinition(yaml) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async replaceCrdFromFile(filePath: string, resourceVersion: string): Promise { + const yaml = this.safeLoadFromYamlFile(filePath) + if (!yaml.metadata || !yaml.metadata.name) { + throw new Error(`Name is not defined in: ${filePath}`) + } + + yaml.metadata.resourceVersion = resourceVersion + if (yaml.apiVersion === this.API_EXTENSIONS_V1BETA1) { + return this.replaceCrdV1Beta1(yaml) + } + return this.replaceCrdV1(yaml) + } + + private async replaceCrdV1Beta1(yaml: any): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) + try { + await k8sApi.replaceCustomResourceDefinition(yaml.metadata.name, yaml) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + private async replaceCrdV1(yaml: any): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) + try { + await k8sApi.replaceCustomResourceDefinition(yaml.metadata.name, yaml) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getCrd(name: string): Promise { + if (await this.IsAPIExtensionSupported('v1')) { + return this.getCrdV1(name) + } + return this.getCrdV1beta1(name) + } + + private async getCrdV1(name: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) + try { + const { body } = await k8sApi.readCustomResourceDefinition(name) + return body + } catch (e) { + if (e.response.statusCode === 404) { + return + } + + throw this.wrapK8sClientError(e) + } + } + + private async getCrdV1beta1(name: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) + try { + const { body } = await k8sApi.readCustomResourceDefinition(name) + return body + } catch (e) { + if (e.response.statusCode === 404) { + return + } + + throw this.wrapK8sClientError(e) + } + } + + async getCrdStorageVersion(name: string): Promise { + const crd = await this.getCrd(name) + if (!crd.spec.versions) { + // Should never happen + return 'v1' + } + + const version = crd.spec.versions.find((v: any) => v.storage) + return version ? version.name : 'v1' + } + + async deleteCrd(name: string): Promise { + if (await this.IsAPIExtensionSupported('v1')) { + return this.deleteCrdV1(name) + } + return this.deleteCrdV1Beta1(name) + } + + private async deleteCrdV1Beta1(name: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1beta1Api) + try { + await k8sApi.deleteCustomResourceDefinition(name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + private async deleteCrdV1(name: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(ApiextensionsV1Api) + try { + await k8sApi.deleteCustomResourceDefinition(name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async createCheCluster(cheClusterCR: any, flags: any, ctx: any, useDefaultCR: boolean): Promise { + const cheNamespace = flags.chenamespace + if (useDefaultCR) { + // If CheCluster CR is not explicitly provided, then modify the default example CR + // with values derived from the other parameters + + if (VersionHelper.isDeployingStableVersion(flags)) { + // Use images from operator defaults in case of a stable version + cheClusterCR.spec.server.cheImage = '' + cheClusterCR.spec.server.cheImageTag = '' + cheClusterCR.spec.server.pluginRegistryImage = '' + cheClusterCR.spec.server.devfileRegistryImage = '' + cheClusterCR.spec.auth.identityProviderImage = '' + } + const cheImage = flags.cheimage + if (cheImage) { + const imageAndTag = cheImage.split(':', 2) + cheClusterCR.spec.server.cheImage = imageAndTag[0] + cheClusterCR.spec.server.cheImageTag = imageAndTag.length === 2 ? imageAndTag[1] : 'latest' + } + + if ((flags.installer === 'olm' && !flags['catalog-source-yaml']) || (flags['catalog-source-yaml'] && flags['olm-channel'] === OLM_STABLE_CHANNEL_NAME)) { + // use default image tag for `olm` to install stable Che, because we don't have nightly channel for OLM catalog. + cheClusterCR.spec.server.cheImageTag = '' + } + cheClusterCR.spec.server.cheDebug = flags.debug ? flags.debug.toString() : 'false' + + if (isKubernetesPlatformFamily(flags.platform) || !cheClusterCR.spec.auth.openShiftoAuth) { + cheClusterCR.spec.auth.updateAdminPassword = true + } + + if (!cheClusterCR.spec.k8s) { + cheClusterCR.spec.k8s = {} + } + if (flags.tls) { + cheClusterCR.spec.server.tlsSupport = flags.tls + if (!cheClusterCR.spec.k8s.tlsSecretName) { + cheClusterCR.spec.k8s.tlsSecretName = 'che-tls' + } + } + if (flags.domain) { + cheClusterCR.spec.k8s.ingressDomain = flags.domain + } + const pluginRegistryUrl = flags['plugin-registry-url'] + if (pluginRegistryUrl) { + cheClusterCR.spec.server.pluginRegistryUrl = pluginRegistryUrl + cheClusterCR.spec.server.externalPluginRegistry = true + } + const devfileRegistryUrl = flags['devfile-registry-url'] + if (devfileRegistryUrl) { + cheClusterCR.spec.server.devfileRegistryUrl = devfileRegistryUrl + cheClusterCR.spec.server.externalDevfileRegistry = true + } + + cheClusterCR.spec.storage.postgresPVCStorageClassName = flags['postgres-pvc-storage-class-name'] + cheClusterCR.spec.storage.workspacePVCStorageClassName = flags['workspace-pvc-storage-class-name'] + + if (flags['workspace-engine'] === 'dev-workspace') { + cheClusterCR.spec.devWorkspace.enable = true + } + + // Use self-signed TLS certificate by default (for versions before 7.14.3). + // In modern versions of Che this field is ignored. + cheClusterCR.spec.server.selfSignedCert = true + } + + cheClusterCR.spec.server.cheClusterRoles = ctx.namespaceEditorClusterRoleName + + // override default values + if (ctx.crPatch) { + merge(cheClusterCR, ctx.crPatch) + } + + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.createNamespacedCustomObject('org.eclipse.che', 'v1', cheNamespace, 'checlusters', cheClusterCR) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async patchCheCluster(name: string, namespace: string, patch: any): Promise { + try { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + const { body } = await customObjectsApi.patchNamespacedCustomObject('org.eclipse.che', 'v1', namespace, 'checlusters', name, patch, undefined, undefined, undefined, { headers: { 'Content-Type': 'application/merge-patch+json' } }) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + /** + * Returns `checlusters.org.eclipse.che' in the given namespace. + */ + async getCheCluster(cheNamespace: string): Promise { + return this.getCustomResource(cheNamespace, CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL) + } + + /** + * Returns custom resource in the given namespace. + */ + async getCustomResource(namespace: string, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.listNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural) + if (!(body as any).items) { + return + } + + const crs = (body as any).items as any[] + if (crs.length === 0) { + return + } 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) + } + } + } + + /** + * Deletes `checlusters.org.eclipse.che' resources in the given namespace. + */ + async getAllCheClusters(): Promise { + return this.getAllCustomResources(CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL) + } + + /** + * Returns all custom resources + */ + async getAllCustomResources(resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.listClusterCustomObject(resourceAPIGroup, resourceAPIVersion, resourcePlural) + return (body as any).items ? (body as any).items : [] + } catch (e) { + if (e.response.statusCode === 404) { + // There is no CRD + return [] + } + throw this.wrapK8sClientError(e) + } + } + + /** + * Deletes `checlusters.org.eclipse.che' resources in the given namespace. + */ + async deleteCheCluster(namespace: string): Promise { + return this.deleteCustomResource(namespace, CHE_CLUSTER_API_GROUP, CHE_CLUSTER_API_VERSION, CHE_CLUSTER_KIND_PLURAL) + } + + /** + * Deletes custom resources in the given namespace. + */ + async deleteCustomResource(namespace: string, resourceAPIGroup: string, resourceAPIVersion: string, resourcePlural: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.listNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural) + if (!(body as any).items) { + return + } + + const crs = (body as any).items as any[] + for (const cr of crs) { + await customObjectsApi.deleteNamespacedCustomObject(resourceAPIGroup, resourceAPIVersion, namespace, resourcePlural, cr.metadata.name) + } + } catch (e) { + if (e.response.statusCode === 404) { + // There is no CRD + return + } + throw this.wrapK8sClientError(e) + } + } + + async isPreInstalledOLM(): Promise { + const apiApi = this.kubeConfig.makeApiClient(ApisApi) + try { + const { body } = await apiApi.getAPIVersions() + const OLMAPIGroup = body.groups.find(apiGroup => apiGroup.name === 'operators.coreos.com') + return Boolean(OLMAPIGroup) + } catch { + return false + } + } + + async getUsersNumber(): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + let amountOfUsers: number + try { + const { body } = await customObjectsApi.listClusterCustomObject('user.openshift.io', 'v1', 'users') + if (!(body as any).items) { + throw new Error('Unable to get list users.') + } + amountOfUsers = (body as any).items.length + } catch (e) { + throw this.wrapK8sClientError(e) + } + return amountOfUsers + } + + async getOpenshiftAuthProviders(): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + const oAuthName = 'cluster' + const { body } = await customObjectsApi.getClusterCustomObject('config.openshift.io', 'v1', 'oauths', oAuthName) + return (body as OAuth).spec.identityProviders + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async operatorSourceExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorsources', name) + return this.compare(body, name) + } catch { + return false + } + } + + async catalogSourceExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) + return this.compare(body, name) + } catch { + return false + } + } + + async getOAuthClientAuthorizations(clientName: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.listClusterCustomObject('oauth.openshift.io', 'v1', 'oauthclientauthorizations') + + if (!(body as any).items) { + return [] + } + const oauthClientAuthorizations = (body as any).items as any[] + return oauthClientAuthorizations.filter(o => o.clientName === clientName) + } catch (e) { + if (e.response.statusCode === 404) { + // There is no 'oauthclientauthorizations` + return [] + } + throw this.wrapK8sClientError(e) + } + } + + async deleteOAuthClientAuthorizations(oAuthClientAuthorizations: any[]): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const filetOauthAuthorizations = oAuthClientAuthorizations.filter((e => e.metadata && e.metadata.name)) + for (const oauthAuthorization of filetOauthAuthorizations) { + await customObjectsApi.deleteClusterCustomObject('oauth.openshift.io', 'v1', 'oauthclientauthorizations', oauthAuthorization.metadata.name) + } + } catch (e) { + if (e.response.statusCode === 404) { + return + } + throw this.wrapK8sClientError(e) + } + } + + async consoleLinkExists(name: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + await customObjectsApi.getClusterCustomObject('console.openshift.io', 'v1', 'consolelinks', name) + return true + } catch (e) { + if (e.response.statusCode === 404) { + // There are no consoleLink + return false + } + throw this.wrapK8sClientError(e) + } + } + + async deleteConsoleLink(name: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + await customObjectsApi.deleteClusterCustomObject('console.openshift.io', 'v1', 'consolelinks', name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async getCatalogSource(name: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.getNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', name) + return body as CatalogSource + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + readCatalogSourceFromFile(filePath: string): CatalogSource { + const catalogSource = this.safeLoadFromYamlFile(filePath) as CatalogSource + if (!catalogSource.metadata || !catalogSource.metadata.name) { + throw new Error(`CatalogSource from ${filePath} must have specified metadata and name`) + } + return catalogSource + } + + async createCatalogSource(catalogSource: CatalogSource) { + const customObjectsApi = this.kubeConfig.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.kubeConfig) + const request = await 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 deleteCatalogSource(namespace: string, catalogSourceName: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'catalogsources', catalogSourceName) + } catch (e) { + if (e.response.statusCode === 404) { + return + } + throw this.wrapK8sClientError(e) + } + } + + async operatorGroupExists(name: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.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.kubeConfig.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): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1', namespace, 'operatorgroups', operatorGroupName) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async createOperatorSubscription(subscription: Subscription) { + const namespace: string = subscription.metadata.namespace! + + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const { body } = await customObjectsApi.createNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', subscription) + return body + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getOperatorSubscription(name: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.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.kubeConfig.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): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'subscriptions', operatorSubscriptionName) + } catch (e) { + if (e.response.statusCode !== 404) { + 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.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.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) + }) + } + + async approveOperatorInstallationPlan(name = '', namespace = '') { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + const patch: InstallPlan = { + spec: { + approved: true, + }, + } + await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'installplans', name, patch, undefined, undefined, undefined, { headers: { 'Content-Type': 'application/merge-patch+json' } }) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async waitUntilOperatorIsInstalled(installPlanName: string, namespace: string, timeout = 240) { + return new Promise(async (resolve, reject) => { + const watcher = new Watch(this.kubeConfig) + const request = await 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.phase === 'Failed') { + const errorMessage = [] + for (const condition of installPlan.status.conditions) { + if (!condition.reason) { + errorMessage.push(`Reason: ${condition.reason}`) + errorMessage.push(!condition.message ? `Message: ${condition.message}` : '') + } + } + reject(errorMessage.join(' ')) + } + if (installPlan.status && installPlan.status.conditions) { + for (const condition of installPlan.status.conditions) { + if (condition.type === 'Installed' && condition.status === 'True') { + resolve(installPlan) + } + } + } + }, + error => { + if (error) { + reject(error) + } + }) + + setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${installPlanName}" has go status 'Installed'.`) + }, timeout * 1000) + }) + } + + async getCSV(csvName: string, namespace: string): Promise { + const csvs = await this.getClusterServiceVersions(namespace) + return csvs.items.find(item => item.metadata.name === csvName) + } + + async getClusterServiceVersions(namespace: string): Promise { + const customObjectsApi = this.kubeConfig.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 patchClusterServiceVersion(namespace: string, name: string, jsonPatch: any[]): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + const requestOptions = { + headers: { + 'content-type': 'application/json-patch+json', + }, + } + try { + const response = await customObjectsApi.patchNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', name, jsonPatch, undefined, undefined, undefined, requestOptions) + return response.body as ClusterServiceVersion + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async deleteClusterServiceVersion(namespace: string, csvName: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + try { + await customObjectsApi.deleteNamespacedCustomObject('operators.coreos.com', 'v1alpha1', namespace, 'clusterserviceversions', csvName) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async getPackageManifect(name: string): Promise { + const customObjectsApi = this.kubeConfig.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.kubeConfig.makeApiClient(CoreV1Api) + try { + await k8sCoreApi.deleteNamespace(namespace) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + /** + * Returns CRD version of Cert Manager + */ + async getCertManagerK8sApiVersion(): Promise { + return this.getCrdStorageVersion('certificates.cert-manager.io') + } + + async clusterIssuerExists(name: string, version: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + // If cluster issuers doesn't exist an exception will be thrown + await customObjectsApi.getClusterCustomObject('cert-manager.io', version, 'clusterissuers', name) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async isNamespacedCertificateExists(name: string, version: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + // If cluster issuers doesn't exist an exception will be thrown + await customObjectsApi.getNamespacedCustomObject('cert-manager.io', version, namespace, 'certificates', name) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async deleteNamespacedCertificate(name: string, version: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + // If cluster certificates doesn't exist an exception will be thrown + await customObjectsApi.deleteNamespacedCustomObject('cert-manager.io', version, namespace, 'certificates', name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async deleteNamespacedIssuer(name: string, version: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + await customObjectsApi.deleteNamespacedCustomObject('cert-manager.io', version, namespace, 'issuers', name) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async listClusterIssuers(version: string, labelSelector?: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + let res + try { + res = await customObjectsApi.listClusterCustomObject('cert-manager.io', version, 'clusterissuers', undefined, undefined, undefined, labelSelector) + } catch (e) { + throw this.wrapK8sClientError(e) + } + + if (!res || !res.body) { + throw new Error('Unable to get cluster issuers list') + } + const clusterIssuersList: { items?: any[] } = res.body + + return clusterIssuersList.items || [] + } + + async createCheClusterIssuer(cheClusterIssuerYamlPath: string, version: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + const cheClusterIssuer = this.safeLoadFromYamlFile(cheClusterIssuerYamlPath) + try { + await customObjectsApi.createClusterCustomObject('cert-manager.io', version, 'clusterissuers', cheClusterIssuer) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async createCertificateIssuer(cheClusterIssuerYamlPath: string, version: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + const certificateIssuer = this.safeLoadFromYamlFile(cheClusterIssuerYamlPath) + try { + await customObjectsApi.createNamespacedCustomObject('cert-manager.io', version, namespace, 'issuers', certificateIssuer) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async isCertificateIssuerExists(name: string, version: string, namespace: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + // If issuers doesn't exist an exception will be thrown + await customObjectsApi.getNamespacedCustomObject('cert-manager.io', version, namespace, 'issuers', name) + return true + } catch (e) { + if (e.response.statusCode === 404) { + return false + } + + throw this.wrapK8sClientError(e) + } + } + + async createCheClusterCertificate(certificate: V1Certificate, version: string): Promise { + const customObjectsApi = this.kubeConfig.makeApiClient(CustomObjectsApi) + + try { + await customObjectsApi.createNamespacedCustomObject('cert-manager.io', version, certificate.metadata.namespace, 'certificates', certificate) + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async currentContext(): Promise { + return this.kubeConfig.getCurrentContext() + } + + getContext(name: string): Context | null { + return this.kubeConfig.getContextObject(name) + } + + /** + * Retrieve the default token from the default serviceAccount. + */ + async getDefaultServiceAccountToken(): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + const namespaceName = 'default' + const saName = 'default' + let res + // now get the matching secrets + try { + res = await k8sCoreApi.listNamespacedSecret(namespaceName) + } catch (e) { + throw this.wrapK8sClientError(e) + } + if (!res || !res.body) { + throw new Error('Unable to get default service account') + } + const v1SecretList = res.body + + if (!v1SecretList.items || v1SecretList.items.length === 0) { + throw new Error(`Unable to get default service account token since there is no secret in '${namespaceName}' namespace`) + } + + const v1DefaultSATokenSecret = v1SecretList.items.find(secret => secret.metadata!.annotations && + secret.metadata!.annotations['kubernetes.io/service-account.name'] === saName && + secret.type === 'kubernetes.io/service-account-token') + + if (!v1DefaultSATokenSecret) { + throw new Error(`Secret for '${saName}' service account is not found in namespace '${namespaceName}'`) + } + + return Buffer.from(v1DefaultSATokenSecret.data!.token, 'base64').toString() + } + + async checkKubeApi() { + const currentCluster = this.kubeConfig.getCurrentCluster() + if (!currentCluster) { + throw new Error(`The current context is unknown. It should be set using '${getClusterClientCommand()} config use-context ' or in another way.`) + } + + try { + await this.requestKubeHealthz(currentCluster) + } catch (error) { + if (error.message && (error.message as string).includes('E_K8S_API_UNAUTHORIZED')) { + const token = await this.getDefaultServiceAccountToken() + await this.requestKubeHealthz(currentCluster, token) + } else { + throw error + } + } + } + + async requestKubeHealthz(currentCluster: Cluster, token?: string) { + const endpoint = `${currentCluster.server}/healthz` + + try { + const config: AxiosRequestConfig = { + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + requestCert: true, + }), + headers: token && { Authorization: 'bearer ' + token }, + } + + const response = await axios.get(`${endpoint}`, config) + if (!response || response.status !== 200 || response.data !== 'ok') { + throw new Error('E_BAD_RESP_K8S_API') + } + } catch (error) { + if (error.response && error.response.status === 403) { + throw new Error(`E_K8S_API_FORBIDDEN - Message: ${error.response.data.message}`) + } + if (error.response && error.response.status === 401) { + throw new Error(`E_K8S_API_UNAUTHORIZED - Message: ${error.response.data.message}`) + } + if (error.response) { + // The request was made and the server responded with a status code + // that falls out of the range of 2xx + throw new Error(`E_K8S_API_UNKNOWN_ERROR - Status: ${error.response.status}`) + } 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 + throw new Error(`E_K8S_API_NO_RESPONSE - Endpoint: ${endpoint} - Error message: ${error.message}`) + } else { + // Something happened in setting up the request that triggered an Error + throw new Error(`E_CHECTL_UNKNOWN_ERROR - Message: ${error.message}`) + } + } + } + + async isOpenShift(): Promise { + return this.IsAPIGroupSupported('apps.openshift.io') + } + + async isOpenShift3(): Promise { + const isAppsAPISupported = await this.IsAPIGroupSupported('apps.openshift.io') + const isConfigAPISupported = await this.IsAPIGroupSupported('config.openshift.io') + return isAppsAPISupported && !isConfigAPISupported + } + + async isOpenShift4(): Promise { + const isRouteAPISupported = await this.IsAPIGroupSupported('route.openshift.io') + const isConfigAPISupported = await this.IsAPIGroupSupported('config.openshift.io') + return isRouteAPISupported && isConfigAPISupported + } + + async IsAPIExtensionSupported(version: string): Promise { + return this.IsAPIGroupSupported('apiextensions.k8s.io', version) + } + + async IsAPIGroupSupported(name: string, version?: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(ApisApi) + try { + const res = await k8sCoreApi.getAPIVersions() + if (!res || !res.body || !res.body.groups) { + return false + } + + const group = res.body.groups.find(g => g.name === name) + if (!group) { + return false + } + + if (version) { + return Boolean(group.versions.find(v => v.version === version)) + } else { + return Boolean(group) + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getIngressHost(name = '', namespace = ''): Promise { + const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) + try { + const res = await k8sExtensionsApi.readNamespacedIngress(name, namespace) + if (res && res.body && + res.body.spec && + res.body.spec.rules && + res.body.spec.rules.length > 0) { + return res.body.spec.rules[0].host || '' + } + throw new Error('ERR_INGRESS_NO_HOST') + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getIngressProtocol(name = '', namespace = ''): Promise { + const k8sExtensionsApi = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) + try { + const res = await k8sExtensionsApi.readNamespacedIngress(name, namespace) + if (!res || !res.body || !res.body.spec) { + throw new Error('ERR_INGRESS_NO_HOST') + } + if (res.body.spec.tls && res.body.spec.tls.length > 0) { + return 'https' + } else { + return 'http' + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async getIngressesBySelector(labelSelector = '', namespace = ''): Promise { + const k8sV1Beta = this.kubeConfig.makeApiClient(ExtensionsV1beta1Api) + try { + const res = await k8sV1Beta.listNamespacedIngress(namespace, 'true', undefined, undefined, undefined, labelSelector) + if (res && res.body) { + return res.body + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + throw new Error('ERR_LIST_INGRESSES') + } + + async getSecret(name = '', namespace = 'default'): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + + // now get the matching secrets + try { + const res = await k8sCoreApi.readNamespacedSecret(name, namespace) + if (res && res.body && res.body) { + return res.body + } else { + return + } + } catch { + return + } + } + + /** + * Creates a secret with given name and data. + * Data should not be base64 encoded. + */ + async createSecret(name: string, data: { [key: string]: string }, namespace: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + + const secret = new V1Secret() + secret.metadata = new V1ObjectMeta() + secret.metadata.name = name + secret.metadata.namespace = namespace + secret.stringData = data + + try { + return (await k8sCoreApi.createNamespacedSecret(namespace, secret)).body + } catch { + return + } + } + + /** + * Awaits secret to be present and contain non-empty data fields specified in dataKeys parameter. + */ + async waitSecret(secretName: string, namespace: string, dataKeys: string[] = [], timeout = AWAIT_TIMEOUT_S): Promise { + return new Promise(async (resolve, reject) => { + // Set up watcher + const watcher = new Watch(this.kubeConfig) + const request = await watcher + .watch(`/api/v1/namespaces/${namespace}/secrets/`, { fieldSelector: `metadata.name=${secretName}` }, + (_phase: string, obj: any) => { + const secret = obj as V1Secret + + // Check all required data fields to be present + if (dataKeys.length > 0 && secret.data) { + for (const key of dataKeys) { + if (!secret.data[key]) { + // Key is missing or empty + return + } + } + } + + // The secret with all specified fields is present, stop watching + if (request) { + request.abort() + } + // Release awaiter + resolve() + }, + error => { + if (error) { + reject(error) + } + }) + + // Automatically stop watching after timeout + const timeoutHandler = setTimeout(() => { + request.abort() + reject(`Timeout reached while waiting for "${secretName}" secret.`) + }, timeout * 1000) + + // Request secret, for case if it is already exist + const secret = await this.getSecret(secretName, namespace) + if (secret) { + // Stop watching + request.abort() + clearTimeout(timeoutHandler) + + // Relese awaiter + resolve() + } + }) + } + + async persistentVolumeClaimExist(name = '', namespace = ''): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const { body } = await k8sCoreApi.readNamespacedPersistentVolumeClaim(name, namespace) + return this.compare(body, name) + } catch { + return false + } + } + + async deletePersistentVolumeClaim(name: string, namespace: string): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + await k8sCoreApi.deleteNamespacedPersistentVolumeClaim(name, namespace) + } catch (e) { + if (e.response.statusCode !== 404) { + throw this.wrapK8sClientError(e) + } + } + } + + async getPersistentVolumeClaimsBySelector(labelSelector = '', namespace = ''): Promise { + const k8sCoreApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const res = await k8sCoreApi.listNamespacedPersistentVolumeClaim(namespace, 'true', undefined, undefined, undefined, labelSelector) + if (res && res.body) { + return res.body + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + throw new Error('ERR_LIST_PVCS') + } + + async listNamespace(): Promise { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const res = await k8sApi.listNamespace() + if (res && res.body) { + return res.body + } else { + return { + items: [], + } + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + async listNamespacedPod(namespace: string, fieldSelector?: string, labelSelector?: string): Promise { + const k8sApi = this.kubeConfig.makeApiClient(CoreV1Api) + try { + const res = await k8sApi.listNamespacedPod(namespace, undefined, undefined, undefined, fieldSelector, labelSelector) + if (res && res.body) { + return res.body + } else { + return { + items: [], + } + } + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + /** + * Reads log by chunk and writes into a file. + */ + async readNamespacedPodLog(pod: string, namespace: string, container: string, filename: string, follow: boolean): Promise { + return new Promise(async (resolve, reject) => { + const logHelper = new Log(this.kubeConfig) + const stream = new Writable() + stream._write = function (chunk, encoding, done) { + fs.appendFileSync(filename, chunk, { encoding }) + done() + } + + await logHelper.log(namespace, pod, container, stream, error => { + stream.end() + if (error) { + reject(error) + } else { + resolve() + } + }, { follow }) + }) + } + + /** + * Forwards port, based on the example + * https://github.com/kubernetes-client/javascript/blob/master/examples/typescript/port-forward/port-forward.ts + */ + async portForward(podName: string, namespace: string, port: number): Promise { + const portForwardHelper = new PortForward(this.kubeConfig, true) + try { + const server = net.createServer(async socket => { + await portForwardHelper.portForward(namespace, podName, [port], socket, null, socket) + }) + server.listen(port, 'localhost') + return + } catch (e) { + throw this.wrapK8sClientError(e) + } + } + + /** + * Checks if message is present and returns error with it + * or returns error with the specified error if message is not found. + * + * @param e k8s error to wrap + */ + private wrapK8sClientError(e: any): Error { + if (e.response && e.response.body) { + return newError(e.response.body, e) + } + return e + } + + public safeLoadFromYamlFile(filePath: string): any { + return safeLoadFromYamlFile(filePath) + } } class PatchedK8sApi extends CoreV1Api { diff --git a/src/api/openshift.ts b/src/api/openshift.ts index 74d0b09e9..bf905787c 100644 --- a/src/api/openshift.ts +++ b/src/api/openshift.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -35,8 +35,9 @@ export class OpenShiftHelper { const termination = stdout.trim() if (termination && termination.includes('edge') || termination.includes('passthrough') || termination.includes('reencrypt')) { return 'https' + } else { + return 'http' } - return 'http' } async routeExist(name: string, namespace = ''): Promise { diff --git a/src/api/typings/cert-manager.d.ts b/src/api/typings/cert-manager.d.ts index eae8d420f..4669bf032 100644 --- a/src/api/typings/cert-manager.d.ts +++ b/src/api/typings/cert-manager.d.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/api/typings/olm.d.ts b/src/api/typings/olm.d.ts index 54677a1b5..9cf1c537b 100644 --- a/src/api/typings/olm.d.ts +++ b/src/api/typings/olm.d.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/api/typings/openshift.d.ts b/src/api/typings/openshift.d.ts index f062081ac..853ea1790 100644 --- a/src/api/typings/openshift.d.ts +++ b/src/api/typings/openshift.d.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/api/version.ts b/src/api/version.ts index 184db09ea..f760d820e 100644 --- a/src/api/version.ts +++ b/src/api/version.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -200,7 +200,7 @@ export namespace VersionHelper { const { data } = await axiosInstance.get(`https://che-incubator.github.io/chectl/channels/${channel}/linux-x64`) return data.version } catch { - + return } } diff --git a/src/commands/auth/delete.ts b/src/commands/auth/delete.ts index 2eb673711..5651616e4 100644 --- a/src/commands/auth/delete.ts +++ b/src/commands/auth/delete.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/auth/get.ts b/src/commands/auth/get.ts index ff04956ac..0a4baa5ad 100644 --- a/src/commands/auth/get.ts +++ b/src/commands/auth/get.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/auth/list.ts b/src/commands/auth/list.ts index fdfb16fb9..719b934d5 100644 --- a/src/commands/auth/list.ts +++ b/src/commands/auth/list.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index daf26f830..6d6373def 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/auth/logout.ts b/src/commands/auth/logout.ts index 4ecce90fb..c1011d7c8 100644 --- a/src/commands/auth/logout.ts +++ b/src/commands/auth/logout.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/auth/use.ts b/src/commands/auth/use.ts index 4dbe299ab..aff51d730 100644 --- a/src/commands/auth/use.ts +++ b/src/commands/auth/use.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -130,7 +130,7 @@ export default class Use extends Command { if (allLogins.size === 0) { cli.info('No login session exists') return - } if (allLogins.size === 1) { + } else if (allLogins.size === 1) { // Retrieve the only login info cheApiEndpoint = allLogins.keys().next().value username = allLogins.get(cheApiEndpoint)![0] diff --git a/src/commands/cacert/export.ts b/src/commands/cacert/export.ts index 2f0d210ea..d2fe02732 100644 --- a/src/commands/cacert/export.ts +++ b/src/commands/cacert/export.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/dashboard/open.ts b/src/commands/dashboard/open.ts index 012037071..fba10b9b8 100644 --- a/src/commands/dashboard/open.ts +++ b/src/commands/dashboard/open.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/devfile/generate.ts b/src/commands/devfile/generate.ts index 79cb45ee5..87c62dc50 100644 --- a/src/commands/devfile/generate.ts +++ b/src/commands/devfile/generate.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -251,8 +251,9 @@ export default class Generate extends Command { const updatedArgs = process.argv.slice(index).map(arg => { if (arg.indexOf(' ') >= 0) { return arg.replace(/(.*?)=(.*)/g, '$1=\"$2\"') + } else { + return arg } - return arg }) this.log(`# chectl ${updatedArgs.join(' ')}`) this.log(yaml.safeDump(devfile)) diff --git a/src/commands/server/debug.ts b/src/commands/server/debug.ts index 932a1da8a..5460fab1d 100644 --- a/src/commands/server/debug.ts +++ b/src/commands/server/debug.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/server/delete.ts b/src/commands/server/delete.ts index 5f1e8aa2e..a8c682ce8 100644 --- a/src/commands/server/delete.ts +++ b/src/commands/server/delete.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/server/deploy.ts b/src/commands/server/deploy.ts index fd447ecf1..32802679c 100644 --- a/src/commands/server/deploy.ts +++ b/src/commands/server/deploy.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -29,447 +29,447 @@ import { PlatformTasks } from '../../tasks/platforms/platform' import { askForChectlUpdateIfNeeded, getCommandSuccessMessage, getEmbeddedTemplatesDirectory, getProjectName, isKubernetesPlatformFamily, isOpenshiftPlatformFamily, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Deploy extends Command { - static description = 'Deploy Eclipse Che server' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - chenamespace: cheNamespace, - batch, - 'listr-renderer': listrRenderer, - 'deployment-name': cheDeployment, - cheimage: string({ - char: 'i', - description: 'Eclipse Che server container image', - env: 'CHE_CONTAINER_IMAGE', - }), - templates: string({ - char: 't', - description: 'Path to the templates folder', - env: 'CHE_TEMPLATES_FOLDER', - exclusive: [DEPLOY_VERSION_KEY], - }), - 'devfile-registry-url': string({ - description: 'The URL of the external Devfile registry.', - env: 'CHE_WORKSPACE_DEVFILE__REGISTRY__URL', - }), - 'plugin-registry-url': string({ - description: 'The URL of the external plugin registry.', - env: 'CHE_WORKSPACE_PLUGIN__REGISTRY__URL', - }), - cheboottimeout: string({ - char: 'o', - description: 'Eclipse Che server bootstrap timeout (in milliseconds)', - default: '40000', - required: true, - env: 'CHE_SERVER_BOOT_TIMEOUT', - }), - [K8SPODWAITTIMEOUT_KEY]: k8sPodWaitTimeout, - [K8SPODREADYTIMEOUT_KEY]: k8sPodReadyTimeout, - [K8SPODDOWNLOADIMAGETIMEOUT_KEY]: k8sPodDownloadImageTimeout, - [K8SPODERRORRECHECKTIMEOUT_KEY]: k8sPodErrorRecheckTimeout, - [LOG_DIRECTORY_KEY]: logsDirectory, - multiuser: flags.boolean({ - char: 'm', - description: 'Starts Eclipse Che in multi-user mode', - default: false, - }), - tls: flags.boolean({ - char: 's', - description: `Deprecated. Enable TLS encryption. - Note, this option is turned on by default. - To provide own certificate for Kubernetes infrastructure, 'che-tls' secret with TLS certificate must be pre-created in the configured namespace. - In case of providing own self-signed certificate 'self-signed-certificate' secret should be also created. - For OpenShift, router will use default cluster certificates. - Please see the docs how to deploy Eclipse Che on different infrastructures: ${DOCS_LINK_INSTALL_RUNNING_CHE_LOCALLY}`, - hidden: true, - }), - 'self-signed-cert': flags.boolean({ - description: 'Deprecated. The flag is ignored. Self signed certificates usage is autodetected now.', - default: false, - hidden: true, - }), - platform: string({ - char: 'p', - description: 'Type of Kubernetes platform. Valid values are \"minikube\", \"minishift\", \"k8s (for kubernetes)\", \"openshift\", \"crc (for CodeReady Containers)\", \"microk8s\".', - options: ['minikube', 'minishift', 'k8s', 'openshift', 'microk8s', 'docker-desktop', 'crc'], - }), - installer: string({ - char: 'a', - description: 'Installer type. If not set, default is "olm" for OpenShift 4.x platform otherwise "operator".', - options: ['helm', 'operator', 'olm'], - }), - domain: string({ - char: 'b', - description: `Domain of the Kubernetes cluster (e.g. example.k8s-cluster.com or .nip.io) - This flag makes sense only for Kubernetes family infrastructures and will be autodetected for Minikube and MicroK8s in most cases. - However, for Kubernetes cluster it is required to specify. - Please note, that just setting this flag will not likely work out of the box. - According changes should be done in Kubernetes cluster configuration as well. - In case of Openshift, domain adjustment should be done on the cluster configuration level.`, - default: '', - }), - debug: boolean({ - description: 'Enables the debug mode for Eclipse Che server. To debug Eclipse Che server from localhost use \'server:debug\' command.', - default: false, - }), - 'che-operator-image': string({ - description: 'Container image of the operator. This parameter is used only when the installer is the operator', - }), - [CHE_OPERATOR_CR_YAML_KEY]: cheOperatorCRYaml, - [CHE_OPERATOR_CR_PATCH_YAML_KEY]: cheOperatorCRPatchYaml, - 'helm-patch-yaml': string({ - description: `Path to yaml file with Helm Chart values patch. - The file format is identical to values.yaml from the chart. - Note, Provided command line arguments take precedence over patch file.`, - default: '', - }), - 'workspace-pvc-storage-class-name': string({ - description: 'persistent volume(s) storage class name to use to store Eclipse Che workspaces data', - env: 'CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME', - default: '', - }), - 'postgres-pvc-storage-class-name': string({ - description: 'persistent volume storage class name to use to store Eclipse Che postgres database', - default: '', - }), - 'skip-version-check': flags.boolean({ - description: 'Skip minimal versions check.', - default: false, - }), - '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, - }), - '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 this flag is enabled. - This parameter is used only when the installer is 'olm'.`, - allowNo: true, - exclusive: ['starting-csv'], - }), - 'starting-csv': flags.string({ - 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'.`, - }), - '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'.`, - }), - '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'.`, - }), - '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'.`, - }), - 'catalog-source-name': string({ - description: `OLM catalog source to install Eclipse Che operator. - This parameter is used only when the installer is the 'olm'.`, - }), - 'catalog-source-namespace': string({ - description: `Namespace for OLM catalog source to install Eclipse Che operator. - This parameter is used only when the installer is the 'olm'.`, - }), - 'cluster-monitoring': boolean({ - default: false, - hidden: true, - description: `Enable cluster monitoring to scrape Eclipse Che metrics in Prometheus. - This parameter is used only when the platform is 'openshift'.`, - }), - 'olm-suggested-namespace': boolean({ - default: true, - allowNo: true, - description: `Indicate to deploy Eclipse Che in OLM suggested namespace: '${DEFAULT_OLM_SUGGESTED_NAMESPACE}'. - Flag 'chenamespace' is ignored in this case - This parameter is used only when the installer is 'olm'.`, - }), - 'skip-kubernetes-health-check': skipK8sHealthCheck, - 'workspace-engine': string({ - description: 'Workspace Engine. If not set, default is "che-server". "dev-workspace" is experimental.', - options: ['che-server', 'dev-workspace'], - default: 'che-server', - }), - 'dev-workspace-controller-namespace': devWorkspaceControllerNamespace, - telemetry: CHE_TELEMETRY, - [DEPLOY_VERSION_KEY]: cheDeployVersion, - } - - async setPlaformDefaults(flags: any, ctx: any): Promise { - flags.tls = await this.checkTlsMode(ctx) - if (flags['self-signed-cert']) { - this.warn('"self-signed-cert" flag is deprecated and has no effect. Autodetection is used instead.') - } - - if (!flags.installer) { - await this.setDefaultInstaller(flags, ctx) - cli.info(`โ€บ Installer type is set to: '${flags.installer}'`) - } - - if (flags.installer === 'olm' && flags['olm-suggested-namespace']) { - flags.chenamespace = DEFAULT_OLM_SUGGESTED_NAMESPACE - cli.info(` โ•olm-suggested-namespace flag is turned on. Eclipse Che will be deployed in namespace: ${DEFAULT_OLM_SUGGESTED_NAMESPACE}.`) - } - - if (!ctx.isChectl && flags.version) { - // Flavors of chectl should not use upstream repositories, so version flag is not applicable - this.error(`${getProjectName()} does not support '--version' flag.`) - } - if (!flags.templates && !flags.version) { - // Use build-in templates if no custom templates nor version to deploy specified. - // All flavors should use embedded templates if not custom templates is given. - flags.templates = getEmbeddedTemplatesDirectory() - } - } - - /** - * 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 checkTlsMode(ctx: any): Promise { - const crPatch = ctx.crPatch - if (crPatch && crPatch.spec && crPatch.spec.server && crPatch.spec.server.tlsSupport === false) { - return false - } - - const customCR = ctx.customCR - if (customCR && customCR.spec && customCR.spec.server && customCR.spec.server.tlsSupport === false) { - return false - } - - return true - } - - private isDevWorkspaceEnabled(ctx: any): boolean { - const crPatch = ctx.crPatch - if (crPatch && crPatch.spec && crPatch.spec.devWorkspace && crPatch.spec.devWorkspace.enable) { - return true - } - - const customCR = ctx.customCR - if (customCR && customCR.spec && customCR.spec.devWorkspace && customCR.spec.devWorkspace.enable) { - return true - } - - return false - } - - private checkCompatibility(flags: any) { - if (flags.installer === 'operator' && flags[CHE_OPERATOR_CR_YAML_KEY]) { - const ignoredFlags = [] - flags['plugin-registry-url'] && ignoredFlags.push('--plugin-registry-url') - flags['devfile-registry-url'] && ignoredFlags.push('--devfile-registry-url') - flags['postgres-pvc-storage-class-name'] && ignoredFlags.push('--postgres-pvc-storage-class-name') - flags['workspace-pvc-storage-class-name'] && ignoredFlags.push('--workspace-pvc-storage-class-name') - flags.tls && ignoredFlags.push('--tls') - flags.cheimage && ignoredFlags.push('--cheimage') - flags.debug && ignoredFlags.push('--debug') - flags.domain && ignoredFlags.push('--domain') - flags.multiuser && ignoredFlags.push('--multiuser') - - if (ignoredFlags.length) { - this.warn(`--${CHE_OPERATOR_CR_YAML_KEY} is used. The following flag(s) will be ignored: ${ignoredFlags.join('\t')}`) - } - } - - if (flags.domain && !flags[CHE_OPERATOR_CR_YAML_KEY] && isOpenshiftPlatformFamily(flags.platform)) { - this.warn('"--domain" flag is ignored for Openshift family infrastructures. It should be done on the cluster level.') - } - - if (flags.installer === 'helm') { - if (!isKubernetesPlatformFamily(flags.platform) && flags.platform !== 'docker-desktop') { - this.error(`๐Ÿ›‘ Current platform is ${flags.platform}. Helm installer is only available on top of Kubernetes flavor platform (including Minikube, Docker Desktop).`) - } - } - - if (flags.installer === 'olm') { - // OLM installer only checks - if (flags.platform === 'minishift') { - this.error(`๐Ÿ›‘ The specified installer ${flags.installer} does not support Minishift`) - } - - if (flags['catalog-source-name'] && flags['catalog-source-yaml']) { - this.error('should be provided only one argument: "catalog-source-name" or "catalog-source-yaml"') - } - if (flags.version) { - if (flags['starting-csv']) { - this.error('"starting-csv" and "version" flags are mutually exclusive. Please specify only one of them.') - } - if (flags['olm-channel']) { - this.error('"starting-csv" and "version" flags are mutually exclusive. Use "starting-csv" with "olm-channel" flag.') - } - if (flags['auto-update']) { - this.error('enabled "auto-update" flag cannot be used with version flag. Deploy latest version instead.') - } - } - - if (!flags['package-manifest-name'] && flags['catalog-source-yaml']) { - this.error('you need to define "package-manifest-name" flag to use "catalog-source-yaml".') - } - if (!flags['olm-channel'] && flags['catalog-source-yaml']) { - this.error('you need to define "olm-channel" flag to use "catalog-source-yaml".') - } - } else { - // Not OLM installer - if (flags['starting-csv']) { - this.error('"starting-csv" flag should be used only with "olm" installer.') - } - if (flags['catalog-source-yaml']) { - this.error('"catalog-source-yaml" flag should be used only with "olm" installer.') - } - if (flags['olm-channel']) { - this.error('"olm-channel" flag should be used only with "olm" installer.') - } - if (flags['package-manifest-name']) { - this.error('"package-manifest-name" flag should be used only with "olm" installer.') - } - if (flags['catalog-source-name']) { - this.error('"catalog-source-name" flag should be used only with "olm" installer.') - } - if (flags['catalog-source-namespace']) { - this.error('"package-manifest-name" flag should be used only with "olm" installer.') - } - if (flags['cluster-monitoring'] && flags.platform !== 'openshift') { - this.error('"cluster-monitoring" flag should be used only with "olm" installer and "openshift" platform.') - } - } - - if (flags.version) { - // Check minimal allowed version to install - let minAllowedVersion: string - switch (flags.installer) { - case 'olm': - minAllowedVersion = MIN_OLM_INSTALLER_VERSION - break - case 'operator': - minAllowedVersion = MIN_CHE_OPERATOR_INSTALLER_VERSION - break - case 'helm': - minAllowedVersion = MIN_HELM_INSTALLER_VERSION - break - default: - // Should never happen - minAllowedVersion = 'latest' - } - - let isVersionAllowed = false - try { - isVersionAllowed = semver.gte(flags.version, minAllowedVersion) - } catch (error) { - // not to fail unexpectedly - cli.debug(`Failed to compare versions '${flags.version}' and '${minAllowedVersion}': ${error}`) - } - - if (!isVersionAllowed) { - throw new Error(`This chectl version can deploy version ${minAllowedVersion} and higher. If you need to deploy ${flags.version} or lower, download the corresponding legacy chectl version.`) - } - } - } - - async run() { - const { flags } = this.parse(Deploy) - flags.chenamespace = flags.chenamespace || DEFAULT_CHE_NAMESPACE - const ctx = await ChectlContext.initAndGet(flags, this) - - if (!flags.batch && ctx.isChectl) { - await askForChectlUpdateIfNeeded() - } - - await this.setPlaformDefaults(flags, ctx) - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Deploy.id, flags }) - - const cheTasks = new CheTasks(flags) - const platformTasks = new PlatformTasks() - const installerTasks = new InstallerTasks() - const apiTasks = new ApiTasks() - const devWorkspaceTasks = new DevWorkspaceTasks(flags) - - // Platform Checks - const platformCheckTasks = new Listr(platformTasks.preflightCheckTasks(flags, this), ctx.listrOptions) - - // Checks if Eclipse Che is already deployed - const preInstallTasks = new Listr(undefined, ctx.listrOptions) - preInstallTasks.add(apiTasks.testApiTasks(flags)) - preInstallTasks.add({ - title: '๐Ÿ‘€ Looking for an already existing Eclipse Che instance', - task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags)), - }) - preInstallTasks.add(checkChectlAndCheVersionCompatibility(flags)) - preInstallTasks.add(downloadTemplates(flags)) - preInstallTasks.add({ - title: '๐Ÿงช DevWorkspace engine (experimental / technology preview) ๐Ÿšจ', - enabled: () => (this.isDevWorkspaceEnabled(ctx) || flags['workspace-engine'] === 'dev-workspace') && !ctx.isOpenShift, - task: () => new Listr(devWorkspaceTasks.getInstallTasks(flags)), - }) - const installTasks = new Listr(installerTasks.installTasks(flags, this), ctx.listrOptions) - - // Post Install Checks - const postInstallTasks = new Listr([ - { - title: 'โœ… Post installation checklist', - task: () => new Listr(cheTasks.waitDeployedChe()), - }, - getRetrieveKeycloakCredentialsTask(flags), - retrieveCheCaCertificateTask(flags), - ...cheTasks.preparePostInstallationOutput(flags), - getPrintHighlightedMessagesTask(), - ], ctx.listrOptions) - - const logsTasks = new Listr([{ - title: 'Following Eclipse Che logs', - task: () => new Listr(cheTasks.serverLogsTasks(flags, true)), - }], ctx.listrOptions) - - try { - await preInstallTasks.run(ctx) - - if (ctx.isCheDeployed) { - let message = 'Eclipse Che has been already deployed.' - if (!ctx.isCheReady) { - message += ' Use server:start command to start a stopped Eclipse Che instance.' - } - cli.warn(message) - } else { - this.checkCompatibility(flags) - await platformCheckTasks.run(ctx) - await logsTasks.run(ctx) - await installTasks.run(ctx) - await postInstallTasks.run(ctx) - this.log(getCommandSuccessMessage()) - } - } catch (err) { - this.error(wrapCommandError(err)) - } - - notifyCommandCompletedSuccessfully() - this.exit(0) - } - - /** - * Sets default installer which is `olm` for OpenShift 4 with stable version of chectl - * and `operator` for other cases. - */ - async setDefaultInstaller(flags: any, _ctx: any): Promise { - const kubeHelper = new KubeHelper(flags) - - const isOlmPreinstalled = await kubeHelper.isPreInstalledOLM() - if ((flags['catalog-source-name'] || flags['catalog-source-yaml']) && isOlmPreinstalled) { - flags.installer = 'olm' - return - } - - if (flags.platform === 'openshift' && await kubeHelper.isOpenShift4() && isOlmPreinstalled) { - flags.installer = 'olm' - } else { - flags.installer = 'operator' - } - } + static description = 'Deploy Eclipse Che server' + + static flags: flags.Input = { + help: flags.help({ char: 'h' }), + chenamespace: cheNamespace, + batch, + 'listr-renderer': listrRenderer, + 'deployment-name': cheDeployment, + cheimage: string({ + char: 'i', + description: 'Eclipse Che server container image', + env: 'CHE_CONTAINER_IMAGE', + }), + templates: string({ + char: 't', + description: 'Path to the templates folder', + env: 'CHE_TEMPLATES_FOLDER', + exclusive: [DEPLOY_VERSION_KEY], + }), + 'devfile-registry-url': string({ + description: 'The URL of the external Devfile registry.', + env: 'CHE_WORKSPACE_DEVFILE__REGISTRY__URL', + }), + 'plugin-registry-url': string({ + description: 'The URL of the external plugin registry.', + env: 'CHE_WORKSPACE_PLUGIN__REGISTRY__URL', + }), + cheboottimeout: string({ + char: 'o', + description: 'Eclipse Che server bootstrap timeout (in milliseconds)', + default: '40000', + required: true, + env: 'CHE_SERVER_BOOT_TIMEOUT', + }), + [K8SPODWAITTIMEOUT_KEY]: k8sPodWaitTimeout, + [K8SPODREADYTIMEOUT_KEY]: k8sPodReadyTimeout, + [K8SPODDOWNLOADIMAGETIMEOUT_KEY]: k8sPodDownloadImageTimeout, + [K8SPODERRORRECHECKTIMEOUT_KEY]: k8sPodErrorRecheckTimeout, + [LOG_DIRECTORY_KEY]: logsDirectory, + multiuser: flags.boolean({ + char: 'm', + description: 'Starts Eclipse Che in multi-user mode', + default: false, + }), + tls: flags.boolean({ + char: 's', + description: `Deprecated. Enable TLS encryption. + Note, this option is turned on by default. + To provide own certificate for Kubernetes infrastructure, 'che-tls' secret with TLS certificate must be pre-created in the configured namespace. + In case of providing own self-signed certificate 'self-signed-certificate' secret should be also created. + For OpenShift, router will use default cluster certificates. + Please see the docs how to deploy Eclipse Che on different infrastructures: ${DOCS_LINK_INSTALL_RUNNING_CHE_LOCALLY}`, + hidden: true, + }), + 'self-signed-cert': flags.boolean({ + description: 'Deprecated. The flag is ignored. Self signed certificates usage is autodetected now.', + default: false, + hidden: true, + }), + platform: string({ + char: 'p', + description: 'Type of Kubernetes platform. Valid values are \"minikube\", \"minishift\", \"k8s (for kubernetes)\", \"openshift\", \"crc (for CodeReady Containers)\", \"microk8s\".', + options: ['minikube', 'minishift', 'k8s', 'openshift', 'microk8s', 'docker-desktop', 'crc'], + }), + installer: string({ + char: 'a', + description: 'Installer type. If not set, default is "olm" for OpenShift 4.x platform otherwise "operator".', + options: ['helm', 'operator', 'olm'], + }), + domain: string({ + char: 'b', + description: `Domain of the Kubernetes cluster (e.g. example.k8s-cluster.com or .nip.io) + This flag makes sense only for Kubernetes family infrastructures and will be autodetected for Minikube and MicroK8s in most cases. + However, for Kubernetes cluster it is required to specify. + Please note, that just setting this flag will not likely work out of the box. + According changes should be done in Kubernetes cluster configuration as well. + In case of Openshift, domain adjustment should be done on the cluster configuration level.`, + default: '', + }), + debug: boolean({ + description: 'Enables the debug mode for Eclipse Che server. To debug Eclipse Che server from localhost use \'server:debug\' command.', + default: false, + }), + 'che-operator-image': string({ + description: 'Container image of the operator. This parameter is used only when the installer is the operator', + }), + [CHE_OPERATOR_CR_YAML_KEY]: cheOperatorCRYaml, + [CHE_OPERATOR_CR_PATCH_YAML_KEY]: cheOperatorCRPatchYaml, + 'helm-patch-yaml': string({ + description: `Path to yaml file with Helm Chart values patch. + The file format is identical to values.yaml from the chart. + Note, Provided command line arguments take precedence over patch file.`, + default: '', + }), + 'workspace-pvc-storage-class-name': string({ + description: 'persistent volume(s) storage class name to use to store Eclipse Che workspaces data', + env: 'CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME', + default: '', + }), + 'postgres-pvc-storage-class-name': string({ + description: 'persistent volume storage class name to use to store Eclipse Che postgres database', + default: '', + }), + 'skip-version-check': flags.boolean({ + description: 'Skip minimal versions check.', + default: false, + }), + '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, + }), + '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 this flag is enabled. + This parameter is used only when the installer is 'olm'.`, + allowNo: true, + exclusive: ['starting-csv'], + }), + 'starting-csv': flags.string({ + 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'.`, + }), + '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'.`, + }), + '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'.`, + }), + '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'.`, + }), + 'catalog-source-name': string({ + description: `OLM catalog source to install Eclipse Che operator. + This parameter is used only when the installer is the 'olm'.`, + }), + 'catalog-source-namespace': string({ + description: `Namespace for OLM catalog source to install Eclipse Che operator. + This parameter is used only when the installer is the 'olm'.`, + }), + 'cluster-monitoring': boolean({ + default: false, + hidden: true, + description: `Enable cluster monitoring to scrape Eclipse Che metrics in Prometheus. + This parameter is used only when the platform is 'openshift'.`, + }), + 'olm-suggested-namespace': boolean({ + default: true, + allowNo: true, + description: `Indicate to deploy Eclipse Che in OLM suggested namespace: '${DEFAULT_OLM_SUGGESTED_NAMESPACE}'. + Flag 'chenamespace' is ignored in this case + This parameter is used only when the installer is 'olm'.`, + }), + 'skip-kubernetes-health-check': skipK8sHealthCheck, + 'workspace-engine': string({ + description: 'Workspace Engine. If not set, default is "che-server". "dev-workspace" is experimental.', + options: ['che-server', 'dev-workspace'], + default: 'che-server', + }), + 'dev-workspace-controller-namespace': devWorkspaceControllerNamespace, + telemetry: CHE_TELEMETRY, + [DEPLOY_VERSION_KEY]: cheDeployVersion, + } + + async setPlaformDefaults(flags: any, ctx: any): Promise { + flags.tls = await this.checkTlsMode(ctx) + if (flags['self-signed-cert']) { + this.warn('"self-signed-cert" flag is deprecated and has no effect. Autodetection is used instead.') + } + + if (!flags.installer) { + await this.setDefaultInstaller(flags, ctx) + cli.info(`โ€บ Installer type is set to: '${flags.installer}'`) + } + + if (flags.installer === 'olm' && flags['olm-suggested-namespace']) { + flags.chenamespace = DEFAULT_OLM_SUGGESTED_NAMESPACE + cli.info(` โ•olm-suggested-namespace flag is turned on. Eclipse Che will be deployed in namespace: ${DEFAULT_OLM_SUGGESTED_NAMESPACE}.`) + } + + if (!ctx.isChectl && flags.version) { + // Flavors of chectl should not use upstream repositories, so version flag is not applicable + this.error(`${getProjectName()} does not support '--version' flag.`) + } + if (!flags.templates && !flags.version) { + // Use build-in templates if no custom templates nor version to deploy specified. + // All flavors should use embedded templates if not custom templates is given. + flags.templates = getEmbeddedTemplatesDirectory() + } + } + + /** + * 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 checkTlsMode(ctx: any): Promise { + const crPatch = ctx.crPatch + if (crPatch && crPatch.spec && crPatch.spec.server && crPatch.spec.server.tlsSupport === false) { + return false + } + + const customCR = ctx.customCR + if (customCR && customCR.spec && customCR.spec.server && customCR.spec.server.tlsSupport === false) { + return false + } + + return true + } + + private isDevWorkspaceEnabled(ctx: any): boolean { + const crPatch = ctx.crPatch + if (crPatch && crPatch.spec && crPatch.spec.devWorkspace && crPatch.spec.devWorkspace.enable) { + return true + } + + const customCR = ctx.customCR + if (customCR && customCR.spec && customCR.spec.devWorkspace && customCR.spec.devWorkspace.enable) { + return true + } + + return false + } + + private checkCompatibility(flags: any) { + if (flags.installer === 'operator' && flags[CHE_OPERATOR_CR_YAML_KEY]) { + const ignoredFlags = [] + flags['plugin-registry-url'] && ignoredFlags.push('--plugin-registry-url') + flags['devfile-registry-url'] && ignoredFlags.push('--devfile-registry-url') + flags['postgres-pvc-storage-class-name'] && ignoredFlags.push('--postgres-pvc-storage-class-name') + flags['workspace-pvc-storage-class-name'] && ignoredFlags.push('--workspace-pvc-storage-class-name') + flags.tls && ignoredFlags.push('--tls') + flags.cheimage && ignoredFlags.push('--cheimage') + flags.debug && ignoredFlags.push('--debug') + flags.domain && ignoredFlags.push('--domain') + flags.multiuser && ignoredFlags.push('--multiuser') + + if (ignoredFlags.length) { + this.warn(`--${CHE_OPERATOR_CR_YAML_KEY} is used. The following flag(s) will be ignored: ${ignoredFlags.join('\t')}`) + } + } + + if (flags.domain && !flags[CHE_OPERATOR_CR_YAML_KEY] && isOpenshiftPlatformFamily(flags.platform)) { + this.warn('"--domain" flag is ignored for Openshift family infrastructures. It should be done on the cluster level.') + } + + if (flags.installer === 'helm') { + if (!isKubernetesPlatformFamily(flags.platform) && flags.platform !== 'docker-desktop') { + this.error(`๐Ÿ›‘ Current platform is ${flags.platform}. Helm installer is only available on top of Kubernetes flavor platform (including Minikube, Docker Desktop).`) + } + } + + if (flags.installer === 'olm') { + // OLM installer only checks + if (flags.platform === 'minishift') { + this.error(`๐Ÿ›‘ The specified installer ${flags.installer} does not support Minishift`) + } + + if (flags['catalog-source-name'] && flags['catalog-source-yaml']) { + this.error('should be provided only one argument: "catalog-source-name" or "catalog-source-yaml"') + } + if (flags.version) { + if (flags['starting-csv']) { + this.error('"starting-csv" and "version" flags are mutually exclusive. Please specify only one of them.') + } + if (flags['olm-channel']) { + this.error('"starting-csv" and "version" flags are mutually exclusive. Use "starting-csv" with "olm-channel" flag.') + } + if (flags['auto-update']) { + this.error('enabled "auto-update" flag cannot be used with version flag. Deploy latest version instead.') + } + } + + if (!flags['package-manifest-name'] && flags['catalog-source-yaml']) { + this.error('you need to define "package-manifest-name" flag to use "catalog-source-yaml".') + } + if (!flags['olm-channel'] && flags['catalog-source-yaml']) { + this.error('you need to define "olm-channel" flag to use "catalog-source-yaml".') + } + } else { + // Not OLM installer + if (flags['starting-csv']) { + this.error('"starting-csv" flag should be used only with "olm" installer.') + } + if (flags['catalog-source-yaml']) { + this.error('"catalog-source-yaml" flag should be used only with "olm" installer.') + } + if (flags['olm-channel']) { + this.error('"olm-channel" flag should be used only with "olm" installer.') + } + if (flags['package-manifest-name']) { + this.error('"package-manifest-name" flag should be used only with "olm" installer.') + } + if (flags['catalog-source-name']) { + this.error('"catalog-source-name" flag should be used only with "olm" installer.') + } + if (flags['catalog-source-namespace']) { + this.error('"package-manifest-name" flag should be used only with "olm" installer.') + } + if (flags['cluster-monitoring'] && flags.platform !== 'openshift') { + this.error('"cluster-monitoring" flag should be used only with "olm" installer and "openshift" platform.') + } + } + + if (flags.version) { + // Check minimal allowed version to install + let minAllowedVersion: string + switch (flags.installer) { + case 'olm': + minAllowedVersion = MIN_OLM_INSTALLER_VERSION + break + case 'operator': + minAllowedVersion = MIN_CHE_OPERATOR_INSTALLER_VERSION + break + case 'helm': + minAllowedVersion = MIN_HELM_INSTALLER_VERSION + break + default: + // Should never happen + minAllowedVersion = 'latest' + } + + let isVersionAllowed = false + try { + isVersionAllowed = semver.gte(flags.version, minAllowedVersion) + } catch (error) { + // not to fail unexpectedly + cli.debug(`Failed to compare versions '${flags.version}' and '${minAllowedVersion}': ${error}`) + } + + if (!isVersionAllowed) { + throw new Error(`This chectl version can deploy version ${minAllowedVersion} and higher. If you need to deploy ${flags.version} or lower, download the corresponding legacy chectl version.`) + } + } + } + + async run() { + const { flags } = this.parse(Deploy) + flags.chenamespace = flags.chenamespace || DEFAULT_CHE_NAMESPACE + const ctx = await ChectlContext.initAndGet(flags, this) + + if (!flags.batch && ctx.isChectl) { + await askForChectlUpdateIfNeeded() + } + + await this.setPlaformDefaults(flags, ctx) + await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Deploy.id, flags }) + + const cheTasks = new CheTasks(flags) + const platformTasks = new PlatformTasks() + const installerTasks = new InstallerTasks() + const apiTasks = new ApiTasks() + const devWorkspaceTasks = new DevWorkspaceTasks(flags) + + // Platform Checks + const platformCheckTasks = new Listr(platformTasks.preflightCheckTasks(flags, this), ctx.listrOptions) + + // Checks if Eclipse Che is already deployed + const preInstallTasks = new Listr(undefined, ctx.listrOptions) + preInstallTasks.add(apiTasks.testApiTasks(flags)) + preInstallTasks.add({ + title: '๐Ÿ‘€ Looking for an already existing Eclipse Che instance', + task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags)), + }) + preInstallTasks.add(checkChectlAndCheVersionCompatibility(flags)) + preInstallTasks.add(downloadTemplates(flags)) + preInstallTasks.add({ + title: '๐Ÿงช DevWorkspace engine (experimental / technology preview) ๐Ÿšจ', + enabled: () => (this.isDevWorkspaceEnabled(ctx) || flags['workspace-engine'] === 'dev-workspace') && !ctx.isOpenShift, + task: () => new Listr(devWorkspaceTasks.getInstallTasks(flags)), + }) + const installTasks = new Listr(installerTasks.installTasks(flags, this), ctx.listrOptions) + + // Post Install Checks + const postInstallTasks = new Listr([ + { + title: 'โœ… Post installation checklist', + task: () => new Listr(cheTasks.waitDeployedChe()), + }, + getRetrieveKeycloakCredentialsTask(flags), + retrieveCheCaCertificateTask(flags), + ...cheTasks.preparePostInstallationOutput(flags), + getPrintHighlightedMessagesTask(), + ], ctx.listrOptions) + + const logsTasks = new Listr([{ + title: 'Following Eclipse Che logs', + task: () => new Listr(cheTasks.serverLogsTasks(flags, true)), + }], ctx.listrOptions) + + try { + await preInstallTasks.run(ctx) + + if (ctx.isCheDeployed) { + let message = 'Eclipse Che has been already deployed.' + if (!ctx.isCheReady) { + message += ' Use server:start command to start a stopped Eclipse Che instance.' + } + cli.warn(message) + } else { + this.checkCompatibility(flags) + await platformCheckTasks.run(ctx) + await logsTasks.run(ctx) + await installTasks.run(ctx) + await postInstallTasks.run(ctx) + this.log(getCommandSuccessMessage()) + } + } catch (err) { + this.error(wrapCommandError(err)) + } + + notifyCommandCompletedSuccessfully() + this.exit(0) + } + + /** + * Sets default installer which is `olm` for OpenShift 4 with stable version of chectl + * and `operator` for other cases. + */ + async setDefaultInstaller(flags: any, _ctx: any): Promise { + const kubeHelper = new KubeHelper(flags) + + const isOlmPreinstalled = await kubeHelper.isPreInstalledOLM() + if ((flags['catalog-source-name'] || flags['catalog-source-yaml']) && isOlmPreinstalled) { + flags.installer = 'olm' + return + } + + if (flags.platform === 'openshift' && await kubeHelper.isOpenShift4() && isOlmPreinstalled) { + flags.installer = 'olm' + } else { + flags.installer = 'operator' + } + } } diff --git a/src/commands/server/logs.ts b/src/commands/server/logs.ts index c88296916..1af299979 100644 --- a/src/commands/server/logs.ts +++ b/src/commands/server/logs.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index c9c697d58..83ae7b095 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -21,65 +21,65 @@ import { ApiTasks } from '../../tasks/platforms/api' import { findWorkingNamespace, getCommandSuccessMessage, notifyCommandCompletedSuccessfully, wrapCommandError } from '../../util' export default class Start extends Command { - static description = 'Start Eclipse Che server' + static description = 'Start Eclipse Che server' - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - chenamespace: cheNamespace, - 'listr-renderer': listrRenderer, - 'deployment-name': cheDeployment, - [K8SPODWAITTIMEOUT_KEY]: k8sPodWaitTimeout, - [K8SPODREADYTIMEOUT_KEY]: k8sPodReadyTimeout, - [K8SPODDOWNLOADIMAGETIMEOUT_KEY]: k8sPodDownloadImageTimeout, - [K8SPODERRORRECHECKTIMEOUT_KEY]: k8sPodErrorRecheckTimeout, - [LOG_DIRECTORY_KEY]: logsDirectory, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - } + static flags: flags.Input = { + help: flags.help({ char: 'h' }), + chenamespace: cheNamespace, + 'listr-renderer': listrRenderer, + 'deployment-name': cheDeployment, + [K8SPODWAITTIMEOUT_KEY]: k8sPodWaitTimeout, + [K8SPODREADYTIMEOUT_KEY]: k8sPodReadyTimeout, + [K8SPODDOWNLOADIMAGETIMEOUT_KEY]: k8sPodDownloadImageTimeout, + [K8SPODERRORRECHECKTIMEOUT_KEY]: k8sPodErrorRecheckTimeout, + [LOG_DIRECTORY_KEY]: logsDirectory, + 'skip-kubernetes-health-check': skipKubeHealthzCheck, + } - async run() { - const { flags } = this.parse(Start) - flags.chenamespace = await findWorkingNamespace(flags) - const ctx = await ChectlContext.initAndGet(flags, this) + async run() { + const { flags } = this.parse(Start) + flags.chenamespace = await findWorkingNamespace(flags) + const ctx = await ChectlContext.initAndGet(flags, this) - const cheTasks = new CheTasks(flags) - const apiTasks = new ApiTasks() + const cheTasks = new CheTasks(flags) + const apiTasks = new ApiTasks() - // Checks if Eclipse Che is already deployed - const preInstallTasks = new Listr([ - apiTasks.testApiTasks(flags), - { - title: '๐Ÿ‘€ Looking for an already existing Eclipse Che instance', - task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags)), - }, - ], ctx.listrOptions) + // Checks if Eclipse Che is already deployed + const preInstallTasks = new Listr([ + apiTasks.testApiTasks(flags), + { + title: '๐Ÿ‘€ Looking for an already existing Eclipse Che instance', + task: () => new Listr(cheTasks.checkIfCheIsInstalledTasks(flags)), + }, + ], ctx.listrOptions) - const logsTasks = new Listr([{ - title: 'Following Eclipse Che logs', - task: () => new Listr(cheTasks.serverLogsTasks(flags, true)), - }], ctx.listrOptions) + const logsTasks = new Listr([{ + title: 'Following Eclipse Che logs', + task: () => new Listr(cheTasks.serverLogsTasks(flags, true)), + }], ctx.listrOptions) - const startCheTasks = new Listr([{ - title: 'Starting Eclipse Che', - task: () => new Listr(cheTasks.scaleCheUpTasks()), - }], ctx.listrOptions) + const startCheTasks = new Listr([{ + title: 'Starting Eclipse Che', + task: () => new Listr(cheTasks.scaleCheUpTasks()), + }], ctx.listrOptions) - try { - await preInstallTasks.run(ctx) + try { + await preInstallTasks.run(ctx) - if (!ctx.isCheDeployed) { - cli.warn('Eclipse Che has not been deployed yet. Use server:deploy command to deploy a new Eclipse Che instance.') - } else if (ctx.isCheReady) { - cli.info('Eclipse Che has been already started.') - } else { - await logsTasks.run(ctx) - await startCheTasks.run(ctx) - this.log(getCommandSuccessMessage()) - } - } catch (err) { - this.error(wrapCommandError(err)) - } + if (!ctx.isCheDeployed) { + cli.warn('Eclipse Che has not been deployed yet. Use server:deploy command to deploy a new Eclipse Che instance.') + } else if (ctx.isCheReady) { + cli.info('Eclipse Che has been already started.') + } else { + await logsTasks.run(ctx) + await startCheTasks.run(ctx) + this.log(getCommandSuccessMessage()) + } + } catch (err) { + this.error(wrapCommandError(err)) + } - notifyCommandCompletedSuccessfully() - this.exit(0) - } + notifyCommandCompletedSuccessfully() + this.exit(0) + } } diff --git a/src/commands/server/status.ts b/src/commands/server/status.ts index eb80e9594..152d7c234 100644 --- a/src/commands/server/status.ts +++ b/src/commands/server/status.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/server/stop.ts b/src/commands/server/stop.ts index f1d1e6f73..53f2d081a 100644 --- a/src/commands/server/stop.ts +++ b/src/commands/server/stop.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/server/update.ts b/src/commands/server/update.ts index 3e0568673..7a7a296f2 100644 --- a/src/commands/server/update.ts +++ b/src/commands/server/update.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -252,9 +252,10 @@ export default class Update extends Command { // Despite the operator image is the same, CR patch might contain some changes. cli.info('Patching existing Eclipse Che installation.') return true + } else { + cli.info('Eclipse Che is already up to date.') + return false } - cli.info('Eclipse Che is already up to date.') - return false } if (this.isUpgrade(ctx.deployedCheOperatorImageTag, ctx.newCheOperatorImageTag)) { diff --git a/src/commands/workspace/create.ts b/src/commands/workspace/create.ts index 05bb071a3..a9f3ffb32 100644 --- a/src/commands/workspace/create.ts +++ b/src/commands/workspace/create.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/workspace/delete.ts b/src/commands/workspace/delete.ts index f8c5c023c..f45b9c4f9 100644 --- a/src/commands/workspace/delete.ts +++ b/src/commands/workspace/delete.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/workspace/inject.ts b/src/commands/workspace/inject.ts index 4e5efaafc..c4d275b02 100644 --- a/src/commands/workspace/inject.ts +++ b/src/commands/workspace/inject.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -147,7 +147,9 @@ export default class Inject extends Command { const { exitCode } = await execa(`${this.command} exec ${pod} -n ${namespace} -c ${container} -- tar --version `, { timeout: 10000, reject: false, shell: true }) if (exitCode === 0) { return true - } return false + } else { + return false + } } /** @@ -211,13 +213,16 @@ export default class Inject extends Command { await execa(this.command, ['config', configPathFlag, kubeConfigPath, 'use-context', contextToInject.name], { timeout: 10000 }) await execa(this.command, ['cp', kubeConfigPath, `${namespace}/${workspacePod}:${containerHomeDir}.kube/config`, '-c', container], { timeout: 10000 }) + return } private async fileExists(namespace: string, pod: string, container: string, file: string): Promise { const { exitCode } = await execa(`${this.command} exec ${pod} -n ${namespace} -c ${container} -- test -e ${file}`, { timeout: 10000, reject: false, shell: true }) if (exitCode === 0) { return true - } return false + } else { + return false + } } private async containerExists(namespace: string, pod: string, container: string): Promise { diff --git a/src/commands/workspace/list.ts b/src/commands/workspace/list.ts index 996ac492b..a1a4ca146 100644 --- a/src/commands/workspace/list.ts +++ b/src/commands/workspace/list.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/workspace/logs.ts b/src/commands/workspace/logs.ts index 6b5be2895..b10e01456 100644 --- a/src/commands/workspace/logs.ts +++ b/src/commands/workspace/logs.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/workspace/start.ts b/src/commands/workspace/start.ts index 3550f7eb7..0cb521528 100644 --- a/src/commands/workspace/start.ts +++ b/src/commands/workspace/start.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/commands/workspace/stop.ts b/src/commands/workspace/stop.ts index 5fdda649e..31dfe0741 100644 --- a/src/commands/workspace/stop.ts +++ b/src/commands/workspace/stop.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/common-flags.ts b/src/common-flags.ts index 47ebd8474..f169ff93c 100644 --- a/src/common-flags.ts +++ b/src/common-flags.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/constants.ts b/src/constants.ts index 1f69cad4e..0d352a7e8 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/hooks/analytics/analytics.ts b/src/hooks/analytics/analytics.ts index 362384090..867f2317d 100644 --- a/src/hooks/analytics/analytics.ts +++ b/src/hooks/analytics/analytics.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/hooks/analytics/segment-adapter.ts b/src/hooks/analytics/segment-adapter.ts index 65d3656e0..20a48fdb5 100644 --- a/src/hooks/analytics/segment-adapter.ts +++ b/src/hooks/analytics/segment-adapter.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/hooks/prerun/new-version-warning.ts b/src/hooks/prerun/new-version-warning.ts index 299e4351f..774bf8754 100644 --- a/src/hooks/prerun/new-version-warning.ts +++ b/src/hooks/prerun/new-version-warning.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/index.ts b/src/index.ts index 71556b35c..7aa48bea8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/che.ts b/src/tasks/che.ts index 52e5a664d..7e4743ccb 100644 --- a/src/tasks/che.ts +++ b/src/tasks/che.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/component-installers/cert-manager.ts b/src/tasks/component-installers/cert-manager.ts index 432e529c4..859adcf0c 100644 --- a/src/tasks/component-installers/cert-manager.ts +++ b/src/tasks/component-installers/cert-manager.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/component-installers/devfile-workspace-operator-installer.ts b/src/tasks/component-installers/devfile-workspace-operator-installer.ts index 933720ac9..ccb00c9c3 100644 --- a/src/tasks/component-installers/devfile-workspace-operator-installer.ts +++ b/src/tasks/component-installers/devfile-workspace-operator-installer.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index 6a63c9625..d8d404eca 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/installers/helm.ts b/src/tasks/installers/helm.ts index c888c9354..f2e3e9f2d 100644 --- a/src/tasks/installers/helm.ts +++ b/src/tasks/installers/helm.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -238,21 +238,27 @@ export class HelmTasks { const { exitCode } = await execa('kubectl', ['get', 'clusterrolebinding', `${cheNamespace}-che-clusterrole-binding`], { timeout: execTimeout, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async removeClusterRoleBinding(cheNamespace: string, execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['delete', 'clusterrolebinding', `${cheNamespace}-che-clusterrole-binding`], { timeout: execTimeout, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async tillerRoleBindingExist(execTimeout = 30000): Promise { const { exitCode } = await execa('kubectl', ['get', 'clusterrolebinding', 'add-on-cluster-admin'], { timeout: execTimeout, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async createTillerRoleBinding(execTimeout = 30000) { @@ -263,7 +269,9 @@ export class HelmTasks { const { exitCode } = await execa('kubectl', ['get', 'serviceaccounts', 'tiller', '--namespace', 'kube-system'], { timeout: execTimeout, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async createTillerServiceAccount(execTimeout = 120000) { @@ -281,7 +289,9 @@ export class HelmTasks { const { exitCode } = await execa('kubectl', ['get', 'services', 'tiller-deploy', '-n', 'kube-system'], { timeout: execTimeout, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async getVersion(execTimeout = 10000): Promise { diff --git a/src/tasks/installers/installer.ts b/src/tasks/installers/installer.ts index 298519c9f..97100d22e 100644 --- a/src/tasks/installers/installer.ts +++ b/src/tasks/installers/installer.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index d89f4980b..95c9f7bc6 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -430,8 +430,9 @@ export class OLMTasks { if (csv && csv.metadata.annotations) { const CRRaw = csv.metadata.annotations!['alm-examples'] return (yaml.safeLoad(CRRaw) as Array)[0] + } else { + throw new Error(`Unable to retrieve Che cluster CR definition from CSV: ${currentCSV}`) } - throw new Error(`Unable to retrieve Che cluster CR definition from CSV: ${currentCSV}`) } private getOlmNamespaceLabels(flags: any): any { diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index 5d583fb27..f742ca4db 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/kube.ts b/src/tasks/kube.ts index 50d63380c..c899d3efc 100644 --- a/src/tasks/kube.ts +++ b/src/tasks/kube.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/platforms/api.ts b/src/tasks/platforms/api.ts index 181898f08..2cb0d8045 100644 --- a/src/tasks/platforms/api.ts +++ b/src/tasks/platforms/api.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -17,10 +17,10 @@ import { newError } from '../../util' 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): Listr.ListrTask { const kube = new KubeHelper(flags) return { diff --git a/src/tasks/platforms/common-platform-tasks.ts b/src/tasks/platforms/common-platform-tasks.ts index d6a122e83..a71f21987 100644 --- a/src/tasks/platforms/common-platform-tasks.ts +++ b/src/tasks/platforms/common-platform-tasks.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/platforms/crc.ts b/src/tasks/platforms/crc.ts index 57076dd70..612220ae6 100644 --- a/src/tasks/platforms/crc.ts +++ b/src/tasks/platforms/crc.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -74,8 +74,9 @@ export class CRCHelper { stdout.includes('CRC VM: Running') && stdout.includes('OpenShift: Running')) { return true + } else { + return false } - return false } async getCRCIP(): Promise { diff --git a/src/tasks/platforms/docker-desktop.ts b/src/tasks/platforms/docker-desktop.ts index 4163bd14e..17d4af15a 100644 --- a/src/tasks/platforms/docker-desktop.ts +++ b/src/tasks/platforms/docker-desktop.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/platforms/k8s.ts b/src/tasks/platforms/k8s.ts index dd731567f..c1a6c051c 100644 --- a/src/tasks/platforms/k8s.ts +++ b/src/tasks/platforms/k8s.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/platforms/microk8s.ts b/src/tasks/platforms/microk8s.ts index 2ce059de3..afbac03ab 100644 --- a/src/tasks/platforms/microk8s.ts +++ b/src/tasks/platforms/microk8s.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -106,7 +106,9 @@ export class MicroK8sTasks { const { exitCode } = await execa('microk8s.status', { timeout: 10000, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async startMicroK8s() { diff --git a/src/tasks/platforms/minikube.ts b/src/tasks/platforms/minikube.ts index 7f62984bb..d357f5440 100644 --- a/src/tasks/platforms/minikube.ts +++ b/src/tasks/platforms/minikube.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -135,7 +135,9 @@ export class MinikubeTasks { const { exitCode } = await execa('minikube', ['status'], { timeout: 10000, reject: false }) if (exitCode === 0) { return true - } return false + } else { + return false + } } async startMinikube() { @@ -149,7 +151,6 @@ export class MinikubeTasks { // grab json const json = JSON.parse(stdout) return json.ingress && json.ingress.Status === 'enabled' - // eslint-disable-next-line no-else-return } else { // probably with old minikube, let's try with classic output const { stdout } = await execa('minikube', ['addons', 'list'], { timeout: 10000 }) diff --git a/src/tasks/platforms/minishift.ts b/src/tasks/platforms/minishift.ts index 71b171493..643703473 100644 --- a/src/tasks/platforms/minishift.ts +++ b/src/tasks/platforms/minishift.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -65,8 +65,9 @@ export class MinishiftTasks { stdout.includes('Minishift: Running') && stdout.includes('OpenShift: Running')) { return true + } else { + return false } - return false } async getMinishiftIP(): Promise { diff --git a/src/tasks/platforms/openshift.ts b/src/tasks/platforms/openshift.ts index 0b2231585..0bf98fcdc 100644 --- a/src/tasks/platforms/openshift.ts +++ b/src/tasks/platforms/openshift.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/tasks/platforms/platform.ts b/src/tasks/platforms/platform.ts index 6819f4c8c..e0419b002 100644 --- a/src/tasks/platforms/platform.ts +++ b/src/tasks/platforms/platform.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ diff --git a/src/util.ts b/src/util.ts index 34f462bea..8e0f37e4c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Red Hat, Inc. + * Copyright (c) 2019-2021 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/ @@ -333,7 +333,8 @@ export async function getDistribution(): Promise { const platorm = await promisify(getos)() as getos.LinuxOs return platorm.dist } catch { - + return } } + return } From 4e3ff30ea6888d3dcb52436e463d587e6611ad1e Mon Sep 17 00:00:00 2001 From: Flavius Lacatusu Date: Tue, 8 Jun 2021 18:34:31 +0200 Subject: [PATCH 4/5] fixes Signed-off-by: Flavius Lacatusu --- .eslintrc.js | 1 - package.json | 2 +- yarn.lock | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2515de07c..18f09c1ed 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,4 @@ /** - * * Copyright (c) 2019-2021 Red Hat, Inc. * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 diff --git a/package.json b/package.json index edd6f0b91..defac8665 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ }, "repository": "che-incubator/chectl", "scripts": { - "postinstall": "yarn lint && npm run -s postinstall-repositories && npm run -s postinstall-helm && npm run -s postinstall-cert-manager && npm run -s postinstall-operator && npm run -s postinstall-dev-workspace && npm run -s postinstall-cleanup", + "postinstall": "npm run -s postinstall-repositories && npm run -s postinstall-helm && npm run -s postinstall-cert-manager && npm run -s postinstall-operator && npm run -s postinstall-dev-workspace && npm run -s postinstall-cleanup", "postinstall-helm": "rimraf templates/kubernetes && cpx 'node_modules/eclipse-che-server/deploy/kubernetes/**' 'templates/kubernetes'", "postinstall-cert-manager": "rimraf templates/cert-manager && cpx 'node_modules/eclipse-che-server/deploy/cert-manager/**' 'templates/cert-manager'", "postinstall-dev-workspace": "rimraf templates/devworkspace && cpx 'node_modules/eclipse-che-devfile-workspace-operator/deploy/**' 'templates/devworkspace'", diff --git a/yarn.lock b/yarn.lock index 857466625..deb1ce8d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2538,7 +2538,7 @@ ecc-jsbn@~0.1.1: "eclipse-che-operator@git://github.com/eclipse-che/che-operator#main": version "0.0.0" - resolved "git://github.com/eclipse-che/che-operator#cad643a6e5af2f85b9eebe8e5e7dbc5af50a0853" + resolved "git://github.com/eclipse-che/che-operator#8b7f59890c7f4b30d9ccaf711b157e7cb3e01983" "eclipse-che-server@git://github.com/eclipse-che/che-server#main": version "0.0.0" From 7902123af62afdf1848baf875f2b40bb66a6ccd0 Mon Sep 17 00:00:00 2001 From: Flavius Lacatusu Date: Tue, 8 Jun 2021 18:43:54 +0200 Subject: [PATCH 5/5] add lint to checks Signed-off-by: Flavius Lacatusu --- .github/workflows/unit-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index b6bab6c70..83c14861a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -24,5 +24,7 @@ jobs: node-version: '12' - name: Install chectl dependencies run: yarn + - name: Run eslint + run: yarn lint - name: Run unit tests run: yarn test