diff --git a/fullstack-network-manager/src/commands/base.mjs b/fullstack-network-manager/src/commands/base.mjs index 3e91b9064..ebd010685 100644 --- a/fullstack-network-manager/src/commands/base.mjs +++ b/fullstack-network-manager/src/commands/base.mjs @@ -10,12 +10,13 @@ export class BaseCommand extends ShellRunner { this.logger.debug(cmd) await this.run(cmd) } catch (e) { - this.logger.error("%s", e) + this.logger.showUserError(e) return false } return true } + /** * Check if 'kind' CLI program is installed or not * @returns {Promise} @@ -77,105 +78,81 @@ export class BaseCommand extends ShellRunner { */ async getInstalledCharts(namespaceName) { try { - let cmd = `helm list -n ${namespaceName} -q` - - let output = await this.run(cmd) - this.logger.showUser("\nList of installed charts\n--------------------------\n%s", output) - - return output.split(/\r?\n/) + return await this.helm.list(`-n ${namespaceName}`, '-q') } catch (e) { - this.logger.error("%s", e) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return [] } - async chartInstall(namespaceName, releaseName, chartPath, valuesArg) { + async chartInstall(namespaceName, chartName, chartPath, valuesArg = '') { try { - this.logger.showUser(chalk.cyan(`Setting up FST network...`)) - - let charts= await this.getInstalledCharts(namespaceName) - if (!charts.includes(releaseName)) { - let cmd = `helm install -n ${namespaceName} ${releaseName} ${chartPath} ${valuesArg}` - this.logger.showUser(chalk.cyan(`Installing ${releaseName} chart`)) - - let output = await this.run(cmd) - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is installed`) + let charts = await this.getInstalledCharts(namespaceName) + if (!charts.includes(chartName)) { + this.logger.showUser(chalk.cyan('> running helm dependency update for chart:'), chalk.yellow(`${chartName} ...`)) + await this.helm.dependency('update', chartPath) + + this.logger.showUser(chalk.cyan('> installing chart:'), chalk.yellow(`${chartName}`)) + await this.helm.install(`-n ${namespaceName} ${chartName} ${chartPath} ${valuesArg}`) + this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is installed`) } else { - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is already installed`) + this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is already installed`) } - this.logger.showUser(chalk.yellow("Chart setup is complete")) - return true } catch (e) { - this.logger.error("%s", e.stack) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false } - async chartUninstall(namespaceName, releaseName) { + async chartUninstall(namespaceName, chartName) { try { - this.logger.showUser(chalk.cyan(`Uninstalling FST network ...`)) - - let charts= await this.getInstalledCharts(namespaceName) - if (charts.includes(releaseName)) { - let cmd = `helm uninstall ${releaseName} -n ${namespaceName}` - this.logger.showUser(chalk.cyan(`Uninstalling ${releaseName} chart`)) - - let output = await this.run(cmd) - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is uninstalled`) - await this.getInstalledCharts(namespaceName) + this.logger.showUser(chalk.cyan('> checking chart:'), chalk.yellow(`${chartName}`)) + let charts = await this.getInstalledCharts(namespaceName) + if (charts.includes(chartName)) { + this.logger.showUser(chalk.cyan('> uninstalling chart:'), chalk.yellow(`${chartName}`)) + await this.helm.uninstall(`-n ${namespaceName} ${chartName}`) + this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is uninstalled`) } else { - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is already uninstalled`) + this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is already uninstalled`) } - this.logger.showUser(chalk.yellow("Chart uninstallation is complete")) - return true } catch (e) { - this.logger.error("%s", e.stack) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false } - async chartUpgrade(namespaceName, releaseName, chartPath, valuesArg) { + async chartUpgrade(namespaceName, chartName, chartPath, valuesArg = '') { try { - this.logger.showUser(chalk.cyan(`Upgrading FST network deployment chart ...`)) - - let charts= await this.getInstalledCharts(namespaceName) - if (charts.includes(releaseName)) { - let cmd = `helm upgrade ${releaseName} -n ${namespaceName} ${chartPath} ${valuesArg}` - this.logger.showUser(chalk.cyan(`Upgrading ${releaseName} chart`)) - - let output = await this.run(cmd) - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is upgraded`) - await this.getInstalledCharts(namespaceName) - - this.logger.showUser(chalk.yellow("Chart upgrade is complete")) - } else { - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is not installed`) - return false - } + this.logger.showUser(chalk.cyan('> upgrading chart:'), chalk.yellow(`${chartName}`)) + await this.helm.upgrade(`-n ${namespaceName} ${chartName} ${chartPath} ${valuesArg}`) + this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is upgraded`) return true } catch (e) { - this.logger.error("%s", e.stack) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false } - constructor(opts) { super(opts); + if (!opts || !opts.kind) throw new Error('An instance of core/Kind is required') + if (!opts || !opts.helm) throw new Error('An instance of core/Helm is required') + if (!opts || !opts.kubectl) throw new Error('An instance of core/Kubectl is required') + + this.kind = opts.kind + this.helm = opts.helm + this.kubectl = opts.kubectl + // map of dependency checks this.checks = new Map() .set(core.constants.KIND, () => this.checkKind()) diff --git a/fullstack-network-manager/src/commands/chart.mjs b/fullstack-network-manager/src/commands/chart.mjs index 9018f1446..b7e10c457 100644 --- a/fullstack-network-manager/src/commands/chart.mjs +++ b/fullstack-network-manager/src/commands/chart.mjs @@ -6,7 +6,7 @@ import * as flags from "./flags.mjs"; export class ChartCommand extends BaseCommand { chartPath = `${core.constants.FST_HOME_DIR}/full-stack-testing/charts/fullstack-deployment` - releaseName = "fullstack-deployment" + chartName = "fullstack-deployment" prepareValuesArg(argv) { let {valuesFile, mirrorNode, hederaExplorer} = argv @@ -25,21 +25,20 @@ export class ChartCommand extends BaseCommand { let namespace = argv.namespace let valuesArg = this.prepareValuesArg(argv) - await this.run(`helm dependency update ${this.chartPath}`) - return await this.chartInstall(namespace, this.releaseName, this.chartPath, valuesArg) + return await this.chartInstall(namespace, this.chartName, this.chartPath, valuesArg) } async uninstall(argv) { let namespace = argv.namespace - return await this.chartUninstall(namespace, this.releaseName) + return await this.chartUninstall(namespace, this.chartName) } async upgrade(argv) { let namespace = argv.namespace let valuesArg = this.prepareValuesArg(argv) - return await this.chartUpgrade(namespace, this.releaseName, this.chartPath, valuesArg) + return await this.chartUpgrade(namespace, this.chartName, this.chartPath, valuesArg) } static getCommandDefinition(chartCmd) { diff --git a/fullstack-network-manager/src/commands/cluster.mjs b/fullstack-network-manager/src/commands/cluster.mjs index dc7ff3bad..36dac7f9f 100644 --- a/fullstack-network-manager/src/commands/cluster.mjs +++ b/fullstack-network-manager/src/commands/cluster.mjs @@ -8,12 +8,6 @@ import {Kind} from "../core/kind.mjs"; * Define the core functionalities of 'cluster' command */ export class ClusterCommand extends BaseCommand { - - constructor(opts) { - super(opts); - this.kind = new Kind(opts) - } - /** * List available clusters * @returns {Promise} @@ -22,13 +16,45 @@ export class ClusterCommand extends BaseCommand { try { return await this.kind.getClusters('-q') } catch (e) { - this.logger.error("%s", e) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return [] } + showList(itemType, items = []) { + this.logger.showUser(chalk.green(`\n *** List of available ${itemType} ***`)) + this.logger.showUser(chalk.green(`---------------------------------------`)) + if (items.length > 0) { + items.forEach(name => this.logger.showUser(chalk.yellow(` - ${name}`))) + } else { + this.logger.showUser(chalk.blue(`[ None ]`)) + } + + this.logger.showUser("\n") + return true + } + + async showClusterList(argv) { + this.showList("clusters", await this.getClusters()) + return true + } + + /** + * List available namespaces + * @returns {Promise} + */ + async getNameSpaces() { + try { + return await this.kubectl.getNamespace(`--no-headers`, `-o name`) + } catch (e) { + this.logger.showUserError(e) + } + + return [] + } + + /** * Get cluster-info for the given cluster name * @param argv arguments containing cluster name @@ -40,22 +66,40 @@ export class ClusterCommand extends BaseCommand { let cmd = `kubectl cluster-info --context kind-${clusterName}` let output = await this.run(cmd) - this.logger.showUser(`\nCluster information (${clusterName})\n---------------------------------------`) + this.logger.showUser(`Cluster information (${clusterName})\n---------------------------------------`) output.forEach(line => this.logger.showUser(line)) + this.logger.showUser("\n") return true } catch (e) { - this.logger.error("%s", e) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false } - async showClusterList(argv) { - let clusters = await this.getClusters() - this.logger.showUser("\nList of available clusters \n---------------------------------------") - clusters.forEach(name => this.logger.showUser(name)) + async createNamespace(argv) { + try { + let namespace = argv.namespace + let namespaces = await this.getNameSpaces() + this.logger.showUser(chalk.cyan('> checking namespace:'), chalk.yellow(`${namespace}`)) + if (!namespaces.includes(`namespace/${namespace}`)) { + this.logger.showUser(chalk.cyan('> creating namespace:'), chalk.yellow(`${namespace} ...`)) + await this.kubectl.createNamespace(namespace) + this.logger.showUser(chalk.green('OK'), `namespace '${namespace}' is created`) + } else { + this.logger.showUser(chalk.green('OK'), `namespace '${namespace}' already exists`) + } + + this.showList("namespaces", await this.getNameSpaces()) + + return true + } catch (e) { + this.logger.showUserError(e) + } + + return false } + /** * Create a cluster * @param argv @@ -66,13 +110,16 @@ export class ClusterCommand extends BaseCommand { let clusterName = argv.clusterName let clusters = await this.getClusters() + this.logger.showUser(chalk.cyan('> checking cluster:'), chalk.yellow(`${clusterName}`)) if (!clusters.includes(clusterName)) { - this.logger.showUser(chalk.cyan('Creating cluster:'), chalk.yellow(`${clusterName}...`)) + this.logger.showUser(chalk.cyan('> creating cluster:'), chalk.yellow(`${clusterName} ...`)) await this.kind.createCluster( `-n ${clusterName}`, `--config ${core.constants.RESOURCES_DIR}/dev-cluster.yaml`, ) - this.logger.showUser(chalk.green('Created cluster:'), chalk.yellow(clusterName)) + this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is created`) + } else { + this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' already exists`) } // show all clusters and cluster-info @@ -80,10 +127,11 @@ export class ClusterCommand extends BaseCommand { await this.getClusterInfo(argv) + await this.createNamespace(argv) + return true } catch (e) { - this.logger.error("%s", e) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false @@ -98,43 +146,28 @@ export class ClusterCommand extends BaseCommand { try { let clusterName = argv.clusterName let clusters = await this.getClusters() + this.logger.showUser(chalk.cyan('> checking cluster:'), chalk.yellow(`${clusterName}`)) if (clusters.includes(clusterName)) { - this.logger.showUser(chalk.cyan('Deleting cluster:'), chalk.yellow(`${clusterName}...`)) + this.logger.showUser(chalk.cyan('> deleting cluster:'), chalk.yellow(`${clusterName} ...`)) await this.kind.deleteCluster(clusterName) - await this.showClusterList() + this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is deleted`) } else { - this.logger.showUser(`Cluster '${clusterName}' does not exist`) + this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is already deleted`) } + this.showList('clusters', await this.getClusters()) return true } catch (e) { - this.logger.error("%s", e.stack) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false } - /** - * List available clusters - * @returns {Promise} - */ - async getInstalledCharts(argv) { - try { - let namespaceName = argv.namespace - let cmd = `helm list -n ${namespaceName} -q` - let output = await this.run(cmd) - this.logger.showUser("\nList of installed charts\n--------------------------\n%s", output) - - return output.split(/\r?\n/) - } catch (e) { - this.logger.error("%s", e) - this.logger.showUser(e.message) - } - - return [] + async showInstalledChartList(namespace) { + this.showList("charts installed", await this.getInstalledCharts(namespace)) } /** @@ -144,33 +177,21 @@ export class ClusterCommand extends BaseCommand { */ async setup(argv) { try { - let clusterName = argv.clusterName - let releaseName = "fullstack-cluster-setup" - let namespaceName = argv.namespace - let chartPath = `${core.constants.FST_HOME_DIR}/full-stack-testing/charts/fullstack-cluster-setup` - - this.logger.showUser(chalk.cyan(`Setting up cluster ${clusterName}...`)) + // create cluster + await this.create(argv) - let charts= await this.getInstalledCharts(argv) - - if (!charts.includes(releaseName)) { - // install fullstack-cluster-setup chart - let cmd = `helm install -n ${namespaceName} ${releaseName} ${chartPath}` - this.logger.showUser(chalk.cyan("Installing fullstack-cluster-setup chart")) - this.logger.debug(`Invoking '${cmd}'...`) - - let output = await this.run(cmd) - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is installed`) - } else { - this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is already installed`) - } + let clusterName = argv.clusterName + let chartName = "fullstack-cluster-setup" + let namespace = argv.namespace + let chartPath = `${core.constants.FST_HOME_DIR}/full-stack-testing/charts/${chartName}` - this.logger.showUser(chalk.yellow("Chart setup complete")) + this.logger.showUser(chalk.cyan('> setting up cluster:'), chalk.yellow(`${clusterName}`)) + await this.chartInstall(namespace, chartName, chartPath) + await this.showInstalledChartList(namespace) return true } catch (e) { - this.logger.error("%s", e.stack) - this.logger.showUser(e.message) + this.logger.showUserError(e) } return false @@ -191,6 +212,7 @@ export class ClusterCommand extends BaseCommand { desc: 'Create a cluster', builder: yargs => { yargs.option('cluster-name', flags.clusterNameFlag) + yargs.option('namespace', flags.namespaceFlag) }, handler: argv => { clusterCmd.logger.debug("==== Running 'cluster create' ===") diff --git a/fullstack-network-manager/src/core/helm.mjs b/fullstack-network-manager/src/core/helm.mjs new file mode 100644 index 000000000..9812a1094 --- /dev/null +++ b/fullstack-network-manager/src/core/helm.mjs @@ -0,0 +1,61 @@ +import {ShellRunner} from "./shell_runner.mjs"; + +export class Helm extends ShellRunner { + /** + * Prepare a `helm` shell command string + * @param action represents a helm command (e.g. create | install | get ) + * @param args args of the command + * @returns {string} + */ + prepareCommand(action, ...args) { + let cmd = `helm ${action} ` + args.forEach(arg => {cmd += ` ${arg}`}) + return cmd + } + + /** + * Invoke `helm install` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async install(...args) { + return this.run(this.prepareCommand('install', ...args)) + } + + /** + * Invoke `helm uninstall` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async uninstall(...args) { + return this.run(this.prepareCommand('uninstall', ...args)) + } + + /** + * Invoke `helm upgrade` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async upgrade(...args) { + return this.run(this.prepareCommand('upgrade', ...args)) + } + + /** + * Invoke `helm list` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async list(...args) { + return this.run(this.prepareCommand('list', ...args)) + } + + /** + * Invoke `helm dependency` command + * @param subCommand sub-command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async dependency(subCommand,...args) { + return this.run(this.prepareCommand('dependency', subCommand, ...args)) + } +} \ No newline at end of file diff --git a/fullstack-network-manager/src/core/index.mjs b/fullstack-network-manager/src/core/index.mjs index a509cff01..f75019e41 100644 --- a/fullstack-network-manager/src/core/index.mjs +++ b/fullstack-network-manager/src/core/index.mjs @@ -1,5 +1,8 @@ import * as logging from './logging.mjs' import {constants} from './constants.mjs' +import {Kind} from './kind.mjs' +import {Helm} from './helm.mjs' +import {Kubectl} from "./kubectl.mjs"; // Expose components from the core module -export {logging, constants} +export {logging, constants, Kind, Helm, Kubectl} diff --git a/fullstack-network-manager/src/core/kubectl.mjs b/fullstack-network-manager/src/core/kubectl.mjs new file mode 100644 index 000000000..1ffe5bdb3 --- /dev/null +++ b/fullstack-network-manager/src/core/kubectl.mjs @@ -0,0 +1,72 @@ +import {ShellRunner} from "./shell_runner.mjs"; + +export class Kubectl extends ShellRunner { + /** + * Prepare a `kubectl` shell command string + * @param action represents a helm command (e.g. create | install | get ) + * @param args args of the command + * @returns {string} + */ + prepareCommand(action, ...args) { + let cmd = `kubectl ${action} ` + args.forEach(arg => {cmd += ` ${arg}`}) + return cmd + } + + /** + * Invoke `kubectl create` command + * @param resource a kubernetes resource type (e.g. pod | svc etc.) + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async create(resource, ...args) { + return this.run(this.prepareCommand('create', resource, ...args)) + } + + /** + * Invoke `kubectl create ns` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async createNamespace(...args) { + return this.run(this.prepareCommand('create', 'ns', ...args)) + } + + /** + * Invoke `kubectl delete` command + * @param resource a kubernetes resource type (e.g. pod | svc etc.) + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async delete(resource, ...args) { + return this.run(this.prepareCommand('delete', resource, ...args)) + } + + /** + * Invoke `kubectl delete ns` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async deleteNamespace(...args) { + return this.run(this.prepareCommand('delete', 'ns', ...args)) + } + + /** + * Invoke `kubectl get` command + * @param resource a kubernetes resource type (e.g. pod | svc etc.) + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async get(resource, ...args) { + return this.run(this.prepareCommand('get', resource, ...args)) + } + + /** + * Invoke `kubectl get ns` command + * @param args args of the command + * @returns {Promise} console output as an array of strings + */ + async getNamespace(...args) { + return this.run(this.prepareCommand('get', 'ns', ...args)) + } +} \ No newline at end of file diff --git a/fullstack-network-manager/src/core/logging.mjs b/fullstack-network-manager/src/core/logging.mjs index 927d2a4e4..e2093c467 100644 --- a/fullstack-network-manager/src/core/logging.mjs +++ b/fullstack-network-manager/src/core/logging.mjs @@ -2,6 +2,7 @@ import * as winston from 'winston' import {constants} from "./constants.mjs"; import {v4 as uuidv4} from 'uuid'; import * as util from "util"; +import chalk from "chalk"; const customFormat = winston.format.combine( winston.format.label({label: 'FST', message: false}), @@ -92,6 +93,10 @@ const Logger = class { showUser(msg, ...args) { console.log(util.format(msg, ...args)) } + showUserError(err) { + console.log(chalk.red(err.message)) + console.log(err.stack) + } critical(msg, ...args) { this.winsonLogger.crit(msg, ...args, this.prepMeta()) diff --git a/fullstack-network-manager/src/core/shell_runner.mjs b/fullstack-network-manager/src/core/shell_runner.mjs index cfacd628d..1705e9312 100644 --- a/fullstack-network-manager/src/core/shell_runner.mjs +++ b/fullstack-network-manager/src/core/shell_runner.mjs @@ -1,8 +1,9 @@ import {spawn} from "child_process"; +import chalk from "chalk"; export class ShellRunner { constructor(opts) { - if (!opts || !opts.logger === undefined) throw new Error("logger must be provided") + if (!opts || !opts.logger === undefined) throw new Error("An instance of core/Logger is required") this.logger = opts.logger } @@ -13,10 +14,9 @@ export class ShellRunner { */ async run(cmd) { const self = this + let callStack= new Error().stack // capture the callstack to be included in error return new Promise((resolve, reject) => { - self.logger.debug(cmd) - const child = spawn(cmd, { shell: true, }) @@ -31,16 +31,30 @@ export class ShellRunner { }) }) - - child.on('error', err => { - reject(err) + let errOutput= [] + child.stderr.on('data', d => { + let items = d.toString().split(/\r?\n/) + items.forEach(item => { + if (item) { + errOutput.push(item) + } + }) }) - child.on('close', (code, signal) => { + + child.on('exit', (code, signal) => { if (code) { - reject(new Error(`Command exit with error code: ${code}`)) + let err = new Error(`Command exit with error code: ${code}`) + + // include the callStack to the parent run() instead of from inside this handler. + // this is needed to ensure we capture the proper callstack for easier debugging. + err.stack = callStack + + errOutput.forEach(m => self.logger.showUser(chalk.red(m))) + reject(err) } + self.logger.debug(cmd, {'commandExitCode': code, 'commandExitSignal': signal, 'commandOutput': output}) resolve(output) }) }) diff --git a/fullstack-network-manager/src/index.mjs b/fullstack-network-manager/src/index.mjs index d36e630dc..f13457434 100644 --- a/fullstack-network-manager/src/index.mjs +++ b/fullstack-network-manager/src/index.mjs @@ -5,8 +5,15 @@ import * as core from './core/index.mjs' export function main(argv) { const logger = core.logging.NewLogger('debug') + const kind = new core.Kind({logger: logger}) + const helm = new core.Helm({logger: logger}) + const kubectl= new core.Kubectl({logger: logger}) + const opts = { - logger: logger + logger: logger, + kind: kind, + helm: helm, + kubectl: kubectl, } logger.debug("Constants: %s", JSON.stringify(core.constants)) diff --git a/fullstack-network-manager/test/commands/base.test.js b/fullstack-network-manager/test/commands/base.test.js index 7f4474723..559f01617 100644 --- a/fullstack-network-manager/test/commands/base.test.js +++ b/fullstack-network-manager/test/commands/base.test.js @@ -1,12 +1,21 @@ import {test, expect, it, describe} from "@jest/globals"; -import {logging} from "../../src/core/index.mjs"; +import {Helm, Kubectl, logging} from "../../src/core/index.mjs"; import {BaseCommand} from "../../src/commands/base.mjs"; import * as core from "../../src/core/index.mjs" +import {Kind} from "../../src/core/kind.mjs"; const testLogger = logging.NewLogger("debug") describe('BaseCommand', () => { - const baseCmd = new BaseCommand({logger: testLogger}) + const kind = new Kind({logger: testLogger}) + const helm = new Helm({logger: testLogger}) + const kubectl = new Kubectl({logger: testLogger}) + const baseCmd = new BaseCommand({ + logger: testLogger, + kind: kind, + helm: helm, + kubectl: kubectl, + }) describe('runShell', () => { it('should fail during invalid program check', async() => { diff --git a/fullstack-network-manager/test/commands/init.test.js b/fullstack-network-manager/test/commands/init.test.js index 3d25005e4..62acbfc20 100644 --- a/fullstack-network-manager/test/commands/init.test.js +++ b/fullstack-network-manager/test/commands/init.test.js @@ -1,10 +1,20 @@ import {InitCommand} from "../../src/commands/init.mjs"; import {expect, describe, it} from "@jest/globals"; import * as core from "../../src/core/index.mjs"; +import {Helm, Kind, Kubectl} from "../../src/core/index.mjs"; +import {BaseCommand} from "../../src/commands/base.mjs"; const testLogger = core.logging.NewLogger('debug') describe('InitCommand', () => { - const initCmd = new InitCommand({logger: testLogger}) + const kind = new Kind({logger: testLogger}) + const helm = new Helm({logger: testLogger}) + const kubectl = new Kubectl({logger: testLogger}) + const initCmd = new InitCommand({ + logger: testLogger, + kind: kind, + helm: helm, + kubectl: kubectl, + }) describe('commands', () => { it('init execution should succeed', async () => {