diff --git a/fullstack-network-manager/src/commands/base.mjs b/fullstack-network-manager/src/commands/base.mjs index bdbe52880..23c721658 100644 --- a/fullstack-network-manager/src/commands/base.mjs +++ b/fullstack-network-manager/src/commands/base.mjs @@ -72,10 +72,13 @@ export const BaseCommand = class BaseCommand { /** * Run the specified bash command * @param cmd is a bash command including args - * @returns {Promise} + * @returns {Promise} */ runExec(cmd) { + let self = this + return new Promise((resolve, reject) => { + self.logger.debug(`Invoking '${cmd}'...`) exec(cmd, (error, stdout, stderr) => { if (error) { reject(error) diff --git a/fullstack-network-manager/src/commands/cluster.mjs b/fullstack-network-manager/src/commands/cluster.mjs index 4439a2407..85ee0ba2c 100644 --- a/fullstack-network-manager/src/commands/cluster.mjs +++ b/fullstack-network-manager/src/commands/cluster.mjs @@ -8,7 +8,14 @@ import chalk from "chalk"; const clusterNameFlag = { describe: 'Name of the cluster', default: core.constants.CLUSTER_NAME, - alias: 'n', + alias: 'c', + type: 'string' +} + +const namespaceFlag = { + describe: 'Name of the namespace', + default: core.constants.NAMESPACE_NAME, + alias: 's', type: 'string' } @@ -19,21 +26,22 @@ export const ClusterCommand = class extends BaseCommand { /** * List available clusters - * @returns {Promise} + * @returns {Promise} */ async getClusters() { - let cmd = `kind get clusters` - try { + let cmd = `kind get clusters -q` + let output = await this.runExec(cmd) this.logger.showUser("\nList of available clusters \n--------------------------\n%s", output) - return true + + return output.split(/\r?\n/) } catch (e) { this.logger.error("%s", e) this.logger.showUser(e.message) } - return false + return [] } /** @@ -42,11 +50,13 @@ export const ClusterCommand = class extends BaseCommand { * @returns {Promise} */ async getClusterInfo(argv) { - let cmd = `kubectl cluster-info --context kind-${argv.name}` - try { + let clusterName = argv.clusterName + let cmd = `kubectl cluster-info --context kind-${clusterName}` + let output = await this.runExec(cmd) this.logger.showUser(output) + return true } catch (e) { this.logger.error("%s", e) @@ -62,14 +72,14 @@ export const ClusterCommand = class extends BaseCommand { * @returns {Promise} */ async create(argv) { - let cmd = `kind create cluster -n ${argv.name} --config ${core.constants.RESOURCES_DIR}/dev-cluster.yaml` - try { - this.logger.showUser(chalk.cyan('Creating cluster:'), chalk.yellow(`${argv.name}...`)) - this.logger.debug(`Invoking '${cmd}'...`) + let clusterName = argv.clusterName + let cmd = `kind create cluster -n ${clusterName} --config ${core.constants.RESOURCES_DIR}/dev-cluster.yaml` + + this.logger.showUser(chalk.cyan('Creating cluster:'), chalk.yellow(`${clusterName}...`)) let output = await this.runExec(cmd) this.logger.debug(output) - this.logger.showUser(chalk.green('Created cluster:'), chalk.yellow(argv.name)) + this.logger.showUser(chalk.green('Created cluster:'), chalk.yellow(clusterName)) // show all clusters and cluster-info await this.getClusters() @@ -90,10 +100,11 @@ export const ClusterCommand = class extends BaseCommand { * @returns {Promise} */ async delete(argv) { - let cmd = `kind delete cluster -n ${argv.name}` try { - this.logger.debug(`Invoking '${cmd}'...`) - this.logger.showUser(chalk.cyan('Deleting cluster:'), chalk.yellow(`${argv.name}...`)) + let clusterName = argv.clusterName + let cmd = `kind delete cluster -n ${clusterName}` + + this.logger.showUser(chalk.cyan('Deleting cluster:'), chalk.yellow(`${clusterName}...`)) await this.runExec(cmd) await this.getClusters() @@ -106,6 +117,66 @@ export const ClusterCommand = class extends BaseCommand { 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.runExec(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 [] + } + + /** + * Setup cluster with shared components + * @param argv + * @returns {Promise} + */ + 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}...`)) + + 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.runExec(cmd) + this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is installed`) + } else { + this.logger.showUser(chalk.green('OK'), `chart '${releaseName}' is already installed`) + } + + this.logger.showUser(chalk.yellow("Chart setup complete")) + + return true + } catch (e) { + this.logger.error("%s", e.stack) + this.logger.showUser(e.message) + } + + return false + } + /** * Return Yargs command definition for 'cluster' command * @param clusterCmd an instance of ClusterCommand @@ -120,45 +191,83 @@ export const ClusterCommand = class extends BaseCommand { command: 'create', desc: 'Create a cluster', builder: yargs => { - yargs.option('name', clusterNameFlag) + yargs.option('cluster-name', clusterNameFlag) }, handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster create' ===") + clusterCmd.logger.debug(argv) + clusterCmd.create(argv).then(r => { if (!r) process.exit(1) }) + + clusterCmd.logger.debug("==== Finished running `cluster create`====") } }) .command({ command: 'delete', desc: 'Delete a cluster', builder: yargs => { - yargs.option('name', clusterNameFlag) + yargs.option('cluster-name', clusterNameFlag) }, handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster delete' ===") + clusterCmd.logger.debug(argv) + clusterCmd.delete(argv).then(r => { if (!r) process.exit(1) }) + + clusterCmd.logger.debug("==== Finished running `cluster delete`====") } }) .command({ command: 'list', desc: 'List all clusters', handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster list' ===") + clusterCmd.logger.debug(argv) + clusterCmd.getClusters().then(r => { if (!r) process.exit(1) }) + + clusterCmd.logger.debug("==== Finished running `cluster list`====") } }) .command({ command: 'info', desc: 'Get cluster info', builder: yargs => { - yargs.option('name', clusterNameFlag) + yargs.option('cluster-name', clusterNameFlag) }, handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster info' ===") + clusterCmd.logger.debug(argv) + clusterCmd.getClusterInfo(argv).then(r => { if (!r) process.exit(1) }) + + clusterCmd.logger.debug("==== Finished running `cluster info`====") + } + }) + .command({ + command: 'setup', + desc: 'Setup cluster with shared components', + builder: yargs => { + yargs.option('cluster-name', clusterNameFlag) + yargs.option('namespace', namespaceFlag) + }, + handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster setup' ===") + clusterCmd.logger.debug(argv) + + clusterCmd.setup(argv).then(r => { + if (!r) process.exit(1) + }) + + clusterCmd.logger.debug("==== Finished running `cluster setup`====") } }) .demand(1, 'Select a cluster command') diff --git a/fullstack-network-manager/src/commands/index.mjs b/fullstack-network-manager/src/commands/index.mjs index 69f56bc76..44b5c4065 100644 --- a/fullstack-network-manager/src/commands/index.mjs +++ b/fullstack-network-manager/src/commands/index.mjs @@ -1,5 +1,6 @@ import {ClusterCommand} from "./cluster.mjs"; import {InitCommand} from "./init.mjs"; +import {NetworkCommand} from "./network.mjs" /* * Return a list of Yargs command builder to be exposed through CLI @@ -8,10 +9,12 @@ import {InitCommand} from "./init.mjs"; function Initialize(opts) { const initCmd = new InitCommand(opts) const clusterCmd = new ClusterCommand(opts) + const networkCmd = new NetworkCommand(opts) return [ InitCommand.getCommandDefinition(initCmd), ClusterCommand.getCommandDefinition(clusterCmd), + NetworkCommand.getCommandDefinition(networkCmd), ] } diff --git a/fullstack-network-manager/src/commands/network.mjs b/fullstack-network-manager/src/commands/network.mjs new file mode 100644 index 000000000..133ec0bb9 --- /dev/null +++ b/fullstack-network-manager/src/commands/network.mjs @@ -0,0 +1,63 @@ +import {BaseCommand} from "./base.mjs"; +import chalk from "chalk"; + + +export const NetworkCommand = class NetworkCommand extends BaseCommand { + async deployShared(argv) { + this.logger.showUser(chalk.green("Deploying FST network....%s"), chalk.yellow(JSON.stringify(argv))) + return false + } + + async deploy(argv) { + this.logger.showUser(chalk.green("Deploying FST network....%s"), chalk.yellow(JSON.stringify(argv))) + return false + } + + static getCommandDefinition(networkCmd) { + return { + command: 'network', + desc: 'Manage FST network deployment', + builder: yargs => { + return yargs + .command({ + command: 'deploy', + desc: 'Deploy a FST network', + builder: yargs => { + yargs.option('haproxy', { + describe: 'Deploy HAProxy', + default: true, + alias: 'p', + type: 'boolean' + }) + + yargs.option('envoy-proxy', { + describe: 'Deploy Envoy proxy', + default: true, + alias: 'e', + type: 'boolean' + }) + + yargs.option('mirror-node', { + describe: 'Deploy mirror node', + default: true, + alias: 'm', + type: 'boolean' + }) + yargs.option('hedera-explorer', { + describe: 'Deploy hedera explorer', + default: true, + alias: 'x', + type: 'boolean' + }) + }, + handler: argv => { + networkCmd.deploy(argv).then(r => { + if (!r) process.exit(1) + }) + } + }) + .demand(1, 'Select a network command') + } + } + } +} \ No newline at end of file diff --git a/fullstack-network-manager/src/core/constants.mjs b/fullstack-network-manager/src/core/constants.mjs index b53733ec0..66534324a 100644 --- a/fullstack-network-manager/src/core/constants.mjs +++ b/fullstack-network-manager/src/core/constants.mjs @@ -6,7 +6,9 @@ const CUR_FILE_DIR = dirname(fileURLToPath(import.meta.url)) const USER = `${process.env.USER}` export const constants = { USER: `${USER}`, - CLUSTER_NAME: `fst-${USER}`, + CLUSTER_NAME: `fst`, + RELEASE_NAME: `fst`, + NAMESPACE_NAME: `fst-${USER}`, HELM: 'helm', KIND: 'kind', KUBECTL: 'kubectl',