diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3a9abb89f..b86723d48 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -27,19 +27,6 @@ }, "group": "build" }, - { - "label": "[Minikube] Install Che with custom che-operator and che-server images", - "command": "./bin/run server:deploy -n che -m -p minikube --che-operator-image=${IMAGE_REGISTRY_HOST}/${IMAGE_REGISTRY_USER_NAME}/che-operator:next --cheimage=${IMAGE_REGISTRY_HOST}/${IMAGE_REGISTRY_USER_NAME}/che-server:next", - "type": "shell", - "args": [], - "problemMatcher": [ - "$tsc" - ], - "presentation": { - "reveal": "always" - }, - "group": "build" - }, { "label": "[Openshift] Install Che", "command": "./bin/run server:deploy -n che -m -p openshift", diff --git a/README.md b/README.md index 49f8ce070..8b2f6dbf4 100644 --- a/README.md +++ b/README.md @@ -80,12 +80,6 @@ USAGE ``` # Commands -* [`chectl auth:delete CHE-API-ENDPOINT`](#chectl-authdelete-che-api-endpoint) -* [`chectl auth:get`](#chectl-authget) -* [`chectl auth:list`](#chectl-authlist) -* [`chectl auth:login [CHE-API-ENDPOINT]`](#chectl-authlogin-che-api-endpoint) -* [`chectl auth:logout`](#chectl-authlogout) -* [`chectl auth:use [CHE-API-ENDPOINT]`](#chectl-authuse-che-api-endpoint) * [`chectl autocomplete [SHELL]`](#chectl-autocomplete-shell) * [`chectl cacert:export`](#chectl-cacertexport) * [`chectl dashboard:open`](#chectl-dashboardopen) @@ -101,159 +95,6 @@ USAGE * [`chectl server:stop`](#chectl-serverstop) * [`chectl server:update`](#chectl-serverupdate) * [`chectl update [CHANNEL]`](#chectl-update-channel) -* [`chectl workspace:create`](#chectl-workspacecreate) -* [`chectl workspace:delete WORKSPACE`](#chectl-workspacedelete-workspace) -* [`chectl workspace:inject`](#chectl-workspaceinject) -* [`chectl workspace:list`](#chectl-workspacelist) -* [`chectl workspace:logs`](#chectl-workspacelogs) -* [`chectl workspace:start WORKSPACE`](#chectl-workspacestart-workspace) -* [`chectl workspace:stop WORKSPACE`](#chectl-workspacestop-workspace) - -## `chectl auth:delete CHE-API-ENDPOINT` - -Delete specified login session(s) - -``` -USAGE - $ chectl auth:delete CHE-API-ENDPOINT - -ARGUMENTS - CHE-API-ENDPOINT Eclipse Che server API endpoint - -OPTIONS - -h, --help show CLI help - -u, --username=username Eclipse Che username - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry - -EXAMPLES - # Delete login session of the specified user on the cluster: - chectl auth:delete che-che.apps-crc.testing/api -u username - - - # Delete all login sessions on the cluster: - chectl auth:delete che-che.apps-crc.testing -``` - -_See code: [src/commands/auth/delete.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/auth/delete.ts)_ - -## `chectl auth:get` - -Display active login session - -``` -USAGE - $ chectl auth:get - -OPTIONS - -h, --help show CLI help - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/auth/get.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/auth/get.ts)_ - -## `chectl auth:list` - -Show all existing login sessions - -``` -USAGE - $ chectl auth:list - -OPTIONS - -h, --help show CLI help - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/auth/list.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/auth/list.ts)_ - -## `chectl auth:login [CHE-API-ENDPOINT]` - -Log in to Eclipse Che server - -``` -USAGE - $ chectl auth:login [CHE-API-ENDPOINT] - -ARGUMENTS - CHE-API-ENDPOINT Eclipse Che server API endpoint - -OPTIONS - -h, --help show CLI help - -n, --chenamespace=chenamespace Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - -p, --password=password Eclipse Che user password - -t, --refresh-token=refresh-token Keycloak refresh token - -u, --username=username Eclipse Che username - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry - -EXAMPLES - # Log in with username and password (when OpenShift OAuth is not enabled): - chectl auth:login https://che-che.apps-crc.testing/api -u username -p password - - - # Log in with username and password (password will be asked interactively): - chectl auth:login che-che.apps-crc.testing -u username - - - # Log in with token (when OpenShift OAuth is enabled): - chectl auth:login che.openshift.io -t token - - - # Log in with oc token (when logged into an OpenShift cluster with oc and OpenShift OAuth is enabled): - chectl auth:login che.my.server.net -``` - -_See code: [src/commands/auth/login.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/auth/login.ts)_ - -## `chectl auth:logout` - -Log out of the active login session - -``` -USAGE - $ chectl auth:logout - -OPTIONS - -h, --help show CLI help - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/auth/logout.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/auth/logout.ts)_ - -## `chectl auth:use [CHE-API-ENDPOINT]` - -Set active login session - -``` -USAGE - $ chectl auth:use [CHE-API-ENDPOINT] - -ARGUMENTS - CHE-API-ENDPOINT Eclipse Che server API endpoint - -OPTIONS - -h, --help show CLI help - -i, --interactive Select an active login session in interactive mode - -u, --username=username Eclipse Che username - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry - -EXAMPLES - # Set an active login session for the specified user on the given cluster: - chectl auth:use che-che.apps-crc.testing/api -u username - - - # Switch to another user on the same cluster: - chectl auth:use -u another-user-on-this-server - - - # Switch to the only user on the given cluster: - chectl auth:use my.cluster.net - - - # Select an active login session in interactive mode: - chectl auth:use -i -``` - -_See code: [src/commands/auth/use.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/auth/use.ts)_ ## `chectl autocomplete [SHELL]` @@ -289,12 +130,9 @@ USAGE OPTIONS -d, --destination=destination Destination where to store Che self-signed CA certificate. - If the destination is a file (might not exist), then the certificate will be saved there in PEM - format. - If the destination is a directory, then cheCA.crt 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 cheCA.crt. + If the destination is a file (might not exist), then the certificate will be saved there in PEM format. + If the destination is a directory, then cheCA.crt 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 cheCA.crt. -h, --help show CLI help @@ -342,7 +180,7 @@ OPTIONS --all see all commands in CLI ``` -_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.3/src/commands/help.ts)_ +_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.14/src/commands/help.ts)_ ## `chectl server:backup` @@ -455,12 +293,12 @@ OPTIONS -b, --domain=domain 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. + 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. -d, --directory=directory Directory to store logs into @@ -478,7 +316,7 @@ OPTIONS (required) [default: 40000] Eclipse Che server bootstrap timeout (in milliseconds) -p, --platform=minikube|minishift|k8s|openshift|microk8s|docker-desktop|crc - Type of Kubernetes platform. Valid values are "minikube", "minishift", "k8s (for kubernetes)", "openshift", "crc + Type of Kubernetes platform. Valid values are "minikube", "minishift", "k8s (for kubernetes)", "openshift", "crc (for CodeReady Containers)", "microk8s". -t, --templates=templates @@ -489,40 +327,40 @@ OPTIONS --[no-]auto-update Auto update approval strategy for installation Eclipse Che. - With this strategy will be provided auto-update Eclipse Che without any human interaction. - By default this flag is enabled. - This parameter is used only when the installer is 'olm'. + 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'. --batch Batch mode. Running a command without end user interaction. --catalog-source-name=catalog-source-name 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=catalog-source-namespace 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'. --catalog-source-yaml=catalog-source-yaml Path to a yaml file that describes custom catalog source for installation Eclipse Che operator. - Catalog source will be applied to the namespace with Che operator. - Also you need define 'olm-channel' name and 'package-manifest-name'. - This parameter is used only when the installer is the 'olm'. + 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'. --che-operator-cr-patch-yaml=che-operator-cr-patch-yaml - Path to a yaml file that overrides the default values in CheCluster CR used by the operator. This parameter is used + Path to a yaml file that overrides the default values in CheCluster CR used by the operator. This parameter is used only when the installer is the 'operator' or the 'olm'. --che-operator-cr-yaml=che-operator-cr-yaml - Path to a yaml file that defines a CheCluster used by the operator. This parameter is used only when the installer + 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'. --che-operator-image=che-operator-image Container image of the operator. This parameter is used only when the installer is the operator or OLM. --debug - Enables the debug mode for Eclipse Che server. To debug Eclipse Che server from localhost use 'server:debug' + Enables the debug mode for Eclipse Che server. To debug Eclipse Che server from localhost use 'server:debug' command. --deployment-name=deployment-name @@ -545,17 +383,17 @@ OPTIONS --olm-channel=olm-channel Olm channel to install Eclipse Che, f.e. stable. - If options was not set, will be used default version for package manifest. - This parameter is used only when the installer is the 'olm'. + If options was not set, will be used default version for package manifest. + This parameter is used only when the installer is the 'olm'. --[no-]olm-suggested-namespace Indicate to deploy Eclipse Che in OLM suggested namespace: 'eclipse-che'. - Flag 'chenamespace' is ignored in this case - This parameter is used only when the installer is 'olm'. + Flag 'chenamespace' is ignored in this case + This parameter is used only when the installer is 'olm'. --package-manifest-name=package-manifest-name Package manifest name to subscribe to Eclipse Che OLM package manifest. - This parameter is used only when the installer is the 'olm'. + This parameter is used only when the installer is the 'olm'. --plugin-registry-url=plugin-registry-url The URL of the external plugin registry. @@ -574,12 +412,12 @@ OPTIONS --starting-csv=starting-csv Starting cluster service version(CSV) for installation Eclipse Che. - Flags uses to set up start installation version Che. - For example: 'starting-csv' provided with value 'eclipse-che.v7.10.0' for stable channel. - Then OLM will install Eclipse Che with version 7.10.0. - Notice: this flag will be ignored with 'auto-update' flag. OLM with auto-update mode installs - the latest known version. - This parameter is used only when the installer is 'olm'. + 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'. --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry @@ -737,30 +575,22 @@ USAGE $ chectl server:stop OPTIONS - -h, --help - show CLI help - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' + -h, --help show CLI help + -n, --chenamespace=chenamespace Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. + --access-token=access-token Eclipse Che OIDC Access Token. See the documentation how to obtain token: + https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#o + btaining-the-token-from-keycloak_authenticating-to-the-che-server and https://www.e + clipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the- + token-from-openshift-token-through-keycloak_authenticating-to-the-che-server. - --che-selector=che-selector - [default: app=che,component=che] Selector for Eclipse Che server resources + --che-selector=che-selector [default: app=che,component=che] Selector for Eclipse Che server resources - --deployment-name=deployment-name - [default: che] Eclipse Che deployment name + --deployment-name=deployment-name [default: che] Eclipse Che deployment name - --skip-kubernetes-health-check - Skip Kubernetes health check + --skip-kubernetes-health-check Skip Kubernetes health check - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry + --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry ``` _See code: [src/commands/server/stop.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/server/stop.ts)_ @@ -823,277 +653,6 @@ OPTIONS ``` _See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v1.5.0/src/commands/update.ts)_ - -## `chectl workspace:create` - -Creates a workspace from a devfile - -``` -USAGE - $ chectl workspace:create - -OPTIONS - -d, --debug - 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. - - -f, --devfile=devfile - Path or URL to a valid devfile - - -h, --help - show CLI help - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - - -s, --start - Starts the workspace after creation - - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. - - --che-api-endpoint=che-api-endpoint - Eclipse Che server API endpoint - - --name=name - Workspace name: overrides the workspace name to use instead of the one defined in the devfile. - - --skip-kubernetes-health-check - Skip Kubernetes health check - - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/create.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/create.ts)_ - -## `chectl workspace:delete WORKSPACE` - -Delete a stopped workspace - use workspace:stop to stop the workspace before deleting it - -``` -USAGE - $ chectl workspace:delete WORKSPACE - -ARGUMENTS - WORKSPACE The workspace id to delete - -OPTIONS - -h, --help - show CLI help - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. - - --che-api-endpoint=che-api-endpoint - Eclipse Che server API endpoint - - --delete-namespace - Indicates that a Kubernetes namespace where workspace was created will be deleted as well - - --skip-kubernetes-health-check - Skip Kubernetes health check - - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/delete.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/delete.ts)_ - -## `chectl workspace:inject` - -Inject configurations and tokens in a workspace - -``` -USAGE - $ chectl workspace:inject - -OPTIONS - -c, --container=container - The container name. If not specified, configuration files will be injected in all containers of the workspace pod - - -h, --help - show CLI help - - -k, --kubeconfig - (required) Inject the local Kubernetes configuration - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - - -w, --workspace=workspace - 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. - - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. - - --che-api-endpoint=che-api-endpoint - Eclipse Che server API endpoint - - --kube-context=kube-context - Kubeconfig context to inject - - --skip-kubernetes-health-check - Skip Kubernetes health check - - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/inject.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/inject.ts)_ - -## `chectl workspace:list` - -List workspaces - -``` -USAGE - $ chectl workspace:list - -OPTIONS - -h, --help - show CLI help - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. - - --che-api-endpoint=che-api-endpoint - Eclipse Che server API endpoint - - --skip-kubernetes-health-check - Skip Kubernetes health check - - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/list.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/list.ts)_ - -## `chectl workspace:logs` - -Collect workspace(s) logs - -``` -USAGE - $ chectl workspace:logs - -OPTIONS - -d, --directory=directory Directory to store logs into - -h, --help show CLI help - - -n, --namespace=namespace (required) The namespace where workspace is located. Can be found in workspace - configuration 'attributes.infrastructureNamespace' field. - - -w, --workspace=workspace (required) Target workspace id. Can be found in workspace configuration 'id' field. - - --follow Indicate if logs should be streamed - - --skip-kubernetes-health-check Skip Kubernetes health check - - --telemetry=on|off Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/logs.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/logs.ts)_ - -## `chectl workspace:start WORKSPACE` - -Starts a workspace - -``` -USAGE - $ chectl workspace:start WORKSPACE - -ARGUMENTS - WORKSPACE The workspace id to start - -OPTIONS - -d, --debug - Debug workspace start. It is useful when workspace start fails and it is needed to print more logs on startup. - - -h, --help - show CLI help - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. - - --che-api-endpoint=che-api-endpoint - Eclipse Che server API endpoint - - --skip-kubernetes-health-check - Skip Kubernetes health check - - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/start.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/start.ts)_ - -## `chectl workspace:stop WORKSPACE` - -Stop a running workspace - -``` -USAGE - $ chectl workspace:stop WORKSPACE - -ARGUMENTS - WORKSPACE The workspace id to stop - -OPTIONS - -h, --help - show CLI help - - -n, --chenamespace=chenamespace - Eclipse Che Kubernetes namespace. Default to 'eclipse-che' - - --access-token=access-token - Eclipse Che OIDC Access Token. See the documentation how to obtain token: - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-keycloak_ - authenticating-to-the-che-server and - https://www.eclipse.org/che/docs/che-7/administration-guide/authenticating-users/#obtaining-the-token-from-openshift - -token-through-keycloak_authenticating-to-the-che-server. - - --che-api-endpoint=che-api-endpoint - Eclipse Che server API endpoint - - --skip-kubernetes-health-check - Skip Kubernetes health check - - --telemetry=on|off - Enable or disable telemetry. This flag skips a prompt and enable/disable telemetry -``` - -_See code: [src/commands/workspace/stop.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/workspace/stop.ts)_ diff --git a/package.json b/package.json index c91a665a1..cf8b58b5a 100644 --- a/package.json +++ b/package.json @@ -24,13 +24,11 @@ "countries-and-timezones": "^3.2.3", "debug": "^4.3.2", "eclipse-che-operator": "git://github.com/eclipse-che/che-operator#main", - "esprima": "^4.0.1", "execa": "^5.1.1", "fancy-test": "^1.4.9", "fs-extra": "^10.0.0", "getos": "^3.2.1", "gnirts": "^1.1.7", - "inquirer": "^8.2.0", "js-yaml": "^4.0.2", "listr": "^0.14.3", "listr-verbose-renderer": "^0.6.0", @@ -39,10 +37,8 @@ "node-forge": "^0.10.0", "node-notifier": "^10.0.0", "os-locale": "^5.0.0", - "querystring": "^0.2.1", "rimraf": "^3.0.2", "semver": "^7.3.4", - "stream-buffers": "^3.0.2", "tslib": "^2", "unzipper": "0.10.11", "uuid": "^8.3.2" @@ -73,13 +69,11 @@ "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", "chai": "^4.3.4", - "cpx": "^1.5.0", "eslint": "^7.32.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", "jest": "^26.6.3", "nock": "^13.2.1", "ts-jest": "^26.5.6", @@ -157,7 +151,6 @@ "test": "jest --collect-coverage", "test-watch": "jest --watchAll", "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": "yarn lint && rm -rf lib && rm -rf tsconfig.tsbuildinfo && tsc -b && oclif-dev manifest && oclif-dev readme && yarn gnirts-ci", diff --git a/src/api/che-api-client.ts b/src/api/che-api-client.ts index 0d0303942..51430dfad 100644 --- a/src/api/che-api-client.ts +++ b/src/api/che-api-client.ts @@ -10,9 +10,7 @@ * Red Hat, Inc. - initial API and implementation */ -import { che as chetypes } from '@eclipse-che/api' import axios, { AxiosInstance } from 'axios' -import { cli } from 'cli-ux' import * as https from 'https' import { newError, sleep } from '../util' @@ -106,237 +104,6 @@ export class CheApiClient { 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}` : '') diff --git a/src/api/che-login-manager.ts b/src/api/che-login-manager.ts deleted file mode 100644 index 2ce4c620b..000000000 --- a/src/api/che-login-manager.ts +++ /dev/null @@ -1,579 +0,0 @@ -/** - * 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 - */ - -import axios, { AxiosInstance } from 'axios' -import * as fs from 'fs-extra' -import * as https from 'https' -import * as path from 'path' -import * as querystring from 'querystring' - -import { ACCESS_TOKEN_KEY } from '../common-flags' -import { findWorkingNamespace } from '../util' - -import { CheHelper } from './che' -import { CheApiClient } from './che-api-client' -import { ChectlContext } from './context' -import { KubeHelper } from './kube' - -// Represents login information to use for requests -// Notice: accessToken is undefined for single user mode -export interface LoginData { - cheApiEndpoint: string - accessToken: string | undefined -} - -// Credentials file format -export interface CheServerLoginConfig { - // Defines file format version - version?: string - // Define current login session. Empty if none. - lastLoginUrl?: string - lastUserName?: string - // Registered logins - logins?: Logins -} - -// API URL -> logins into server -export type Logins = { [key: string]: ServerLogins } -// username -> login data -export type ServerLogins = { [key: string]: RefreshTokenLoginRecord } -export type LoginRecord = RefreshTokenLoginRecord | PasswordLoginRecord | OcUserTokenLoginRecord - -export interface RefreshTokenLoginRecord { - refreshToken: string - // Expiration datetime (in seconds) for local timezone - expires: number -} - -export interface OcUserTokenLoginRecord { - subjectToken: string - subjectIssuer: string -} - -export interface PasswordLoginRecord { - username: string - password: string -} - -export function isRefreshTokenLoginData(loginData: LoginRecord): loginData is RefreshTokenLoginRecord { - return Boolean((loginData as RefreshTokenLoginRecord).refreshToken) -} - -export function isOcUserTokenLoginData(loginData: LoginRecord): loginData is OcUserTokenLoginRecord { - return Boolean((loginData as OcUserTokenLoginRecord).subjectToken) -} - -export function isPasswordLoginData(loginData: LoginRecord): loginData is PasswordLoginRecord { - return Boolean((loginData as PasswordLoginRecord).password) -} - -// Response structure from /api/keycloak/settings -interface CheKeycloakSettings { - 'che.keycloak.logout.endpoint': string - 'che.keycloak.jwks.endpoint': string - 'che.keycloak.token.endpoint': string - 'che.keycloak.userinfo.endpoint': string - 'che.keycloak.client_id': string - 'che.keycloak.username_claim': string - 'che.keycloak.js_adapter_url': string - 'che.keycloak.use_nonce': string - - 'che.keycloak.profile.endpoint'?: string - 'che.keycloak.auth_server_url'?: string - 'che.keycloak.password.endpoint'?: string - 'che.keycloak.realm'?: string - - 'che.keycloak.oidc_provider'?: string - 'che.keycloak.github.endpoint'?: string -} - -// Response structure from Keycloak get access token endpoint -interface KeycloakAuthTokenResponse { - access_token: string - expires_in: number | string - refresh_token: string - refresh_expires_in?: number | string - token_type: string - scope?: string -} - -const REQUEST_TIMEOUT_MS = 10000 -const LOGIN_DATA_FILE_NAME = 'che-login-config.json' - -let loginContext: CheServerLoginManager | undefined -/** - * Che server login sessions manager. Singleton. - * Uses refresh tokens for authentication. - * Usually, just using of getLoginData function is suitable. - */ -export class CheServerLoginManager { - private loginData: CheServerLoginConfig - - private apiUrl: string - - private username: string - - private readonly dataFilePath: string - - private readonly axios: AxiosInstance - - private constructor(dataFilePath: string) { - this.dataFilePath = dataFilePath - - this.loginData = {} - this.readLoginData() - this.apiUrl = this.loginData.lastLoginUrl || '' - this.username = this.loginData.lastUserName || '' - - // Remove outdated login records - this.removeExpiredLogins() - - // Make axios ignore untrusted certificate error for self-signed certificate case. - const httpsAgent = new https.Agent({ rejectUnauthorized: false }) - this.axios = axios.create({ - httpsAgent, - }) - } - - /** - * Returns Che server login sessions manager. - */ - static async getInstance(): Promise { - const ctx = ChectlContext.get() - const configDir = ctx[ChectlContext.CONFIG_DIR] - - if (!fs.existsSync(configDir)) { - fs.mkdirsSync(configDir) - } - const dataFilePath = path.join(configDir, LOGIN_DATA_FILE_NAME) - if (loginContext && loginContext.dataFilePath === dataFilePath) { - return loginContext - } - - loginContext = new CheServerLoginManager(dataFilePath) - return loginContext - } - - /** - * Checks whether login credentials exists for given server and user. - * @param apiUrl API URL of the Che server - * @param username username - */ - public hasLoginFor(apiUrl: string, username?: string): boolean { - apiUrl = CheApiClient.normalizeCheApiEndpointUrl(apiUrl) - if (username) { - return Boolean(this.getLoginRecord(apiUrl, username)) - } else { - return Boolean(this.loginData.logins![apiUrl]) - } - } - - public getCurrentLoginInfo(): { cheApiEndpoint: string, username: string } { - return { cheApiEndpoint: this.apiUrl, username: this.username } - } - - public getCurrentServerApiUrl(): string { - return this.apiUrl - } - - public getAllLogins(): Map { - this.removeExpiredLogins() - - const allLogins = new Map() - for (const [apiUrl, serverLogins] of Object.entries(this.loginData.logins!)) { - allLogins.set(apiUrl, [...Object.keys(serverLogins)]) - } - return allLogins - } - - /** - * Logins user in specified instance of Che Server. - * Makes this login data default context. - * If a context with the same data already exists it will be replaced. - * If provided data is invalid, exception will be thrown. - * Returns username of the login. - * @param apiUrl Che server API URL - * @param loginRecord user credentials - */ - public async setLoginContext(apiUrl: string, loginRecord: LoginRecord): Promise { - apiUrl = CheApiClient.normalizeCheApiEndpointUrl(apiUrl) - const cheKeycloakSettings = await this.retrieveKeycloakSettings(apiUrl) - - // Check whether provided login credentials valid and get refresh token. - const keycloakAuthData = await this.keycloakAuth(apiUrl, loginRecord, cheKeycloakSettings) - const now = (Date.now() / 1000) - let refreshTokenExpiresIn: string | number = keycloakAuthData.refresh_expires_in ? keycloakAuthData.refresh_expires_in : keycloakAuthData.expires_in - if (typeof refreshTokenExpiresIn === 'string') { - refreshTokenExpiresIn = parseFloat(refreshTokenExpiresIn) - } - const refreshTokenLoginRecord: RefreshTokenLoginRecord = { - refreshToken: keycloakAuthData.refresh_token, - expires: now + refreshTokenExpiresIn, - } - - const username = isPasswordLoginData(loginRecord) ? loginRecord.username : - await this.getCurrentUserName(cheKeycloakSettings, keycloakAuthData.access_token) - - // Delete outdated logins as config file will be rewritten - this.removeExpiredLogins() - - // Credentials are valid, make them current - this.setCurrentLoginContext(apiUrl, username, refreshTokenLoginRecord) - // Save changes permanently - this.saveLoginData() - return username - } - - /** - * Changes current login. - */ - public async switchLoginContext(apiUrl: string, username: string): Promise { - // Get rid of outdated credentials before trying to switch current login - this.removeExpiredLogins() - - apiUrl = CheApiClient.normalizeCheApiEndpointUrl(apiUrl) - const loginRecord = this.getLoginRecord(apiUrl, username) - if (!loginRecord) { - throw new Error(`User "${username}" is not logged in on "${apiUrl}" server`) - } - - // Ensure the server is reachable and credentials are still valid - const keycloakAuthData = await this.keycloakAuth(apiUrl, loginRecord) - // Update refresh token - loginRecord.refreshToken = keycloakAuthData.refresh_token - - this.setCurrentLoginContext(apiUrl, username, loginRecord) - this.saveLoginData() - } - - /** - * Logouts user from specified Che server. - * If no parameters given current login session will be deleted. - * @param apiUrl Che server API URL - * @param username username on the given server - */ - public deleteLoginContext(apiUrl?: string, username?: string): void { - if (!this.loginData.logins) { - return - } - - if (!apiUrl) { - if (!this.apiUrl) { - // Not logged in - return - } - // Delete current login context - return this.deleteLoginContext(this.apiUrl, this.username) - } - - apiUrl = CheApiClient.normalizeCheApiEndpointUrl(apiUrl) - - if (!username) { - // Delete all logins on the server - delete this.loginData.logins![apiUrl] - } else { - // Delete specific login record if any - const serverLogins = this.loginData.logins[apiUrl] - if (!serverLogins) { - // No logins for specified server - return - } - delete serverLogins[username] - if (Object.keys(serverLogins).length < 1) { - // Delete server without logins - delete this.loginData.logins[apiUrl] - } - } - - if (apiUrl === this.apiUrl) { - // Current login info should be deleted - this.loginData.lastLoginUrl = this.apiUrl = '' - this.loginData.lastUserName = this.username = '' - } - this.removeExpiredLogins() - this.saveLoginData() - } - - private readLoginData(): void { - if (fs.existsSync(this.dataFilePath)) { - this.loginData = JSON.parse(fs.readFileSync(this.dataFilePath).toString()) as CheServerLoginConfig - } else { - this.loginData = {} - } - - if (!this.loginData.logins) { - this.loginData.logins = {} - } - - if (!this.loginData.version) { - // So far there is only one existing file format - this.loginData.version = 'v1' - } - } - - private saveLoginData(): void { - this.loginData.lastLoginUrl = this.apiUrl - this.loginData.lastUserName = this.username - fs.writeFileSync(this.dataFilePath, JSON.stringify(this.loginData)) - } - - /** - * Searches for login data by API URL and user name. - * Returns undefined if nothing found by given keys. - */ - private getLoginRecord(apiUrl: string, username: string): RefreshTokenLoginRecord | undefined { - const serverLogins = this.loginData.logins![apiUrl] - if (!serverLogins) { - return - } - return serverLogins[username] - } - - /** - * Sets current login credentials by given API URL and username. - * If loginRecord is provided, then a new credentials are added, replacing existing if any. - * This method doesn't check credentials validity. - * Returns true if operation was successful. - */ - private setCurrentLoginContext(apiUrl: string, username: string, loginRecord?: RefreshTokenLoginRecord): boolean { - if (!loginRecord) { - // Find existing login context and make current - loginRecord = this.getLoginRecord(apiUrl, username) - if (!loginRecord) { - return false - } - } else { - // Set given login config as current - let serverLogins = this.loginData.logins![apiUrl] - if (!serverLogins) { - serverLogins = {} - this.loginData.logins![apiUrl] = serverLogins - } - serverLogins[username] = loginRecord - } - - this.apiUrl = apiUrl - this.username = username - return true - } - - private removeExpiredLogins(): void { - if (!this.loginData.logins) { - return - } - - const now = Date.now() / 1000 - for (const [apiUrl, serverLogins] of Object.entries(this.loginData.logins)) { - for (const [username, loginRecord] of Object.entries(serverLogins)) { - if (loginRecord.expires <= now) { - // Token is expired, delete it - delete serverLogins[username] - } - } - if (Object.keys(serverLogins).length < 1) { - // Delete server without logins - delete this.loginData.logins[apiUrl] - } - } - - // Check if current login is still present - if (!this.getLoginRecord(this.apiUrl, this.username)) { - this.loginData.lastLoginUrl = this.apiUrl = '' - this.loginData.lastUserName = this.username = '' - } - } - - private async retrieveKeycloakSettings(apiUrl: string): Promise { - const cheApi = CheApiClient.getInstance(apiUrl) - const keycloakSettings = await cheApi.getKeycloakSettings() - if (!keycloakSettings) { - // Single user mode - throw new Error(`Authentication is not supported on the server: "${apiUrl}"`) - } - return keycloakSettings - } - - /** - * Returns new Keycloak access token for current login session. - * Updates session timeout. - */ - public async getNewAccessToken(): Promise { - if (!this.apiUrl || !this.username) { - throw new Error('Login context is not set. Please login first.') - } - - const loginRecord = this.getLoginRecord(this.apiUrl, this.username) - if (!loginRecord) { - // Should never happen - throw new Error('Invalid login state') - } - - const keycloakAuthData = await this.keycloakAuth(this.apiUrl, loginRecord) - // Update refresh token - loginRecord.refreshToken = keycloakAuthData.refresh_token - this.removeExpiredLogins() - this.setCurrentLoginContext(this.apiUrl, this.username, loginRecord) - this.saveLoginData() - - return keycloakAuthData.access_token - } - - private async keycloakAuth(apiUrl: string, loginRecord: LoginRecord, cheKeycloakSettings?: CheKeycloakSettings): Promise { - if (!cheKeycloakSettings) { - cheKeycloakSettings = await this.retrieveKeycloakSettings(apiUrl) - } - 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') - } - } - } - - private async getKeycloakAuthDataByUserNameAndPassword(cheKeycloakSettings: CheKeycloakSettings, username: string, password: string): Promise { - const keycloakTokenUrl = cheKeycloakSettings['che.keycloak.token.endpoint'] - const data = { - client_id: cheKeycloakSettings['che.keycloak.client_id'], - grant_type: 'password', - username, - password, - } - const headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - } - try { - const response = await this.axios.post(keycloakTokenUrl, querystring.stringify(data), { headers, timeout: REQUEST_TIMEOUT_MS }) - if (!response || response.status !== 200 || !response.data) { - throw new Error('E_BAD_RESP_KEYCLOAK') - } - return response.data - } catch (error) { - let message = error.message - if (error && error.response && error.response.data && error.response.data.error_description) { - message = error.response.data.error_description - } - throw new Error(`Failed to get access token from ${keycloakTokenUrl}. Cause: ${message}`) - } - } - - private async getKeycloakAuthDataByRefreshToken(cheKeycloakSettings: CheKeycloakSettings, refreshToken: string): Promise { - const data = { - client_id: cheKeycloakSettings['che.keycloak.client_id'], - grant_type: 'refresh_token', - refresh_token: refreshToken, - } - return this.requestKeycloakAuth(cheKeycloakSettings['che.keycloak.token.endpoint'], data) - } - - private async getKeycloakAuthDataByOcToken(cheKeycloakSettings: CheKeycloakSettings, subjectToken: string, subjectIssuer: string): Promise { - const data = { - client_id: cheKeycloakSettings['che.keycloak.client_id'], - grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', - subject_token: subjectToken, - subject_issuer: subjectIssuer, - } - return this.requestKeycloakAuth(cheKeycloakSettings['che.keycloak.token.endpoint'], data) - } - - private async requestKeycloakAuth(keycloakTokenUrl: string, requestData: any): Promise { - const headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - } - try { - const response = await this.axios.post(keycloakTokenUrl, querystring.stringify(requestData), { headers, timeout: REQUEST_TIMEOUT_MS }) - if (!response || response.status !== 200 || !response.data) { - throw new Error('E_BAD_RESP_KEYCLOAK') - } - return response.data - } catch (error) { - let message = error.message - if (error && error.response && error.response.data && error.response.data.error_description) { - message = error.response.data.error_description - } - throw new Error(`Failed to get the access token from ${keycloakTokenUrl}. Cause: ${message}`) - } - } - - private async getCurrentUserName(cheKeycloakSettings: CheKeycloakSettings, accessToken: string): Promise { - const endpoint = cheKeycloakSettings['che.keycloak.userinfo.endpoint'] - const headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: `bearer ${accessToken}`, - } - try { - const response = await this.axios.get(endpoint, { headers, timeout: REQUEST_TIMEOUT_MS }) - if (!response || response.status !== 200 || !response.data) { - throw new Error('E_BAD_RESP_KEYCLOAK') - } - return response.data.preferred_username - } catch (error) { - throw new Error(`Failed to get userdata from ${endpoint}. Cause: ${error.message}`) - } - } -} - -/** - * Helper function to get valid credentials. Designed to be used from commands. - * @param cheApiEndpoint user provided server API URL if any - * @param accessToken user provied access token if any - */ -export async function getLoginData(cheApiEndpoint: string, accessToken: string | undefined, flags: any): Promise { - if (cheApiEndpoint) { - // User provides credential manually - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - await cheApiClient.checkCheApiEndpointUrl() - if (!accessToken && await cheApiClient.isAuthenticationEnabled()) { - throw new Error(`Parameter "--${ACCESS_TOKEN_KEY}" is expected.`) - } - // Single user mode, proceed without token - } else { - if (accessToken !== undefined) { - throw new Error('Eclipse Che server API endpoint is required. Use \'--che-api-endpoint\' to provide it.') - } - - // Use login manager to get Che API URL and token - const loginManager = await CheServerLoginManager.getInstance() - cheApiEndpoint = loginManager.getCurrentServerApiUrl() - if (!cheApiEndpoint) { - cheApiEndpoint = await getCheApiEndpoint(flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - if (await cheApiClient.isAuthenticationEnabled()) { - throw new Error('There is no active login session. Please use "auth:login" first.') - } else { - return { cheApiEndpoint, accessToken } - } - } - accessToken = await loginManager.getNewAccessToken() - } - return { cheApiEndpoint, accessToken } -} - -/** - * Gets cheApiEndpoint for the given namespace. - */ -export async function getCheApiEndpoint(flags: any): Promise { - const kube = new KubeHelper(flags) - const namespace = await findWorkingNamespace(flags) - if (!await kube.hasReadPermissionsForNamespace(namespace)) { - throw new Error('Please provide server API URL argument') - } - - // Retrieve API URL from routes - const cheHelper = new CheHelper(flags) - return await cheHelper.cheURL(namespace) + '/api' -} diff --git a/src/api/che.ts b/src/api/che.ts index 681752582..4a8346112 100644 --- a/src/api/che.ts +++ b/src/api/che.ts @@ -10,14 +10,10 @@ * Red Hat, Inc. - initial API and implementation */ -import { che as chetypes } from '@eclipse-che/api' -import { CoreV1Api, V1Pod, Watch } from '@kubernetes/client-node' -import axios, { AxiosInstance } from 'axios' +import { V1Pod, Watch } from '@kubernetes/client-node' import * as cp from 'child_process' import * as commandExists from 'command-exists' import * as fs from 'fs-extra' -import * as https from 'https' -import * as yaml from 'js-yaml' import * as nodeforge from 'node-forge' import * as os from 'os' import * as path from 'path' @@ -26,8 +22,6 @@ import * as unzipper from 'unzipper' import { OpenShiftHelper } from '../api/openshift' import { CHE_ROOT_CA_SECRET_NAME, DEFAULT_CHE_OLM_PACKAGE_NAME, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, OPERATOR_TEMPLATE_DIR } from '../constants' import { base64Decode, downloadFile } from '../util' -import { CheApiClient } from './che-api-client' -import { Devfile } from './types/devfile' import { KubeHelper } from './kube' import { OperatorGroup, Subscription } from './types/olm' @@ -35,73 +29,10 @@ export class CheHelper { defaultCheResponseTimeoutMs = 3000 kube: KubeHelper - oc = new OpenShiftHelper() - private readonly axios: AxiosInstance - - constructor(private readonly flags: any) { + constructor(flags: any) { this.kube = new KubeHelper(flags) - - // Make axios ignore untrusted certificate error for self-signed certificate case. - const httpsAgent = new https.Agent({ rejectUnauthorized: false }) - - this.axios = axios.create({ - httpsAgent, - }) - } - - /** - * Finds a pod where workspace is running. - * Rejects if no workspace is found for the given workspace ID - * or if workspace ID wasn't specified but more than one workspace is found. - */ - async getWorkspacePodName(namespace: string, cheWorkspaceId: string): Promise { - const k8sApi = this.kube.kubeConfig.makeApiClient(CoreV1Api) - - const res = await k8sApi.listNamespacedPod(namespace) - const pods = res.body.items - const wsPods = pods.filter(pod => pod.metadata!.labels!['che.workspace_id'] && pod.metadata!.labels!['che.original_name'] !== 'che-jwtproxy') - if (wsPods.length === 0) { - throw new Error('No workspace pod is found') - } - - if (cheWorkspaceId) { - const wsPod = wsPods.find(p => p.metadata!.labels!['che.workspace_id'] === cheWorkspaceId) - if (wsPod) { - return wsPod.metadata!.name! - } - throw new Error('Pod is not found for the given workspace ID') - } else { - if (wsPods.length === 1) { - return wsPods[0].metadata!.name! - } - throw new Error('More than one pod with running workspace is found. Please, specify Workspace ID.') - } - } - - async getWorkspacePodContainers(namespace: string, cheWorkspaceId?: string): Promise { - const k8sApi = this.kube.kubeConfig.makeApiClient(CoreV1Api) - - const res = await k8sApi.listNamespacedPod(namespace) - const pods = res.body.items - const wsPods = pods.filter(pod => pod.metadata!.labels!['che.workspace_id'] && pod.metadata!.labels!['che.original_name'] !== 'che-jwtproxy') - if (wsPods.length === 0) { - throw new Error('No workspace pod is found') - } - - if (cheWorkspaceId) { - const wsPod = wsPods.find(p => p.metadata!.labels!['che.workspace_id'] === cheWorkspaceId) - if (wsPod) { - return wsPod.spec!.containers.map(c => c.name) - } - throw new Error('Pod is not found for the given workspace ID') - } else { - if (wsPods.length === 1) { - return wsPods[0].spec!.containers.map(c => c.name) - } - throw new Error('More than one pod with running workspace is found. Please, specify Workspace ID.') - } } async cheURL(namespace = ''): Promise { @@ -116,24 +47,6 @@ export class CheHelper { } } - async chePluginRegistryURL(namespace = ''): Promise { - // provided through command line ? - if (this.flags['plugin-registry-url']) { - return this.flags['plugin-registry-url'] - } - // check - if (!await this.kube.getNamespace(namespace)) { - throw new Error(`ERR_NAMESPACE_NO_EXIST - No namespace ${namespace} is found`) - } - - // grab URL - if (await this.kube.isOpenShift()) { - return this.chePluginRegistryOpenShiftURL(namespace) - } else { - return this.chePluginRegistryK8sURL(namespace) - } - } - async isSelfSignedCertificateSecretExist(namespace: string): Promise { const selfSignedCertSecret = await this.kube.getSecret(CHE_ROOT_CA_SECRET_NAME, namespace) return Boolean(selfSignedCertSecret) @@ -185,38 +98,6 @@ export class CheHelper { throw new Error(`Secret "${CHE_ROOT_CA_SECRET_NAME}" has invalid format: "ca.crt" key not found in data.`) } - /** - * Retrieves Keycloak admin user credentials. - * Works only with installers which use Che CR (operator, olm). - * Returns credentials as an array of two values: [login, password] - * In case of an error an array with undefined values will be returned. - */ - async retrieveKeycloakAdminCredentials(cheNamespace: string): Promise { - let adminUsername - let adminPassword - - const cheCluster = await this.kube.getCheCluster(cheNamespace) - if (!cheCluster || cheCluster.spec.auth.externalIdentityProvider) { - return [] - } - - const keycloakCredentialsSecretName = cheCluster.spec.auth.identityProviderSecret - if (keycloakCredentialsSecretName) { - // Keycloak credentials are stored in secret - const keycloakCredentialsSecret = await this.kube.getSecret(keycloakCredentialsSecretName, cheNamespace) - if (keycloakCredentialsSecret && keycloakCredentialsSecret.data) { - adminUsername = base64Decode(keycloakCredentialsSecret.data.user) - adminPassword = base64Decode(keycloakCredentialsSecret.data.password) - } - } else { - // Keycloak credentials are stored in Che custom resource - adminUsername = cheCluster.spec.auth.identityProviderAdminUserName - adminPassword = cheCluster.spec.auth.identityProviderPassword - } - - return [adminUsername, adminPassword] - } - async chePluginRegistryK8sURL(namespace = ''): Promise { if (await this.kube.isIngressExist('plugin-registry', namespace)) { const protocol = await this.kube.getIngressProtocol('plugin-registry', namespace) @@ -259,48 +140,10 @@ export class CheHelper { throw new Error(`ERR_ROUTE_NO_EXIST - No route ${route_names} in namespace ${namespace}`) } - async createWorkspaceFromDevfile(cheApiEndpoint: string, devfilePath: string, workspaceName?: string, accessToken?: string): Promise { - let devfile: string | undefined - try { - devfile = await this.parseDevfile(devfilePath) - if (workspaceName) { - const json = yaml.load(devfile) as Devfile - json.metadata.name = workspaceName - devfile = yaml.dump(json) - } - } catch (error) { - if (!devfile) { - throw new Error(`E_NOT_FOUND_DEVFILE - ${devfilePath} - ${error.message}`) - } - } - - const cheApi = CheApiClient.getInstance(cheApiEndpoint) - return cheApi.createWorkspaceFromDevfile(devfile, accessToken) - } - - async parseDevfile(devfilePath = ''): Promise { - if (devfilePath.startsWith('http')) { - const response = await this.axios.get(devfilePath) - return response.data - } else { - return fs.readFileSync(devfilePath, 'utf8') - } - } - async buildDashboardURL(ideURL: string): Promise { return ideURL.replace(/\/[^/|.]*\/[^/|.]*$/g, '\/dashboard\/#\/ide$&') } - /** - * Finds workspace pods and reads logs from it. - */ - async readWorkspacePodLog(namespace: string, workspaceId: string, directory: string, follow: boolean): Promise { - const podLabelSelector = `che.workspace_id=${workspaceId}` - - await this.readPodLog(namespace, podLabelSelector, directory, follow) - await this.readNamespaceEvents(namespace, directory, follow) - } - /** * Reads logs from pods that match a given selector. */ diff --git a/src/api/kube.ts b/src/api/kube.ts index fcc6bded6..5cdf06273 100644 --- a/src/api/kube.ts +++ b/src/api/kube.ts @@ -2103,7 +2103,7 @@ export class KubeHelper { namespace, }, spec: { - targetNamespaces: [namespace], + targetNamespaces: [], }, } diff --git a/src/commands/auth/delete.ts b/src/commands/auth/delete.ts deleted file mode 100644 index 5651616e4..000000000 --- a/src/commands/auth/delete.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheApiClient } from '../../api/che-api-client' -import { CheServerLoginManager } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, username, USERNAME_KEY } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Delete extends Command { - static description = 'Delete specified login session(s)' - - static args = [ - { - name: CHE_API_ENDPOINT_KEY, - description: 'Eclipse Che server API endpoint', - required: true, - }, - ] - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - [USERNAME_KEY]: username, - telemetry: CHE_TELEMETRY, - } - - static examples = [ - '# Delete login session of the specified user on the cluster:\n' + - 'chectl auth:delete che-che.apps-crc.testing/api -u username', - '\n\n# Delete all login sessions on the cluster:\n' + - 'chectl auth:delete che-che.apps-crc.testing', - ] - - async run() { - const { args, flags } = this.parse(Delete) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Delete.id, flags }) - let cheApiEndpoint = CheApiClient.normalizeCheApiEndpointUrl(args[CHE_API_ENDPOINT_KEY]) - const username: string | undefined = flags[USERNAME_KEY] - - const loginManager = await CheServerLoginManager.getInstance() - - if (!loginManager.hasLoginFor(cheApiEndpoint)) { - // Maybe /api suffix isn't provided - const cheApiEndpointGuess = cheApiEndpoint + '/api' - if (!loginManager.hasLoginFor(cheApiEndpointGuess)) { - cli.info(`No registered login sessions on server ${cheApiEndpoint}`) - return - } - cheApiEndpoint = cheApiEndpointGuess - } - - if (username) { - if (!loginManager.hasLoginFor(cheApiEndpoint, username)) { - cli.info(`${username} is not logged in on ${cheApiEndpoint}. Nothing to delete.`) - return - } - } - - loginManager.deleteLoginContext(cheApiEndpoint, username) - if (username) { - cli.info(`Successfully logged out ${username} on ${cheApiEndpoint}`) - } else { - cli.info(`Successfully logged out all users on ${cheApiEndpoint}`) - } - } -} diff --git a/src/commands/auth/get.ts b/src/commands/auth/get.ts deleted file mode 100644 index 0a4baa5ad..000000000 --- a/src/commands/auth/get.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheServerLoginManager } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { CHE_TELEMETRY } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Get extends Command { - static description = 'Display active login session' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - telemetry: CHE_TELEMETRY, - } - - async run() { - const { flags } = this.parse(Get) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Get.id, flags }) - - const loginManager = await CheServerLoginManager.getInstance() - const currentLogin = loginManager.getCurrentLoginInfo() - if (currentLogin.username) { - cli.info(`Logged into ${currentLogin.cheApiEndpoint} as ${currentLogin.username}`) - } else { - cli.info('There is no active login session') - } - } -} diff --git a/src/commands/auth/list.ts b/src/commands/auth/list.ts deleted file mode 100644 index 719b934d5..000000000 --- a/src/commands/auth/list.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheServerLoginManager } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { CHE_TELEMETRY } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class List extends Command { - static description = 'Show all existing login sessions' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - telemetry: CHE_TELEMETRY, - } - - async run() { - const { flags } = this.parse(List) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: List.id, flags }) - - const loginManager = await CheServerLoginManager.getInstance() - const logins = loginManager.getAllLogins() - const currentLogin = loginManager.getCurrentLoginInfo() - this.printLogins(logins, currentLogin) - } - - private printLogins(allLogins: Map, currentLogin: { cheApiEndpoint: string, username: string }): void { - const currentLoginMarker = ' * ' - const indent = ' ' - - let output: string - if (allLogins.size > 0) { - output = 'Available logins:\n' - allLogins.forEach((serverLogins: string[], serverUrl: string) => { - output += indent + serverUrl + '\n' - for (const login of serverLogins) { - output += (currentLogin.cheApiEndpoint === serverUrl && currentLogin.username === login) ? currentLoginMarker : indent - output += indent + login + '\n' - } - }) - } else { - output = 'There are no login sessions' - } - - cli.info(output) - } -} diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts deleted file mode 100644 index 6d6373def..000000000 --- a/src/commands/auth/login.ts +++ /dev/null @@ -1,170 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { string } from '@oclif/parser/lib/flags' -import { cli } from 'cli-ux' -import * as execa from 'execa' - -import { CheApiClient } from '../../api/che-api-client' -import { CheServerLoginManager, getCheApiEndpoint, LoginRecord } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { KubeHelper } from '../../api/kube' -import { cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, username, USERNAME_KEY } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' -import { OPENSHIFT_CLI, wrapCommandError } from '../../util' - -const REFRESH_TOKEN_KEY = 'refresh-token' -const PASSWORD_KEY = 'password' - -export default class Login extends Command { - static description = 'Log in to Eclipse Che server' - - static args = [ - { - 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 - }, - ] - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - chenamespace: cheNamespace, - [REFRESH_TOKEN_KEY]: string({ - char: 't', - description: 'Keycloak refresh token', - env: 'CHE_KEYCLOAK_REFRESH_TOKEN', - required: false, - exclusive: [USERNAME_KEY, PASSWORD_KEY], - }), - [USERNAME_KEY]: username, - [PASSWORD_KEY]: string({ - char: 'p', - description: 'Eclipse Che user password', - env: 'CHE_USER_PASSWORD', - required: false, - exclusive: [REFRESH_TOKEN_KEY], - }), - telemetry: CHE_TELEMETRY, - } - - static examples = [ - '# Log in with username and password (when OpenShift OAuth is not enabled):\n' + - 'chectl auth:login https://che-che.apps-crc.testing/api -u username -p password', - '\n\n# Log in with username and password (password will be asked interactively):\n' + - 'chectl auth:login che-che.apps-crc.testing -u username', - '\n\n# Log in with token (when OpenShift OAuth is enabled):\n' + - 'chectl auth:login che.openshift.io -t token', - '\n\n# Log in with oc token (when logged into an OpenShift cluster with oc and OpenShift OAuth is enabled):\n' + - 'chectl auth:login che.my.server.net', - ] - - async run() { - const { args, flags } = this.parse(Login) - await ChectlContext.init(flags, this) - - // Not recommended to track user and password in telemetry - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Login.id, flags }) - - const loginManager = await CheServerLoginManager.getInstance() - - let cheApiClient: CheApiClient - let cheApiEndpoint: string | undefined = args[CHE_API_ENDPOINT_KEY] - if (!cheApiEndpoint) { - cheApiEndpoint = await getCheApiEndpoint(flags) - cli.info(`Using ${cheApiEndpoint} server API URL to log in`) - cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - } else { - cheApiEndpoint = CheApiClient.normalizeCheApiEndpointUrl(cheApiEndpoint) - cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - try { - await cheApiClient.checkCheApiEndpointUrl() - } catch (error) { - // Wrong API URL, try to guess, maybe base url is provided - if (!cheApiEndpoint.endsWith('api')) { - cheApiEndpoint += '/api' - cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - await cheApiClient.checkCheApiEndpointUrl() - } else { - throw error - } - } - } - - if (!await cheApiClient.isAuthenticationEnabled()) { - cli.info(`Authentication is not supported on the server: "${cheApiEndpoint}"`) - return - } - - // Try to login user - const refreshToken: string | undefined = flags[REFRESH_TOKEN_KEY] - const username: string | undefined = flags[USERNAME_KEY] - - let loginData: LoginRecord | undefined - if (refreshToken) { - loginData = { refreshToken, expires: Date.now() / 1000 + 60 } - } else if (username) { - let password = flags[PASSWORD_KEY] - if (!password) { - // Password wasn't provided, ask user to input it - password = await cli.prompt(`Password for ${flags.username} on ${cheApiEndpoint}`, { type: 'hide' }) - if (!password) { - throw new Error('Password is required') - } - } - - loginData = { username, password } - } else { - const kube = new KubeHelper(flags) - - // User is logged into cluster with oc or kubectl - // Try to retrieve oc user token - if (await kube.isOpenShift()) { - let ocUserToken: string - const getUserTokenArgs = ['whoami', '--show-token'] - try { - ocUserToken = (await execa(OPENSHIFT_CLI, getUserTokenArgs, { timeout: 10000 })).stdout - } catch { - // Che is running on a Kubernetes cluster - throw new Error(`No credentials provided. Please provide "--${REFRESH_TOKEN_KEY}" or "--${USERNAME_KEY}" parameter`) - } - - const subjectIssuer = (await kube.isOpenShift4()) ? 'openshift-v4' : 'openshift-v3' - - loginData = { subjectToken: ocUserToken, subjectIssuer } - } else { - const username = await cli.prompt(`Username on ${cheApiEndpoint}`) - if (!username) { - throw new Error('Username is required') - } - const password = await cli.prompt(`Password for ${username} on ${cheApiEndpoint}`, { type: 'hide' }) - if (!password) { - throw new Error('Password is required') - } - loginData = { username, password } - } - } - - if (!loginData) { - throw new Error('Login data is required. Please provide token or username and password.') - } - - try { - const username = await loginManager.setLoginContext(cheApiEndpoint, loginData) - cli.info(`Successfully logged into ${cheApiEndpoint} as ${username}`) - } catch (err) { - this.error(wrapCommandError(err)) - } - } -} diff --git a/src/commands/auth/logout.ts b/src/commands/auth/logout.ts deleted file mode 100644 index c1011d7c8..000000000 --- a/src/commands/auth/logout.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheServerLoginManager } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { CHE_TELEMETRY } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Logout extends Command { - static description = 'Log out of the active login session' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - telemetry: CHE_TELEMETRY, - } - - async run() { - const { flags } = this.parse(Logout) - - await ChectlContext.init(flags, this) - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Logout.id, flags }) - - const loginManager = await CheServerLoginManager.getInstance() - const currentLogin = loginManager.getCurrentLoginInfo() - - const cheApiEndpoint = currentLogin.cheApiEndpoint - const username = currentLogin.username - if (!cheApiEndpoint || !username) { - cli.info('There is no active login session') - return - } - - loginManager.deleteLoginContext(cheApiEndpoint, username) - cli.info(`Successfully logged out ${username} on ${cheApiEndpoint}`) - } -} diff --git a/src/commands/auth/use.ts b/src/commands/auth/use.ts deleted file mode 100644 index aff51d730..000000000 --- a/src/commands/auth/use.ts +++ /dev/null @@ -1,180 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' -import * as inquirer from 'inquirer' - -import { CheApiClient } from '../../api/che-api-client' -import { CheServerLoginManager } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, username, USERNAME_KEY } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Use extends Command { - static description = 'Set active login session' - - static args = [ - { - name: CHE_API_ENDPOINT_KEY, - description: 'Eclipse Che server API endpoint', - required: false, - }, - ] - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - [USERNAME_KEY]: username, - interactive: flags.boolean({ - char: 'i', - description: 'Select an active login session in interactive mode', - required: false, - exclusive: [USERNAME_KEY], - }), - telemetry: CHE_TELEMETRY, - } - - static examples = [ - '# Set an active login session for the specified user on the given cluster:\n' + - 'chectl auth:use che-che.apps-crc.testing/api -u username', - '\n\n# Switch to another user on the same cluster:\n' + - 'chectl auth:use -u another-user-on-this-server', - '\n\n# Switch to the only user on the given cluster:\n' + - 'chectl auth:use my.cluster.net', - '\n\n# Select an active login session in interactive mode:\n' + - 'chectl auth:use -i', - ] - - async run() { - const { args, flags } = this.parse(Use) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Use.id, flags }) - - if (flags.interactive) { - await this.interactiveSwitch() - return - } - - let cheApiEndpoint: string | undefined = args[CHE_API_ENDPOINT_KEY] - let username: string | undefined = flags[USERNAME_KEY] - - if (!cheApiEndpoint && !username) { - throw new Error('No arguments provided') - } - - const loginManager = await CheServerLoginManager.getInstance() - - if (!cheApiEndpoint) { - // Try to use current server - const currentLogin = loginManager.getCurrentLoginInfo() - cheApiEndpoint = currentLogin.cheApiEndpoint - if (!cheApiEndpoint) { - // There is no current server to switch user on - throw new Error('No current login session. Please specify it directly.') - } - - if (username === currentLogin.username) { - // This is already current context - cli.info(`Already logged in as ${username} on ${cheApiEndpoint} server`) - return - } - } else { - cheApiEndpoint = CheApiClient.normalizeCheApiEndpointUrl(cheApiEndpoint) - // Check if any login exist for provided Che server - if (!loginManager.hasLoginFor(cheApiEndpoint)) { - // Maybe /api suffix isn't provided - const cheApiEndpointGuess = cheApiEndpoint + '/api' - if (!loginManager.hasLoginFor(cheApiEndpointGuess)) { - cli.info(`No registered login sessions on server ${cheApiEndpoint}`) - return - } - cheApiEndpoint = cheApiEndpointGuess - } - } - - if (!username) { - // Check if given server has only one login session to use - const serverLogins = loginManager.getAllLogins().get(cheApiEndpoint) - if (!serverLogins || (serverLogins && serverLogins.length < 1)) { - cli.info(`No registered login sessions for ${cheApiEndpoint} server`) - return - } - if (serverLogins.length !== 1) { - throw new Error(`Username on ${cheApiEndpoint} server is expected. Please provide "--${USERNAME_KEY}" parameter`) - } - // Use the only logged in user on the server - username = serverLogins[0] - } - - await loginManager.switchLoginContext(cheApiEndpoint, username) - cli.info(`Now active login is ${username} on ${cheApiEndpoint} server`) - } - - private async interactiveSwitch(): Promise { - const loginManager = await CheServerLoginManager.getInstance() - const allLogins = loginManager.getAllLogins() - const currentLogin = loginManager.getCurrentLoginInfo() - - let cheApiEndpoint = '' - let username = '' - if (allLogins.size === 0) { - cli.info('No login session exists') - return - } else if (allLogins.size === 1) { - // Retrieve the only login info - cheApiEndpoint = allLogins.keys().next().value - username = allLogins.get(cheApiEndpoint)![0] - } else { - // Ask user to interactively select - const choices: inquirer.Answers[] = [] - let current: inquirer.Answers | undefined - allLogins.forEach((serverLogins: string[], serverUrl: string) => { - choices.push(new inquirer.Separator(serverUrl)) - for (const login of serverLogins) { - const choise = { - name: ` ${login}`, - value: { cheApiEndpoint: serverUrl, username: login }, - } - choices.push(choise) - if (currentLogin.cheApiEndpoint === serverUrl && currentLogin.username === login) { - current = choise - } - } - }) - - const userResponse = await inquirer.prompt([{ - name: 'context', - type: 'list', - message: 'Select login session', - choices, - default: current ? current.value : undefined, - }]) - - if (userResponse && userResponse.context) { - cheApiEndpoint = userResponse.context.cheApiEndpoint - username = userResponse.context.username - } - } - - if (cheApiEndpoint && username) { - if (currentLogin.cheApiEndpoint === cheApiEndpoint && currentLogin.username === username) { - cli.info(`Already logged in as ${username} on ${cheApiEndpoint} server`) - return - } - await loginManager.switchLoginContext(cheApiEndpoint, username) - cli.info(`Now active login is ${username} on ${cheApiEndpoint} server`) - } else { - cli.info('Nothing to change') - } - } -} diff --git a/src/commands/server/deploy.ts b/src/commands/server/deploy.ts index 8a4a927ba..7f6a018fe 100644 --- a/src/commands/server/deploy.ts +++ b/src/commands/server/deploy.ts @@ -18,7 +18,7 @@ import * as semver from 'semver' import { ChectlContext, OLM } from '../../api/context' import { KubeHelper } from '../../api/kube' import { batch, cheDeployment, cheDeployVersion, cheNamespace, cheOperatorCRPatchYaml, cheOperatorCRYaml, CHE_OPERATOR_CR_PATCH_YAML_KEY, CHE_OPERATOR_CR_YAML_KEY, CHE_TELEMETRY, DEPLOY_VERSION_KEY, k8sPodDownloadImageTimeout, K8SPODDOWNLOADIMAGETIMEOUT_KEY, k8sPodErrorRecheckTimeout, K8SPODERRORRECHECKTIMEOUT_KEY, k8sPodReadyTimeout, K8SPODREADYTIMEOUT_KEY, k8sPodWaitTimeout, K8SPODWAITTIMEOUT_KEY, listrRenderer, logsDirectory, LOG_DIRECTORY_KEY, skipKubeHealthzCheck as skipK8sHealthCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME, DEFAULT_CHE_NAMESPACE, DEFAULT_OLM_SUGGESTED_NAMESPACE, DOCS_LINK_INSTALL_RUNNING_CHE_LOCALLY, MIN_CHE_OPERATOR_INSTALLER_VERSION, MIN_OLM_INSTALLER_VERSION, OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME } from '../../constants' +import { DEFAULT_ANALYTIC_HOOK_NAME, DEFAULT_CHE_NAMESPACE, DEFAULT_OLM_SUGGESTED_NAMESPACE, DOCS_LINK_INSTALL_RUNNING_CHE_LOCALLY, MIN_CHE_OPERATOR_INSTALLER_VERSION, MIN_OLM_INSTALLER_VERSION } from '../../constants' import { CheTasks } from '../../tasks/che' import { DevWorkspaceTasks } from '../../tasks/component-installers/devfile-workspace-operator-installer' import { DexTasks } from '../../tasks/component-installers/dex' @@ -255,10 +255,6 @@ export default class Deploy extends Command { this.error(`🛑 The specified installer ${flags.installer} does not support Kubernentes`) } - if (flags[OLM.CHANNEL] === OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME && isKubernetesPlatformFamily(flags.platform)) { - this.error('"stable-all-namespaces" channel is supported only in "openshift" platform') - } - if (flags[OLM.CATALOG_SOURCE_NAME] && flags[OLM.CATALOG_SOURCE_YAML]) { this.error(`should be provided only one argument: "${OLM.CATALOG_SOURCE_NAME}" or "${OLM.CATALOG_SOURCE_YAML}"`) } diff --git a/src/commands/workspace/create.ts b/src/commands/workspace/create.ts deleted file mode 100644 index a9f3ffb32..000000000 --- a/src/commands/workspace/create.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { boolean, string } from '@oclif/parser/lib/flags' -import { cli } from 'cli-ux' -import * as fs from 'fs' - -import { CheHelper } from '../../api/che' -import { CheApiClient } from '../../api/che-api-client' -import { getLoginData } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Create extends Command { - static description = 'Creates a workspace from a devfile' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - chenamespace: cheNamespace, - devfile: string({ - char: 'f', - description: 'Path or URL to a valid devfile', - env: 'DEVFILE_PATH', - required: false, - }), - name: string({ - description: 'Workspace name: overrides the workspace name to use instead of the one defined in the devfile.', - required: false, - }), - start: boolean({ - char: 's', - description: 'Starts the workspace after creation', - 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, - }), - [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, - [ACCESS_TOKEN_KEY]: accessToken, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - } - - async run() { - const { flags } = this.parse(Create) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Create.id, flags }) - - const devfilePath = this.getDevfilePath(flags.devfile) - const cheHelper = new CheHelper(flags) - - const { cheApiEndpoint, accessToken } = await getLoginData(flags[CHE_API_ENDPOINT_KEY], flags[ACCESS_TOKEN_KEY], flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - - let workspace = await cheHelper.createWorkspaceFromDevfile(cheApiEndpoint, devfilePath, flags.name, accessToken) - const workspaceId = workspace.id! - - if (flags.start) { - await cheApiClient.startWorkspace(workspaceId, flags.debug, accessToken) - this.log('Workspace has been successfully created and workspace start request has been sent.') - this.log('Workspace will be available shortly:') - } else { - this.log('Workspace has been successfully created:') - } - workspace = await cheApiClient.getWorkspaceById(workspaceId, accessToken) - if (workspace.links && workspace.links.ide) { - const workspaceIdeURL = await cheHelper.buildDashboardURL(workspace.links.ide) - cli.url(workspaceIdeURL, workspaceIdeURL) - } - - this.exit(0) - } - - private getDevfilePath(devfilePath?: string) { - if (!devfilePath) { - if (fs.existsSync('devfile.yaml')) { - devfilePath = 'devfile.yaml' - } else if (fs.existsSync('devfile.yml')) { - devfilePath = 'devfile.yml' - } else { - throw new Error("E_DEVFILE_MISSING - Devfile wasn't specified via '-f' option and 'devfile.yaml' is not present in current directory.") - } - } - return devfilePath - } -} diff --git a/src/commands/workspace/delete.ts b/src/commands/workspace/delete.ts deleted file mode 100644 index f45b9c4f9..000000000 --- a/src/commands/workspace/delete.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheApiClient } from '../../api/che-api-client' -import { getLoginData } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { KubeHelper } from '../../api/kube' -import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Delete extends Command { - static description = 'Delete a stopped workspace - use workspace:stop to stop the workspace before deleting it' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - chenamespace: cheNamespace, - 'delete-namespace': flags.boolean({ - description: 'Indicates that a Kubernetes namespace where workspace was created will be deleted as well', - default: false, - }), - [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, - [ACCESS_TOKEN_KEY]: accessToken, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - } - - static args = [ - { - name: 'workspace', - description: 'The workspace id to delete', - required: true, - }, - ] - - async run() { - const { flags, args } = this.parse(Delete) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Delete.id, flags }) - - const workspaceId = args.workspace - - const { cheApiEndpoint, accessToken } = await getLoginData(flags[CHE_API_ENDPOINT_KEY], flags[ACCESS_TOKEN_KEY], flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - await cheApiClient.deleteWorkspaceById(workspaceId, accessToken) - cli.log(`Workspace with id '${workspaceId}' deleted.`) - - if (flags['delete-namespace']) { - const workspace = await cheApiClient.getWorkspaceById(workspaceId, accessToken) - const infrastructureNamespace = workspace!.attributes!.infrastructureNamespace - - if (infrastructureNamespace === flags.chenamespace) { - cli.warn(`It is not possible to delete namespace '${infrastructureNamespace}' since it is used for Eclipse Che deployment.`) - return - } - - const kube = new KubeHelper(flags) - if (await kube.getNamespace(infrastructureNamespace)) { - try { - await kube.deleteNamespace(infrastructureNamespace) - cli.log(`Namespace '${infrastructureNamespace}' deleted.`) - } catch (error) { - cli.warn(`Failed to delete namespace '${infrastructureNamespace}'. Reason: ${error.message}`) - } - } - } - - this.exit(0) - } -} diff --git a/src/commands/workspace/inject.ts b/src/commands/workspace/inject.ts deleted file mode 100644 index c4d275b02..000000000 --- a/src/commands/workspace/inject.ts +++ /dev/null @@ -1,232 +0,0 @@ -/** - * 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 - */ - -import { Context } from '@kubernetes/client-node/dist/config_types' -import { Command, flags } from '@oclif/command' -import { string } from '@oclif/parser/lib/flags' -import { cli } from 'cli-ux' -import * as execa from 'execa' -import * as fs from 'fs' -import * as os from 'os' -import * as path from 'path' - -import { CheHelper } from '../../api/che' -import { CheApiClient } from '../../api/che-api-client' -import { getLoginData } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { KubeHelper } from '../../api/kube' -import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' -import { getClusterClientCommand, OPENSHIFT_CLI, wrapCommandError } from '../../util' - -export default class Inject extends Command { - static description = 'Inject configurations and tokens in a workspace' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - kubeconfig: flags.boolean({ - char: 'k', - description: 'Inject the local Kubernetes configuration', - 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.`, - }), - 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, - }), - 'kube-context': string({ - description: 'Kubeconfig context to inject', - required: false, - }), - [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, - [ACCESS_TOKEN_KEY]: accessToken, - chenamespace: cheNamespace, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - } - - // Holds cluster CLI tool name: kubectl or oc - private readonly command = getClusterClientCommand() - - async run() { - const { flags } = this.parse(Inject) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Inject.id, flags }) - - const cheHelper = new CheHelper(flags) - - const { cheApiEndpoint, accessToken } = await getLoginData(flags[CHE_API_ENDPOINT_KEY], flags[ACCESS_TOKEN_KEY], flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - - let workspaceId = flags.workspace - let workspaceNamespace = '' - if (!workspaceId) { - const workspaces = await cheApiClient.getAllWorkspaces(accessToken) - const runningWorkspaces = workspaces.filter(w => w.status === 'RUNNING') - if (runningWorkspaces.length === 1) { - workspaceId = runningWorkspaces[0].id - workspaceNamespace = runningWorkspaces[0].attributes!.infrastructureNamespace - } else if (runningWorkspaces.length === 0) { - cli.error('There are no running workspaces. Please start workspace first.') - } else { - cli.error('There are more than 1 running workspaces. Please, specify the workspace id by providing \'--workspace\' flag.\nSee more details with the --help flag.') - } - } else { - const workspace = await cheApiClient.getWorkspaceById(workspaceId, accessToken) - if (workspace.status !== 'RUNNING') { - cli.error(`Workspace '${workspaceId}' is not running. Please start workspace first.`) - } - workspaceNamespace = workspace.attributes!.infrastructureNamespace - } - - const workspacePodName = await cheHelper.getWorkspacePodName(workspaceNamespace, workspaceId!) - if (flags.container && !await this.containerExists(workspaceNamespace, workspacePodName, flags.container)) { - cli.error(`The specified container '${flags.container}' doesn't exist. The configuration cannot be injected.`) - } - - try { - await this.injectKubeconfig(flags, workspaceNamespace, workspacePodName, workspaceId!) - } catch (err) { - this.error(wrapCommandError(err)) - } - } - - async injectKubeconfig(flags: any, workspaceNamespace: string, workspacePodName: string, workspaceId: string): Promise { - const kubeContext = flags['kube-context'] - let contextToInject: Context | null - const kh = new KubeHelper(flags) - if (kubeContext) { - contextToInject = kh.getContext(kubeContext) - if (!contextToInject) { - this.error(`Context ${kubeContext} is not found in the source kubeconfig`) - } - } else { - const currentContext = await kh.currentContext() - contextToInject = kh.getContext(currentContext) - } - - const che = new CheHelper(flags) - const containers = flags.container ? [flags.container] : await che.getWorkspacePodContainers(workspaceNamespace, workspaceId) - for (const container of containers) { - // che-machine-exec container is very limited for a security reason. - // We cannot copy file into it. - if (container.startsWith('che-machine-exec') || container.startsWith('che-jwtproxy')) { - continue - } - - try { - if (await this.canInject(workspaceNamespace, workspacePodName, container)) { - await this.doInjectKubeconfig(workspaceNamespace, workspacePodName, container, contextToInject!) - cli.info(`Configuration successfully injected into ${container} container`) - } - } catch (error) { - cli.warn(`Failed to injected configuration into ${container} container.\nError: ${error.message}`) - } - } - } - - /** - * Tests whether a file can be injected into the specified container. - */ - 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 - } - } - - /** - * Copies the local kubeconfig into the specified container. - * If returns, it means injection was completed successfully. If throws an error, injection failed - */ - private async doInjectKubeconfig(namespace: string, workspacePod: string, container: string, contextToInject: Context): Promise { - const { stdout } = await execa(`${this.command} exec ${workspacePod} -n ${namespace} -c ${container} env | grep ^HOME=`, { timeout: 10000, shell: true }) - const kc = new KubeHelper() - let containerHomeDir = stdout.split('=')[1] - if (!containerHomeDir.endsWith('/')) { - containerHomeDir += '/' - } - - if (await this.fileExists(namespace, workspacePod, container, `${containerHomeDir}.kube/config`)) { - throw new Error('kubeconfig already exists in the target container') - } - await execa(`${this.command} exec ${workspacePod} -n ${namespace} -c ${container} -- mkdir ${containerHomeDir}.kube -p`, { timeout: 10000, shell: true }) - - const kubeConfigPath = path.join(os.tmpdir(), 'che-kubeconfig') - const cluster = kc.kubeConfig.getCluster(contextToInject.cluster) - if (!cluster) { - throw new Error(`Context ${contextToInject.name} has no cluster object`) - } - const user = kc.kubeConfig.getUser(contextToInject.user) - if (!user) { - throw new Error(`Context ${contextToInject.name} has no user object`) - } - - // Despite oc has --kubeconfig flag it actually does nothing, so we need to use --config instead - const configPathFlag = this.command === OPENSHIFT_CLI ? '--config' : '--kubeconfig' - - const setClusterArgs = ['config', configPathFlag, kubeConfigPath, 'set-cluster', cluster.name, `--server=${cluster.server}`] - // Prepare CA certificate file - if (cluster.caFile) { - setClusterArgs.push(`--certificate-authority=${cluster.caFile}`) - setClusterArgs.push('--embed-certs=true') - } else if (cluster.caData) { - const caFile = path.join(os.tmpdir(), 'cluster-ca-file.pem') - // Write caData into a file and pass it as the parameter - fs.writeFileSync(caFile, cluster.caData, 'utf8') - - setClusterArgs.push(`--certificate-authority=${caFile}`) - setClusterArgs.push('--embed-certs=true') - } - await execa(this.command, setClusterArgs, { timeout: 10000 }) - - const setCredentialsArgs = ['config', configPathFlag, kubeConfigPath, 'set-credentials', user.name] - if (user.certFile) { - setCredentialsArgs.push(`--client-certificate=${user.certFile}`) - } - if (user.keyFile) { - setCredentialsArgs.push(`--client-key=${user.keyFile}`) - } - if (user.certFile || user.keyFile) { - setCredentialsArgs.push('--embed-certs=true') - } - await execa(this.command, setCredentialsArgs, { timeout: 10000 }) - - await execa(this.command, ['config', configPathFlag, kubeConfigPath, 'set-context', contextToInject.name, `--cluster=${contextToInject.cluster}`, `--user=${contextToInject.user}`, `--namespace=${namespace}`], { timeout: 10000 }) - 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 - } - } - - private async containerExists(namespace: string, pod: string, container: string): Promise { - const { stdout } = await execa(this.command, ['get', 'pods', `${pod}`, '-n', `${namespace}`, '-o', 'jsonpath={.spec.containers[*].name}'], { timeout: 10000 }) - return stdout.split(' ').some(c => c === container) - } -} diff --git a/src/commands/workspace/list.ts b/src/commands/workspace/list.ts deleted file mode 100644 index a1a4ca146..000000000 --- a/src/commands/workspace/list.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheApiClient } from '../../api/che-api-client' -import { getLoginData } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class List extends Command { - static description = 'List workspaces' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - chenamespace: cheNamespace, - [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, - [ACCESS_TOKEN_KEY]: accessToken, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - } - - async run() { - const { flags } = this.parse(List) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: List.id, flags }) - const { cheApiEndpoint, accessToken } = await getLoginData(flags[CHE_API_ENDPOINT_KEY], flags[ACCESS_TOKEN_KEY], flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - const workspaces = await cheApiClient.getAllWorkspaces(accessToken) - - this.printWorkspaces(workspaces) - } - - private printWorkspaces(workspaces: any[]): void { - const data: any[] = [] - workspaces.forEach((workspace: any) => { - data.push({ - id: workspace.id, - name: workspace.devfile.metadata.name, - 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() : '', - }) - }) - cli.table(data, { id: {}, name: {}, namespace: {}, status: {}, created: {}, updated: {} }) - } -} diff --git a/src/commands/workspace/logs.ts b/src/commands/workspace/logs.ts deleted file mode 100644 index b10e01456..000000000 --- a/src/commands/workspace/logs.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { string } from '@oclif/parser/lib/flags' -import * as os from 'os' -import * as path from 'path' - -import { CheHelper } from '../../api/che' -import { ChectlContext } from '../../api/context' -import { CHE_TELEMETRY, FOLLOW_LOGS, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Logs extends Command { - static description = 'Collect workspace(s) logs' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - workspace: string({ - char: 'w', - description: 'Target workspace id. Can be found in workspace configuration \'id\' field.', - required: true, - }), - namespace: string({ - char: 'n', - description: 'The namespace where workspace is located. Can be found in workspace configuration \'attributes.infrastructureNamespace\' field.', - required: true, - }), - directory: string({ - char: 'd', - description: 'Directory to store logs into', - env: 'CHE_LOGS', - }), - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - follow: FOLLOW_LOGS, - } - - async run() { - const { flags } = this.parse(Logs) - await ChectlContext.init(flags, this) - - const logsDirectory = path.resolve(flags.directory ? flags.directory : path.resolve(os.tmpdir(), 'chectl-logs', Date.now().toString())) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Logs.id, flags }) - const cheHelper = new CheHelper(flags) - await cheHelper.readWorkspacePodLog(flags.namespace, flags.workspace, logsDirectory, flags.follow) - - if (flags.follow) { - this.log(`Workspace logs are available in '${logsDirectory}'`) - } else { - this.log(`Workspace logs are being collected in '${logsDirectory}'`) - } - } -} diff --git a/src/commands/workspace/start.ts b/src/commands/workspace/start.ts deleted file mode 100644 index 0cb521528..000000000 --- a/src/commands/workspace/start.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * 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 - */ - -import Command, { flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheHelper } from '../../api/che' -import { CheApiClient } from '../../api/che-api-client' -import { getLoginData } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Start extends Command { - static description = 'Starts a workspace' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - 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, - }), - [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, - [ACCESS_TOKEN_KEY]: accessToken, - chenamespace: cheNamespace, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - } - - static args = [ - { - name: 'workspace', - description: 'The workspace id to start', - required: true, - }, - ] - - async run() { - const { flags, args } = this.parse(Start) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Start.id, flags }) - - const workspaceId = args.workspace - const cheHelper = new CheHelper(flags) - - const { cheApiEndpoint, accessToken } = await getLoginData(flags[CHE_API_ENDPOINT_KEY], flags[ACCESS_TOKEN_KEY], flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - await cheApiClient.startWorkspace(workspaceId, flags.debug, accessToken) - - const workspace = await cheApiClient.getWorkspaceById(workspaceId, accessToken) - if (workspace.links && workspace.links.ide) { - const workspaceIdeURL = await cheHelper.buildDashboardURL(workspace.links.ide) - cli.log('Workspace start request has been sent, workspace will be available shortly:') - cli.url(workspaceIdeURL, workspaceIdeURL) - } else { - cli.log('Workspace start request has been sent, workspace will be available shortly.') - } - - this.exit(0) - } -} diff --git a/src/commands/workspace/stop.ts b/src/commands/workspace/stop.ts deleted file mode 100644 index 31dfe0741..000000000 --- a/src/commands/workspace/stop.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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 - */ - -import { Command, flags } from '@oclif/command' -import { cli } from 'cli-ux' - -import { CheApiClient } from '../../api/che-api-client' -import { getLoginData } from '../../api/che-login-manager' -import { ChectlContext } from '../../api/context' -import { accessToken, ACCESS_TOKEN_KEY, cheApiEndpoint, cheNamespace, CHE_API_ENDPOINT_KEY, CHE_TELEMETRY, skipKubeHealthzCheck } from '../../common-flags' -import { DEFAULT_ANALYTIC_HOOK_NAME } from '../../constants' - -export default class Stop extends Command { - static description = 'Stop a running workspace' - - static flags: flags.Input = { - help: flags.help({ char: 'h' }), - [CHE_API_ENDPOINT_KEY]: cheApiEndpoint, - [ACCESS_TOKEN_KEY]: accessToken, - chenamespace: cheNamespace, - 'skip-kubernetes-health-check': skipKubeHealthzCheck, - telemetry: CHE_TELEMETRY, - } - - static args = [ - { - name: 'workspace', - description: 'The workspace id to stop', - required: true, - }, - ] - - async run() { - const { flags, args } = this.parse(Stop) - await ChectlContext.init(flags, this) - - await this.config.runHook(DEFAULT_ANALYTIC_HOOK_NAME, { command: Stop.id, flags }) - - const workspaceId = args.workspace - - const { cheApiEndpoint, accessToken } = await getLoginData(flags[CHE_API_ENDPOINT_KEY], flags[ACCESS_TOKEN_KEY], flags) - const cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - await cheApiClient.stopWorkspace(workspaceId, accessToken) - cli.log(`Workspace ${workspaceId} successfully stopped.`) - - this.exit(0) - } -} diff --git a/src/constants.ts b/src/constants.ts index 32ec530f5..d9755f041 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -49,10 +49,7 @@ export const DEVWORKSPACE_CVS_PREFIX = 'devworkspace-operator' // OLM channels export const OLM_STABLE_CHANNEL_NAME = 'stable' export const OLM_STABLE_CHANNEL_STARTING_CSV_TEMPLATE = 'eclipse-che.v{{VERSION}}' -export const OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME = 'tech-preview-stable-all-namespaces' -export const OLM_STABLE_ALL_NAMESPACES_CHANNEL_STARTING_CSV_TEMPLATE = 'eclipse-che.v{{VERSION}}-all-namespaces' export const OLM_NEXT_CHANNEL_NAME = 'next' -export const OLM_NEXT_ALL_NAMESPACES_CHANNEL_NAME = 'next-all-namespaces' // OLM namespaces export const DEFAULT_OPENSHIFT_MARKET_PLACE_NAMESPACE = 'openshift-marketplace' export const DEFAULT_OLM_KUBERNETES_NAMESPACE = 'olm' diff --git a/src/tasks/che.ts b/src/tasks/che.ts index 8d0398f8a..487e65d77 100644 --- a/src/tasks/che.ts +++ b/src/tasks/che.ts @@ -14,7 +14,6 @@ import { Command } from '@oclif/command' import * as Listr from 'listr' import { CheHelper } from '../api/che' import { CheApiClient } from '../api/che-api-client' -import { CheServerLoginManager } from '../api/che-login-manager' import { KubeHelper } from '../api/kube' import { OpenShiftHelper } from '../api/openshift' import { VersionHelper } from '../api/version' @@ -27,41 +26,22 @@ 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' constructor(flags: any) { @@ -69,8 +49,6 @@ export class CheTasks { this.kubeTasks = new KubeTasks(flags) this.che = new CheHelper(flags) - this.cheAccessToken = flags['access-token'] - this.cheNamespace = flags.chenamespace this.cheDeploymentName = flags['deployment-name'] } @@ -230,15 +208,13 @@ export class CheTasks { { title: 'Check Eclipse Che server status', enabled: (ctx: any) => ctx.isCheDeployed && ctx.isCheReady, - task: async (ctx: any, task: any) => { + task: async (_ctx: any, task: any) => { let cheURL = '' try { cheURL = await this.che.cheURL(this.cheNamespace) const cheApi = CheApiClient.getInstance(cheURL + '/api') const status = await cheApi.getCheServerStatus() - ctx.isAuthEnabled = await cheApi.isAuthenticationEnabled() - const auth = ctx.isAuthEnabled ? '(auth enabled)' : '(auth disabled)' - task.title = `${task.title}...${status} ${auth}` + task.title = `${task.title}...${status}` } catch (error) { return newError(`Failed to check Eclipse Che status (URL: ${cheURL}).`, error) } @@ -321,32 +297,12 @@ export class CheTasks { */ scaleCheDownTasks(): ReadonlyArray { return [{ - title: 'Stop Eclipse Che Server and wait until it\'s ready to shutdown', - enabled: (ctx: any) => !ctx.isCheStopped, - task: async (task: any) => { - try { - const cheURL = await this.che.cheURL(this.cheNamespace) - const cheApi = CheApiClient.getInstance(cheURL + '/api') - let cheAccessToken = this.cheAccessToken - if (!cheAccessToken && await cheApi.isAuthenticationEnabled()) { - const loginManager = await CheServerLoginManager.getInstance() - cheAccessToken = await loginManager.getNewAccessToken() - } - await cheApi.startCheServerShutdown(cheAccessToken) - await cheApi.waitUntilCheServerReadyToShutdown() - task.title = `${task.title}...done` - } catch (error) { - return newError('Failed to shutdown Eclipse Che server.', error) - } - }, - }, - { title: `Scale \"${this.cheDeploymentName}\" deployment to zero`, enabled: (ctx: any) => !ctx.isCheStopped, task: async (_ctx: any, task: any) => { try { await this.kube.scaleDeployment(this.cheDeploymentName, this.cheNamespace, 0) - task.title = await `${task.title}...done` + task.title = `${task.title}...done` } catch (error) { return newError(`Failed to scale ${this.cheDeploymentName} deployment.`, error) } @@ -358,7 +314,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { try { await this.kube.scaleDeployment(this.dashboardDeploymentName, this.cheNamespace, 0) - task.title = await `${task.title}...done` + task.title = `${task.title}...done` } catch (error) { return newError('Failed to scale dashboard deployment.', error) } @@ -370,7 +326,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { try { await this.kube.scaleDeployment(this.keycloakDeploymentName, this.cheNamespace, 0) - task.title = await `${task.title}...done` + task.title = `${task.title}...done` } catch (error) { return newError('Failed to scale keycloak deployment.', error) } @@ -382,7 +338,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { try { await this.kube.scaleDeployment(this.postgresDeploymentName, this.cheNamespace, 0) - task.title = await `${task.title}...done` + task.title = `${task.title}...done` } catch (error) { return newError('Failed to scale postgres deployment.', error) } @@ -394,7 +350,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { try { await this.kube.scaleDeployment(this.devfileRegistryDeploymentName, this.cheNamespace, 0) - task.title = await `${task.title}...done` + task.title = `${task.title}...done` } catch (error) { return newError('Failed to scale devfile registry deployment.', error) } @@ -406,7 +362,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { try { await this.kube.scaleDeployment(this.pluginRegistryDeploymentName, this.cheNamespace, 0) - task.title = await `${task.title}...done` + task.title = `${task.title}...done` } catch (error) { return newError('Failed to scale plugin registry deployment.', error) } @@ -423,14 +379,14 @@ export class CheTasks { title: 'Delete all deployments', task: async (_ctx: any, task: any) => { await this.kube.deleteAllDeployments(flags.chenamespace) - task.title = await `${task.title}...OK` + task.title = `${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` + task.title = `${task.title}...OK` }, }, { @@ -438,7 +394,7 @@ export class CheTasks { enabled: (ctx: any) => !ctx.isOpenShift, task: async (_ctx: any, task: any) => { await this.kube.deleteAllIngresses(flags.chenamespace) - task.title = await `${task.title}...OK` + task.title = `${task.title}...OK` }, }, { @@ -446,7 +402,7 @@ export class CheTasks { enabled: (ctx: any) => ctx.isOpenShift, task: async (_ctx: any, task: any) => { await this.oc.deleteAllRoutes(flags.chenamespace) - task.title = await `${task.title}...OK` + task.title = `${task.title}...OK` }, }, { @@ -454,7 +410,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { await this.kube.deleteConfigMap('che', flags.chenamespace) await this.kube.deleteConfigMap('che-operator', flags.chenamespace) - task.title = await `${task.title}...OK` + task.title = `${task.title}...OK` }, }, { @@ -464,7 +420,7 @@ export class CheTasks { await this.kube.deleteRoleBinding('che-operator', flags.chenamespace) await this.kube.deleteRoleBinding('che-workspace-exec', flags.chenamespace) await this.kube.deleteRoleBinding('che-workspace-view', flags.chenamespace) - task.title = await `${task.title}...OK` + task.title = `${task.title}...OK` }, }, { @@ -472,7 +428,7 @@ export class CheTasks { task: async (_ctx: any, task: any) => { await this.kube.deleteServiceAccount('che', flags.chenamespace) await this.kube.deleteServiceAccount('che-workspace', flags.chenamespace) - task.title = await `${task.title}...OK` + task.title = `${task.title}...OK` }, }, { @@ -573,18 +529,6 @@ export class CheTasks { }] } - /** - * Verifies if workspace running and puts #V1Pod into a context. - */ - verifyWorkspaceRunTask(flags: any, command: Command): ReadonlyArray { - return [{ - 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)) - }, - }] - } - /** * Return tasks to collect Eclipse Che logs. */ @@ -601,49 +545,49 @@ export class CheTasks { 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` + task.title = `${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` + task.title = `${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` + task.title = `${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` + task.title = `${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` + task.title = `${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` + task.title = `${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` + task.title = `${task.title}...done` }, }, ] @@ -750,19 +694,6 @@ export class CheTasks { messages.push('Dex user credentials : user5@che:password') messages.push(OUTPUT_SEPARATOR) } - } else if (cheConfigMap.data.CHE_KEYCLOAK_AUTH__SERVER__URL) { - messages.push(`Identity Provider URL : ${addTrailingSlash(cheConfigMap.data.CHE_KEYCLOAK_AUTH__SERVER__URL)}`) - - if (ctx.identityProviderUsername && ctx.identityProviderPassword) { - messages.push(`Identity Provider login : "${ctx.identityProviderUsername}:${ctx.identityProviderPassword}".`) - } else { - const [login, password] = await this.che.retrieveKeycloakAdminCredentials(flags.chenamespace) - if (login && password) { - messages.push(`Identity Provider login : "${login}:${password}".`) - } - } - - messages.push(OUTPUT_SEPARATOR) } } diff --git a/src/tasks/installers/olm.ts b/src/tasks/installers/olm.ts index c7807eded..905a82220 100644 --- a/src/tasks/installers/olm.ts +++ b/src/tasks/installers/olm.ts @@ -20,7 +20,7 @@ import { CheHelper } from '../../api/che' import { KubeHelper } from '../../api/kube' import { CatalogSource, Subscription } from '../../api/types/olm' import { VersionHelper } from '../../api/version' -import { CUSTOM_CATALOG_SOURCE_NAME, CVS_PREFIX, DEFAULT_CHE_NAMESPACE, DEFAULT_CHE_OLM_PACKAGE_NAME, DEFAULT_OLM_KUBERNETES_NAMESPACE, DEFAULT_OPENSHIFT_MARKET_PLACE_NAMESPACE, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, KUBERNETES_OLM_CATALOG, NEXT_CATALOG_SOURCE_NAME, OLM_NEXT_CHANNEL_NAME, OLM_STABLE_CHANNEL_NAME, OPENSHIFT_OLM_CATALOG, OPERATOR_GROUP_NAME, OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME, DEFAULT_CHE_OPERATOR_SUBSCRIPTION_NAME, OLM_NEXT_ALL_NAMESPACES_CHANNEL_NAME, OLM_STABLE_CHANNEL_STARTING_CSV_TEMPLATE, OLM_STABLE_ALL_NAMESPACES_CHANNEL_STARTING_CSV_TEMPLATE } from '../../constants' +import { CUSTOM_CATALOG_SOURCE_NAME, CVS_PREFIX, DEFAULT_CHE_NAMESPACE, DEFAULT_CHE_OLM_PACKAGE_NAME, DEFAULT_OLM_KUBERNETES_NAMESPACE, DEFAULT_OPENSHIFT_MARKET_PLACE_NAMESPACE, DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, KUBERNETES_OLM_CATALOG, NEXT_CATALOG_SOURCE_NAME, OLM_NEXT_CHANNEL_NAME, OLM_STABLE_CHANNEL_NAME, OPENSHIFT_OLM_CATALOG, OPERATOR_GROUP_NAME, DEFAULT_CHE_OPERATOR_SUBSCRIPTION_NAME, OLM_STABLE_CHANNEL_STARTING_CSV_TEMPLATE } from '../../constants' import { getEmbeddedTemplatesDirectory, isKubernetesPlatformFamily } from '../../util' import { createEclipseCheCluster, patchingEclipseCheCluster } from './common-tasks' @@ -49,10 +49,7 @@ export class OLMTasks { title: 'Configure context information', task: async (ctx: any, task: any) => { ctx.operatorNamespace = flags.chenamespace || DEFAULT_CHE_NAMESPACE - if (flags[OLM.CHANNEL] === OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME || flags[OLM.CHANNEL] === OLM_NEXT_ALL_NAMESPACES_CHANNEL_NAME) { - ctx.operatorNamespace = DEFAULT_OPENSHIFT_OPERATORS_NS_NAME - } - + ctx.operatorNamespace = DEFAULT_OPENSHIFT_OPERATORS_NS_NAME ctx.defaultCatalogSourceNamespace = isKubernetesPlatformFamily(flags.platform) ? DEFAULT_OLM_KUBERNETES_NAMESPACE : DEFAULT_OPENSHIFT_MARKET_PLACE_NAMESPACE // catalog source name for stable Che version ctx.catalogSourceNameStable = isKubernetesPlatformFamily(flags.platform) ? KUBERNETES_OLM_CATALOG : OPENSHIFT_OLM_CATALOG @@ -68,8 +65,6 @@ export class OLMTasks { ctx.startingCSV = flags[OLM.STARTING_CSV] } else if (flags[OLM.CHANNEL] === OLM_STABLE_CHANNEL_NAME) { ctx.startingCSV = OLM_STABLE_CHANNEL_STARTING_CSV_TEMPLATE.replace(new RegExp('{{VERSION}}', 'g'), flags.version) - } else if (flags[OLM.CHANNEL] === OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME) { - ctx.startingCSV = OLM_STABLE_ALL_NAMESPACES_CHANNEL_STARTING_CSV_TEMPLATE.replace(new RegExp('{{VERSION}}', 'g'), flags.version) } // else use latest in the channel // Set approval starategy to manual to prevent autoupdate to the latest version right before installation ctx.approvalStrategy = OLMInstallationUpdate.MANUAL @@ -116,7 +111,6 @@ export class OLMTasks { }, { title: 'Create operator group', - // 'stable-all-namespaces' and 'next-all-namespaces' channels install the operator in openshift-operators namespace and there already exists a pre-created operator-group. enabled: (ctx: any) => ctx.operatorNamespace !== DEFAULT_OPENSHIFT_OPERATORS_NS_NAME, task: async (_ctx: any, task: any) => { if (await che.findCheOperatorOperatorGroup(flags.chenamespace)) { @@ -174,10 +168,10 @@ export class OLMTasks { // custom Che CatalogSource const catalogSourceNamespace = flags[OLM.CATALOG_SOURCE_NAMESPACE] || ctx.operatorNamespace subscription = this.constructSubscription(ctx.subscriptionName, flags[OLM.PACKAGE_MANIFEST_NAME], ctx.operatorNamespace, catalogSourceNamespace, channel || OLM_STABLE_CHANNEL_NAME, ctx.sourceName, ctx.approvalStrategy, ctx.startingCSV) - } else if (channel === OLM_STABLE_CHANNEL_NAME || channel === OLM_STABLE_ALL_NAMESPACES_CHANNEL_NAME || (VersionHelper.isDeployingStableVersion(flags) && !channel)) { + } else if (channel === OLM_STABLE_CHANNEL_NAME || (VersionHelper.isDeployingStableVersion(flags) && !channel)) { // stable Che CatalogSource subscription = this.constructSubscription(ctx.subscriptionName, DEFAULT_CHE_OLM_PACKAGE_NAME, ctx.operatorNamespace, ctx.defaultCatalogSourceNamespace, channel || OLM_STABLE_CHANNEL_NAME, ctx.catalogSourceNameStable, ctx.approvalStrategy, ctx.startingCSV) - } else if (channel === OLM_NEXT_CHANNEL_NAME || channel === OLM_NEXT_ALL_NAMESPACES_CHANNEL_NAME || !channel) { + } else if (channel === OLM_NEXT_CHANNEL_NAME || !channel) { // next Che CatalogSource subscription = this.constructSubscription(ctx.subscriptionName, `eclipse-che-preview-${ctx.generalPlatformName}`, ctx.operatorNamespace, ctx.operatorNamespace, channel || OLM_NEXT_CHANNEL_NAME, NEXT_CATALOG_SOURCE_NAME, ctx.approvalStrategy, ctx.startingCSV) } else { diff --git a/test/api/che-api-client.test.ts b/test/api/che-api-client.test.ts deleted file mode 100644 index 02048fec2..000000000 --- a/test/api/che-api-client.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * 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 - */ - -import { expect, fancy } from 'fancy-test' - -import { CheApiClient } from '../../src/api/che-api-client' - -const cheApiEndpoint = 'https://che-che.192.168.64.34.nip.io/api' -const devfileEndpoint = '/workspace/devfile' -let cheApiClient = CheApiClient.getInstance(cheApiEndpoint) - -describe('Eclipse Che Server API client', () => { - describe('isCheServerReady', () => { - fancy - .nock(cheApiEndpoint, api => api - .get('/system/state') - .reply(200)) - .it('detects if Eclipse Che server is ready', async () => { - const res = await cheApiClient.isCheServerReady() - expect(res).to.equal(true) - }) - fancy - .nock(cheApiEndpoint, api => api - .get('/system/state') - .delayConnection(1000) - .reply(200)) - .it('detects if Eclipse Che server is NOT ready', async () => { - const res = await cheApiClient.isCheServerReady(500) - expect(res).to.equal(false) - }) - fancy - .nock(cheApiEndpoint, api => api - .get('/system/state') - .delayConnection(1000) - .reply(200)) - .it('waits until Eclipse Che server is ready', async () => { - const res = await cheApiClient.isCheServerReady(2000) - expect(res).to.equal(true) - }) - fancy - .nock(cheApiEndpoint, api => api - .get('/system/state') - .reply(404) - .get('/system/state') - .reply(503) - .get('/system/state') - .reply(200)) - .it('continues requesting until Eclipse Che server is ready', async () => { - const res = await cheApiClient.isCheServerReady(2000) - expect(res).to.equal(true) - }) - fancy - .nock(cheApiEndpoint, api => api - .get('/system/state') - .reply(404) - .get('/system/state') - .reply(404) - .get('/system/state') - .reply(503)) - .it('continues requesting but fails if Eclipse Che server is NOT ready after timeout', async () => { - const res = await cheApiClient.isCheServerReady(20) - expect(res).to.equal(false) - }) - }) - describe('createWorkspaceFromDevfile', () => { - fancy - .nock(cheApiEndpoint, api => api - .post(devfileEndpoint) - .replyWithFile(201, __dirname + '/replies/create-workspace-from-valid-devfile.json', { 'Content-Type': 'application/json' })) - .it('succeds creating a workspace from a valid devfile', async () => { - const res = await cheApiClient.createWorkspaceFromDevfile(__dirname + '/requests/devfile.valid') - expect(res.links!.ide).to.equal('https://che-che.192.168.64.39.nip.io/che/chectl') - }) - fancy - .nock(cheApiEndpoint, api => api - .post(devfileEndpoint) - .replyWithFile(400, __dirname + '/replies/create-workspace-from-invalid-devfile.json', { - 'Content-Type': 'application/json' - })) - .do(() => cheApiClient.createWorkspaceFromDevfile(__dirname + '/requests/devfile.invalid')) - .catch(/E_BAD_DEVFILE_FORMAT/) - .it('fails creating a workspace from an invalid devfile') - }) - describe('isAuthenticationEnabled', () => { - fancy - .nock(cheApiEndpoint, api => api - .get('/keycloak/settings') - .replyWithFile(200, __dirname + '/replies/get-keycloak-settings.json', { - 'Content-Type': 'application/json' - })) - .it('should return true if the api/keycloak/settings endpoint exist', async () => { - const authEnabled = await cheApiClient.isAuthenticationEnabled() - expect(authEnabled).to.equal(true) - }) - fancy - .nock(cheApiEndpoint, api => api - .get('/keycloak/settings') - .reply(404, 'Page does not exist', { - 'Content-Type': 'text/plain' - })) - .it('should return false if the api/keycloak/settings endpoint doesn\'t exist', async () => { - const authEnabled = await cheApiClient.isAuthenticationEnabled() - expect(authEnabled).to.equal(false) - }) - }) -}) diff --git a/test/api/che.test.ts b/test/api/che.test.ts index f1cd58b32..31261ad2e 100644 --- a/test/api/che.test.ts +++ b/test/api/che.test.ts @@ -16,11 +16,6 @@ import { expect, fancy } from 'fancy-test' import { CheHelper } from '../../src/api/che' const namespace = 'che' -const workspace = 'workspace-0123' -const theiaContainer = 'theia-123' -const cheURL = 'https://che-che.192.168.64.34.nip.io' -const devfileServerURL = 'https://devfile-server' -const devfileEndpoint = '/api/workspace/devfile' let ch = new CheHelper({}) let kube = ch.kube let oc = ch.oc @@ -116,36 +111,6 @@ describe('Eclipse Che helper', () => { expect(res).to.equal(true) }) }) - describe('createWorkspaceFromDevfile', () => { - fancy - .stub(ch, 'cheNamespaceExist', () => true) - .stub(ch, 'cheURL', () => cheURL) - .do(() => ch.createWorkspaceFromDevfile(namespace, __dirname + '/requests/devfile.inexistent', undefined)) - .catch(/E_NOT_FOUND_DEVFILE/) - .it('fails creating a workspace from a non-existing devfile') - fancy - .stub(ch, 'cheNamespaceExist', () => true) - .stub(ch, 'cheURL', () => cheURL) - .nock(devfileServerURL, api => api - .get('/devfile.yaml') - .replyWithFile(200, __dirname + '/requests/devfile.valid', { 'Content-Type': 'text/plain; charset=utf-8' })) - .nock(cheURL, api => api - .post(devfileEndpoint) - .replyWithFile(201, __dirname + '/replies/create-workspace-from-valid-devfile.json', { 'Content-Type': 'application/json' })) - .it('succeeds creating a workspace from a remote devfile', async () => { - const res = await ch.createWorkspaceFromDevfile(cheURL + '/api', devfileServerURL + '/devfile.yaml') - expect(res.links!.ide).to.equal('https://che-che.192.168.64.39.nip.io/che/chectl') - }) - fancy - .stub(ch, 'cheNamespaceExist', () => true) - .stub(ch, 'cheURL', () => cheURL) - .nock(devfileServerURL, api => api - .get('/devfile.yaml') - .reply(404, '404 - Not Found')) - .do(() => ch.createWorkspaceFromDevfile(namespace, devfileServerURL + '/devfile.yaml', undefined)) - .catch(/E_NOT_FOUND_DEVFILE/) - .it('fails creating a workspace from a non-existing remote devfile') - }) describe('buildDashboardURL', () => { fancy .it('builds the Dashboard URL of a workspace given the IDE link', async () => { @@ -155,85 +120,6 @@ describe('Eclipse Che helper', () => { expect(res).to.equal(dashboardURL) }) }) - describe('getWorkspacePod', () => { - fancy - .stub(kube.kubeConfig, 'makeApiClient', () => k8sApi) - .stub(k8sApi, 'listNamespacedPod', () => ({ response: '', body: { items: [{ metadata: { name: 'pod-name', labels: { 'che.workspace_id': workspace } } }] } })) - .it('should return pod name where workspace with the given ID is running', async () => { - const pod = await ch.getWorkspacePodName(namespace, workspace) - expect(pod).to.equal('pod-name') - }) - fancy - .stub(kube.kubeConfig, 'makeApiClient', () => k8sApi) - .stub(k8sApi, 'listNamespacedPod', () => ({ response: '', body: { items: [{ metadata: { labels: { 'che.workspace_id': `${workspace}1` } } }] } })) - .do(() => ch.getWorkspacePodName(namespace, workspace)) - .catch(/Pod is not found for the given workspace ID/) - .it('should fail if no workspace is found for the given ID') - }) - describe('getWorkspacePodContainers', () => { - fancy - .stub(kube.kubeConfig, 'makeApiClient', () => k8sApi) - .stub(k8sApi, 'listNamespacedPod', () => ({ response: '', body: { items: [{ metadata: { name: 'pod-name', labels: { 'che.workspace_id': workspace } }, spec : { containers : [{ name: theiaContainer }] } }] } })) - .it('should return pod name where workspace with the given ID is running', async () => { - const wsPodContainers = await ch.getWorkspacePodContainers(namespace, workspace) - expect(wsPodContainers).to.contain(theiaContainer) - }) - fancy - .stub(kube.kubeConfig, 'makeApiClient', () => k8sApi) - .stub(k8sApi, 'listNamespacedPod', () => ({ response: '', body: { items: [{ metadata: { name: 'pod-name', labels: { 'che.workspace_id': `${workspace}1` } }, spec : { containers : [{ name: theiaContainer }] } }] } })) - .do(() => ch.getWorkspacePodContainers(namespace, workspace)) - .catch(/Pod is not found for the given workspace ID/) - .it('should fail if no workspace is found for the given ID') - }) - describe('chePluginRegistryURL', () => { - fancy - .stub(kube, 'getNamespace', () => ({})) - .stub(kube, 'isOpenShift', () => true) - .stub(oc, 'routeExist', () => true) - .stub(oc, 'getRouteProtocol', () => 'https') - .stub(oc, 'getRouteHost', () => 'ocp-plugins-example.org') - .it('computes Plugin Registry URL on Openshift', async () => { - const chePluginRegistryURL = await ch.chePluginRegistryURL('che-namespace') - expect(chePluginRegistryURL).to.equals('https://ocp-plugins-example.org') - }) - fancy - .stub(kube, 'getNamespace', () => ({})) - .stub(kube, 'isIngressExist', () => true) - .stub(kube, 'isOpenShift', () => false) - .stub(kube, 'getIngressProtocol', () => 'https') - .stub(kube, 'getIngressHost', () => 'example.org') - .it('computes Plugin Registry URL on K8s', async () => { - const chePluginRegistryURL = await ch.chePluginRegistryURL('che-namespace') - expect(chePluginRegistryURL).to.equals('https://example.org') - }) - fancy - .stub(kube, 'getNamespace', () => ({})) - .stub(kube, 'isIngressExist', () => false) - .stub(kube, 'isOpenShift', () => true) - .stub(oc, 'routeExist', () => false) - .do(() => ch.chePluginRegistryURL('che-namespace')) //ERR_ROUTE_NO_EXIST - .catch(err => expect(err.message).to.match(/ERR_ROUTE_NO_EXIST/)) - .it('fails fetching Plugin Registry URL when ingress does not exist') - fancy - .stub(kube, 'getNamespace', () => ({})) - .stub(kube, 'isIngressExist', () => false) - .stub(kube, 'isOpenShift', () => false) - .do(() => ch.chePluginRegistryURL('che-namespace')) - .catch(err => expect(err.message).to.match(/ERR_INGRESS_NO_EXIST/)) - .it('fails fetching Plugin Registry URL when ingress does not exist') - fancy - .stub(kube, 'getNamespace', () => ({})) - .stub(kube, 'isOpenShift', () => true) - .stub(oc, 'routeExist', () => false) - .do(() => ch.chePluginRegistryURL('che-namespace')) - .catch(/ERR_ROUTE_NO_EXIST/) - .it('fails fetching Plugin Registry URL when route does not exist') - fancy - .stub(kube, 'getNamespace', () => undefined) - .do(() => ch.chePluginRegistryURL('che-namespace')) - .catch(err => expect(err.message).to.match(/ERR_NAMESPACE_NO_EXIST/)) - .it('fails fetching Plugin Registry URL when namespace does not exist') - }) describe('isSelfSignedCertificateSecretExist', () => { fancy .stub(kube, 'getSecret', () => true) diff --git a/test/e2e/e2e-rollback.test.ts b/test/e2e/e2e-rollback.test.ts index 43f1b2941..1178bb480 100644 --- a/test/e2e/e2e-rollback.test.ts +++ b/test/e2e/e2e-rollback.test.ts @@ -34,7 +34,7 @@ describe('Test rollback Che update', () => { // Retrieve pre-latest and latest stable Che version [previousCheVersion, latestCheVersion] = await helper.getTwoLatestReleasedVersions(INSTALLER) - let deployCommand = `${binChectl} server:deploy --batch --platform=${PLATFORM} --installer=${INSTALLER} --version=${previousCheVersion} --chenamespace=${NAMESPACE} --telemetry=off --che-operator-cr-patch-yaml=test/e2e/resources/cr-patch.yaml` + let deployCommand = `${binChectl} server:deploy --workspace-engine=dev-workspace --batch --platform=${PLATFORM} --installer=${INSTALLER} --version=${previousCheVersion} --chenamespace=${NAMESPACE} --telemetry=off` if (INSTALLER === 'olm') { deployCommand += ` --olm-channel=${OLM_CHANNEL}` } diff --git a/test/e2e/e2e-upgrade.test.ts b/test/e2e/e2e-upgrade.test.ts index 60e1f2e15..f7bb5c400 100644 --- a/test/e2e/e2e-upgrade.test.ts +++ b/test/e2e/e2e-upgrade.test.ts @@ -33,7 +33,7 @@ describe('Test Che upgrade', () => { // Retrieve latest stable Che version cheVersion = await helper.getLatestReleasedVersion() - const deployCommand = `${binChectl} server:deploy --batch --platform=${PLATFORM} --installer=${INSTALLER} --version=${cheVersion} --chenamespace=${NAMESPACE} --telemetry=off --che-operator-cr-patch-yaml=test/e2e/resources/cr-patch.yaml` + const deployCommand = `${binChectl} server:deploy --batch --workspace-engine=dev-workspace --platform=${PLATFORM} --installer=${INSTALLER} --version=${cheVersion} --chenamespace=${NAMESPACE} --telemetry=off` await helper.runCliCommand(deployCommand) await helper.waitForVersionInCheCR(cheVersion, CHE_VERSION_TIMEOUT_MS) diff --git a/test/e2e/e2e.test.ts b/test/e2e/e2e.test.ts index aab3512ae..6c9e1b668 100644 --- a/test/e2e/e2e.test.ts +++ b/test/e2e/e2e.test.ts @@ -27,49 +27,13 @@ const NAMESPACE = DEFAULT_OLM_SUGGESTED_NAMESPACE const PLATFORM = process.env.PLATFORM || '' const INSTALLER = process.env.INSTALLER || '' -const PLATFORM_OPENSHIFT = 'openshift' -const PLATFORM_CRC = 'crc' -const PLATFORM_MINISHIFT = 'minishift' -const PLATFORM_MINIKUBE = 'minikube' - -const INSTALLER_OPERATOR = 'operator' -const INSTALLER_OLM = 'olm' - function getDeployCommand(): string { - const cheVersion = helper.getNewVersion() + let command = `${binChectl} server:deploy --batch --workspace-engine=dev-workspace --platform=${PLATFORM} --installer=${INSTALLER} --chenamespace=${NAMESPACE} --telemetry=off` - let command: string - switch (PLATFORM) { - case PLATFORM_OPENSHIFT: - if (!(INSTALLER === INSTALLER_OPERATOR || INSTALLER === INSTALLER_OLM)) { - throw new Error(`Unknown installer ${INSTALLER}`) - } - command = `${binChectl} server:deploy --batch --platform=${PLATFORM} --installer=${INSTALLER} --chenamespace=${NAMESPACE} --telemetry=off --che-operator-cr-patch-yaml=test/e2e/resources/cr-patch.yaml` - break - - case PLATFORM_CRC: - case PLATFORM_MINISHIFT: - if (INSTALLER !== INSTALLER_OPERATOR) { - throw new Error(`Unknown installer ${INSTALLER}`) - } - command = `${binChectl} server:deploy --batch --platform=${PLATFORM} --installer=${INSTALLER} --chenamespace=${NAMESPACE} --telemetry=off --che-operator-cr-patch-yaml=test/e2e/resources/cr-patch.yaml` - break - - case PLATFORM_MINIKUBE: - if (INSTALLER !== INSTALLER_OPERATOR) { - throw new Error(`Unknown installer ${INSTALLER}`) - } - const patchOption = '--che-operator-cr-patch-yaml=test/e2e/resources/cr-patch.yaml' - command = `${binChectl} server:deploy --batch --platform=${PLATFORM} --installer=${INSTALLER} --telemetry=off --chenamespace=${NAMESPACE} ${patchOption} --multiuser --skip-cluster-availability-check` - break - - default: - throw new Error(`Unknown platform: ${PLATFORM}`) - } + const cheVersion = helper.getNewVersion() if (cheVersion != 'next') { command = command + ` --version=${cheVersion}` } - command = command + ` --workspace-engine=dev-workspace` return command } diff --git a/test/e2e/resources/cr-patch.yaml b/test/e2e/resources/cr-patch.yaml deleted file mode 100644 index 0569aa7ce..000000000 --- a/test/e2e/resources/cr-patch.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# 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 -# - -spec: - auth: - updateAdminPassword: false - openShiftoAuth: false diff --git a/yarn.lock b/yarn.lock index 02eeef462..ed418b23f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1630,14 +1630,6 @@ any-observable@^0.3.0: resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== -anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1671,19 +1663,12 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= - dependencies: - arr-flatten "^1.0.1" - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= -arr-flatten@^1.0.1, arr-flatten@^1.1.0: +arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== @@ -1698,11 +1683,6 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -1735,11 +1715,6 @@ astral-regex@^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" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - async@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" @@ -1845,14 +1820,6 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-runtime@^6.9.2: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -1893,11 +1860,6 @@ big-integer@^1.6.17: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - binary@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" @@ -1906,13 +1868,6 @@ binary@~0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" @@ -1922,15 +1877,6 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - bluebird@~3.4.1: version "3.4.7" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" @@ -1944,15 +1890,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -2138,7 +2075,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2151,11 +2088,6 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - charenc@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" @@ -2166,22 +2098,6 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chokidar@^1.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -2238,13 +2154,6 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - cli-progress@^3.4.0: version "3.8.2" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.8.2.tgz#abaf1fc6d6401351f16f068117a410554a0eb8c7" @@ -2253,11 +2162,6 @@ cli-progress@^3.4.0: colors "^1.1.2" string-width "^4.2.0" -cli-spinners@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" - integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== - cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -2298,11 +2202,6 @@ cli-ux@^5.2.1, cli-ux@^5.5.1, cli-ux@^5.6.3: supports-hyperlinks "^2.1.0" tslib "^2.0.0" -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -2319,11 +2218,6 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2430,11 +2324,6 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js@^2.4.0: - version "2.6.11" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" - integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2445,23 +2334,6 @@ countries-and-timezones@*, countries-and-timezones@^3.2.3: resolved "https://registry.yarnpkg.com/countries-and-timezones/-/countries-and-timezones-3.2.3.tgz#3b158405a0e7c576e2b801a70ca09b39dc890ecf" integrity sha512-0thBj2T0cFndkCSRAmcMdUXISgX4lZIwqyPg3XfstNy5zT2EGgy5Hx+J4k5GU6X5ZLlveRW8KF7WhLH3/qMp4g== -cpx@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/cpx/-/cpx-1.5.0.tgz#185be018511d87270dedccc293171e37655ab88f" - integrity sha1-GFvgGFEdhycN7czCkxceN2VauI8= - dependencies: - babel-runtime "^6.9.2" - chokidar "^1.6.0" - duplexer "^0.1.1" - glob "^7.0.5" - glob2base "^0.0.12" - minimatch "^3.0.2" - mkdirp "^0.5.1" - resolve "^1.1.7" - safe-buffer "^5.0.1" - shell-quote "^1.6.1" - subarg "^1.0.0" - create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2588,13 +2460,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - defer-to-connect@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" @@ -2685,11 +2550,6 @@ duplexer2@~0.1.4: dependencies: readable-stream "^2.0.2" -duplexer@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -2700,7 +2560,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#aa46157554ae5c53a5ea87c05100543c58853121" + resolved "git://github.com/eclipse-che/che-operator#100e6a448ad40a7efd93de4a4933f87ec8f27ee2" editorconfig@^0.15.0: version "0.15.3" @@ -3067,13 +2927,6 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= - dependencies: - is-posix-bracket "^0.1.0" - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -3087,13 +2940,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - expect@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" @@ -3126,22 +2972,6 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= - dependencies: - is-extglob "^1.0.0" - extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -3240,13 +3070,6 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - 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" @@ -3254,32 +3077,11 @@ file-entry-cache@^6.0.1: 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" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= - filesize@^6.1.0: version "6.4.0" resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.4.0.tgz#914f50471dd66fdca3cefe628bd0cde4ef769bcd" integrity sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ== -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -3297,11 +3099,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-index@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" - integrity sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ= - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -3335,18 +3132,11 @@ follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e" integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw== -for-in@^1.0.1, for-in@^1.0.2: +for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -3431,14 +3221,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.0.0: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - fsevents@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" @@ -3534,21 +3316,6 @@ github-slugger@^1.2.1: dependencies: emoji-regex ">=6.0.0 <=6.1.1" -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - glob-parent@^5.1.0, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3556,14 +3323,7 @@ glob-parent@^5.1.0, glob-parent@^5.1.2: 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" - integrity sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY= - dependencies: - find-index "^0.1.1" - -glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, 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== @@ -3601,7 +3361,7 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^11, globby@^11.0.1, globby@^11.0.3: +globby@^11.0.1, globby@^11.0.3: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -3635,7 +3395,7 @@ got@^11.8.0: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -3786,7 +3546,7 @@ hyperlinker@^1.0.0: resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3852,31 +3612,11 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.0.tgz#f44f008dd344bbfc4b30031f45d984e034a3ac3a" - integrity sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.2.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -3906,13 +3646,6 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3969,18 +3702,6 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= - dependencies: - is-primitive "^2.0.0" - is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -3993,11 +3714,6 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -4025,13 +3741,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= - dependencies: - is-extglob "^1.0.0" - 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" @@ -4039,18 +3748,6 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -4058,11 +3755,6 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -4092,21 +3784,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= - is-potential-custom-element-name@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= - is-promise@^2.1.0: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" @@ -4132,11 +3814,6 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4986,14 +4663,6 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - log-update@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -5061,11 +4730,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -math-random@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" - integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== - md5@^2.2.1: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -5094,26 +4758,7 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^2.1.5: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -5172,14 +4817,14 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -5217,7 +4862,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.1: +"mkdirp@>=0.5 0": version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -5249,16 +4894,6 @@ ms@^2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nan@^2.12.1: - version "2.14.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" - integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== - nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -5365,7 +5000,7 @@ normalize-package-data@^3.0.0: semver "^7.3.4" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: +normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= @@ -5442,14 +5077,6 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -5520,21 +5147,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - os-locale@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-5.0.0.tgz#6d26c1d95b6597c5d5317bf5fba37eccec3672e0" @@ -5544,11 +5156,6 @@ os-locale@^5.0.0: lcid "^3.0.0" mem "^5.0.0" -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - p-cancelable@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" @@ -5605,16 +5212,6 @@ parent-module@^1.0.0: 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" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -5725,11 +5322,6 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= - pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -5820,11 +5412,6 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -querystring@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== - quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -5835,15 +5422,6 @@ ramda@^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" - integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -5890,15 +5468,6 @@ readable-stream@^3.1.1, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -5913,18 +5482,6 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== - dependencies: - is-equal-shallow "^0.1.3" - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -5958,7 +5515,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -6047,7 +5604,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -6077,14 +5634,6 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -6119,11 +5668,6 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - run-parallel@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -6248,11 +5792,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" - integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== - shelljs@^0.8.2: version "0.8.4" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" @@ -6559,13 +6098,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^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" - integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= - dependencies: - minimist "^1.1.0" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -6682,11 +6214,6 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - tmp-promise@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.2.tgz#6e933782abff8b00c3119d63589ca1fb9caaa62a" @@ -6694,13 +6221,6 @@ tmp-promise@^3.0.2: dependencies: tmp "^0.2.0" -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - tmp@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" @@ -7056,13 +6576,6 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"