diff --git a/.github/workflows/flow-task-test.yaml b/.github/workflows/flow-task-test.yaml
index 6aa191d33..8d4792dca 100644
--- a/.github/workflows/flow-task-test.yaml
+++ b/.github/workflows/flow-task-test.yaml
@@ -63,13 +63,6 @@ jobs:
verbosity: 3
wait: 120s
- - name: Install Dependencies
- id: npm-deps
- run: npm ci
-
- - name: Compile Project
- run: npm run build
-
- name: Run Example Task File Test
run: |
task default-with-relay
diff --git a/.github/workflows/zxc-update-readme.yaml b/.github/workflows/zxc-update-readme.yaml
index 49dac6020..903e4a1ef 100644
--- a/.github/workflows/zxc-update-readme.yaml
+++ b/.github/workflows/zxc-update-readme.yaml
@@ -146,7 +146,7 @@ jobs:
if : ${{ github.event.inputs.dry-run-enabled != 'true' && !cancelled() && !failure() && inputs.commit-changes }}
uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1
with:
- commit_message: "auto update docs/content/User/StepByStepGuide.md [skip ci]"
+ commit_message: "chore: auto update docs/content/User/StepByStepGuide.md"
commit_options: '--no-verify --signoff'
add_options: '-u'
file_pattern: 'docs/content/User/StepByStepGuide.md'
diff --git a/README.md b/README.md
index f52604dd4..1728baa0c 100644
--- a/README.md
+++ b/README.md
@@ -63,4 +63,4 @@ expected to uphold this code of conduct.
## License
-[Apache License 2.0](LICENSE)
+[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)
diff --git a/Taskfile.helper.yml b/Taskfile.helper.yml
index ccc17a41d..2e6f9760e 100644
--- a/Taskfile.helper.yml
+++ b/Taskfile.helper.yml
@@ -33,6 +33,7 @@ env:
tasks:
init:
cmds:
+ - task: "install:solo"
- task: "var:check"
- task: "run:build"
diff --git a/Taskfile.yml b/Taskfile.yml
index 1f7261e03..5994169b9 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -21,7 +21,6 @@ tasks:
- echo "This command is meant to deploy a Solo network to a Kind cluster on your local machine, "
- echo "ctrl-c if this is not what you want to do."
- sleep 5
- - task: "install:solo"
- task: "install"
- task: "start"
diff --git a/docs/content/User/SDK.md b/docs/content/User/SDK.md
index 3156e1974..cbb5ef1f8 100644
--- a/docs/content/User/SDK.md
+++ b/docs/content/User/SDK.md
@@ -1,8 +1,8 @@
# Using Solo with Hedera JavaScript SDK
First, please follow solo repository README to install solo and Docker Desktop.
-You also need to install the Taskfile tool following the instructions here:
-https://taskfile.dev/installation/
+You also need to install the Taskfile tool following the instructions [here](https://taskfile.dev/installation/).
+
Then we start with launching a local Solo network with the following commands:
diff --git a/docs/layouts/_default/_markup/render-link.html b/docs/layouts/_default/_markup/render-link.html
index 22e9e912d..aebd36bc9 100644
--- a/docs/layouts/_default/_markup/render-link.html
+++ b/docs/layouts/_default/_markup/render-link.html
@@ -1,2 +1,6 @@
+{{ $destination := .Destination }}
+{{ if strings.HasPrefix $destination "http" }}
+{{ .Text | safeHTML }}
+{{ else }}
{{ .Text | safeHTML }}
-
+{{ end }}
diff --git a/examples/Taskfile.examples.yml b/examples/Taskfile.examples.yml
index 38ac0d4db..f8db6a49b 100644
--- a/examples/Taskfile.examples.yml
+++ b/examples/Taskfile.examples.yml
@@ -11,7 +11,6 @@ tasks:
cmds:
- task: "install:kubectl:darwin"
- task: "install:kubectl:linux"
- - task: "install:solo"
- task: "install"
- task: "start"
diff --git a/package-lock.json b/package-lock.json
index e68bd5c09..0771cb67a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -71,7 +71,7 @@
"@types/uuid": "^10.0.0",
"@types/ws": "^8.5.13",
"@types/yargs": "^17.0.33",
- "@typescript-eslint/utils": "^8.19.0",
+ "@typescript-eslint/utils": "^8.19.1",
"c8": "^10.1.3",
"chai": "^5.1.2",
"chai-as-promised": "^8.0.1",
@@ -105,7 +105,7 @@
"tsx": "^4.19.2",
"typedoc": "^0.27.6",
"typescript": "^5.7.2",
- "typescript-eslint": "^8.19.0"
+ "typescript-eslint": "^8.19.1"
},
"engines": {
"node": ">=20.14.0",
@@ -2863,20 +2863,20 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz",
- "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz",
+ "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.19.0",
- "@typescript-eslint/type-utils": "8.19.0",
- "@typescript-eslint/utils": "8.19.0",
- "@typescript-eslint/visitor-keys": "8.19.0",
+ "@typescript-eslint/scope-manager": "8.19.1",
+ "@typescript-eslint/type-utils": "8.19.1",
+ "@typescript-eslint/utils": "8.19.1",
+ "@typescript-eslint/visitor-keys": "8.19.1",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2891,16 +2891,28 @@
"typescript": ">=4.8.4 <5.8.0"
}
},
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
+ "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
"node_modules/@typescript-eslint/parser": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz",
- "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz",
+ "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "8.19.0",
- "@typescript-eslint/types": "8.19.0",
- "@typescript-eslint/typescript-estree": "8.19.0",
- "@typescript-eslint/visitor-keys": "8.19.0",
+ "@typescript-eslint/scope-manager": "8.19.1",
+ "@typescript-eslint/types": "8.19.1",
+ "@typescript-eslint/typescript-estree": "8.19.1",
+ "@typescript-eslint/visitor-keys": "8.19.1",
"debug": "^4.3.4"
},
"engines": {
@@ -2916,13 +2928,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz",
- "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz",
+ "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "8.19.0",
- "@typescript-eslint/visitor-keys": "8.19.0"
+ "@typescript-eslint/types": "8.19.1",
+ "@typescript-eslint/visitor-keys": "8.19.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2933,15 +2945,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz",
- "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz",
+ "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.19.0",
- "@typescript-eslint/utils": "8.19.0",
+ "@typescript-eslint/typescript-estree": "8.19.1",
+ "@typescript-eslint/utils": "8.19.1",
"debug": "^4.3.4",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2955,10 +2967,22 @@
"typescript": ">=4.8.4 <5.8.0"
}
},
+ "node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
+ "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
"node_modules/@typescript-eslint/types": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz",
- "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz",
+ "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2969,19 +2993,19 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz",
- "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz",
+ "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "8.19.0",
- "@typescript-eslint/visitor-keys": "8.19.0",
+ "@typescript-eslint/types": "8.19.1",
+ "@typescript-eslint/visitor-keys": "8.19.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3018,16 +3042,28 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/ts-api-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
+ "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
"node_modules/@typescript-eslint/utils": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz",
- "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz",
+ "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.19.0",
- "@typescript-eslint/types": "8.19.0",
- "@typescript-eslint/typescript-estree": "8.19.0"
+ "@typescript-eslint/scope-manager": "8.19.1",
+ "@typescript-eslint/types": "8.19.1",
+ "@typescript-eslint/typescript-estree": "8.19.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3042,12 +3078,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz",
- "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz",
+ "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "8.19.0",
+ "@typescript-eslint/types": "8.19.1",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@@ -12357,14 +12393,14 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.0.tgz",
- "integrity": "sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==",
+ "version": "8.19.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.1.tgz",
+ "integrity": "sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.19.0",
- "@typescript-eslint/parser": "8.19.0",
- "@typescript-eslint/utils": "8.19.0"
+ "@typescript-eslint/eslint-plugin": "8.19.1",
+ "@typescript-eslint/parser": "8.19.1",
+ "@typescript-eslint/utils": "8.19.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/package.json b/package.json
index acba6cc2c..72b73686f 100644
--- a/package.json
+++ b/package.json
@@ -100,7 +100,7 @@
"@types/uuid": "^10.0.0",
"@types/ws": "^8.5.13",
"@types/yargs": "^17.0.33",
- "@typescript-eslint/utils": "^8.19.0",
+ "@typescript-eslint/utils": "^8.19.1",
"c8": "^10.1.3",
"chai": "^5.1.2",
"chai-as-promised": "^8.0.1",
@@ -134,7 +134,7 @@
"tsx": "^4.19.2",
"typedoc": "^0.27.6",
"typescript": "^5.7.2",
- "typescript-eslint": "^8.19.0"
+ "typescript-eslint": "^8.19.1"
},
"repository": {
"type": "git",
diff --git a/src/commands/base.ts b/src/commands/base.ts
index cecdcbaca..38e756761 100644
--- a/src/commands/base.ts
+++ b/src/commands/base.ts
@@ -98,6 +98,10 @@ export abstract class BaseCommand extends ShellRunner {
return this.configManager;
}
+ getChartManager(): ChartManager {
+ return this.chartManager;
+ }
+
/**
* Dynamically builds a class with properties from the provided list of flags
* and extra properties, will keep track of which properties are used. Call
@@ -171,6 +175,10 @@ export abstract class BaseCommand extends ShellRunner {
return newConfigInstance;
}
+ getLeaseManager(): LeaseManager {
+ return this.leaseManager;
+ }
+
/**
* Get the list of unused configurations that were not accessed
* @returns an array of unused configurations
diff --git a/src/commands/cluster.ts b/src/commands/cluster.ts
deleted file mode 100644
index ee9beb889..000000000
--- a/src/commands/cluster.ts
+++ /dev/null
@@ -1,394 +0,0 @@
-/**
- * Copyright (C) 2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-import {ListrEnquirerPromptAdapter} from '@listr2/prompt-adapter-enquirer';
-import {Listr} from 'listr2';
-import {SoloError} from '../core/errors.js';
-import {Flags as flags} from './flags.js';
-import {BaseCommand} from './base.js';
-import chalk from 'chalk';
-import * as constants from '../core/constants.js';
-import path from 'path';
-import {ListrLease} from '../core/lease/listr_lease.js';
-import {type CommandBuilder} from '../types/aliases.js';
-
-/**
- * Define the core functionalities of 'cluster' command
- */
-export class ClusterCommand extends BaseCommand {
- showClusterList() {
- this.logger.showList('Clusters', this.k8.getClusters());
- return true;
- }
-
- /** Get cluster-info for the given cluster name */
- getClusterInfo() {
- try {
- const cluster = this.k8.getKubeConfig().getCurrentCluster();
- this.logger.showJSON(`Cluster Information (${cluster.name})`, cluster);
- this.logger.showUser('\n');
- return true;
- } catch (e: Error | any) {
- this.logger.showUserError(e);
- }
-
- return false;
- }
-
- /** Show list of installed chart */
- async showInstalledChartList(clusterSetupNamespace: string) {
- this.logger.showList('Installed Charts', await this.chartManager.getInstalledCharts(clusterSetupNamespace));
- }
-
- /** Setup cluster with shared components */
- async setup(argv: any) {
- const self = this;
-
- interface Context {
- config: {
- chartDir: string;
- clusterSetupNamespace: string;
- deployCertManager: boolean;
- deployCertManagerCrds: boolean;
- deployMinio: boolean;
- deployPrometheusStack: boolean;
- soloChartVersion: string;
- };
- isChartInstalled: boolean;
- chartPath: string;
- valuesArg: string;
- }
-
- const tasks = new Listr(
- [
- {
- title: 'Initialize',
- task: async (ctx, task) => {
- self.configManager.update(argv);
- flags.disablePrompts([flags.chartDirectory]);
-
- await self.configManager.executePrompt(task, [
- flags.chartDirectory,
- flags.clusterSetupNamespace,
- flags.deployCertManager,
- flags.deployCertManagerCrds,
- flags.deployMinio,
- flags.deployPrometheusStack,
- ]);
-
- ctx.config = {
- chartDir: self.configManager.getFlag(flags.chartDirectory) as string,
- clusterSetupNamespace: self.configManager.getFlag(flags.clusterSetupNamespace) as string,
- deployCertManager: self.configManager.getFlag(flags.deployCertManager) as boolean,
- deployCertManagerCrds: self.configManager.getFlag(flags.deployCertManagerCrds) as boolean,
- deployMinio: self.configManager.getFlag(flags.deployMinio) as boolean,
- deployPrometheusStack: self.configManager.getFlag(flags.deployPrometheusStack) as boolean,
- soloChartVersion: self.configManager.getFlag(flags.soloChartVersion) as string,
- };
-
- self.logger.debug('Prepare ctx.config', {config: ctx.config, argv});
-
- ctx.isChartInstalled = await this.chartManager.isChartInstalled(
- ctx.config.clusterSetupNamespace,
- constants.SOLO_CLUSTER_SETUP_CHART,
- );
- },
- },
- {
- title: 'Prepare chart values',
- task: async (ctx, _) => {
- ctx.chartPath = await this.prepareChartPath(
- ctx.config.chartDir,
- constants.SOLO_TESTING_CHART_URL,
- constants.SOLO_CLUSTER_SETUP_CHART,
- );
- ctx.valuesArg = this.prepareValuesArg(
- ctx.config.chartDir,
- ctx.config.deployPrometheusStack,
- ctx.config.deployMinio,
- ctx.config.deployCertManager,
- ctx.config.deployCertManagerCrds,
- );
- },
- skip: ctx => ctx.isChartInstalled,
- },
- {
- title: `Install '${constants.SOLO_CLUSTER_SETUP_CHART}' chart`,
- task: async (ctx, _) => {
- const clusterSetupNamespace = ctx.config.clusterSetupNamespace;
- const version = ctx.config.soloChartVersion;
- const valuesArg = ctx.valuesArg;
-
- try {
- self.logger.debug(`Installing chart chartPath = ${ctx.chartPath}, version = ${version}`);
- await self.chartManager.install(
- clusterSetupNamespace,
- constants.SOLO_CLUSTER_SETUP_CHART,
- ctx.chartPath,
- version,
- valuesArg,
- );
- } catch (e: Error | any) {
- // if error, uninstall the chart and rethrow the error
- self.logger.debug(
- `Error on installing ${constants.SOLO_CLUSTER_SETUP_CHART}. attempting to rollback by uninstalling the chart`,
- e,
- );
- try {
- await self.chartManager.uninstall(clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART);
- } catch (ex) {
- // ignore error during uninstall since we are doing the best-effort uninstall here
- }
-
- throw e;
- }
-
- if (argv.dev) {
- await self.showInstalledChartList(clusterSetupNamespace);
- }
- },
- skip: ctx => ctx.isChartInstalled,
- },
- ],
- {
- concurrent: false,
- rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
- },
- );
-
- try {
- await tasks.run();
- } catch (e: Error | any) {
- throw new SoloError('Error on cluster setup', e);
- }
-
- return true;
- }
-
- async reset(argv: any) {
- const self = this;
- const lease = await self.leaseManager.create();
-
- interface Context {
- config: {
- clusterName: string;
- clusterSetupNamespace: string;
- };
- isChartInstalled: boolean;
- }
-
- const tasks = new Listr(
- [
- {
- title: 'Initialize',
- task: async (ctx, task) => {
- if (!argv[flags.force.name]) {
- const confirm = await task.prompt(ListrEnquirerPromptAdapter).run({
- type: 'toggle',
- default: false,
- message: 'Are you sure you would like to uninstall solo-cluster-setup chart?',
- });
-
- if (!confirm) {
- process.exit(0);
- }
- }
-
- self.configManager.update(argv);
- ctx.config = {
- clusterName: self.configManager.getFlag(flags.clusterName) as string,
- clusterSetupNamespace: self.configManager.getFlag(flags.clusterSetupNamespace) as string,
- };
-
- ctx.isChartInstalled = await this.chartManager.isChartInstalled(
- ctx.config.clusterSetupNamespace,
- constants.SOLO_CLUSTER_SETUP_CHART,
- );
- if (!ctx.isChartInstalled) {
- throw new SoloError('No chart found for the cluster');
- }
-
- return ListrLease.newAcquireLeaseTask(lease, task);
- },
- },
- {
- title: `Uninstall '${constants.SOLO_CLUSTER_SETUP_CHART}' chart`,
- task: async (ctx, _) => {
- const clusterSetupNamespace = ctx.config.clusterSetupNamespace;
- await self.chartManager.uninstall(clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART);
- if (argv.dev) {
- await self.showInstalledChartList(clusterSetupNamespace);
- }
- },
- skip: ctx => !ctx.isChartInstalled,
- },
- ],
- {
- concurrent: false,
- rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
- },
- );
-
- try {
- await tasks.run();
- } catch (e: Error | any) {
- throw new SoloError('Error on cluster reset', e);
- } finally {
- await lease.release();
- }
-
- return true;
- }
-
- /** Return Yargs command definition for 'cluster' command */
- getCommandDefinition(): {command: string; desc: string; builder: CommandBuilder} {
- const self = this;
- return {
- command: 'cluster',
- desc: 'Manage solo testing cluster',
- builder: (yargs: any) => {
- return yargs
- .command({
- command: 'list',
- desc: 'List all available clusters',
- handler: (argv: any) => {
- self.logger.debug("==== Running 'cluster list' ===", {argv});
-
- try {
- const r = self.showClusterList();
- self.logger.debug('==== Finished running `cluster list`====');
-
- if (!r) process.exit(1);
- } catch (err) {
- self.logger.showUserError(err);
- process.exit(1);
- }
- },
- })
- .command({
- command: 'info',
- desc: 'Get cluster info',
- handler: (argv: any) => {
- self.logger.debug("==== Running 'cluster info' ===", {argv});
- try {
- const r = this.getClusterInfo();
- self.logger.debug('==== Finished running `cluster info`====');
-
- if (!r) process.exit(1);
- } catch (err: Error | any) {
- self.logger.showUserError(err);
- process.exit(1);
- }
- },
- })
- .command({
- command: 'setup',
- desc: 'Setup cluster with shared components',
- builder: (y: any) =>
- flags.setCommandFlags(
- y,
- flags.chartDirectory,
- flags.clusterName,
- flags.clusterSetupNamespace,
- flags.deployCertManager,
- flags.deployCertManagerCrds,
- flags.deployMinio,
- flags.deployPrometheusStack,
- flags.quiet,
- flags.soloChartVersion,
- ),
- handler: (argv: any) => {
- self.logger.debug("==== Running 'cluster setup' ===", {argv});
-
- self
- .setup(argv)
- .then(r => {
- self.logger.debug('==== Finished running `cluster setup`====');
-
- if (!r) process.exit(1);
- })
- .catch(err => {
- self.logger.showUserError(err);
- process.exit(1);
- });
- },
- })
- .command({
- command: 'reset',
- desc: 'Uninstall shared components from cluster',
- builder: (y: any) =>
- flags.setCommandFlags(y, flags.clusterName, flags.clusterSetupNamespace, flags.force, flags.quiet),
- handler: (argv: any) => {
- self.logger.debug("==== Running 'cluster reset' ===", {argv});
-
- self
- .reset(argv)
- .then(r => {
- self.logger.debug('==== Finished running `cluster reset`====');
-
- if (!r) process.exit(1);
- })
- .catch(err => {
- self.logger.showUserError(err);
- process.exit(1);
- });
- },
- })
- .demandCommand(1, 'Select a cluster command');
- },
- };
- }
-
- /**
- * Prepare values arg for cluster setup command
- *
- * @param [chartDir] - local charts directory (default is empty)
- * @param [prometheusStackEnabled] - a bool to denote whether to install prometheus stack
- * @param [minioEnabled] - a bool to denote whether to install minio
- * @param [certManagerEnabled] - a bool to denote whether to install cert manager
- * @param [certManagerCrdsEnabled] - a bool to denote whether to install cert manager CRDs
- */
- prepareValuesArg(
- chartDir = flags.chartDirectory.definition.defaultValue as string,
- prometheusStackEnabled = flags.deployPrometheusStack.definition.defaultValue as boolean,
- minioEnabled = flags.deployMinio.definition.defaultValue as boolean,
- certManagerEnabled = flags.deployCertManager.definition.defaultValue as boolean,
- certManagerCrdsEnabled = flags.deployCertManagerCrds.definition.defaultValue as boolean,
- ) {
- let valuesArg = chartDir ? `-f ${path.join(chartDir, 'solo-cluster-setup', 'values.yaml')}` : '';
-
- valuesArg += ` --set cloud.prometheusStack.enabled=${prometheusStackEnabled}`;
- valuesArg += ` --set cloud.minio.enabled=${minioEnabled}`;
- valuesArg += ` --set cloud.certManager.enabled=${certManagerEnabled}`;
- valuesArg += ` --set cert-manager.installCRDs=${certManagerCrdsEnabled}`;
-
- if (certManagerEnabled && !certManagerCrdsEnabled) {
- this.logger.showUser(
- chalk.yellowBright('> WARNING:'),
- chalk.yellow(
- 'cert-manager CRDs are required for cert-manager, please enable it if you have not installed it independently.',
- ),
- );
- }
-
- return valuesArg;
- }
-
- close(): Promise {
- // no-op
- return Promise.resolve();
- }
-}
diff --git a/src/commands/cluster/configs.ts b/src/commands/cluster/configs.ts
new file mode 100644
index 000000000..f579e6670
--- /dev/null
+++ b/src/commands/cluster/configs.ts
@@ -0,0 +1,124 @@
+/**
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the ""License"");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an ""AS IS"" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import {type NodeAlias} from '../../types/aliases.js';
+import {Flags as flags} from '../flags.js';
+import * as constants from '../../core/constants.js';
+import {ListrEnquirerPromptAdapter} from '@listr2/prompt-adapter-enquirer';
+import {SoloError} from '../../core/errors.js';
+
+export const CONNECT_CONFIGS_NAME = 'connectConfig';
+
+export const connectConfigBuilder = async function (argv, ctx, task) {
+ const config = this.getConfig(CONNECT_CONFIGS_NAME, argv.flags, [
+ 'currentDeploymentName',
+ ]) as ClusterConnectConfigClass;
+
+ // set config in the context for later tasks to use
+ ctx.config = config;
+
+ return ctx.config;
+};
+
+export const setupConfigBuilder = async function (argv, ctx, task) {
+ const parent = this.parent;
+ const configManager = parent.getConfigManager();
+ configManager.update(argv);
+ flags.disablePrompts([flags.chartDirectory]);
+
+ await configManager.executePrompt(task, [
+ flags.chartDirectory,
+ flags.clusterSetupNamespace,
+ flags.deployCertManager,
+ flags.deployCertManagerCrds,
+ flags.deployMinio,
+ flags.deployPrometheusStack,
+ ]);
+
+ ctx.config = {
+ chartDir: configManager.getFlag(flags.chartDirectory) as string,
+ clusterSetupNamespace: configManager.getFlag(flags.clusterSetupNamespace) as string,
+ deployCertManager: configManager.getFlag(flags.deployCertManager) as boolean,
+ deployCertManagerCrds: configManager.getFlag(flags.deployCertManagerCrds) as boolean,
+ deployMinio: configManager.getFlag(flags.deployMinio) as boolean,
+ deployPrometheusStack: configManager.getFlag(flags.deployPrometheusStack) as boolean,
+ soloChartVersion: configManager.getFlag(flags.soloChartVersion) as string,
+ } as ClusterSetupConfigClass;
+
+ parent.logger.debug('Prepare ctx.config', {config: ctx.config, argv});
+
+ ctx.isChartInstalled = await parent
+ .getChartManager()
+ .isChartInstalled(ctx.config.clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART);
+
+ return ctx.config;
+};
+
+export const resetConfigBuilder = async function (argv, ctx, task) {
+ if (!argv[flags.force.name]) {
+ const confirm = await task.prompt(ListrEnquirerPromptAdapter).run({
+ type: 'toggle',
+ default: false,
+ message: 'Are you sure you would like to uninstall solo-cluster-setup chart?',
+ });
+
+ if (!confirm) {
+ process.exit(0);
+ }
+ }
+
+ this.parent.getConfigManager().update(argv);
+
+ ctx.config = {
+ clusterName: this.parent.getConfigManager().getFlag(flags.clusterName) as string,
+ clusterSetupNamespace: this.parent.getConfigManager().getFlag(flags.clusterSetupNamespace) as string,
+ } as ClusterResetConfigClass;
+
+ ctx.isChartInstalled = await this.parent
+ .getChartManager()
+ .isChartInstalled(ctx.config.clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART);
+ if (!ctx.isChartInstalled) {
+ throw new SoloError('No chart found for the cluster');
+ }
+
+ return ctx.config;
+};
+
+export interface ClusterConnectConfigClass {
+ app: string;
+ cacheDir: string;
+ devMode: boolean;
+ namespace: string;
+ nodeAlias: NodeAlias;
+ context: string;
+ clusterName: string;
+}
+
+export interface ClusterSetupConfigClass {
+ chartDir: string;
+ clusterSetupNamespace: string;
+ deployCertManager: boolean;
+ deployCertManagerCrds: boolean;
+ deployMinio: boolean;
+ deployPrometheusStack: boolean;
+ soloChartVersion: string;
+}
+
+export interface ClusterResetConfigClass {
+ clusterName: string;
+ clusterSetupNamespace: string;
+}
diff --git a/src/commands/context/flags.ts b/src/commands/cluster/flags.ts
similarity index 57%
rename from src/commands/context/flags.ts
rename to src/commands/cluster/flags.ts
index 2dc606063..fd318e757 100644
--- a/src/commands/context/flags.ts
+++ b/src/commands/cluster/flags.ts
@@ -17,6 +17,34 @@
import {Flags as flags} from '../flags.js';
+export const DEFAULT_FLAGS = {
+ requiredFlags: [],
+ requiredFlagsWithDisabledPrompt: [],
+ optionalFlags: [],
+};
+
+export const SETUP_FLAGS = {
+ requiredFlags: [],
+ requiredFlagsWithDisabledPrompt: [],
+ optionalFlags: [
+ flags.chartDirectory,
+ flags.clusterName,
+ flags.clusterSetupNamespace,
+ flags.deployCertManager,
+ flags.deployCertManagerCrds,
+ flags.deployMinio,
+ flags.deployPrometheusStack,
+ flags.quiet,
+ flags.soloChartVersion,
+ ],
+};
+
+export const RESET_FLAGS = {
+ requiredFlags: [],
+ requiredFlagsWithDisabledPrompt: [],
+ optionalFlags: [flags.clusterName, flags.clusterSetupNamespace, flags.force, flags.quiet],
+};
+
export const USE_FLAGS = {
requiredFlags: [],
requiredFlagsWithDisabledPrompt: [],
diff --git a/src/commands/cluster/handlers.ts b/src/commands/cluster/handlers.ts
new file mode 100644
index 000000000..805df1473
--- /dev/null
+++ b/src/commands/cluster/handlers.ts
@@ -0,0 +1,147 @@
+/**
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the ""License"");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an ""AS IS"" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import {type BaseCommand, type CommandHandlers} from '../base.js';
+import {type ClusterCommandTasks} from './tasks.js';
+import * as helpers from '../../core/helpers.js';
+import * as constants from '../../core/constants.js';
+import * as ContextFlags from './flags.js';
+import {RemoteConfigTasks} from '../../core/config/remote/remote_config_tasks.js';
+import type {RemoteConfigManager} from '../../core/config/remote/remote_config_manager.js';
+import {connectConfigBuilder, resetConfigBuilder, setupConfigBuilder} from './configs.js';
+import {SoloError} from '../../core/errors.js';
+
+export class ClusterCommandHandlers implements CommandHandlers {
+ readonly parent: BaseCommand;
+ readonly tasks: ClusterCommandTasks;
+ public readonly remoteConfigManager: RemoteConfigManager;
+ private getConfig: any;
+
+ constructor(parent: BaseCommand, tasks: ClusterCommandTasks, remoteConfigManager: RemoteConfigManager) {
+ this.parent = parent;
+ this.tasks = tasks;
+ this.remoteConfigManager = remoteConfigManager;
+ this.getConfig = parent.getConfig.bind(parent);
+ }
+
+ async connect(argv: any) {
+ argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS);
+
+ const action = this.parent.commandActionBuilder(
+ [
+ this.tasks.initialize(argv, connectConfigBuilder.bind(this)),
+ this.parent.getLocalConfig().promptLocalConfigTask(this.parent.getK8()),
+ this.tasks.selectContext(argv),
+ RemoteConfigTasks.loadRemoteConfig.bind(this)(argv),
+ this.tasks.updateLocalConfig(argv),
+ ],
+ {
+ concurrent: false,
+ rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
+ },
+ 'cluster connect',
+ null,
+ );
+
+ await action(argv, this);
+ return true;
+ }
+
+ async list(argv: any) {
+ argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS);
+
+ const action = this.parent.commandActionBuilder(
+ [this.tasks.showClusterList()],
+ {
+ concurrent: false,
+ rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
+ },
+ 'cluster list',
+ null,
+ );
+
+ await action(argv, this);
+ return true;
+ }
+
+ async info(argv: any) {
+ argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS);
+
+ const action = this.parent.commandActionBuilder(
+ [this.tasks.getClusterInfo()],
+ {
+ concurrent: false,
+ rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
+ },
+ 'cluster info',
+ null,
+ );
+
+ await action(argv, this);
+ return true;
+ }
+
+ async setup(argv: any) {
+ argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS);
+
+ const action = this.parent.commandActionBuilder(
+ [
+ this.tasks.initialize(argv, setupConfigBuilder.bind(this)),
+ this.tasks.prepareChartValues(argv),
+ this.tasks.installClusterChart(argv),
+ ],
+ {
+ concurrent: false,
+ rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
+ },
+ 'cluster setup',
+ null,
+ );
+
+ try {
+ await action(argv, this);
+ } catch (e: Error | any) {
+ throw new SoloError('Error on cluster setup', e);
+ }
+
+ return true;
+ }
+
+ async reset(argv: any) {
+ argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS);
+
+ const action = this.parent.commandActionBuilder(
+ [
+ this.tasks.initialize(argv, resetConfigBuilder.bind(this)),
+ this.tasks.acquireNewLease(argv),
+ this.tasks.uninstallClusterChart(argv),
+ ],
+ {
+ concurrent: false,
+ rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
+ },
+ 'cluster reset',
+ null,
+ );
+
+ try {
+ await action(argv, this);
+ } catch (e: Error | any) {
+ throw new SoloError('Error on cluster reset', e);
+ }
+ return true;
+ }
+}
diff --git a/src/commands/cluster/index.ts b/src/commands/cluster/index.ts
new file mode 100644
index 000000000..016224b54
--- /dev/null
+++ b/src/commands/cluster/index.ts
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the ""License"");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an ""AS IS"" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import * as ContextFlags from './flags.js';
+import {YargsCommand} from '../../core/yargs_command.js';
+import {BaseCommand} from './../base.js';
+import {type Opts} from '../../types/command_types.js';
+import {ClusterCommandTasks} from './tasks.js';
+import {ClusterCommandHandlers} from './handlers.js';
+import {DEFAULT_FLAGS, RESET_FLAGS, SETUP_FLAGS} from './flags.js';
+
+/**
+ * Defines the core functionalities of 'node' command
+ */
+export class ClusterCommand extends BaseCommand {
+ public handlers: ClusterCommandHandlers;
+
+ constructor(opts: Opts) {
+ super(opts);
+
+ this.handlers = new ClusterCommandHandlers(this, new ClusterCommandTasks(this), this.remoteConfigManager);
+ }
+
+ getCommandDefinition() {
+ return {
+ command: 'cluster',
+ desc: 'Manage solo testing cluster',
+ builder: (yargs: any) => {
+ return yargs
+ .command(
+ new YargsCommand(
+ {
+ command: 'connect',
+ description: 'updates the local configuration by connecting a deployment to a k8s context',
+ commandDef: this,
+ handler: 'connect',
+ },
+ ContextFlags.USE_FLAGS,
+ ),
+ )
+ .command(
+ new YargsCommand(
+ {
+ command: 'list',
+ description: 'List all available clusters',
+ commandDef: this,
+ handler: 'list',
+ },
+ DEFAULT_FLAGS,
+ ),
+ )
+ .command(
+ new YargsCommand(
+ {
+ command: 'info',
+ description: 'Get cluster info',
+ commandDef: this,
+ handler: 'info',
+ },
+ DEFAULT_FLAGS,
+ ),
+ )
+ .command(
+ new YargsCommand(
+ {
+ command: 'setup',
+ description: 'Setup cluster with shared components',
+ commandDef: this,
+ handler: 'setup',
+ },
+ SETUP_FLAGS,
+ ),
+ )
+ .command(
+ new YargsCommand(
+ {
+ command: 'reset',
+ description: 'Uninstall shared components from cluster',
+ commandDef: this,
+ handler: 'reset',
+ },
+ RESET_FLAGS,
+ ),
+ )
+ .demandCommand(1, 'Select a context command');
+ },
+ };
+ }
+
+ close(): Promise {
+ // no-op
+ return Promise.resolve();
+ }
+}
diff --git a/src/commands/context/tasks.ts b/src/commands/cluster/tasks.ts
similarity index 57%
rename from src/commands/context/tasks.ts
rename to src/commands/cluster/tasks.ts
index e8474931c..1ca458b27 100644
--- a/src/commands/context/tasks.ts
+++ b/src/commands/cluster/tasks.ts
@@ -20,8 +20,12 @@ import type {ListrTaskWrapper} from 'listr2';
import type {ConfigBuilder} from '../../types/aliases.js';
import {type BaseCommand} from '../base.js';
import {splitFlagInput} from '../../core/helpers.js';
+import * as constants from '../../core/constants.js';
+import path from 'path';
+import chalk from 'chalk';
+import {ListrLease} from '../../core/lease/listr_lease.js';
-export class ContextCommandTasks {
+export class ClusterCommandTasks {
private readonly parent: BaseCommand;
constructor(parent) {
@@ -108,6 +112,49 @@ export class ContextCommandTasks {
}
}
+ /**
+ * Prepare values arg for cluster setup command
+ *
+ * @param [chartDir] - local charts directory (default is empty)
+ * @param [prometheusStackEnabled] - a bool to denote whether to install prometheus stack
+ * @param [minioEnabled] - a bool to denote whether to install minio
+ * @param [certManagerEnabled] - a bool to denote whether to install cert manager
+ * @param [certManagerCrdsEnabled] - a bool to denote whether to install cert manager CRDs
+ */
+ private prepareValuesArg(
+ chartDir = flags.chartDirectory.definition.defaultValue as string,
+ prometheusStackEnabled = flags.deployPrometheusStack.definition.defaultValue as boolean,
+ minioEnabled = flags.deployMinio.definition.defaultValue as boolean,
+ certManagerEnabled = flags.deployCertManager.definition.defaultValue as boolean,
+ certManagerCrdsEnabled = flags.deployCertManagerCrds.definition.defaultValue as boolean,
+ ) {
+ let valuesArg = chartDir ? `-f ${path.join(chartDir, 'solo-cluster-setup', 'values.yaml')}` : '';
+
+ valuesArg += ` --set cloud.prometheusStack.enabled=${prometheusStackEnabled}`;
+ valuesArg += ` --set cloud.minio.enabled=${minioEnabled}`;
+ valuesArg += ` --set cloud.certManager.enabled=${certManagerEnabled}`;
+ valuesArg += ` --set cert-manager.installCRDs=${certManagerCrdsEnabled}`;
+
+ if (certManagerEnabled && !certManagerCrdsEnabled) {
+ this.parent.logger.showUser(
+ chalk.yellowBright('> WARNING:'),
+ chalk.yellow(
+ 'cert-manager CRDs are required for cert-manager, please enable it if you have not installed it independently.',
+ ),
+ );
+ }
+
+ return valuesArg;
+ }
+
+ /** Show list of installed chart */
+ private async showInstalledChartList(clusterSetupNamespace: string) {
+ this.parent.logger.showList(
+ 'Installed Charts',
+ await this.parent.getChartManager().getInstalledCharts(clusterSetupNamespace),
+ );
+ }
+
selectContext(argv) {
return new Task('Read local configuration settings', async (ctx: any, task: ListrTaskWrapper) => {
this.parent.logger.info('Read local configuration settings...');
@@ -186,4 +233,102 @@ export class ContextCommandTasks {
ctx.config = await configInit(argv, ctx, task);
});
}
+
+ showClusterList() {
+ return new Task('List all available clusters', async (ctx: any, task: ListrTaskWrapper) => {
+ this.parent.logger.showList('Clusters', this.parent.getK8().getClusters());
+ });
+ }
+
+ getClusterInfo() {
+ return new Task('Get cluster info', async (ctx: any, task: ListrTaskWrapper) => {
+ try {
+ const cluster = this.parent.getK8().getKubeConfig().getCurrentCluster();
+ this.parent.logger.showJSON(`Cluster Information (${cluster.name})`, cluster);
+ this.parent.logger.showUser('\n');
+ } catch (e: Error | any) {
+ this.parent.logger.showUserError(e);
+ }
+ });
+ }
+
+ prepareChartValues(argv) {
+ return new Task(
+ 'Prepare chart values',
+ async (ctx: any, task: ListrTaskWrapper) => {
+ ctx.chartPath = await this.parent.prepareChartPath(
+ ctx.config.chartDir,
+ constants.SOLO_TESTING_CHART_URL,
+ constants.SOLO_CLUSTER_SETUP_CHART,
+ );
+ ctx.valuesArg = this.prepareValuesArg(
+ ctx.config.chartDir,
+ ctx.config.deployPrometheusStack,
+ ctx.config.deployMinio,
+ ctx.config.deployCertManager,
+ ctx.config.deployCertManagerCrds,
+ );
+ },
+ ctx => ctx.isChartInstalled,
+ );
+ }
+
+ installClusterChart(argv) {
+ const parent = this.parent;
+ return new Task(
+ `Install '${constants.SOLO_CLUSTER_SETUP_CHART}' chart`,
+ async (ctx: any, task: ListrTaskWrapper) => {
+ const clusterSetupNamespace = ctx.config.clusterSetupNamespace;
+ const version = ctx.config.soloChartVersion;
+ const valuesArg = ctx.valuesArg;
+
+ try {
+ parent.logger.debug(`Installing chart chartPath = ${ctx.chartPath}, version = ${version}`);
+ await parent
+ .getChartManager()
+ .install(clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART, ctx.chartPath, version, valuesArg);
+ } catch (e: Error | any) {
+ // if error, uninstall the chart and rethrow the error
+ parent.logger.debug(
+ `Error on installing ${constants.SOLO_CLUSTER_SETUP_CHART}. attempting to rollback by uninstalling the chart`,
+ e,
+ );
+ try {
+ await parent.getChartManager().uninstall(clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART);
+ } catch (ex) {
+ // ignore error during uninstall since we are doing the best-effort uninstall here
+ }
+
+ throw e;
+ }
+
+ if (argv.dev) {
+ await this.showInstalledChartList(clusterSetupNamespace);
+ }
+ },
+ ctx => ctx.isChartInstalled,
+ );
+ }
+
+ acquireNewLease(argv) {
+ return new Task('Acquire new lease', async (ctx: any, task: ListrTaskWrapper) => {
+ const lease = await this.parent.getLeaseManager().create();
+ return ListrLease.newAcquireLeaseTask(lease, task);
+ });
+ }
+
+ uninstallClusterChart(argv) {
+ const parent = this.parent;
+ return new Task(
+ `Uninstall '${constants.SOLO_CLUSTER_SETUP_CHART}' chart`,
+ async (ctx: any, task: ListrTaskWrapper) => {
+ const clusterSetupNamespace = ctx.config.clusterSetupNamespace;
+ await parent.getChartManager().uninstall(clusterSetupNamespace, constants.SOLO_CLUSTER_SETUP_CHART);
+ if (argv.dev) {
+ await this.showInstalledChartList(clusterSetupNamespace);
+ }
+ },
+ ctx => !ctx.isChartInstalled,
+ );
+ }
}
diff --git a/src/commands/context/configs.ts b/src/commands/context/configs.ts
deleted file mode 100644
index 6a60f41fe..000000000
--- a/src/commands/context/configs.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright (C) 2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import {type NodeAlias} from '../../types/aliases.js';
-
-export const CONNECT_CONFIGS_NAME = 'connectConfig';
-
-export const connectConfigBuilder = async function (argv, ctx, task) {
- const config = this.getConfig(CONNECT_CONFIGS_NAME, argv.flags, [
- 'currentDeploymentName',
- ]) as ContextConnectConfigClass;
-
- // set config in the context for later tasks to use
- ctx.config = config;
-
- return ctx.config;
-};
-
-export interface ContextConnectConfigClass {
- app: string;
- cacheDir: string;
- devMode: boolean;
- namespace: string;
- nodeAlias: NodeAlias;
- context: string;
- clusterName: string;
-}
diff --git a/src/commands/context/handlers.ts b/src/commands/context/handlers.ts
deleted file mode 100644
index 3c183ca08..000000000
--- a/src/commands/context/handlers.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright (C) 2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-import {type BaseCommand, type CommandHandlers} from '../base.js';
-import {type ContextCommandTasks} from './tasks.js';
-import * as helpers from '../../core/helpers.js';
-import * as constants from '../../core/constants.js';
-import * as ContextFlags from './flags.js';
-import {RemoteConfigTasks} from '../../core/config/remote/remote_config_tasks.js';
-import type {RemoteConfigManager} from '../../core/config/remote/remote_config_manager.js';
-import {connectConfigBuilder} from './configs.js';
-
-export class ContextCommandHandlers implements CommandHandlers {
- readonly parent: BaseCommand;
- readonly tasks: ContextCommandTasks;
- public readonly remoteConfigManager: RemoteConfigManager;
- private getConfig: any;
-
- constructor(parent: BaseCommand, tasks: ContextCommandTasks, remoteConfigManager: RemoteConfigManager) {
- this.parent = parent;
- this.tasks = tasks;
- this.remoteConfigManager = remoteConfigManager;
- this.getConfig = parent.getConfig.bind(parent);
- }
-
- async connect(argv: any) {
- argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS);
-
- const action = this.parent.commandActionBuilder(
- [
- this.tasks.initialize(argv, connectConfigBuilder.bind(this)),
- this.parent.getLocalConfig().promptLocalConfigTask(this.parent.getK8()),
- this.tasks.selectContext(argv),
- RemoteConfigTasks.loadRemoteConfig.bind(this)(argv),
- this.tasks.updateLocalConfig(argv),
- ],
- {
- concurrent: false,
- rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
- },
- 'context use',
- null,
- );
-
- await action(argv, this);
- return true;
- }
-}
diff --git a/src/commands/context/index.ts b/src/commands/context/index.ts
deleted file mode 100644
index 54fee60ad..000000000
--- a/src/commands/context/index.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import * as ContextFlags from './flags.js';
-import {YargsCommand} from '../../core/yargs_command.js';
-import {BaseCommand} from './../base.js';
-import {type Opts} from '../../types/command_types.js';
-import {ContextCommandTasks} from './tasks.js';
-import {ContextCommandHandlers} from './handlers.js';
-
-/**
- * Defines the core functionalities of 'node' command
- */
-export class ContextCommand extends BaseCommand {
- private handlers: ContextCommandHandlers;
-
- constructor(opts: Opts) {
- super(opts);
-
- this.handlers = new ContextCommandHandlers(this, new ContextCommandTasks(this), this.remoteConfigManager);
- }
-
- getCommandDefinition() {
- return {
- command: 'context',
- desc: 'Manage local and remote configurations',
- builder: (yargs: any) => {
- return yargs
- .command(
- new YargsCommand(
- {
- command: 'connect',
- description: 'updates the local configuration by connecting a deployment to a k8s context',
- commandDef: this,
- handler: 'connect',
- },
- ContextFlags.USE_FLAGS,
- ),
- )
- .demandCommand(1, 'Select a context command');
- },
- };
- }
-
- close(): Promise {
- // no-op
- return Promise.resolve();
- }
-}
diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts
index 4e6e0c0a5..74deb0f06 100644
--- a/src/commands/deployment.ts
+++ b/src/commands/deployment.ts
@@ -60,7 +60,11 @@ export class DeploymentCommand extends BaseCommand {
self.configManager.update(argv);
self.logger.debug('Updated config with argv', {config: self.configManager.config});
- await self.configManager.executePrompt(task, DeploymentCommand.DEPLOY_FLAGS_LIST);
+ await self.configManager.executePrompt(task, [
+ flags.contextClusterUnparsed,
+ flags.namespace,
+ flags.deploymentClusters,
+ ]);
ctx.config = {
contextClusterUnparsed: self.configManager.getFlag(flags.contextClusterUnparsed),
diff --git a/src/commands/index.ts b/src/commands/index.ts
index 4b717ca83..dec7a7de5 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*
*/
-import {ClusterCommand} from './cluster.js';
-import {ContextCommand} from './context/index.js';
+import {ClusterCommand} from './cluster/index.js';
import {InitCommand} from './init.js';
import {MirrorNodeCommand} from './mirror_node.js';
import {NetworkCommand} from './network.js';
@@ -33,7 +32,6 @@ import {type Opts} from '../types/command_types.js';
export function Initialize(opts: Opts) {
const initCmd = new InitCommand(opts);
const clusterCmd = new ClusterCommand(opts);
- const contextCmd = new ContextCommand(opts);
const networkCommand = new NetworkCommand(opts);
const nodeCmd = new NodeCommand(opts);
const relayCmd = new RelayCommand(opts);
@@ -45,7 +43,6 @@ export function Initialize(opts: Opts) {
initCmd.getCommandDefinition(),
accountCmd.getCommandDefinition(),
clusterCmd.getCommandDefinition(),
- contextCmd.getCommandDefinition(),
networkCommand.getCommandDefinition(),
nodeCmd.getCommandDefinition(),
relayCmd.getCommandDefinition(),
diff --git a/src/core/config/local_config.ts b/src/core/config/local_config.ts
index ce0712568..9dbfb910d 100644
--- a/src/core/config/local_config.ts
+++ b/src/core/config/local_config.ts
@@ -15,7 +15,6 @@
*
*/
import {IsEmail, IsNotEmpty, IsObject, IsString, validateSync} from 'class-validator';
-import type {ListrTask, ListrTaskWrapper} from 'listr2';
import fs from 'fs';
import * as yaml from 'yaml';
import {Flags as flags} from '../../commands/flags.js';
@@ -35,6 +34,7 @@ import {type K8} from '../k8.js';
import {splitFlagInput} from '../helpers.js';
import {inject, injectable} from 'tsyringe-neo';
import {patchInject} from '../container_helper.js';
+import type {SoloListrTask, SoloListrTaskWrapper} from '../../types/index.js';
@injectable()
export class LocalConfig implements LocalConfigData {
@@ -162,13 +162,17 @@ export class LocalConfig implements LocalConfigData {
this.logger.info(`Wrote local config to ${this.filePath}: ${yamlContent}`);
}
- public promptLocalConfigTask(k8: K8): ListrTask {
+ public promptLocalConfigTask(k8: K8): SoloListrTask {
const self = this;
return {
title: 'Prompt local configuration',
skip: this.skipPromptTask,
- task: async (_: any, task: ListrTaskWrapper): Promise => {
+ task: async (_: any, task: SoloListrTaskWrapper): Promise => {
+ if (self.configFileExists) {
+ self.configManager.setFlag(flags.userEmailAddress, self.userEmailAddress);
+ }
+
const isQuiet = self.configManager.getFlag(flags.quiet);
const contexts = self.configManager.getFlag(flags.context);
const deploymentName = self.configManager.getFlag(flags.namespace);
diff --git a/test/e2e/commands/cluster.test.ts b/test/e2e/commands/cluster.test.ts
index b734ab8d6..d0bd6e6b3 100644
--- a/test/e2e/commands/cluster.test.ts
+++ b/test/e2e/commands/cluster.test.ts
@@ -67,7 +67,7 @@ describe('ClusterCommand', () => {
await k8.deleteNamespace(namespace);
argv[flags.clusterSetupNamespace.name] = constants.SOLO_SETUP_NAMESPACE;
configManager.update(argv);
- await clusterCmd.setup(argv); // restore solo-cluster-setup for other e2e tests to leverage
+ await clusterCmd.handlers.setup(argv); // restore solo-cluster-setup for other e2e tests to leverage
do {
await sleep(Duration.ofSeconds(5));
} while (
@@ -85,33 +85,33 @@ describe('ClusterCommand', () => {
it('should cleanup existing deployment', async () => {
if (await chartManager.isChartInstalled(constants.SOLO_SETUP_NAMESPACE, constants.SOLO_CLUSTER_SETUP_CHART)) {
- expect(await clusterCmd.reset(argv)).to.be.true;
+ expect(await clusterCmd.handlers.reset(argv)).to.be.true;
}
}).timeout(Duration.ofMinutes(1).toMillis());
it('solo cluster setup should fail with invalid cluster name', async () => {
argv[flags.clusterSetupNamespace.name] = 'INVALID';
configManager.update(argv);
- await expect(clusterCmd.setup(argv)).to.be.rejectedWith('Error on cluster setup');
+ await expect(clusterCmd.handlers.setup(argv)).to.be.rejectedWith('Error on cluster setup');
}).timeout(Duration.ofMinutes(1).toMillis());
it('solo cluster setup should work with valid args', async () => {
argv[flags.clusterSetupNamespace.name] = namespace;
configManager.update(argv);
- expect(await clusterCmd.setup(argv)).to.be.true;
+ expect(await clusterCmd.handlers.setup(argv)).to.be.true;
}).timeout(Duration.ofMinutes(1).toMillis());
- it('function getClusterInfo should return true', () => {
- expect(clusterCmd.getClusterInfo()).to.be.ok;
+ it('solo cluster info should work', () => {
+ expect(clusterCmd.handlers.info(argv)).to.be.ok;
}).timeout(Duration.ofMinutes(1).toMillis());
- it('function showClusterList should return right true', async () => {
- expect(clusterCmd.showClusterList()).to.be.ok;
+ it('solo cluster list', async () => {
+ expect(clusterCmd.handlers.list(argv)).to.be.ok;
}).timeout(Duration.ofMinutes(1).toMillis());
it('function showInstalledChartList should return right true', async () => {
// @ts-ignore
- await expect(clusterCmd.showInstalledChartList()).to.eventually.be.undefined;
+ await expect(clusterCmd.handlers.tasks.showInstalledChartList()).to.eventually.be.undefined;
}).timeout(Duration.ofMinutes(1).toMillis());
// helm list would return an empty list if given invalid namespace
@@ -120,7 +120,7 @@ describe('ClusterCommand', () => {
configManager.update(argv);
try {
- await expect(clusterCmd.reset(argv)).to.be.rejectedWith('Error on cluster reset');
+ await expect(clusterCmd.handlers.reset(argv)).to.be.rejectedWith('Error on cluster reset');
} catch (e) {
clusterCmd.logger.showUserError(e);
expect.fail();
@@ -130,6 +130,6 @@ describe('ClusterCommand', () => {
it('solo cluster reset should work with valid args', async () => {
argv[flags.clusterSetupNamespace.name] = namespace;
configManager.update(argv);
- expect(await clusterCmd.reset(argv)).to.be.true;
+ expect(await clusterCmd.handlers.reset(argv)).to.be.true;
}).timeout(Duration.ofMinutes(1).toMillis());
});
diff --git a/test/e2e/commands/network.test.ts b/test/e2e/commands/network.test.ts
index 176cad97e..4c5acba64 100644
--- a/test/e2e/commands/network.test.ts
+++ b/test/e2e/commands/network.test.ts
@@ -67,7 +67,7 @@ describe('NetworkCommand', () => {
before(async () => {
await initCmd.init(argv);
- await clusterCmd.setup(argv);
+ await clusterCmd.handlers.setup(argv);
fs.mkdirSync(applicationEnvParentDirectory, {recursive: true});
fs.writeFileSync(applicationEnvFilePath, applicationEnvFileContents);
});
diff --git a/test/test_util.ts b/test/test_util.ts
index 598b59d91..e271c4a52 100644
--- a/test/test_util.ts
+++ b/test/test_util.ts
@@ -23,7 +23,7 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import {Flags as flags} from '../src/commands/flags.js';
-import {ClusterCommand} from '../src/commands/cluster.js';
+import {ClusterCommand} from '../src/commands/cluster/index.js';
import {InitCommand} from '../src/commands/init.js';
import {NetworkCommand} from '../src/commands/network.js';
import {NodeCommand} from '../src/commands/node/index.js';
@@ -261,7 +261,7 @@ export function e2eTestSuite(
if (
!(await chartManager.isChartInstalled(constants.SOLO_SETUP_NAMESPACE, constants.SOLO_CLUSTER_SETUP_CHART))
) {
- await clusterCmd.setup(argv);
+ await clusterCmd.handlers.setup(argv);
}
}).timeout(Duration.ofMinutes(2).toMillis());
diff --git a/test/unit/commands/cluster.test.ts b/test/unit/commands/cluster.test.ts
index 6ff3afd2d..db90391f1 100644
--- a/test/unit/commands/cluster.test.ts
+++ b/test/unit/commands/cluster.test.ts
@@ -18,8 +18,14 @@ import sinon from 'sinon';
import {describe, it, beforeEach} from 'mocha';
import {expect} from 'chai';
-import {ClusterCommand} from '../../../src/commands/cluster.js';
-import {getDefaultArgv, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER} from '../../test_util.js';
+import {ClusterCommand} from '../../../src/commands/cluster/index.js';
+import {
+ getDefaultArgv,
+ getTestCacheDir,
+ HEDERA_PLATFORM_VERSION_TAG,
+ TEST_CLUSTER,
+ testLocalConfigData,
+} from '../../test_util.js';
import {Flags as flags} from '../../../src/commands/flags.js';
import * as version from '../../../version.js';
import * as constants from '../../../src/core/constants.js';
@@ -32,6 +38,25 @@ import path from 'path';
import {container} from 'tsyringe-neo';
import {resetTestContainer} from '../../test_container.js';
import * as test from 'node:test';
+import {ClusterCommandTasks} from '../../../src/commands/cluster/tasks.js';
+import type {BaseCommand} from '../../../src/commands/base.js';
+import {LocalConfig} from '../../../src/core/config/local_config.js';
+import type {CommandFlag} from '../../../src/types/flag_types.js';
+import {K8} from '../../../src/core/k8.js';
+import {type Cluster, KubeConfig} from '@kubernetes/client-node';
+import {RemoteConfigManager} from '../../../src/core/config/remote/remote_config_manager.js';
+import {DependencyManager} from '../../../src/core/dependency_managers/index.js';
+import {PackageDownloader} from '../../../src/core/package_downloader.js';
+import {KeyManager} from '../../../src/core/key_manager.js';
+import {AccountManager} from '../../../src/core/account_manager.js';
+import {PlatformInstaller} from '../../../src/core/platform_installer.js';
+import {ProfileManager} from '../../../src/core/profile_manager.js';
+import {LeaseManager} from '../../../src/core/lease/lease_manager.js';
+import {CertificateManager} from '../../../src/core/certificate_manager.js';
+import type {Opts} from '../../../src/types/command_types.js';
+import type {ListrTaskWrapper} from 'listr2';
+import fs from 'fs';
+import {stringify} from 'yaml';
const getBaseCommandOpts = () => ({
logger: sinon.stub(),
@@ -81,7 +106,7 @@ describe('ClusterCommand unit tests', () => {
it('Install function is called with expected parameters', async () => {
const clusterCommand = new ClusterCommand(opts);
- await clusterCommand.setup(argv);
+ await clusterCommand.handlers.setup(argv);
expect(opts.chartManager.install.args[0][0]).to.equal(constants.SOLO_SETUP_NAMESPACE);
expect(opts.chartManager.install.args[0][1]).to.equal(constants.SOLO_CLUSTER_SETUP_CHART);
@@ -96,11 +121,326 @@ describe('ClusterCommand unit tests', () => {
argv[flags.force.name] = true;
const clusterCommand = new ClusterCommand(opts);
- await clusterCommand.setup(argv);
+ await clusterCommand.handlers.setup(argv);
expect(opts.chartManager.install.args[0][2]).to.equal(
path.join(ROOT_DIR, 'test-directory', constants.SOLO_CLUSTER_SETUP_CHART),
);
});
});
+
+ describe('cluster connect', () => {
+ const filePath = `${getTestCacheDir('ClusterCommandTasks')}/localConfig.yaml`;
+ const sandbox = sinon.createSandbox();
+ let namespacePromptStub: sinon.SinonStub;
+ let clusterNamePromptStub: sinon.SinonStub;
+ let contextPromptStub: sinon.SinonStub;
+ let tasks: ClusterCommandTasks;
+ let command: BaseCommand;
+ let loggerStub: sinon.SinonStubbedInstance;
+ let localConfig: LocalConfig;
+
+ const getBaseCommandOpts = (
+ sandbox: sinon.SinonSandbox,
+ remoteConfig: any = {},
+ // @ts-ignore
+ stubbedFlags: Record[] = [],
+ ) => {
+ const loggerStub = sandbox.createStubInstance(SoloLogger);
+ const k8Stub = sandbox.createStubInstance(K8);
+ k8Stub.getContexts.returns([
+ {cluster: 'cluster-1', user: 'user-1', name: 'context-1', namespace: 'deployment-1'},
+ {cluster: 'cluster-2', user: 'user-2', name: 'context-2', namespace: 'deployment-2'},
+ {cluster: 'cluster-3', user: 'user-3', name: 'context-3', namespace: 'deployment-3'},
+ ]);
+ const kubeConfigStub = sandbox.createStubInstance(KubeConfig);
+ kubeConfigStub.getCurrentContext.returns('context-from-kubeConfig');
+ kubeConfigStub.getCurrentCluster.returns({
+ name: 'cluster-3',
+ caData: 'caData',
+ caFile: 'caFile',
+ server: 'server-3',
+ skipTLSVerify: true,
+ tlsServerName: 'tls-3',
+ } as Cluster);
+
+ const remoteConfigManagerStub = sandbox.createStubInstance(RemoteConfigManager);
+ remoteConfigManagerStub.modify.callsFake(async callback => {
+ await callback(remoteConfig);
+ });
+
+ k8Stub.getKubeConfig.returns(kubeConfigStub);
+
+ const configManager = sandbox.createStubInstance(ConfigManager);
+
+ for (let i = 0; i < stubbedFlags.length; i++) {
+ configManager.getFlag.withArgs(stubbedFlags[i][0]).returns(stubbedFlags[i][1]);
+ }
+
+ return {
+ logger: loggerStub,
+ helm: sandbox.createStubInstance(Helm),
+ k8: k8Stub,
+ chartManager: sandbox.createStubInstance(ChartManager),
+ configManager,
+ depManager: sandbox.createStubInstance(DependencyManager),
+ localConfig: new LocalConfig(filePath),
+ downloader: sandbox.createStubInstance(PackageDownloader),
+ keyManager: sandbox.createStubInstance(KeyManager),
+ accountManager: sandbox.createStubInstance(AccountManager),
+ platformInstaller: sandbox.createStubInstance(PlatformInstaller),
+ profileManager: sandbox.createStubInstance(ProfileManager),
+ leaseManager: sandbox.createStubInstance(LeaseManager),
+ certificateManager: sandbox.createStubInstance(CertificateManager),
+ remoteConfigManager: remoteConfigManagerStub,
+ } as Opts;
+ };
+
+ describe('updateLocalConfig', () => {
+ async function runUpdateLocalConfigTask(opts) {
+ command = new ClusterCommand(opts);
+ tasks = new ClusterCommandTasks(command);
+ const taskObj = tasks.updateLocalConfig({});
+ await taskObj.task({config: {}}, sandbox.stub() as unknown as ListrTaskWrapper);
+ return command;
+ }
+
+ afterEach(async () => {
+ await fs.promises.unlink(filePath);
+ sandbox.restore();
+ });
+
+ after(() => {});
+
+ beforeEach(async () => {
+ namespacePromptStub = sandbox.stub(flags.namespace, 'prompt').callsFake(() => {
+ return new Promise(resolve => {
+ resolve('deployment-3');
+ });
+ });
+ clusterNamePromptStub = sandbox.stub(flags.clusterName, 'prompt').callsFake(() => {
+ return new Promise(resolve => {
+ resolve('cluster-3');
+ });
+ });
+ contextPromptStub = sandbox.stub(flags.context, 'prompt').callsFake(() => {
+ return new Promise(resolve => {
+ resolve('context-3');
+ });
+ });
+ loggerStub = sandbox.createStubInstance(SoloLogger);
+ await fs.promises.writeFile(filePath, stringify(testLocalConfigData));
+ });
+
+ it('should update currentDeployment with clusters from remoteConfig', async () => {
+ const remoteConfig = {
+ clusters: {
+ 'cluster-2': 'deployment',
+ },
+ };
+ const opts = getBaseCommandOpts(sandbox, remoteConfig, []);
+ command = await runUpdateLocalConfigTask(opts); // @ts-ignore
+ localConfig = new LocalConfig(filePath);
+
+ expect(localConfig.currentDeploymentName).to.equal('deployment');
+ expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2']);
+ expect(localConfig.clusterContextMapping).to.deep.equal({
+ 'cluster-1': 'context-1',
+ 'cluster-2': 'context-2',
+ });
+ });
+
+ it('should update clusterContextMapping with provided context', async () => {
+ const remoteConfig = {
+ clusters: {
+ 'cluster-2': 'deployment',
+ },
+ };
+ const opts = getBaseCommandOpts(sandbox, remoteConfig, [[flags.context, 'provided-context']]);
+ command = await runUpdateLocalConfigTask(opts); // @ts-ignore
+ localConfig = new LocalConfig(filePath);
+
+ expect(localConfig.currentDeploymentName).to.equal('deployment');
+ expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2']);
+ expect(localConfig.clusterContextMapping).to.deep.equal({
+ 'cluster-1': 'context-1',
+ 'cluster-2': 'provided-context',
+ });
+ });
+
+ it('should update multiple clusterContextMappings with provided contexts', async () => {
+ const remoteConfig = {
+ clusters: {
+ 'cluster-2': 'deployment',
+ 'cluster-3': 'deployment',
+ 'cluster-4': 'deployment',
+ },
+ };
+ const opts = getBaseCommandOpts(sandbox, remoteConfig, [
+ [flags.context, 'provided-context-2,provided-context-3,provided-context-4'],
+ ]);
+ command = await runUpdateLocalConfigTask(opts); // @ts-ignore
+ localConfig = new LocalConfig(filePath);
+
+ expect(localConfig.currentDeploymentName).to.equal('deployment');
+ expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'cluster-3', 'cluster-4']);
+ expect(localConfig.clusterContextMapping).to.deep.equal({
+ 'cluster-1': 'context-1',
+ 'cluster-2': 'provided-context-2',
+ 'cluster-3': 'provided-context-3',
+ 'cluster-4': 'provided-context-4',
+ });
+ });
+
+ it('should update multiple clusterContextMappings with default KubeConfig context if quiet=true', async () => {
+ const remoteConfig = {
+ clusters: {
+ 'cluster-2': 'deployment',
+ 'cluster-3': 'deployment',
+ },
+ };
+ const opts = getBaseCommandOpts(sandbox, remoteConfig, [[flags.quiet, true]]);
+ command = await runUpdateLocalConfigTask(opts); // @ts-ignore
+ localConfig = new LocalConfig(filePath);
+
+ expect(localConfig.currentDeploymentName).to.equal('deployment');
+ expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'cluster-3']);
+ expect(localConfig.clusterContextMapping).to.deep.equal({
+ 'cluster-1': 'context-1',
+ 'cluster-2': 'context-2',
+ 'cluster-3': 'context-from-kubeConfig',
+ });
+ });
+
+ it('should update multiple clusterContextMappings with prompted context no value was provided', async () => {
+ const remoteConfig = {
+ clusters: {
+ 'cluster-2': 'deployment',
+ 'new-cluster': 'deployment',
+ },
+ };
+ const opts = getBaseCommandOpts(sandbox, remoteConfig, []);
+
+ command = await runUpdateLocalConfigTask(opts); // @ts-ignore
+ localConfig = new LocalConfig(filePath);
+
+ expect(localConfig.currentDeploymentName).to.equal('deployment');
+ expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'new-cluster']);
+ expect(localConfig.clusterContextMapping).to.deep.equal({
+ 'cluster-1': 'context-1',
+ 'cluster-2': 'context-2',
+ 'new-cluster': 'context-3', // prompted value
+ });
+ });
+ });
+
+ describe('selectContext', () => {
+ async function runSelectContextTask(opts) {
+ command = new ClusterCommand(opts);
+ tasks = new ClusterCommandTasks(command);
+ const taskObj = tasks.selectContext({});
+ await taskObj.task({config: {}}, sandbox.stub() as unknown as ListrTaskWrapper);
+ return command;
+ }
+
+ afterEach(async () => {
+ await fs.promises.unlink(filePath);
+ sandbox.restore();
+ });
+
+ beforeEach(async () => {
+ namespacePromptStub = sandbox.stub(flags.namespace, 'prompt').callsFake(() => {
+ return new Promise(resolve => {
+ resolve('deployment-3');
+ });
+ });
+ clusterNamePromptStub = sandbox.stub(flags.clusterName, 'prompt').callsFake(() => {
+ return new Promise(resolve => {
+ resolve('cluster-3');
+ });
+ });
+ contextPromptStub = sandbox.stub(flags.context, 'prompt').callsFake(() => {
+ return new Promise(resolve => {
+ resolve('context-3');
+ });
+ });
+ loggerStub = sandbox.createStubInstance(SoloLogger);
+ await fs.promises.writeFile(filePath, stringify(testLocalConfigData));
+ });
+
+ it('should use first provided context', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [
+ [flags.context, 'provided-context-1,provided-context-2,provided-context-3'],
+ ]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('provided-context-1');
+ });
+
+ it('should use local config mapping to connect to first provided cluster', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [[flags.clusterName, 'cluster-2,cluster-3']]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-2');
+ });
+
+ it('should prompt for context if selected cluster is not found in local config mapping', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [[flags.clusterName, 'cluster-3']]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-3');
+ });
+
+ it('should use default kubeConfig context if selected cluster is not found in local config mapping and quiet=true', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [
+ [flags.clusterName, 'unknown-cluster'],
+ [flags.quiet, true],
+ ]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-from-kubeConfig');
+ });
+
+ it('should use context from local config mapping for the first cluster from the selected deployment', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [[flags.namespace, 'deployment-2']]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-2');
+ });
+
+ it('should prompt for context if selected deployment is found in local config but the context is not', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [[flags.namespace, 'deployment-3']]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-3');
+ });
+
+ it('should use default context if selected deployment is found in local config but the context is not and quiet=true', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [
+ [flags.namespace, 'deployment-3'],
+ [flags.quiet, true],
+ ]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-from-kubeConfig');
+ });
+
+ it('should prompt for clusters and contexts if selected deployment is not found in local config', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [[flags.namespace, 'deployment-4']]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-3');
+ });
+
+ it('should use clusters and contexts from kubeConfig if selected deployment is not found in local config and quiet=true', async () => {
+ const opts = getBaseCommandOpts(sandbox, {}, [
+ [flags.namespace, 'deployment-4'],
+ [flags.quiet, true],
+ ]);
+
+ command = await runSelectContextTask(opts); // @ts-ignore
+ expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-from-kubeConfig');
+ });
+ });
+ });
});
diff --git a/test/unit/commands/context.test.ts b/test/unit/commands/context.test.ts
deleted file mode 100644
index ceea764dc..000000000
--- a/test/unit/commands/context.test.ts
+++ /dev/null
@@ -1,361 +0,0 @@
-/**
- * Copyright (C) 2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-import sinon from 'sinon';
-import {describe, it, beforeEach} from 'mocha';
-import {expect} from 'chai';
-
-import {ContextCommandTasks} from '../../../src/commands/context/tasks.js';
-import {DependencyManager} from '../../../src/core/dependency_managers/index.js';
-import {LocalConfig} from '../../../src/core/config/local_config.js';
-import {PackageDownloader} from '../../../src/core/package_downloader.js';
-import {KeyManager} from '../../../src/core/key_manager.js';
-import {AccountManager} from '../../../src/core/account_manager.js';
-import {PlatformInstaller} from '../../../src/core/platform_installer.js';
-import {ProfileManager} from '../../../src/core/profile_manager.js';
-import {LeaseManager} from '../../../src/core/lease/lease_manager.js';
-import {CertificateManager} from '../../../src/core/certificate_manager.js';
-import {RemoteConfigManager} from '../../../src/core/config/remote/remote_config_manager.js';
-import {K8} from '../../../src/core/k8.js';
-import {ConfigManager} from '../../../src/core/config_manager.js';
-import {Helm} from '../../../src/core/helm.js';
-import {ChartManager} from '../../../src/core/chart_manager.js';
-import {getTestCacheDir, testLocalConfigData} from '../../test_util.js';
-import {type BaseCommand} from '../../../src/commands/base.js';
-import {Flags as flags} from '../../../src/commands/flags.js';
-import {SoloLogger} from '../../../src/core/logging.js';
-import {type Opts} from '../../../src/types/command_types.js';
-import fs from 'fs';
-import {stringify} from 'yaml';
-import {type Cluster, KubeConfig} from '@kubernetes/client-node';
-import {type ListrTaskWrapper} from 'listr2';
-import {ContextCommand} from '../../../src/commands/context/index.js';
-import {type CommandFlag} from '../../../src/types/flag_types.js';
-
-describe('ContextCommandTasks unit tests', () => {
- const filePath = `${getTestCacheDir('ContextCommandTasks')}/localConfig.yaml`;
- const sandbox = sinon.createSandbox();
- let namespacePromptStub: sinon.SinonStub;
- let clusterNamePromptStub: sinon.SinonStub;
- let contextPromptStub: sinon.SinonStub;
- let tasks: ContextCommandTasks;
- let command: BaseCommand;
- let loggerStub: sinon.SinonStubbedInstance;
- let localConfig: LocalConfig;
-
- const getBaseCommandOpts = (
- sandbox: sinon.SinonSandbox,
- remoteConfig: any = {},
- // @ts-ignore
- stubbedFlags: Record[] = [],
- ) => {
- const loggerStub = sandbox.createStubInstance(SoloLogger);
- const k8Stub = sandbox.createStubInstance(K8);
- k8Stub.getContexts.returns([
- {cluster: 'cluster-1', user: 'user-1', name: 'context-1', namespace: 'deployment-1'},
- {cluster: 'cluster-2', user: 'user-2', name: 'context-2', namespace: 'deployment-2'},
- {cluster: 'cluster-3', user: 'user-3', name: 'context-3', namespace: 'deployment-3'},
- ]);
- const kubeConfigStub = sandbox.createStubInstance(KubeConfig);
- kubeConfigStub.getCurrentContext.returns('context-from-kubeConfig');
- kubeConfigStub.getCurrentCluster.returns({
- name: 'cluster-3',
- caData: 'caData',
- caFile: 'caFile',
- server: 'server-3',
- skipTLSVerify: true,
- tlsServerName: 'tls-3',
- } as Cluster);
-
- const remoteConfigManagerStub = sandbox.createStubInstance(RemoteConfigManager);
- remoteConfigManagerStub.modify.callsFake(async callback => {
- await callback(remoteConfig);
- });
-
- k8Stub.getKubeConfig.returns(kubeConfigStub);
-
- const configManager = sandbox.createStubInstance(ConfigManager);
-
- for (let i = 0; i < stubbedFlags.length; i++) {
- configManager.getFlag.withArgs(stubbedFlags[i][0]).returns(stubbedFlags[i][1]);
- }
-
- return {
- logger: loggerStub,
- helm: sandbox.createStubInstance(Helm),
- k8: k8Stub,
- chartManager: sandbox.createStubInstance(ChartManager),
- configManager,
- depManager: sandbox.createStubInstance(DependencyManager),
- localConfig: new LocalConfig(filePath),
- downloader: sandbox.createStubInstance(PackageDownloader),
- keyManager: sandbox.createStubInstance(KeyManager),
- accountManager: sandbox.createStubInstance(AccountManager),
- platformInstaller: sandbox.createStubInstance(PlatformInstaller),
- profileManager: sandbox.createStubInstance(ProfileManager),
- leaseManager: sandbox.createStubInstance(LeaseManager),
- certificateManager: sandbox.createStubInstance(CertificateManager),
- remoteConfigManager: remoteConfigManagerStub,
- } as Opts;
- };
-
- describe('updateLocalConfig', () => {
- async function runUpdateLocalConfigTask(opts) {
- command = new ContextCommand(opts);
- tasks = new ContextCommandTasks(command);
- const taskObj = tasks.updateLocalConfig({});
- await taskObj.task({config: {}}, sandbox.stub() as unknown as ListrTaskWrapper);
- return command;
- }
-
- afterEach(async () => {
- await fs.promises.unlink(filePath);
- sandbox.restore();
- });
-
- after(() => {});
-
- beforeEach(async () => {
- namespacePromptStub = sandbox.stub(flags.namespace, 'prompt').callsFake(() => {
- return new Promise(resolve => {
- resolve('deployment-3');
- });
- });
- clusterNamePromptStub = sandbox.stub(flags.clusterName, 'prompt').callsFake(() => {
- return new Promise(resolve => {
- resolve('cluster-3');
- });
- });
- contextPromptStub = sandbox.stub(flags.context, 'prompt').callsFake(() => {
- return new Promise(resolve => {
- resolve('context-3');
- });
- });
- loggerStub = sandbox.createStubInstance(SoloLogger);
- await fs.promises.writeFile(filePath, stringify(testLocalConfigData));
- });
-
- it('should update currentDeployment with clusters from remoteConfig', async () => {
- const remoteConfig = {
- clusters: {
- 'cluster-2': 'deployment',
- },
- };
- const opts = getBaseCommandOpts(sandbox, remoteConfig, []);
- command = await runUpdateLocalConfigTask(opts); // @ts-ignore
- localConfig = new LocalConfig(filePath);
-
- expect(localConfig.currentDeploymentName).to.equal('deployment');
- expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2']);
- expect(localConfig.clusterContextMapping).to.deep.equal({
- 'cluster-1': 'context-1',
- 'cluster-2': 'context-2',
- });
- });
-
- it('should update clusterContextMapping with provided context', async () => {
- const remoteConfig = {
- clusters: {
- 'cluster-2': 'deployment',
- },
- };
- const opts = getBaseCommandOpts(sandbox, remoteConfig, [[flags.context, 'provided-context']]);
- command = await runUpdateLocalConfigTask(opts); // @ts-ignore
- localConfig = new LocalConfig(filePath);
-
- expect(localConfig.currentDeploymentName).to.equal('deployment');
- expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2']);
- expect(localConfig.clusterContextMapping).to.deep.equal({
- 'cluster-1': 'context-1',
- 'cluster-2': 'provided-context',
- });
- });
-
- it('should update multiple clusterContextMappings with provided contexts', async () => {
- const remoteConfig = {
- clusters: {
- 'cluster-2': 'deployment',
- 'cluster-3': 'deployment',
- 'cluster-4': 'deployment',
- },
- };
- const opts = getBaseCommandOpts(sandbox, remoteConfig, [
- [flags.context, 'provided-context-2,provided-context-3,provided-context-4'],
- ]);
- command = await runUpdateLocalConfigTask(opts); // @ts-ignore
- localConfig = new LocalConfig(filePath);
-
- expect(localConfig.currentDeploymentName).to.equal('deployment');
- expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'cluster-3', 'cluster-4']);
- expect(localConfig.clusterContextMapping).to.deep.equal({
- 'cluster-1': 'context-1',
- 'cluster-2': 'provided-context-2',
- 'cluster-3': 'provided-context-3',
- 'cluster-4': 'provided-context-4',
- });
- });
-
- it('should update multiple clusterContextMappings with default KubeConfig context if quiet=true', async () => {
- const remoteConfig = {
- clusters: {
- 'cluster-2': 'deployment',
- 'cluster-3': 'deployment',
- },
- };
- const opts = getBaseCommandOpts(sandbox, remoteConfig, [[flags.quiet, true]]);
- command = await runUpdateLocalConfigTask(opts); // @ts-ignore
- localConfig = new LocalConfig(filePath);
-
- expect(localConfig.currentDeploymentName).to.equal('deployment');
- expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'cluster-3']);
- expect(localConfig.clusterContextMapping).to.deep.equal({
- 'cluster-1': 'context-1',
- 'cluster-2': 'context-2',
- 'cluster-3': 'context-from-kubeConfig',
- });
- });
-
- it('should update multiple clusterContextMappings with prompted context no value was provided', async () => {
- const remoteConfig = {
- clusters: {
- 'cluster-2': 'deployment',
- 'new-cluster': 'deployment',
- },
- };
- const opts = getBaseCommandOpts(sandbox, remoteConfig, []);
-
- command = await runUpdateLocalConfigTask(opts); // @ts-ignore
- localConfig = new LocalConfig(filePath);
-
- expect(localConfig.currentDeploymentName).to.equal('deployment');
- expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'new-cluster']);
- expect(localConfig.clusterContextMapping).to.deep.equal({
- 'cluster-1': 'context-1',
- 'cluster-2': 'context-2',
- 'new-cluster': 'context-3', // prompted value
- });
- });
- });
-
- describe('selectContext', () => {
- async function runSelectContextTask(opts) {
- command = new ContextCommand(opts);
- tasks = new ContextCommandTasks(command);
- const taskObj = tasks.selectContext({});
- await taskObj.task({config: {}}, sandbox.stub() as unknown as ListrTaskWrapper);
- return command;
- }
-
- afterEach(async () => {
- await fs.promises.unlink(filePath);
- sandbox.restore();
- });
-
- beforeEach(async () => {
- namespacePromptStub = sandbox.stub(flags.namespace, 'prompt').callsFake(() => {
- return new Promise(resolve => {
- resolve('deployment-3');
- });
- });
- clusterNamePromptStub = sandbox.stub(flags.clusterName, 'prompt').callsFake(() => {
- return new Promise(resolve => {
- resolve('cluster-3');
- });
- });
- contextPromptStub = sandbox.stub(flags.context, 'prompt').callsFake(() => {
- return new Promise(resolve => {
- resolve('context-3');
- });
- });
- loggerStub = sandbox.createStubInstance(SoloLogger);
- await fs.promises.writeFile(filePath, stringify(testLocalConfigData));
- });
-
- it('should use first provided context', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [
- [flags.context, 'provided-context-1,provided-context-2,provided-context-3'],
- ]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('provided-context-1');
- });
-
- it('should use local config mapping to connect to first provided cluster', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [[flags.clusterName, 'cluster-2,cluster-3']]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-2');
- });
-
- it('should prompt for context if selected cluster is not found in local config mapping', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [[flags.clusterName, 'cluster-3']]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-3');
- });
-
- it('should use default kubeConfig context if selected cluster is not found in local config mapping and quiet=true', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [
- [flags.clusterName, 'unknown-cluster'],
- [flags.quiet, true],
- ]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-from-kubeConfig');
- });
-
- it('should use context from local config mapping for the first cluster from the selected deployment', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [[flags.namespace, 'deployment-2']]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-2');
- });
-
- it('should prompt for context if selected deployment is found in local config but the context is not', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [[flags.namespace, 'deployment-3']]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-3');
- });
-
- it('should use default context if selected deployment is found in local config but the context is not and quiet=true', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [
- [flags.namespace, 'deployment-3'],
- [flags.quiet, true],
- ]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-from-kubeConfig');
- });
-
- it('should prompt for clusters and contexts if selected deployment is not found in local config', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [[flags.namespace, 'deployment-4']]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-3');
- });
-
- it('should use clusters and contexts from kubeConfig if selected deployment is not found in local config and quiet=true', async () => {
- const opts = getBaseCommandOpts(sandbox, {}, [
- [flags.namespace, 'deployment-4'],
- [flags.quiet, true],
- ]);
-
- command = await runSelectContextTask(opts); // @ts-ignore
- expect(command.getK8().getKubeConfig().setCurrentContext).to.have.been.calledWith('context-from-kubeConfig');
- });
- });
-});