From 665dcdf320e8e6587a85280e411bfa6bb7ff19a7 Mon Sep 17 00:00:00 2001 From: Deepak Mishra <107930718+deepak-swirlds@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:28:39 +0530 Subject: [PATCH] Fix eslint configuration and fix lint issues (#542) Signed-off-by: Deepak Mishra --- fullstack-network-manager/.eslintrc.yml | 5 + fullstack-network-manager/fsnetman.mjs | 4 +- fullstack-network-manager/jest.config.mjs | 7 +- .../src/commands/base.mjs | 139 ++-- .../src/commands/chart.mjs | 236 ++++--- .../src/commands/cluster.mjs | 527 ++++++++------- .../src/commands/flags.mjs | 248 +++---- .../src/commands/index.mjs | 30 +- .../src/commands/init.mjs | 117 ++-- .../src/commands/node.mjs | 411 ++++++------ .../src/core/chart_manager.mjs | 186 +++--- .../src/core/config_manager.mjs | 172 ++--- .../src/core/constants.mjs | 66 +- fullstack-network-manager/src/core/errors.mjs | 52 +- fullstack-network-manager/src/core/helm.mjs | 62 +- fullstack-network-manager/src/core/index.mjs | 42 +- fullstack-network-manager/src/core/kind.mjs | 81 ++- .../src/core/kubectl.mjs | 155 +++-- .../src/core/logging.mjs | 234 +++---- .../src/core/package_downloader.mjs | 294 ++++---- .../src/core/platform_installer.mjs | 627 +++++++++--------- .../src/core/shell_runner.mjs | 107 ++- .../src/core/templates.mjs | 35 +- fullstack-network-manager/src/core/zippy.mjs | 114 ++-- fullstack-network-manager/src/index.mjs | 64 +- 25 files changed, 1994 insertions(+), 2021 deletions(-) diff --git a/fullstack-network-manager/.eslintrc.yml b/fullstack-network-manager/.eslintrc.yml index 45cecc0cb..6660f95bd 100644 --- a/fullstack-network-manager/.eslintrc.yml +++ b/fullstack-network-manager/.eslintrc.yml @@ -6,3 +6,8 @@ parserOptions: ecmaVersion: latest sourceType: module rules: {} +overrides: + - files: ["*.mjs"] + parserOptions: + ecmaVersion: latest + sourceType: module \ No newline at end of file diff --git a/fullstack-network-manager/fsnetman.mjs b/fullstack-network-manager/fsnetman.mjs index 95c782168..1d3cd1430 100755 --- a/fullstack-network-manager/fsnetman.mjs +++ b/fullstack-network-manager/fsnetman.mjs @@ -1,3 +1,5 @@ #!/usr/bin/env node import * as fnm from './src/index.mjs' -const cli = fnm.main(process.argv) + +// CLI entry point +fnm.main(process.argv) diff --git a/fullstack-network-manager/jest.config.mjs b/fullstack-network-manager/jest.config.mjs index 2d00751be..f24d6b78b 100644 --- a/fullstack-network-manager/jest.config.mjs +++ b/fullstack-network-manager/jest.config.mjs @@ -1,7 +1,6 @@ const config = { - verbose: true, - transform: {}, -}; + verbose: true, + transform: {} +} export default config - diff --git a/fullstack-network-manager/src/commands/base.mjs b/fullstack-network-manager/src/commands/base.mjs index 530a75cbc..984a52625 100644 --- a/fullstack-network-manager/src/commands/base.mjs +++ b/fullstack-network-manager/src/commands/base.mjs @@ -1,97 +1,96 @@ -"use strict" -import * as core from "../core/index.mjs" -import chalk from "chalk"; -import {ShellRunner} from "../core/shell_runner.mjs"; +'use strict' +import * as core from '../core/index.mjs' +import chalk from 'chalk' +import { ShellRunner } from '../core/shell_runner.mjs' export class BaseCommand extends ShellRunner { - - async checkDep(cmd) { - try { - this.logger.debug(cmd) - await this.run(cmd) - } catch (e) { - this.logger.showUserError(e) - return false - } - - return true + async checkDep (cmd) { + try { + this.logger.debug(cmd) + await this.run(cmd) + } catch (e) { + this.logger.showUserError(e) + return false } - /** + return true + } + + /** * Check if 'kind' CLI program is installed or not * @returns {Promise} */ - async checkKind() { - return this.checkDep(`${core.constants.KIND} --version`) - } + async checkKind () { + return this.checkDep(`${core.constants.KIND} --version`) + } - /** + /** * Check if 'helm' CLI program is installed or not * @returns {Promise} */ - async checkHelm() { - return this.checkDep(`${core.constants.HELM} version`) - } + async checkHelm () { + return this.checkDep(`${core.constants.HELM} version`) + } - /** + /** * Check if 'kubectl' CLI program is installed or not * @returns {Promise} */ - async checkKubectl() { - return this.checkDep(`${core.constants.KUBECTL} version --client`) - } + async checkKubectl () { + return this.checkDep(`${core.constants.KUBECTL} version --client`) + } - /** + /** * Check if all the required dependencies are installed or not * @param deps is a list of dependencies * @returns {Promise} */ - async checkDependencies(deps = []) { - this.logger.debug("Checking for required dependencies: %s", deps) - - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - this.logger.debug("Checking for dependency '%s'", dep) + async checkDependencies (deps = []) { + this.logger.debug('Checking for required dependencies: %s', deps) - let status = false - const check = this.checks.get(dep) - if (check) { - status = await check() - } + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + this.logger.debug("Checking for dependency '%s'", dep) - if (!status) { - this.logger.showUser(chalk.red(`FAIL: '${dep}' is not found`)) - return false - } + let status = false + const check = this.checks.get(dep) + if (check) { + status = await check() + } - this.logger.showUser(chalk.green(`OK: '${dep}' is found`)) - } + if (!status) { + this.logger.showUser(chalk.red(`FAIL: '${dep}' is not found`)) + return false + } - this.logger.debug("All required dependencies are found: %s", deps) - - return true + this.logger.showUser(chalk.green(`OK: '${dep}' is found`)) } - constructor(opts) { - if (!opts || !opts.logger) throw new Error('An instance of core/Logger is required') - 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') - if (!opts || !opts.chartManager) throw new Error('An instance of core/ChartManager is required') - if (!opts || !opts.configManager) throw new Error('An instance of core/ConfigManager is required') - - super(opts.logger); - - this.kind = opts.kind - this.helm = opts.helm - this.kubectl = opts.kubectl - this.chartManager = opts.chartManager - this.configManager = opts.configManager - - // map of dependency checks - this.checks = new Map() - .set(core.constants.KIND, () => this.checkKind()) - .set(core.constants.HELM, () => this.checkHelm()) - .set(core.constants.KUBECTL, () => this.checkKubectl()) - } + this.logger.debug('All required dependencies are found: %s', deps) + + return true + } + + constructor (opts) { + if (!opts || !opts.logger) throw new Error('An instance of core/Logger is required') + 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') + if (!opts || !opts.chartManager) throw new Error('An instance of core/ChartManager is required') + if (!opts || !opts.configManager) throw new Error('An instance of core/ConfigManager is required') + + super(opts.logger) + + this.kind = opts.kind + this.helm = opts.helm + this.kubectl = opts.kubectl + this.chartManager = opts.chartManager + this.configManager = opts.configManager + + // map of dependency checks + this.checks = new Map() + .set(core.constants.KIND, () => this.checkKind()) + .set(core.constants.HELM, () => this.checkHelm()) + .set(core.constants.KUBECTL, () => this.checkKubectl()) + } } diff --git a/fullstack-network-manager/src/commands/chart.mjs b/fullstack-network-manager/src/commands/chart.mjs index 7d3346845..59f41788b 100644 --- a/fullstack-network-manager/src/commands/chart.mjs +++ b/fullstack-network-manager/src/commands/chart.mjs @@ -1,139 +1,133 @@ -import {BaseCommand} from "./base.mjs"; -import * as core from "../core/index.mjs"; -import * as flags from "./flags.mjs"; -import {constants} from "../core/index.mjs"; - +import { BaseCommand } from './base.mjs' +import * as flags from './flags.mjs' +import { constants } from '../core/index.mjs' export class ChartCommand extends BaseCommand { + prepareValuesArg (argv, config) { + const { valuesFile, mirrorNode, hederaExplorer } = argv - prepareValuesArg(argv, config) { - const {valuesFile, mirrorNode, hederaExplorer} = argv - - let valuesArg = '' - let chartDir = this.configManager.flagValue(config, flags.chartDirectory) - if (chartDir) { - valuesArg = `-f ${chartDir}/fullstack-deployment/values.yaml` - } - - if (valuesFile) { - valuesArg += `--values ${valuesFile}` - } - - valuesArg += ` --set hedera-mirror-node.enabled=${mirrorNode} --set hedera-explorer.enabled=${hederaExplorer}` - - return valuesArg + let valuesArg = '' + const chartDir = this.configManager.flagValue(config, flags.chartDirectory) + if (chartDir) { + valuesArg = `-f ${chartDir}/fullstack-deployment/values.yaml` } - prepareChartPath(config) { - const chartDir = this.configManager.flagValue(config, flags.chartDirectory) - let chartPath = `full-stack-testing/fullstack-deployment` - if (chartDir) { - chartPath = `${chartDir}/fullstack-deployment` - } - - return chartPath + if (valuesFile) { + valuesArg += `--values ${valuesFile}` } - async install(argv) { - try { - const namespace = argv.namespace + valuesArg += ` --set hedera-mirror-node.enabled=${mirrorNode} --set hedera-explorer.enabled=${hederaExplorer}` - const config = await this.configManager.setupConfig(argv) - const valuesArg = this.prepareValuesArg(argv, config) - const chartPath = this.prepareChartPath(config) + return valuesArg + } - await this.chartManager.install(namespace, constants.FST_CHART_DEPLOYMENT_NAME, chartPath, config.version, valuesArg) - - this.logger.showList('charts', await this.chartManager.getInstalledCharts(namespace)) - } catch (e) { - this.logger.showUserError(e) - } + prepareChartPath (config) { + const chartDir = this.configManager.flagValue(config, flags.chartDirectory) + let chartPath = 'full-stack-testing/fullstack-deployment' + if (chartDir) { + chartPath = `${chartDir}/fullstack-deployment` } - async uninstall(argv) { - const namespace = argv.namespace + return chartPath + } - return await this.chartManager.uninstall(namespace, constants.FST_CHART_DEPLOYMENT_NAME) - } + async install (argv) { + try { + const namespace = argv.namespace - async upgrade(argv) { - const namespace = argv.namespace + const config = await this.configManager.setupConfig(argv) + const valuesArg = this.prepareValuesArg(argv, config) + const chartPath = this.prepareChartPath(config) - const config = await this.configManager.setupConfig(argv) - const valuesArg = this.prepareValuesArg(argv, config) - const chartPath = this.prepareChartPath(config) + await this.chartManager.install(namespace, constants.FST_CHART_DEPLOYMENT_NAME, chartPath, config.version, valuesArg) - return await this.chartManager.upgrade(namespace, constants.FST_CHART_DEPLOYMENT_NAME, chartPath, valuesArg) + this.logger.showList('charts', await this.chartManager.getInstalledCharts(namespace)) + } catch (e) { + this.logger.showUserError(e) } - - static getCommandDefinition(chartCmd) { - return { - command: 'chart', - desc: 'Manage FST chart deployment', - builder: yargs => { - return yargs - .command({ - command: 'install', - desc: 'Install FST network deployment chart', - builder: y => flags.setCommandFlags(y, - flags.namespace, - flags.deployMirrorNode, - flags.deployHederaExplorer, - flags.valuesFile, - flags.chartDirectory, - ), - handler: argv => { - chartCmd.logger.debug("==== Running 'chart install' ===") - chartCmd.logger.debug(argv) - - chartCmd.install(argv).then(r => { - chartCmd.logger.debug("==== Finished running `chart install`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'uninstall', - desc: 'Uninstall FST network deployment chart', - builder: y => flags.setCommandFlags(y, flags.namespace), - handler: argv => { - chartCmd.logger.debug("==== Running 'chart uninstall' ===") - chartCmd.logger.debug(argv) - - chartCmd.uninstall(argv).then(r => { - chartCmd.logger.debug("==== Finished running `chart uninstall`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'upgrade', - desc: 'Refresh existing FST network deployment with new values', - builder: y => flags.setCommandFlags(y, - flags.namespace, - flags.deployMirrorNode, - flags.deployHederaExplorer, - flags.valuesFile, - flags.chartDirectory, - ), - handler: argv => { - chartCmd.logger.debug("==== Running 'chart upgrade' ===") - chartCmd.logger.debug(argv) - - chartCmd.upgrade(argv).then(r => { - chartCmd.logger.debug("==== Finished running `chart upgrade`====") - - if (!r) process.exit(1) - }) - - } - }) - .demandCommand(1, 'Select a chart command') + } + + async uninstall (argv) { + const namespace = argv.namespace + + return await this.chartManager.uninstall(namespace, constants.FST_CHART_DEPLOYMENT_NAME) + } + + async upgrade (argv) { + const namespace = argv.namespace + + const config = await this.configManager.setupConfig(argv) + const valuesArg = this.prepareValuesArg(argv, config) + const chartPath = this.prepareChartPath(config) + + return await this.chartManager.upgrade(namespace, constants.FST_CHART_DEPLOYMENT_NAME, chartPath, valuesArg) + } + + static getCommandDefinition (chartCmd) { + return { + command: 'chart', + desc: 'Manage FST chart deployment', + builder: yargs => { + return yargs + .command({ + command: 'install', + desc: 'Install FST network deployment chart', + builder: y => flags.setCommandFlags(y, + flags.namespace, + flags.deployMirrorNode, + flags.deployHederaExplorer, + flags.valuesFile, + flags.chartDirectory + ), + handler: argv => { + chartCmd.logger.debug("==== Running 'chart install' ===") + chartCmd.logger.debug(argv) + + chartCmd.install(argv).then(r => { + chartCmd.logger.debug('==== Finished running `chart install`====') + + if (!r) process.exit(1) + }) + } + }) + .command({ + command: 'uninstall', + desc: 'Uninstall FST network deployment chart', + builder: y => flags.setCommandFlags(y, flags.namespace), + handler: argv => { + chartCmd.logger.debug("==== Running 'chart uninstall' ===") + chartCmd.logger.debug(argv) + + chartCmd.uninstall(argv).then(r => { + chartCmd.logger.debug('==== Finished running `chart uninstall`====') + + if (!r) process.exit(1) + }) + } + }) + .command({ + command: 'upgrade', + desc: 'Refresh existing FST network deployment with new values', + builder: y => flags.setCommandFlags(y, + flags.namespace, + flags.deployMirrorNode, + flags.deployHederaExplorer, + flags.valuesFile, + flags.chartDirectory + ), + handler: argv => { + chartCmd.logger.debug("==== Running 'chart upgrade' ===") + chartCmd.logger.debug(argv) + + chartCmd.upgrade(argv).then(r => { + chartCmd.logger.debug('==== Finished running `chart upgrade`====') + + if (!r) process.exit(1) + }) } - } + }) + .demandCommand(1, 'Select a chart command') + } } -} \ No newline at end of file + } +} diff --git a/fullstack-network-manager/src/commands/cluster.mjs b/fullstack-network-manager/src/commands/cluster.mjs index 4c5caecd6..f267e3b25 100644 --- a/fullstack-network-manager/src/commands/cluster.mjs +++ b/fullstack-network-manager/src/commands/cluster.mjs @@ -1,333 +1,324 @@ import * as core from '../core/index.mjs' import * as flags from './flags.mjs' -import {BaseCommand} from "./base.mjs"; -import chalk from "chalk"; -import {constants} from "../core/index.mjs"; +import { BaseCommand } from './base.mjs' +import chalk from 'chalk' +import { constants } from '../core/index.mjs' /** * Define the core functionalities of 'cluster' command */ export class ClusterCommand extends BaseCommand { - /** + /** * List available clusters * @returns {Promise} */ - async getClusters() { - try { - return await this.kind.getClusters('-q') - } catch (e) { - this.logger.showUserError(e) - } - - return [] + async getClusters () { + try { + return await this.kind.getClusters('-q') + } catch (e) { + this.logger.showUserError(e) } - async showClusterList(argv) { - this.logger.showList("clusters", await this.getClusters()) - return true - } + return [] + } + + async showClusterList (argv) { + this.logger.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 [] + 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 * @returns {Promise} */ - async getClusterInfo(argv) { - try { - const clusterName = argv.clusterName - const cmd = `kubectl cluster-info --context kind-${clusterName}` - const output = await this.run(cmd) - - this.logger.showUser(`Cluster information (${clusterName})\n---------------------------------------`) - output.forEach(line => this.logger.showUser(line)) - this.logger.showUser("\n") - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + async getClusterInfo (argv) { + try { + const clusterName = argv.clusterName + const cmd = `kubectl cluster-info --context kind-${clusterName}` + const output = await this.run(cmd) + + this.logger.showUser(`Cluster information (${clusterName})\n---------------------------------------`) + output.forEach(line => this.logger.showUser(line)) + this.logger.showUser('\n') + return true + } catch (e) { + this.logger.showUserError(e) } - async createNamespace(argv) { - try { - const namespace = argv.namespace - const 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`) - } - - await this.kubectl.config(`set-context --current --namespace="${namespace}"`) - - this.logger.showList("namespaces", await this.getNameSpaces()) - - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + return false + } + + async createNamespace (argv) { + try { + const namespace = argv.namespace + const 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`) + } + + await this.kubectl.config(`set-context --current --namespace="${namespace}"`) + + this.logger.showList('namespaces', await this.getNameSpaces()) + + return true + } catch (e) { + this.logger.showUserError(e) } - /** + return false + } + + /** * Create a cluster * @param argv command arguments * @param config config object * @returns {Promise} */ - async create(argv, config = {}) { - try { - const clusterName = argv.clusterName - const clusters = await this.getClusters() - - if (!config) { - config = this.configManager.setupConfig(argv) - } - - this.logger.showUser(chalk.cyan('> checking cluster:'), chalk.yellow(`${clusterName}`)) - if (!clusters.includes(clusterName)) { - this.logger.showUser(chalk.cyan('> creating cluster:'), chalk.yellow(`${clusterName} ...`)) - await this.kind.createCluster( + async create (argv, config = {}) { + try { + const clusterName = argv.clusterName + const clusters = await this.getClusters() + + if (!config) { + config = this.configManager.setupConfig(argv) + } + + this.logger.showUser(chalk.cyan('> checking cluster:'), chalk.yellow(`${clusterName}`)) + if (!clusters.includes(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('OK'), `cluster '${clusterName}' is created`) - } else { - this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' already exists`) - } + `--config ${core.constants.RESOURCES_DIR}/dev-cluster.yaml` + ) + 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 - await this.showClusterList() + // show all clusters and cluster-info + await this.showClusterList() - await this.getClusterInfo(argv) + await this.getClusterInfo(argv) - await this.createNamespace(argv) + await this.createNamespace(argv) - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + return true + } catch (e) { + this.logger.showUserError(e) } - /** + return false + } + + /** * Delete a cluster * @param argv * @returns {Promise} */ - async delete(argv) { - try { - const clusterName = argv.clusterName - const 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} ...`)) - await this.kind.deleteCluster(clusterName) - this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is deleted`) - } else { - this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is already deleted`) - } - - this.logger.showList('clusters', await this.getClusters()) - - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + async delete (argv) { + try { + const clusterName = argv.clusterName + const 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} ...`)) + await this.kind.deleteCluster(clusterName) + this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is deleted`) + } else { + this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is already deleted`) + } + + this.logger.showList('clusters', await this.getClusters()) + + return true + } catch (e) { + this.logger.showUserError(e) } + return false + } - async showInstalledChartList(namespace) { - this.logger.showList("charts installed", await this.chartManager.getInstalledCharts(namespace)) - } + async showInstalledChartList (namespace) { + this.logger.showList('charts installed', await this.chartManager.getInstalledCharts(namespace)) + } - /** + /** * Setup cluster with shared components * @param argv * @returns {Promise} */ - async setup(argv) { - try { - const config = await this.configManager.setupConfig(argv) - const clusterName = argv.clusterName - const namespace = argv.namespace - - // create cluster - await this.create(argv, config) - - // install fullstack-cluster-setup chart - const chartPath = this.prepareChartPath(config) - const valuesArg = this.prepareValuesArg(config, argv.prometheusStack, argv.minio, argv.envoyGateway, - argv.certManager, argv.certManagerCrds) - this.logger.showUser(chalk.cyan('> setting up cluster:'), chalk.yellow(`${chartPath}`, chalk.yellow(valuesArg))) - await this.chartManager.install(namespace, constants.FST_CHART_SETUP_NAME, chartPath, config.version, valuesArg) - await this.showInstalledChartList(namespace) - - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + async setup (argv) { + try { + const config = await this.configManager.setupConfig(argv) + const clusterName = argv.clusterName + const namespace = argv.namespace + + // create cluster + await this.create(argv, config) + + // install fullstack-cluster-setup chart + const chartPath = this.prepareChartPath(config) + const valuesArg = this.prepareValuesArg(config, argv.prometheusStack, argv.minio, argv.envoyGateway, + argv.certManager, argv.certManagerCrds) + this.logger.showUser(chalk.cyan('> setting up cluster:'), chalk.yellow(`${chartPath}`, chalk.yellow(valuesArg))) + await this.chartManager.install(namespace, constants.FST_CHART_SETUP_NAME, chartPath, config.version, valuesArg) + await this.showInstalledChartList(namespace) + + return true + } catch (e) { + this.logger.showUserError(e) } - /** + return false + } + + /** * Return Yargs command definition for 'cluster' command * @param clusterCmd an instance of ClusterCommand */ - static getCommandDefinition(clusterCmd) { - return { - command: 'cluster', - desc: 'Manage FST cluster', - builder: yargs => { - return yargs - .command({ - command: 'create', - desc: 'Create a cluster', - builder: y => flags.setCommandFlags(y, flags.clusterName, flags.namespace), - handler: argv => { - clusterCmd.logger.debug("==== Running 'cluster create' ===") - clusterCmd.logger.debug(argv) - - clusterCmd.create(argv).then(r => { - clusterCmd.logger.debug("==== Finished running `cluster create`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'delete', - desc: 'Delete a cluster', - builder: y => flags.setCommandFlags(y, flags.clusterName), - handler: argv => { - clusterCmd.logger.debug("==== Running 'cluster delete' ===") - clusterCmd.logger.debug(argv) - - clusterCmd.delete(argv).then(r => { - clusterCmd.logger.debug("==== Finished running `cluster delete`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'list', - desc: 'List all clusters', - handler: argv => { - clusterCmd.logger.debug("==== Running 'cluster list' ===") - clusterCmd.logger.debug(argv) - - clusterCmd.showClusterList().then(r => { - clusterCmd.logger.debug("==== Finished running `cluster list`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'info', - desc: 'Get cluster info', - builder: y => flags.setCommandFlags(y, flags.clusterName), - handler: argv => { - clusterCmd.logger.debug("==== Running 'cluster info' ===") - clusterCmd.logger.debug(argv) - - clusterCmd.getClusterInfo(argv).then(r => { - clusterCmd.logger.debug("==== Finished running `cluster info`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'setup', - desc: 'Setup cluster with shared components', - builder: y => flags.setCommandFlags(y, - flags.clusterName, - flags.namespace, - flags.chartDirectory, - flags.deployPrometheusStack, - flags.deployMinio, - flags.deployEnvoyGateway, - flags.deployCertManager, - flags.deployCertManagerCRDs, - ), - handler: argv => { - clusterCmd.logger.debug("==== Running 'cluster setup' ===") - clusterCmd.logger.debug(argv) - - clusterCmd.setup(argv).then(r => { - clusterCmd.logger.debug("==== Finished running `cluster setup`====") - - if (!r) process.exit(1) - }) - - } - }) - .demandCommand(1, 'Select a cluster command') + static getCommandDefinition (clusterCmd) { + return { + command: 'cluster', + desc: 'Manage FST cluster', + builder: yargs => { + return yargs + .command({ + command: 'create', + desc: 'Create a cluster', + builder: y => flags.setCommandFlags(y, flags.clusterName, flags.namespace), + handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster create' ===") + clusterCmd.logger.debug(argv) + + clusterCmd.create(argv).then(r => { + clusterCmd.logger.debug('==== Finished running `cluster create`====') + + if (!r) process.exit(1) + }) + } + }) + .command({ + command: 'delete', + desc: 'Delete a cluster', + builder: y => flags.setCommandFlags(y, flags.clusterName), + handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster delete' ===") + clusterCmd.logger.debug(argv) + + clusterCmd.delete(argv).then(r => { + clusterCmd.logger.debug('==== Finished running `cluster delete`====') + + if (!r) process.exit(1) + }) + } + }) + .command({ + command: 'list', + desc: 'List all clusters', + handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster list' ===") + clusterCmd.logger.debug(argv) + + clusterCmd.showClusterList().then(r => { + clusterCmd.logger.debug('==== Finished running `cluster list`====') + + if (!r) process.exit(1) + }) + } + }) + .command({ + command: 'info', + desc: 'Get cluster info', + builder: y => flags.setCommandFlags(y, flags.clusterName), + handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster info' ===") + clusterCmd.logger.debug(argv) + + clusterCmd.getClusterInfo(argv).then(r => { + clusterCmd.logger.debug('==== Finished running `cluster info`====') + + if (!r) process.exit(1) + }) } - } + }) + .command({ + command: 'setup', + desc: 'Setup cluster with shared components', + builder: y => flags.setCommandFlags(y, + flags.clusterName, + flags.namespace, + flags.chartDirectory, + flags.deployPrometheusStack, + flags.deployMinio, + flags.deployEnvoyGateway, + flags.deployCertManager, + flags.deployCertManagerCRDs + ), + handler: argv => { + clusterCmd.logger.debug("==== Running 'cluster setup' ===") + clusterCmd.logger.debug(argv) + + clusterCmd.setup(argv).then(r => { + clusterCmd.logger.debug('==== Finished running `cluster setup`====') + + if (!r) process.exit(1) + }) + } + }) + .demandCommand(1, 'Select a cluster command') + } + } + } + + prepareValuesArg (config, prometheusStackEnabled, minioEnabled, envoyGatewayEnabled, + certManagerEnabled, certManagerCrdsEnabled) { + let valuesArg = '' + const chartDir = this.configManager.flagValue(config, flags.chartDirectory) + if (chartDir) { + valuesArg = `-f ${chartDir}/fullstack-cluster-setup/values.yaml` } - prepareValuesArg(config, prometheusStackEnabled, minioEnabled, envoyGatewayEnabled, - certManagerEnabled, certManagerCrdsEnabled) { - - let valuesArg = '' - let chartDir = this.configManager.flagValue(config, flags.chartDirectory) - if (chartDir) { - valuesArg = `-f ${chartDir}/fullstack-cluster-setup/values.yaml` - } - - valuesArg += ` --set cloud.prometheusStack.enabled=${prometheusStackEnabled}` - valuesArg += ` --set cloud.minio.enabled=${minioEnabled}` - valuesArg += ` --set cloud.envoyGateway.enabled=${envoyGatewayEnabled}` - 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 + valuesArg += ` --set cloud.prometheusStack.enabled=${prometheusStackEnabled}` + valuesArg += ` --set cloud.minio.enabled=${minioEnabled}` + valuesArg += ` --set cloud.envoyGateway.enabled=${envoyGatewayEnabled}` + 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.')) } - prepareChartPath(config) { - const chartDir = this.configManager.flagValue(config, flags.chartDirectory) - let chartPath = `full-stack-testing/fullstack-cluster-setup` - if (chartDir) { - chartPath = `${chartDir}/fullstack-cluster-setup` - } + return valuesArg + } - return chartPath + prepareChartPath (config) { + const chartDir = this.configManager.flagValue(config, flags.chartDirectory) + let chartPath = 'full-stack-testing/fullstack-cluster-setup' + if (chartDir) { + chartPath = `${chartDir}/fullstack-cluster-setup` } + + return chartPath + } } diff --git a/fullstack-network-manager/src/commands/flags.mjs b/fullstack-network-manager/src/commands/flags.mjs index a6f74cf5c..d52d8c90f 100644 --- a/fullstack-network-manager/src/commands/flags.mjs +++ b/fullstack-network-manager/src/commands/flags.mjs @@ -6,97 +6,97 @@ import * as core from './../core/index.mjs' * @param commandFlags a set of command flags * */ -export function setCommandFlags(y, ...commandFlags) { - commandFlags.forEach(flag => { - y.option(flag.name, flag.definition) - }) +export function setCommandFlags (y, ...commandFlags) { + commandFlags.forEach(flag => { + y.option(flag.name, flag.definition) + }) } // list of common flags across commands. command specific flags are defined in the command's module. export const clusterName = { - name: 'cluster-name', - definition: { - describe: 'Cluster name', - default: core.constants.CLUSTER_NAME, - alias: 'c', - type: 'string' - } + name: 'cluster-name', + definition: { + describe: 'Cluster name', + default: core.constants.CLUSTER_NAME, + alias: 'c', + type: 'string' + } } export const namespace = { - name: 'namespace', - definition: { - describe: 'Namespace', - default: core.constants.NAMESPACE_NAME, - alias: 'n', - type: 'string' - } + name: 'namespace', + definition: { + describe: 'Namespace', + default: core.constants.NAMESPACE_NAME, + alias: 'n', + type: 'string' + } } export const deployMirrorNode = { - name: "mirror-node", - definition: { - describe: 'Deploy mirror node', - default: true, - alias: 'm', - type: 'boolean' - } + name: 'mirror-node', + definition: { + describe: 'Deploy mirror node', + default: true, + alias: 'm', + type: 'boolean' + } } export const deployHederaExplorer = { - name: 'hedera-explorer', - definition: { - describe: 'Deploy hedera explorer', - default: true, - alias: 'x', - type: 'boolean' - } + name: 'hedera-explorer', + definition: { + describe: 'Deploy hedera explorer', + default: true, + alias: 'x', + type: 'boolean' + } } export const valuesFile = { - name: 'values-file', - definition: { - describe: 'Helm chart values file [ to override defaults ]', - default: "", - alias: 'f', - type: 'string' - } + name: 'values-file', + definition: { + describe: 'Helm chart values file [ to override defaults ]', + default: '', + alias: 'f', + type: 'string' + } } export const deployPrometheusStack = { - name: 'prometheus-stack', - definition: { - describe: 'Deploy prometheus stack', - default: true, - type: 'boolean' - } + name: 'prometheus-stack', + definition: { + describe: 'Deploy prometheus stack', + default: true, + type: 'boolean' + } } export const deployMinio = { - name: 'minio', - definition: { - describe: 'Deploy minio operator', - default: true, - type: 'boolean' - } + name: 'minio', + definition: { + describe: 'Deploy minio operator', + default: true, + type: 'boolean' + } } export const deployEnvoyGateway = { - name: 'envoy-gateway', - definition: { - describe: 'Deploy envoy gateway', - default: true, - type: 'boolean' - } + name: 'envoy-gateway', + definition: { + describe: 'Deploy envoy gateway', + default: true, + type: 'boolean' + } } export const deployCertManager = { - name: 'cert-manager', - definition: { - describe: 'Deploy cert manager', - default: false, - type: 'boolean' - } + name: 'cert-manager', + definition: { + describe: 'Deploy cert manager', + default: false, + type: 'boolean' + } } /* @@ -104,77 +104,77 @@ export const deployCertManager = { CRDs are required for cert manager to deploy successfully. */ export const deployCertManagerCRDs = { - name: 'cert-manager-crds', - definition: { - describe: 'Deploy cert manager CRDs', - default: false, - type: 'boolean' - } + name: 'cert-manager-crds', + definition: { + describe: 'Deploy cert manager CRDs', + default: false, + type: 'boolean' + } } export const platformReleaseTag = { - name: 'release-tag', - definition: { - describe: 'Platform release tag (e.g. v0.42.4, fetch build-.zip from https://builds.hedera.com)', - default: "", - alias: 't', - type: 'string' - } + name: 'release-tag', + definition: { + describe: 'Platform release tag (e.g. v0.42.4, fetch build-.zip from https://builds.hedera.com)', + default: '', + alias: 't', + type: 'string' + } } export const platformReleaseDir = { - name: 'release-dir', - definition: { - describe: `Platform release cache dir (containing release directories named as v.. e.g. v0.42)`, - default: core.constants.FST_CACHE_DIR, - alias: 'd', - type: 'string' - } + name: 'release-dir', + definition: { + describe: 'Platform release cache dir (containing release directories named as v.. e.g. v0.42)', + default: core.constants.FST_CACHE_DIR, + alias: 'd', + type: 'string' + } } export const nodeIDs = { - name: 'node-ids', - definition: { - describe: 'Comma separated node IDs (empty means all nodes)', - default: "", - alias: 'i', - type: 'string' - } -} - -export const force= { - name: 'force', - definition: { - describe: 'Force actions even if those can be skipped', - default: false, - alias: 'f', - type: 'boolean' - } -} - -export const chartDirectory= { - name: 'chart-dir', - definition: { - describe: 'Local chart directory path (e.g. ~/full-stack-testing/charts', - default: '', - alias: 'd', - type: 'string' - } + name: 'node-ids', + definition: { + describe: 'Comma separated node IDs (empty means all nodes)', + default: '', + alias: 'i', + type: 'string' + } +} + +export const force = { + name: 'force', + definition: { + describe: 'Force actions even if those can be skipped', + default: false, + alias: 'f', + type: 'boolean' + } +} + +export const chartDirectory = { + name: 'chart-dir', + definition: { + describe: 'Local chart directory path (e.g. ~/full-stack-testing/charts', + default: '', + alias: 'd', + type: 'string' + } } export const allFlags = [ - clusterName, - namespace, - deployMirrorNode, - deployHederaExplorer, - valuesFile, - deployPrometheusStack, - deployMinio, - deployEnvoyGateway, - deployCertManagerCRDs, - platformReleaseTag, - platformReleaseDir, - nodeIDs, - force, - chartDirectory, + clusterName, + namespace, + deployMirrorNode, + deployHederaExplorer, + valuesFile, + deployPrometheusStack, + deployMinio, + deployEnvoyGateway, + deployCertManagerCRDs, + platformReleaseTag, + platformReleaseDir, + nodeIDs, + force, + chartDirectory ] diff --git a/fullstack-network-manager/src/commands/index.mjs b/fullstack-network-manager/src/commands/index.mjs index ad1d70def..2dc083a6a 100644 --- a/fullstack-network-manager/src/commands/index.mjs +++ b/fullstack-network-manager/src/commands/index.mjs @@ -1,24 +1,24 @@ -import {ClusterCommand} from "./cluster.mjs"; -import {InitCommand} from "./init.mjs"; -import {ChartCommand} from "./chart.mjs" -import {NodeCommand} from "./node.mjs" +import { ClusterCommand } from './cluster.mjs' +import { InitCommand } from './init.mjs' +import { ChartCommand } from './chart.mjs' +import { NodeCommand } from './node.mjs' /* * Return a list of Yargs command builder to be exposed through CLI * @param opts it is an Options object containing logger */ -function Initialize(opts) { - const initCmd = new InitCommand(opts) - const clusterCmd = new ClusterCommand(opts) - const chartCmd = new ChartCommand(opts) - const nodeCmd = new NodeCommand(opts) +function Initialize (opts) { + const initCmd = new InitCommand(opts) + const clusterCmd = new ClusterCommand(opts) + const chartCmd = new ChartCommand(opts) + const nodeCmd = new NodeCommand(opts) - return [ - InitCommand.getCommandDefinition(initCmd), - ClusterCommand.getCommandDefinition(clusterCmd), - ChartCommand.getCommandDefinition(chartCmd), - NodeCommand.getCommandDefinition(nodeCmd), - ] + return [ + InitCommand.getCommandDefinition(initCmd), + ClusterCommand.getCommandDefinition(clusterCmd), + ChartCommand.getCommandDefinition(chartCmd), + NodeCommand.getCommandDefinition(nodeCmd) + ] } // Expose components from the command module diff --git a/fullstack-network-manager/src/commands/init.mjs b/fullstack-network-manager/src/commands/init.mjs index 7227dcb68..4324648c8 100644 --- a/fullstack-network-manager/src/commands/init.mjs +++ b/fullstack-network-manager/src/commands/init.mjs @@ -1,89 +1,86 @@ -import {BaseCommand} from "./base.mjs"; -import * as core from "../core/index.mjs" -import chalk from "chalk"; -import {constants} from "../core/index.mjs"; +import { BaseCommand } from './base.mjs' +import * as core from '../core/index.mjs' +import chalk from 'chalk' +import { constants } from '../core/index.mjs' import * as fs from 'fs' -import {FullstackTestingError} from "../core/errors.mjs"; -import * as flags from "./flags.mjs" - +import { FullstackTestingError } from '../core/errors.mjs' +import * as flags from './flags.mjs' /** * Defines the core functionalities of 'init' command */ export class InitCommand extends BaseCommand { - /** + /** * Setup home directories * @param dirs a list of directories that need to be created in sequence * @returns {Promise} */ - async setupHomeDirectory(dirs = [ - constants.FST_HOME_DIR, - constants.FST_LOGS_DIR, - constants.FST_CACHE_DIR, - ]) { - const self = this + async setupHomeDirectory (dirs = [ + constants.FST_HOME_DIR, + constants.FST_LOGS_DIR, + constants.FST_CACHE_DIR + ]) { + const self = this - try { - dirs.forEach(dirPath => { - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath) - } - self.logger.showUser(chalk.green(`OK: setup directory: ${dirPath}`)) - }) - } catch (e) { - this.logger.error(e) - throw new FullstackTestingError(e.message, e) + try { + dirs.forEach(dirPath => { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath) } + self.logger.showUser(chalk.green(`OK: setup directory: ${dirPath}`)) + }) + } catch (e) { + this.logger.error(e) + throw new FullstackTestingError(e.message, e) } + } - /** + /** * Executes the init CLI command * @returns {Promise} */ - async init(argv) { - try { - const config = await this.configManager.setupConfig(argv, true) - await this.setupHomeDirectory() + async init (argv) { + try { + const config = await this.configManager.setupConfig(argv, true) + await this.setupHomeDirectory() - const deps = [ - core.constants.HELM, - core.constants.KIND, - core.constants.KUBECTL, - ] + const deps = [ + core.constants.HELM, + core.constants.KIND, + core.constants.KUBECTL + ] - const status = await this.checkDependencies(deps) - if (!status) { - return false - } + const status = await this.checkDependencies(deps) + if (!status) { + return false + } - this.logger.showUser(chalk.green("OK: All required dependencies are found: %s"), chalk.yellow(deps)) + this.logger.showUser(chalk.green('OK: All required dependencies are found: %s'), chalk.yellow(deps)) - const repoURLs = await this.chartManager.setup() - this.logger.showUser(chalk.green("OK: Chart repositories are initialized"), chalk.yellow(repoURLs)) + const repoURLs = await this.chartManager.setup() + this.logger.showUser(chalk.green('OK: Chart repositories are initialized'), chalk.yellow(repoURLs)) - return status - } catch (e) { - this.logger.showUserError(e) - return false - } + return status + } catch (e) { + this.logger.showUserError(e) + return false } + } - /** + /** * Return Yargs command definition for 'init' command * @param initCmd an instance of InitCommand */ - static getCommandDefinition(initCmd) { - return { - command: "init", - desc: "Perform dependency checks and initialize local environment", - builder: y => flags.setCommandFlags(y, flags.chartDirectory), - handler: (argv) => { - initCmd.init(argv).then(r => { - if (!r) process.exit(1) - }) - } - } + static getCommandDefinition (initCmd) { + return { + command: 'init', + desc: 'Perform dependency checks and initialize local environment', + builder: y => flags.setCommandFlags(y, flags.chartDirectory), + handler: (argv) => { + initCmd.init(argv).then(r => { + if (!r) process.exit(1) + }) + } } + } } - - diff --git a/fullstack-network-manager/src/commands/node.mjs b/fullstack-network-manager/src/commands/node.mjs index 3754dc566..cc786c870 100644 --- a/fullstack-network-manager/src/commands/node.mjs +++ b/fullstack-network-manager/src/commands/node.mjs @@ -1,242 +1,235 @@ -import {BaseCommand} from "./base.mjs"; -import * as flags from "./flags.mjs"; +import { BaseCommand } from './base.mjs' +import * as flags from './flags.mjs' import { - DataValidationError, - FullstackTestingError, - IllegalArgumentError, - MissingArgumentError -} from "../core/errors.mjs"; -import {constants, PackageDownloader, Templates} from "../core/index.mjs"; -import chalk from "chalk"; -import * as fs from "fs"; + FullstackTestingError, + IllegalArgumentError, + MissingArgumentError +} from '../core/errors.mjs' +import { constants, Templates } from '../core/index.mjs' +import chalk from 'chalk' +import * as fs from 'fs' /** * Defines the core functionalities of 'node' command */ export class NodeCommand extends BaseCommand { - constructor(opts) { - super(opts); + constructor (opts) { + super(opts) - if (!opts || !opts.downloader) throw new IllegalArgumentError('An instance of core/PackageDowner is required', opts.downloader) - if (!opts || !opts.platformInstaller) throw new IllegalArgumentError('An instance of core/PlatformInstaller is required', opts.platformInstaller) + if (!opts || !opts.downloader) throw new IllegalArgumentError('An instance of core/PackageDowner is required', opts.downloader) + if (!opts || !opts.platformInstaller) throw new IllegalArgumentError('An instance of core/PlatformInstaller is required', opts.platformInstaller) - this.downloader = opts.downloader - this.plaformInstaller = opts.platformInstaller - } + this.downloader = opts.downloader + this.plaformInstaller = opts.platformInstaller + } - /** + /** * Check if pods are running or not * @param namespace * @param nodeIds * @param timeout * @returns {Promise} */ - async checkNetworkNodePods(namespace, nodeIds = [], timeout = '300s') { - return new Promise(async (resolve, reject) => { - try { - let podNames = [] - if (nodeIds && nodeIds.length > 0) { - for (let nodeId of nodeIds) { - nodeId = nodeId.trim() - const podName = Templates.renderNetworkPodName(nodeId) - - await this.kubectl.wait('pod', - `--for=jsonpath='{.status.phase}'=Running`, - `-l fullstack.hedera.com/type=network-node`, + async checkNetworkNodePods (namespace, nodeIds = [], timeout = '300s') { + return new Promise(async (resolve, reject) => { + try { + const podNames = [] + if (nodeIds && nodeIds.length > 0) { + for (let nodeId of nodeIds) { + nodeId = nodeId.trim() + const podName = Templates.renderNetworkPodName(nodeId) + + await this.kubectl.wait('pod', + '--for=jsonpath=\'{.status.phase}\'=Running', + '-l fullstack.hedera.com/type=network-node', `-l fullstack.hedera.com/node-name=${nodeId}`, `--timeout=${timeout}`, `-n "${namespace}"` - ) - - podNames.push(podName) - } - } else { - nodeIds = [] - let output = await this.kubectl.get('pods', - `-l fullstack.hedera.com/type=network-node`, - '--no-headers', - `-o custom-columns=":metadata.name"`, + ) + + podNames.push(podName) + } + } else { + nodeIds = [] + const output = await this.kubectl.get('pods', + '-l fullstack.hedera.com/type=network-node', + '--no-headers', + '-o custom-columns=":metadata.name"', `-n "${namespace}"` - ) - output.forEach(podName => { - nodeIds.push(Templates.extractNodeIdFromPodName(podName)) - podNames.push(podName) - }) - } - - resolve({podNames: podNames, nodeIDs: nodeIds}) - } catch (e) { - reject(new FullstackTestingError(`Error on detecting pods for nodes (${nodeIds}): ${e.message}`)) - } - - }) - } - - async setup(argv) { - const self = this - if (!argv.releaseTag && !argv.releaseDir) throw new MissingArgumentError('release-tag or release-dir argument is required') - - const namespace = argv.namespace - const force = argv.force - const releaseTag = argv.releaseTag - const releaseDir = argv.releaseDir - - try { - self.logger.showUser(constants.LOG_GROUP_DIVIDER) - - const releasePrefix = Templates.prepareReleasePrefix(releaseTag) - let buildZipFile = `${releaseDir}/${releasePrefix}/build-${releaseTag}.zip` - const stagingDir = `${releaseDir}/${releasePrefix}/staging/${releaseTag}` - const nodeIDsArg = argv.nodeIds ? argv.nodeIds.split(',') : [] - - fs.mkdirSync(stagingDir, {recursive: true}) - - // pre-check - let {podNames, nodeIDs} = await this.checkNetworkNodePods(namespace, nodeIDsArg) - - // fetch platform build-.zip file - if (force || !fs.existsSync(buildZipFile)) { - self.logger.showUser(chalk.cyan('>>'), `Fetching Platform package 'build-${releaseTag}.zip' from '${constants.HEDERA_BUILDS_URL}' ...`) - buildZipFile = await this.downloader.fetchPlatform(releaseTag, releaseDir) - } else { - self.logger.showUser(chalk.cyan('>>'), `Found Platform package in cache: build-${releaseTag}.zip`) - } - self.logger.showUser(chalk.green('OK'), `Platform package: ${buildZipFile}`) - - // prepare staging - await this.plaformInstaller.prepareStaging(nodeIDs, stagingDir, releaseTag, force) - - // setup - for (const podName of podNames) { - await self.plaformInstaller.install(podName, buildZipFile, stagingDir, force); - } - - return true - } catch (e) { - self.logger.showUserError(e) + ) + output.forEach(podName => { + nodeIds.push(Templates.extractNodeIdFromPodName(podName)) + podNames.push(podName) + }) } - return false + resolve({ podNames, nodeIDs: nodeIds }) + } catch (e) { + reject(new FullstackTestingError(`Error on detecting pods for nodes (${nodeIds}): ${e.message}`)) + } + }) + } + + async setup (argv) { + const self = this + if (!argv.releaseTag && !argv.releaseDir) throw new MissingArgumentError('release-tag or release-dir argument is required') + + const namespace = argv.namespace + const force = argv.force + const releaseTag = argv.releaseTag + const releaseDir = argv.releaseDir + + try { + self.logger.showUser(constants.LOG_GROUP_DIVIDER) + + const releasePrefix = Templates.prepareReleasePrefix(releaseTag) + let buildZipFile = `${releaseDir}/${releasePrefix}/build-${releaseTag}.zip` + const stagingDir = `${releaseDir}/${releasePrefix}/staging/${releaseTag}` + const nodeIDsArg = argv.nodeIds ? argv.nodeIds.split(',') : [] + + fs.mkdirSync(stagingDir, { recursive: true }) + + // pre-check + const { podNames, nodeIDs } = await this.checkNetworkNodePods(namespace, nodeIDsArg) + + // fetch platform build-.zip file + if (force || !fs.existsSync(buildZipFile)) { + self.logger.showUser(chalk.cyan('>>'), `Fetching Platform package 'build-${releaseTag}.zip' from '${constants.HEDERA_BUILDS_URL}' ...`) + buildZipFile = await this.downloader.fetchPlatform(releaseTag, releaseDir) + } else { + self.logger.showUser(chalk.cyan('>>'), `Found Platform package in cache: build-${releaseTag}.zip`) + } + self.logger.showUser(chalk.green('OK'), `Platform package: ${buildZipFile}`) + + // prepare staging + await this.plaformInstaller.prepareStaging(nodeIDs, stagingDir, releaseTag, force) + + // setup + for (const podName of podNames) { + await self.plaformInstaller.install(podName, buildZipFile, stagingDir, force) + } + + return true + } catch (e) { + self.logger.showUserError(e) } - async start(argv) { - const self = this - - try { - const namespace = argv.namespace - const nodeIDsArg = argv.nodeIds ? argv.nodeIds.split(',') : [] - let {podNames, nodeIDs} = await this.checkNetworkNodePods(namespace, nodeIDsArg) - for (const podName of podNames) { - self.logger.showUser(chalk.cyan('>>'), `Starting node ${podName}`) - await self.kubectl.execContainer(podName, constants.ROOT_CONTAINER, 'systemctl restart network-node') - self.logger.showUser(chalk.green('OK'), `Started node ${podName}`) - } - - return true - } catch (e) { - self.logger.showUserError(e) - } - - return false + return false + } + + async start (argv) { + const self = this + + try { + const namespace = argv.namespace + const nodeIDsArg = argv.nodeIds ? argv.nodeIds.split(',') : [] + const { podNames, nodeIDs } = await this.checkNetworkNodePods(namespace, nodeIDsArg) + for (const podName of podNames) { + self.logger.showUser(chalk.cyan('>>'), `Starting node ${podName}`) + await self.kubectl.execContainer(podName, constants.ROOT_CONTAINER, 'systemctl restart network-node') + self.logger.showUser(chalk.green('OK'), `Started node ${podName}`) + } + + return true + } catch (e) { + self.logger.showUserError(e) } - async stop(argv) { - const self = this - - try { - const namespace = argv.namespace - const nodeIDsArg = argv.nodeIds ? argv.nodeIds.split(',') : [] - let {podNames, nodeIDs} = await this.checkNetworkNodePods(namespace, nodeIDsArg) - for (const podName of podNames) { - self.logger.showUser(chalk.cyan('>>'), `Stopping node ${podName}`) - await self.kubectl.execContainer(podName, constants.ROOT_CONTAINER, 'systemctl restart network-node') - self.logger.showUser(chalk.green('OK'), `Stopped node ${podName}`) - } - - return true - } catch (e) { - self.logger.showUserError(e) - } - - return false + return false + } + + async stop (argv) { + const self = this + + try { + const namespace = argv.namespace + const nodeIDsArg = argv.nodeIds ? argv.nodeIds.split(',') : [] + const { podNames, nodeIDs } = await this.checkNetworkNodePods(namespace, nodeIDsArg) + for (const podName of podNames) { + self.logger.showUser(chalk.cyan('>>'), `Stopping node ${podName}`) + await self.kubectl.execContainer(podName, constants.ROOT_CONTAINER, 'systemctl restart network-node') + self.logger.showUser(chalk.green('OK'), `Stopped node ${podName}`) + } + + return true + } catch (e) { + self.logger.showUserError(e) } - /** + return false + } + + /** * Return Yargs command definition for 'node' command * @param nodeCmd an instance of NodeCommand */ - static getCommandDefinition(nodeCmd) { - return { - command: "node", - desc: "Manage a FST node running Hedera platform", - builder: yargs => { - return yargs - .command({ - command: 'setup', - desc: 'Setup node with a specific version of Hedera platform', - builder: y => flags.setCommandFlags(y, - flags.namespace, - flags.nodeIDs, - flags.platformReleaseTag, - flags.platformReleaseDir, - flags.force, - ), - handler: argv => { - nodeCmd.logger.debug("==== Running 'node setup' ===") - nodeCmd.logger.debug(argv) - - nodeCmd.setup(argv).then(r => { - nodeCmd.logger.debug("==== Finished running `node setup`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'start', - desc: 'Start a node running Hedera platform', - builder: y => flags.setCommandFlags(y, - flags.namespace, - flags.nodeIDs, - ), - handler: argv => { - console.log("here") - nodeCmd.logger.showUser('here2') - nodeCmd.logger.debug("==== Running 'node start' ===") - nodeCmd.logger.debug(argv) - - nodeCmd.start(argv).then(r => { - nodeCmd.logger.debug("==== Finished running `node start`====") - - if (!r) process.exit(1) - }) - - } - }) - .command({ - command: 'stop', - desc: 'stop a node running Hedera platform', - builder: y => flags.setCommandFlags(y, - flags.namespace, - flags.nodeIDs, - ), - handler: argv => { - nodeCmd.logger.debug("==== Running 'node stop' ===") - nodeCmd.logger.debug(argv) - - nodeCmd.stop(argv).then(r => { - nodeCmd.logger.debug("==== Finished running `node stop`====") - - if (!r) process.exit(1) - }) - - } - }) - .demandCommand(1, 'Select a node command') + static getCommandDefinition (nodeCmd) { + return { + command: 'node', + desc: 'Manage a FST node running Hedera platform', + builder: yargs => { + return yargs + .command({ + command: 'setup', + desc: 'Setup node with a specific version of Hedera platform', + builder: y => flags.setCommandFlags(y, + flags.namespace, + flags.nodeIDs, + flags.platformReleaseTag, + flags.platformReleaseDir, + flags.force + ), + handler: argv => { + nodeCmd.logger.debug("==== Running 'node setup' ===") + nodeCmd.logger.debug(argv) + + nodeCmd.setup(argv).then(r => { + nodeCmd.logger.debug('==== Finished running `node setup`====') + + if (!r) process.exit(1) + }) } - } + }) + .command({ + command: 'start', + desc: 'Start a node running Hedera platform', + builder: y => flags.setCommandFlags(y, + flags.namespace, + flags.nodeIDs + ), + handler: argv => { + console.log('here') + nodeCmd.logger.showUser('here2') + nodeCmd.logger.debug("==== Running 'node start' ===") + nodeCmd.logger.debug(argv) + + nodeCmd.start(argv).then(r => { + nodeCmd.logger.debug('==== Finished running `node start`====') + + if (!r) process.exit(1) + }) + } + }) + .command({ + command: 'stop', + desc: 'stop a node running Hedera platform', + builder: y => flags.setCommandFlags(y, + flags.namespace, + flags.nodeIDs + ), + handler: argv => { + nodeCmd.logger.debug("==== Running 'node stop' ===") + nodeCmd.logger.debug(argv) + + nodeCmd.stop(argv).then(r => { + nodeCmd.logger.debug('==== Finished running `node stop`====') + + if (!r) process.exit(1) + }) + } + }) + .demandCommand(1, 'Select a node command') + } } + } } - - diff --git a/fullstack-network-manager/src/core/chart_manager.mjs b/fullstack-network-manager/src/core/chart_manager.mjs index fe6c570d1..025e38723 100644 --- a/fullstack-network-manager/src/core/chart_manager.mjs +++ b/fullstack-network-manager/src/core/chart_manager.mjs @@ -1,17 +1,17 @@ -import {constants} from "./constants.mjs"; -import chalk from "chalk"; -import {FullstackTestingError} from "./errors.mjs"; +import { constants } from './constants.mjs' +import chalk from 'chalk' +import { FullstackTestingError } from './errors.mjs' export class ChartManager { - constructor(helm, logger) { - if (!logger) throw new Error('An instance of core/Logger is required') - if (!helm) throw new Error('An instance of core/Helm is required') + constructor (helm, logger) { + if (!logger) throw new Error('An instance of core/Logger is required') + if (!helm) throw new Error('An instance of core/Helm is required') - this.logger = logger - this.helm = helm - } + this.logger = logger + this.helm = helm + } - /** + /** * Setup chart repositories * * This must be invoked before calling other methods @@ -20,102 +20,102 @@ export class ChartManager { * @param force whether or not to update the repo * @returns {Promise} */ - async setup(repoURLs = new Map().set('full-stack-testing', constants.FST_CHART_REPO_URL), force= true) { - const self = this - return new Promise(async (resolve, reject) => { - try { - let forceUpdateArg = '' - if (force) { - forceUpdateArg = '--force-update' - } - - const urls = [] - for (const [name, url] of repoURLs.entries()) { - self.logger.debug(`Adding repo ${name} -> ${url}`, {repoName: name, repoURL: url}) - await this.helm.repo('add', name, url, forceUpdateArg) - urls.push(url) - } - - resolve(urls) - } catch (e) { - reject(new FullstackTestingError(`failed to setup chart repositories: ${e.message}`, e)) - } - }) - } + async setup (repoURLs = new Map().set('full-stack-testing', constants.FST_CHART_REPO_URL), force = true) { + const self = this + return new Promise(async (resolve, reject) => { + try { + let forceUpdateArg = '' + if (force) { + forceUpdateArg = '--force-update' + } + + const urls = [] + for (const [name, url] of repoURLs.entries()) { + self.logger.debug(`Adding repo ${name} -> ${url}`, { repoName: name, repoURL: url }) + await this.helm.repo('add', name, url, forceUpdateArg) + urls.push(url) + } + + resolve(urls) + } catch (e) { + reject(new FullstackTestingError(`failed to setup chart repositories: ${e.message}`, e)) + } + }) + } - /** + /** * List available clusters * @returns {Promise} */ - async getInstalledCharts(namespaceName) { - try { - return await this.helm.list(`-n ${namespaceName}`, `--no-headers | awk '{print $9}'`) - } catch (e) { - this.logger.showUserError(e) - } - - return [] + async getInstalledCharts (namespaceName) { + try { + return await this.helm.list(`-n ${namespaceName}`, '--no-headers | awk \'{print $9}\'') + } catch (e) { + this.logger.showUserError(e) } - async install(namespaceName, chartName, chartPath, version, valuesArg = '') { - try { - const isInstalled = await this.isChartInstalled(namespaceName, chartName) - if (!isInstalled) { - this.logger.showUser(chalk.cyan('> installing chart:'), chalk.yellow(`${chartPath}`)) - await this.helm.install(`${chartName} ${chartPath} --version ${version} -n ${namespaceName} ${valuesArg}`) - this.logger.showUser(chalk.green('OK'), `chart '${chartPath}' is installed`) - } else { - this.logger.showUser(chalk.green('OK'), `chart '${chartPath}' is already installed`) - } - - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + return [] + } + + async install (namespaceName, chartName, chartPath, version, valuesArg = '') { + try { + const isInstalled = await this.isChartInstalled(namespaceName, chartName) + if (!isInstalled) { + this.logger.showUser(chalk.cyan('> installing chart:'), chalk.yellow(`${chartPath}`)) + await this.helm.install(`${chartName} ${chartPath} --version ${version} -n ${namespaceName} ${valuesArg}`) + this.logger.showUser(chalk.green('OK'), `chart '${chartPath}' is installed`) + } else { + this.logger.showUser(chalk.green('OK'), `chart '${chartPath}' is already installed`) + } + + return true + } catch (e) { + this.logger.showUserError(e) } - async isChartInstalled(namespaceName, chartName) { - const charts = await this.getInstalledCharts(namespaceName) - for(const item of charts) { - if (item.startsWith(chartName)) return true - } + return false + } - return false + async isChartInstalled (namespaceName, chartName) { + const charts = await this.getInstalledCharts(namespaceName) + for (const item of charts) { + if (item.startsWith(chartName)) return true } - async uninstall(namespaceName, chartName) { - try { - this.logger.showUser(chalk.cyan('> checking chart:'), chalk.yellow(`${chartName}`)) - const isInstalled = await this.isChartInstalled(namespaceName, chartName) - if (isInstalled) { - 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 '${chartName}' is already uninstalled`) - } - - return true - } catch (e) { - this.logger.showUserError(e) - } - - return false + return false + } + + async uninstall (namespaceName, chartName) { + try { + this.logger.showUser(chalk.cyan('> checking chart:'), chalk.yellow(`${chartName}`)) + const isInstalled = await this.isChartInstalled(namespaceName, chartName) + if (isInstalled) { + 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 '${chartName}' is already uninstalled`) + } + + return true + } catch (e) { + this.logger.showUserError(e) } - async upgrade(namespaceName, chartName, chartPath, valuesArg = '') { - try { - 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 false + } - return true - } catch (e) { - this.logger.showUserError(e) - } + async upgrade (namespaceName, chartName, chartPath, valuesArg = '') { + try { + 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 false + return true + } catch (e) { + this.logger.showUserError(e) } -} \ No newline at end of file + + return false + } +} diff --git a/fullstack-network-manager/src/core/config_manager.mjs b/fullstack-network-manager/src/core/config_manager.mjs index 0abadfee9..e21150ac8 100644 --- a/fullstack-network-manager/src/core/config_manager.mjs +++ b/fullstack-network-manager/src/core/config_manager.mjs @@ -1,47 +1,47 @@ -import fs from "fs"; -import {FullstackTestingError, MissingArgumentError} from "./errors.mjs"; -import {constants} from "./constants.mjs"; -import {Logger} from "./logging.mjs" -import * as flags from "../commands/flags.mjs"; -import * as paths from "path"; -import {fileURLToPath} from "url"; +import fs from 'fs' +import { FullstackTestingError, MissingArgumentError } from './errors.mjs' +import { constants } from './constants.mjs' +import { Logger } from './logging.mjs' +import * as flags from '../commands/flags.mjs' +import * as paths from 'path' +import { fileURLToPath } from 'url' // cache current directory const CUR_FILE_DIR = paths.dirname(fileURLToPath(import.meta.url)) export class ConfigManager { - constructor(logger) { - if (!logger || !(logger instanceof Logger)) throw new MissingArgumentError("An instance of core/Logger is required") + constructor (logger) { + if (!logger || !(logger instanceof Logger)) throw new MissingArgumentError('An instance of core/Logger is required') - this.logger = logger - } + this.logger = logger + } - /** + /** * load package.json * @returns {any} */ - loadPackageJSON() { - try { - let raw = fs.readFileSync(`${CUR_FILE_DIR}/../../package.json`) - return JSON.parse(raw.toString()) - } catch (e) { - throw new FullstackTestingError('failed to load package.json', e) - } + loadPackageJSON () { + try { + const raw = fs.readFileSync(`${CUR_FILE_DIR}/../../package.json`) + return JSON.parse(raw.toString()) + } catch (e) { + throw new FullstackTestingError('failed to load package.json', e) } + } - hasFlag(config, flag) { - return !!config.flags[flag.name]; - } - - flagValue(config, flag) { - if (this.hasFlag(config, flag)) { - return config.flags[flag.name] - } + hasFlag (config, flag) { + return !!config.flags[flag.name] + } - return '' + flagValue (config, flag) { + if (this.hasFlag(config, flag)) { + return config.flags[flag.name] } - /** + return '' + } + + /** * Load and store config * * It overwrites previous config values using opts and store in the config file if any value has been changed. @@ -50,62 +50,62 @@ export class ConfigManager { * @param reset if we should reset old values * @returns {Promise} */ - async setupConfig(opts, reset = false) { - const self = this - - return new Promise((resolve, reject) => { - try { - let config = {} - let writeConfig = false - let packageJSON = self.loadPackageJSON() - - // if config exist, then load it first - if (!reset && fs.existsSync(constants.FST_CONFIG_FILE)) { - const configJSON = fs.readFileSync(constants.FST_CONFIG_FILE) - config = JSON.parse(configJSON.toString()) - } - - if (!config['flags']) { - config['flags'] = {} - } - - // we always use packageJSON version as the version, so overwrite. - config.version = packageJSON.version - - // extract flags from argv - if (opts) { - flags.allFlags.forEach(flag => { - if (opts && opts[flag.name]) { - let val = opts[flag.name] - if (val && flag.name === flags.chartDirectory.name) { - val = paths.resolve(val) - } - - config['flags'][flag.name] = val - writeConfig = true - } - }) - - // store last command that was run - if (opts["_"]) { - config['lastCommand'] = opts["_"] - } - } - - // store CLI config - if (reset || writeConfig) { - config.updatedAt = new Date().toISOString() - - let configJSON = JSON.stringify(config) - fs.writeFileSync(`${constants.FST_CONFIG_FILE}`, configJSON) - configJSON = fs.readFileSync(constants.FST_CONFIG_FILE) - config = JSON.parse(configJSON.toString()) - } - - resolve(config) - } catch (e) { - reject(new FullstackTestingError(`failed to load config: ${e.message}`, e)) + async setupConfig (opts, reset = false) { + const self = this + + return new Promise((resolve, reject) => { + try { + let config = {} + let writeConfig = false + const packageJSON = self.loadPackageJSON() + + // if config exist, then load it first + if (!reset && fs.existsSync(constants.FST_CONFIG_FILE)) { + const configJSON = fs.readFileSync(constants.FST_CONFIG_FILE) + config = JSON.parse(configJSON.toString()) + } + + if (!config.flags) { + config.flags = {} + } + + // we always use packageJSON version as the version, so overwrite. + config.version = packageJSON.version + + // extract flags from argv + if (opts) { + flags.allFlags.forEach(flag => { + if (opts && opts[flag.name]) { + let val = opts[flag.name] + if (val && flag.name === flags.chartDirectory.name) { + val = paths.resolve(val) + } + + config.flags[flag.name] = val + writeConfig = true } - }) - } + }) + + // store last command that was run + if (opts._) { + config.lastCommand = opts._ + } + } + + // store CLI config + if (reset || writeConfig) { + config.updatedAt = new Date().toISOString() + + let configJSON = JSON.stringify(config) + fs.writeFileSync(`${constants.FST_CONFIG_FILE}`, configJSON) + configJSON = fs.readFileSync(constants.FST_CONFIG_FILE) + config = JSON.parse(configJSON.toString()) + } + + resolve(config) + } catch (e) { + reject(new FullstackTestingError(`failed to load config: ${e.message}`, e)) + } + }) + } } diff --git a/fullstack-network-manager/src/core/constants.mjs b/fullstack-network-manager/src/core/constants.mjs index bb2973d8e..9ad559015 100644 --- a/fullstack-network-manager/src/core/constants.mjs +++ b/fullstack-network-manager/src/core/constants.mjs @@ -1,41 +1,41 @@ -import {dirname, normalize} from "path" -import {fileURLToPath} from "url" -import chalk from "chalk"; +import { dirname, normalize } from 'path' +import { fileURLToPath } from 'url' +import chalk from 'chalk' // directory of this fle const CUR_FILE_DIR = dirname(fileURLToPath(import.meta.url)) const USER = `${process.env.USER}` const FST_HOME_DIR = `${process.env.HOME}/.fsnetman` -const HGCAPP_DIR = "/opt/hgcapp" +const HGCAPP_DIR = '/opt/hgcapp' export const constants = { - USER: `${USER}`, - CLUSTER_NAME: `fst`, - RELEASE_NAME: `fst`, - NAMESPACE_NAME: `fst-${USER}`, - HELM: 'helm', - KIND: 'kind', - KUBECTL: 'kubectl', - CWD: process.cwd(), - FST_HOME_DIR: FST_HOME_DIR, - FST_LOGS_DIR: `${FST_HOME_DIR}/logs`, - FST_CACHE_DIR: `${FST_HOME_DIR}/cache`, - FST_CONFIG_FILE: `${FST_HOME_DIR}/fsnetman.config`, - RESOURCES_DIR: normalize(CUR_FILE_DIR + "/../../resources"), - HGCAPP_DIR: HGCAPP_DIR, - HGCAPP_SERVICES_HEDERA_PATH: `${HGCAPP_DIR}/services-hedera`, - HAPI_PATH: `${HGCAPP_DIR}/services-hedera/HapiApp2.0`, - ROOT_CONTAINER: 'root-container', - DATA_APPS_DIR: 'data/apps', - DATA_LIB_DIR: 'data/lib', - HEDERA_USER_HOME_DIR: '/home/hedera', - HEDERA_APP_JAR: 'HederaNode.jar', - HEDERA_NODE_DEFAULT_STAKE_AMOUNT: 1, - HEDERA_BUILDS_URL: 'https://builds.hedera.com', - LOG_STATUS_PROGRESS: chalk.cyan('>>'), - LOG_STATUS_DONE: chalk.green('OK'), - LOG_GROUP_DIVIDER: chalk.yellow('----------------------------------------------------------------------------'), - FST_CHART_REPO_URL: 'https://hashgraph.github.io/full-stack-testing/charts', - FST_CHART_SETUP_NAME: 'fullstack-cluster-setup', - FST_CHART_DEPLOYMENT_NAME: 'fullstack-deployment', + USER: `${USER}`, + CLUSTER_NAME: 'fst', + RELEASE_NAME: 'fst', + NAMESPACE_NAME: `fst-${USER}`, + HELM: 'helm', + KIND: 'kind', + KUBECTL: 'kubectl', + CWD: process.cwd(), + FST_HOME_DIR, + FST_LOGS_DIR: `${FST_HOME_DIR}/logs`, + FST_CACHE_DIR: `${FST_HOME_DIR}/cache`, + FST_CONFIG_FILE: `${FST_HOME_DIR}/fsnetman.config`, + RESOURCES_DIR: normalize(CUR_FILE_DIR + '/../../resources'), + HGCAPP_DIR, + HGCAPP_SERVICES_HEDERA_PATH: `${HGCAPP_DIR}/services-hedera`, + HAPI_PATH: `${HGCAPP_DIR}/services-hedera/HapiApp2.0`, + ROOT_CONTAINER: 'root-container', + DATA_APPS_DIR: 'data/apps', + DATA_LIB_DIR: 'data/lib', + HEDERA_USER_HOME_DIR: '/home/hedera', + HEDERA_APP_JAR: 'HederaNode.jar', + HEDERA_NODE_DEFAULT_STAKE_AMOUNT: 1, + HEDERA_BUILDS_URL: 'https://builds.hedera.com', + LOG_STATUS_PROGRESS: chalk.cyan('>>'), + LOG_STATUS_DONE: chalk.green('OK'), + LOG_GROUP_DIVIDER: chalk.yellow('----------------------------------------------------------------------------'), + FST_CHART_REPO_URL: 'https://hashgraph.github.io/full-stack-testing/charts', + FST_CHART_SETUP_NAME: 'fullstack-cluster-setup', + FST_CHART_DEPLOYMENT_NAME: 'fullstack-deployment' } diff --git a/fullstack-network-manager/src/core/errors.mjs b/fullstack-network-manager/src/core/errors.mjs index 5e2d932ba..49163a21a 100644 --- a/fullstack-network-manager/src/core/errors.mjs +++ b/fullstack-network-manager/src/core/errors.mjs @@ -1,5 +1,5 @@ export class FullstackTestingError extends Error { - /** + /** * Create a custom error object * * error metadata will include the `cause` @@ -8,21 +8,21 @@ export class FullstackTestingError extends Error { * @param cause source error (if any) * @param meta additional metadata (if any) */ - constructor(message, cause = {}, meta = {}) { - super(message); - this.name = this.constructor.name + constructor (message, cause = {}, meta = {}) { + super(message) + this.name = this.constructor.name - this.meta = meta - if (cause) { - this.cause = cause - } - - Error.captureStackTrace(this, this.constructor) + this.meta = meta + if (cause) { + this.cause = cause } + + Error.captureStackTrace(this, this.constructor) + } } export class ResourceNotFoundError extends FullstackTestingError { - /** + /** * Create a custom error for resource not found scenario * * error metadata will include `resource` @@ -31,25 +31,25 @@ export class ResourceNotFoundError extends FullstackTestingError { * @param resource name of the resource * @param cause source error (if any) */ - constructor(message, resource, cause = {}) { - super(message, cause, {resource: resource}); - } + constructor (message, resource, cause = {}) { + super(message, cause, { resource }) + } } export class MissingArgumentError extends FullstackTestingError { - /** + /** * Create a custom error for missing argument scenario * * @param message error message * @param cause source error (if any) */ - constructor(message, cause = {}) { - super(message, cause); - } + constructor (message, cause = {}) { + super(message, cause) + } } export class IllegalArgumentError extends FullstackTestingError { - /** + /** * Create a custom error for illegal argument scenario * * error metadata will include `value` @@ -58,13 +58,13 @@ export class IllegalArgumentError extends FullstackTestingError { * @param value value of the invalid argument * @param cause source error (if any) */ - constructor(message, value = '', cause = {}) { - super(message, cause, {value: value}); - } + constructor (message, value = '', cause = {}) { + super(message, cause, { value }) + } } export class DataValidationError extends FullstackTestingError { - /** + /** * Create a custom error for data validation error scenario * * error metadata will include `expected` and `found` values. @@ -74,7 +74,7 @@ export class DataValidationError extends FullstackTestingError { * @param found value found * @param cause source error (if any) */ - constructor(message, expected, found, cause = {}) { - super(message, cause, {expected: expected, found: found}); - } + constructor (message, expected, found, cause = {}) { + super(message, cause, { expected, found }) + } } diff --git a/fullstack-network-manager/src/core/helm.mjs b/fullstack-network-manager/src/core/helm.mjs index fe115fba7..dca6cda92 100644 --- a/fullstack-network-manager/src/core/helm.mjs +++ b/fullstack-network-manager/src/core/helm.mjs @@ -1,71 +1,71 @@ -import {ShellRunner} from "./shell_runner.mjs"; +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 - } + 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)) - } + 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)) - } + 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)) - } + 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)) - } + 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)) - } + async dependency (subCommand, ...args) { + return this.run(this.prepareCommand('dependency', subCommand, ...args)) + } - /** + /** * Invoke `helm repo` command * @param subCommand sub-command * @param args args of the command * @returns {Promise} console output as an array of strings */ - async repo(subCommand, ...args) { - return this.run(this.prepareCommand('repo', subCommand, ...args)) - } + async repo (subCommand, ...args) { + return this.run(this.prepareCommand('repo', subCommand, ...args)) + } } diff --git a/fullstack-network-manager/src/core/index.mjs b/fullstack-network-manager/src/core/index.mjs index a620d3201..b2e4e8f62 100644 --- a/fullstack-network-manager/src/core/index.mjs +++ b/fullstack-network-manager/src/core/index.mjs @@ -1,26 +1,26 @@ 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"; -import {PackageDownloader} from "./package_downloader.mjs"; -import {PlatformInstaller} from "./platform_installer.mjs"; -import {Zippy} from "./zippy.mjs"; -import {Templates} from "./templates.mjs"; -import {ChartManager} from "./chart_manager.mjs"; -import {ConfigManager} from "./config_manager.mjs"; +import { constants } from './constants.mjs' +import { Kind } from './kind.mjs' +import { Helm } from './helm.mjs' +import { Kubectl } from './kubectl.mjs' +import { PackageDownloader } from './package_downloader.mjs' +import { PlatformInstaller } from './platform_installer.mjs' +import { Zippy } from './zippy.mjs' +import { Templates } from './templates.mjs' +import { ChartManager } from './chart_manager.mjs' +import { ConfigManager } from './config_manager.mjs' // Expose components from the core module export { - logging, - constants, - Kind, - Helm, - Kubectl, - PackageDownloader, - PlatformInstaller, - Zippy, - Templates, - ChartManager, - ConfigManager, + logging, + constants, + Kind, + Helm, + Kubectl, + PackageDownloader, + PlatformInstaller, + Zippy, + Templates, + ChartManager, + ConfigManager } diff --git a/fullstack-network-manager/src/core/kind.mjs b/fullstack-network-manager/src/core/kind.mjs index 23d10d90a..8da43bf8f 100644 --- a/fullstack-network-manager/src/core/kind.mjs +++ b/fullstack-network-manager/src/core/kind.mjs @@ -1,4 +1,4 @@ -import {ShellRunner} from "./shell_runner.mjs"; +import { ShellRunner } from './shell_runner.mjs' /** * Kind is a wrapper for kind CLI @@ -9,92 +9,91 @@ import {ShellRunner} from "./shell_runner.mjs"; * @type {Kind} an instance of Kind */ export class Kind extends ShellRunner { - /** + /** * Prepare a `kind` command string * @param action represents a kind command (e.g. create | delete | get ) * @param resource represents kind sub-command (e.g. cluster | nodes ) * @param args args of the command * @returns {string} */ - prepareCommand(action, resource, ...args) { - let cmd = `kind ${action} ${resource}` - args.forEach(arg => {cmd += ` ${arg}`}) - return cmd - } + prepareCommand (action, resource, ...args) { + let cmd = `kind ${action} ${resource}` + args.forEach(arg => { cmd += ` ${arg}` }) + return cmd + } - /** + /** * Invoke `kind create` command * @param resource represents kind sub-command (e.g. cluster | nodes ) * @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)) - } + async create (resource, ...args) { + return this.run(this.prepareCommand('create', resource, ...args)) + } - /** + /** * Invoke `kind create cluster` command * @param args args of the command * @returns {Promise} console output as an array of strings */ - async createCluster(...args) { - return this.create('cluster', ...args) - } + async createCluster (...args) { + return this.create('cluster', ...args) + } - /** + /** * Invoke `kind delete` command * @param resource represents kind sub-command (e.g. cluster | nodes ) * @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)) - } + async delete (resource, ...args) { + return this.run(this.prepareCommand('delete', resource, ...args)) + } - /** + /** * Invoke `kind delete cluster` command * @param name cluster name * @returns {Promise} console output as an array of strings */ - async deleteCluster(name) { - return this.delete('cluster', `-n ${name}`) - } + async deleteCluster (name) { + return this.delete('cluster', `-n ${name}`) + } - /** + /** * Invoke `kind get` command * @param resource represents kind sub-command (e.g. cluster | nodes ) * @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)) - } + async get (resource, ...args) { + return this.run(this.prepareCommand('get', resource, ...args)) + } - /** + /** * Invoke `kind get clusters` command * @param args args of the command * @returns {Promise} console output as an array of strings */ - async getClusters(...args) { - return this.get('clusters', ...args) - } + async getClusters (...args) { + return this.get('clusters', ...args) + } - /** + /** * Invoke `kind get nodes` command * @param args args of the command * @returns {Promise} console output as an array of strings */ - async getNodes(...args) { - return this.get('nodes', ...args) - } + async getNodes (...args) { + return this.get('nodes', ...args) + } - /** + /** * Invoke `kind get kubeconfig` command * @param args args of the command * @returns {Promise} console output as an array of strings */ - async getKubeconfig(...args) { - return this.get('kubeconfig', ...args) - } - -} \ No newline at end of file + async getKubeconfig (...args) { + return this.get('kubeconfig', ...args) + } +} diff --git a/fullstack-network-manager/src/core/kubectl.mjs b/fullstack-network-manager/src/core/kubectl.mjs index d89cfe799..3b1bf18a7 100644 --- a/fullstack-network-manager/src/core/kubectl.mjs +++ b/fullstack-network-manager/src/core/kubectl.mjs @@ -1,158 +1,157 @@ -import {ShellRunner} from "./shell_runner.mjs"; -import {FullstackTestingError} from "./errors.mjs"; +import { ShellRunner } from './shell_runner.mjs' +import { FullstackTestingError } from './errors.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 - } + 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)) - } + 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)) - } + 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)) - } + 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)) - } + 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)) - } + 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)) - } + async getNamespace (...args) { + return this.run(this.prepareCommand('get', 'ns', ...args)) + } - /** + /** * Get pod IP of a pod * @param podName name of the pod * @returns {Promise} console output as an array of strings */ - async getPodIP(podName) { - return new Promise(async (resolve, reject) => { - try { - const output = await this.run(this.prepareCommand('get', 'pod', podName, `-o jsonpath='{.status.podIP}'`)) - if (output) resolve(output[0].trim()) - reject(new FullstackTestingError(`No resource found for ${podName}`)) - } catch (e) { - reject(new FullstackTestingError(`error on detecting IP for pod ${podName}`, e)) - } - }) - } + async getPodIP (podName) { + return new Promise(async (resolve, reject) => { + try { + const output = await this.run(this.prepareCommand('get', 'pod', podName, '-o jsonpath=\'{.status.podIP}\'')) + if (output) resolve(output[0].trim()) + reject(new FullstackTestingError(`No resource found for ${podName}`)) + } catch (e) { + reject(new FullstackTestingError(`error on detecting IP for pod ${podName}`, e)) + } + }) + } - /** + /** * Get cluster IP of a service * @param svcName name of the service * @returns {Promise} console output as an array of strings */ - async getClusterIP(svcName) { - return new Promise(async (resolve, reject) => { - try { - const output = await this.run(this.prepareCommand('get', 'svc', svcName, `-o jsonpath='{.spec.clusterIP}'`)) - if (output) resolve(output[0].trim()) - reject(new FullstackTestingError(`No resource found for ${svcName}`)) - } catch (e) { - reject(new FullstackTestingError(`error on detecting cluster IP for svc ${svcName}`, e)) - } - }) - } + async getClusterIP (svcName) { + return new Promise(async (resolve, reject) => { + try { + const output = await this.run(this.prepareCommand('get', 'svc', svcName, '-o jsonpath=\'{.spec.clusterIP}\'')) + if (output) resolve(output[0].trim()) + reject(new FullstackTestingError(`No resource found for ${svcName}`)) + } catch (e) { + reject(new FullstackTestingError(`error on detecting cluster IP for svc ${svcName}`, e)) + } + }) + } - - /** + /** * Invoke `kubectl wait` 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 wait(resource, ...args) { - return this.run(this.prepareCommand('wait', resource, ...args)) - } + async wait (resource, ...args) { + return this.run(this.prepareCommand('wait', resource, ...args)) + } - /** + /** * Invoke `kubectl exec` command * @param pod a kubernetes pod name * @param args args of the command * @returns {Promise} console output as an array of strings */ - async exec(pod, ...args) { - return this.run(this.prepareCommand('exec', pod, ...args)) - } + async exec (pod, ...args) { + return this.run(this.prepareCommand('exec', pod, ...args)) + } - /** + /** * Invoke bash command within a container * @param pod a kubernetes pod name * @param container name of the container within the pod * @param bashScript bash script to be run within the container (e.g 'ls -la /opt/hgcapp') * @returns {Promise} console output as an array of strings */ - async execContainer(pod, container, bashScript) { - return this.exec(pod, `-c ${container} -- `, `bash -c "${bashScript}"`) - } + async execContainer (pod, container, bashScript) { + return this.exec(pod, `-c ${container} -- `, `bash -c "${bashScript}"`) + } - /** + /** * Invoke `kubectl cp` command * @param pod a kubernetes pod name * @param args args of the command * @returns {Promise} console output as an array of strings */ - async copy(pod, ...args) { - return this.run(this.prepareCommand('cp', ...args)) - } + async copy (pod, ...args) { + return this.run(this.prepareCommand('cp', ...args)) + } - /** + /** * Invoke `kubectl config` command * @param args args of the command * @returns {Promise} console output as an array of strings */ - async config(...args) { - return this.run(this.prepareCommand('config', ...args)) - } -} \ No newline at end of file + async config (...args) { + return this.run(this.prepareCommand('config', ...args)) + } +} diff --git a/fullstack-network-manager/src/core/logging.mjs b/fullstack-network-manager/src/core/logging.mjs index 9ed52a855..5b8f36f9a 100644 --- a/fullstack-network-manager/src/core/logging.mjs +++ b/fullstack-network-manager/src/core/logging.mjs @@ -1,44 +1,44 @@ 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"; +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}), + winston.format.label({ label: 'FST', message: false }), - winston.format.splat(), + winston.format.splat(), - // include timestamp in logs - winston.format.timestamp(), + // include timestamp in logs + winston.format.timestamp(), - winston.format.ms(), + winston.format.ms(), - // add label metadata - winston.format.label({label: ''}), + // add label metadata + winston.format.label({ label: '' }), - // convert levels to upper case - winston.format(data => { - data.level = data.level.toUpperCase(); - return data - })(), + // convert levels to upper case + winston.format(data => { + data.level = data.level.toUpperCase() + return data + })(), - // use custom format TIMESTAMP [LABEL] LEVEL: MESSAGE - winston.format.printf(data => { - return `${data.timestamp}|${data.level}| ${data.message}`; - }), + // use custom format TIMESTAMP [LABEL] LEVEL: MESSAGE + winston.format.printf(data => { + return `${data.timestamp}|${data.level}| ${data.message}` + }), - // Ignore log messages if they have { private: true } - winston.format((data, opts) => { - if (data.private) { - return false; - } - return data; - })(), + // Ignore log messages if they have { private: true } + winston.format((data, opts) => { + if (data.private) { + return false + } + return data + })() ) export const Logger = class { - /** + /** * Create a new logger * @param level logging level as supported by winston library: * { @@ -53,106 +53,106 @@ export const Logger = class { * } * @constructor */ - constructor(level) { - const self = this - this.nextTraceId() - - this.winsonLogger = winston.createLogger({ - level: level, - format: winston.format.combine( - customFormat, - winston.format.json(), - ), - // format: winston.format.json(), - // defaultMeta: { service: 'user-service' }, - transports: [ - // - // - Write all logs with importance level of `error` or less to `error.log` - // - Write all logs with importance level of `info` or less to `fst.log` - // - new winston.transports.File({filename: `${constants.FST_LOGS_DIR}/fst.log`}), - // new winston.transports.File({filename: constants.TMP_DIR + "/logs/error.log", level: 'error'}), - // new winston.transports.Console({format: customFormat}) - ], - }); + constructor (level) { + const self = this + this.nextTraceId() + + this.winsonLogger = winston.createLogger({ + level, + format: winston.format.combine( + customFormat, + winston.format.json() + ), + // format: winston.format.json(), + // defaultMeta: { service: 'user-service' }, + transports: [ + // + // - Write all logs with importance level of `error` or less to `error.log` + // - Write all logs with importance level of `info` or less to `fst.log` + // + new winston.transports.File({ filename: `${constants.FST_LOGS_DIR}/fst.log` }) + // new winston.transports.File({filename: constants.TMP_DIR + "/logs/error.log", level: 'error'}), + // new winston.transports.Console({format: customFormat}) + ] + }) + } + + nextTraceId () { + this.traceId = uuidv4() + } + + prepMeta (meta) { + if (meta === undefined) { + meta = {} } - nextTraceId() { - this.traceId = uuidv4() - } + meta.traceId = this.traceId + return meta + } - prepMeta(meta) { - if (meta === undefined) { - meta = {} - } + showUser (msg, ...args) { + console.log(util.format(msg, ...args)) + } - meta.traceId = this.traceId - return meta - } + showUserError (err) { + this.error(err.message, err) - showUser(msg, ...args) { - console.log(util.format(msg, ...args)) - } + console.log(chalk.red('ERROR: ')) + console.log(err.stack) - showUserError(err) { - this.error(err.message, err) - - console.log(chalk.red('ERROR: ')) - console.log(err.stack) - - if (err.cause) { - let depth = 0 - let cause = err.cause - while (cause !== undefined && depth < 10) { - if (cause.stack) { - console.log(chalk.red('Caused by:')) - console.log(cause.stack) - } - - cause = cause.cause - depth += 1 - } + if (err.cause) { + let depth = 0 + let cause = err.cause + while (cause !== undefined && depth < 10) { + if (cause.stack) { + console.log(chalk.red('Caused by:')) + console.log(cause.stack) } - } - - critical(msg, ...args) { - this.winsonLogger.crit(msg, ...args, this.prepMeta()) - } - - error(msg, ...args) { - this.winsonLogger.error(msg, ...args, this.prepMeta()) - } - warn(msg, ...args) { - this.winsonLogger.warn(msg, ...args, this.prepMeta()) + cause = cause.cause + depth += 1 + } } - - notice(msg, ...args) { - this.winsonLogger.notice(msg, ...args, this.prepMeta()) - } - - info(msg, ...args) { - this.winsonLogger.info(msg, ...args, this.prepMeta()) + } + + critical (msg, ...args) { + this.winsonLogger.crit(msg, ...args, this.prepMeta()) + } + + error (msg, ...args) { + this.winsonLogger.error(msg, ...args, this.prepMeta()) + } + + warn (msg, ...args) { + this.winsonLogger.warn(msg, ...args, this.prepMeta()) + } + + notice (msg, ...args) { + this.winsonLogger.notice(msg, ...args, this.prepMeta()) + } + + info (msg, ...args) { + this.winsonLogger.info(msg, ...args, this.prepMeta()) + } + + debug (msg, ...args) { + this.winsonLogger.debug(msg, ...args, this.prepMeta()) + } + + showList (itemType, items = []) { + this.showUser(chalk.green(`\n *** List of ${itemType} ***`)) + this.showUser(chalk.green('---------------------------------------')) + if (items.length > 0) { + items.forEach(name => this.showUser(chalk.yellow(` - ${name}`))) + } else { + this.showUser(chalk.blue('[ None ]')) } - debug(msg, ...args) { - this.winsonLogger.debug(msg, ...args, this.prepMeta()) - } - - showList(itemType, items = []) { - this.showUser(chalk.green(`\n *** List of ${itemType} ***`)) - this.showUser(chalk.green(`---------------------------------------`)) - if (items.length > 0) { - items.forEach(name => this.showUser(chalk.yellow(` - ${name}`))) - } else { - this.showUser(chalk.blue(`[ None ]`)) - } - - this.showUser("\n") - return true - } + this.showUser('\n') + return true + } } -export function NewLogger(level = 'debug') { - return new Logger(level) +export function NewLogger (level = 'debug') { + return new Logger(level) } diff --git a/fullstack-network-manager/src/core/package_downloader.mjs b/fullstack-network-manager/src/core/package_downloader.mjs index 70851a54d..bd65d525e 100644 --- a/fullstack-network-manager/src/core/package_downloader.mjs +++ b/fullstack-network-manager/src/core/package_downloader.mjs @@ -1,136 +1,136 @@ import * as crypto from 'crypto' -import * as fs from "fs"; -import {pipeline as streamPipeline} from 'node:stream/promises'; -import got from 'got'; -import {DataValidationError, FullstackTestingError, IllegalArgumentError, ResourceNotFoundError} from "./errors.mjs"; -import * as https from "https"; -import {Templates} from "./templates.mjs"; -import {constants} from "./constants.mjs"; +import * as fs from 'fs' +import { pipeline as streamPipeline } from 'node:stream/promises' +import got from 'got' +import { DataValidationError, FullstackTestingError, IllegalArgumentError, ResourceNotFoundError } from './errors.mjs' +import * as https from 'https' +import { Templates } from './templates.mjs' +import { constants } from './constants.mjs' export class PackageDownloader { - /** + /** * Create an instance of Downloader * @param logger an instance of core/Logger */ - constructor(logger) { - if (!logger) throw new IllegalArgumentError("an instance of core/Logger is required", logger) - this.logger = logger + constructor (logger) { + if (!logger) throw new IllegalArgumentError('an instance of core/Logger is required', logger) + this.logger = logger + } + + isValidURL (url) { + try { + // attempt to parse to check URL format + new URL(url) + return true + } catch (e) { } - isValidURL(url) { - try { - // attempt to parse to check URL format - new URL(url); - return true - } catch (e) { - } + return false + } + + async urlExists (url) { + const self = this + + return new Promise((resolve, reject) => { + try { + self.logger.debug(`Checking URL: ${url}`) + // attempt to send a HEAD request to check URL exists + const req = https.request(url, { method: 'HEAD', timeout: 100, headers: { Connection: 'close' } }) + + req.on('response', r => { + const statusCode = r.statusCode + self.logger.debug({ + response: { + connectOptions: r['connect-options'], + statusCode: r.statusCode, + headers: r.headers + } - return false - } + }) - async urlExists(url) { - const self = this - - return new Promise((resolve, reject) => { - try { - self.logger.debug(`Checking URL: ${url}`) - // attempt to send a HEAD request to check URL exists - const req = https.request(url, {method: 'HEAD', timeout: 100, headers: {"Connection": 'close'}}) - - req.on('response', r => { - const statusCode = r.statusCode - self.logger.debug({ - response: { - connectOptions: r['connect-options'], - statusCode: r.statusCode, - headers: r.headers, - } - - }) - - if (statusCode === 200) { - return resolve(true) - } - - resolve(false) - }) - - req.on('error', err => { - self.logger.error(err) - resolve(false) - }) - - req.end() // make the request - } catch (e) { - self.logger.error(e) - resolve(false) - } + if (statusCode === 200) { + return resolve(true) + } + + resolve(false) }) - } - /** + req.on('error', err => { + self.logger.error(err) + resolve(false) + }) + + req.end() // make the request + } catch (e) { + self.logger.error(e) + resolve(false) + } + }) + } + + /** * Fetch data from a URL and save the output to a file * * @param url source file URL * @param destPath destination path for the downloaded file */ - async fetchFile(url, destPath) { - const self = this - - if (!url) throw new IllegalArgumentError('source file URL is required', url) - if (!destPath) throw new IllegalArgumentError('destination path is required', destPath) - if (!this.isValidURL(url)) { - throw new IllegalArgumentError(`source URL is invalid`, url) - } - - return new Promise(async (resolve, reject) => { - if (!await this.urlExists(url)) { - reject(new ResourceNotFoundError(`source URL does not exist`, url)) - } + async fetchFile (url, destPath) { + const self = this - try { - await streamPipeline( - got.stream(url), - fs.createWriteStream(destPath) - ) - resolve(destPath) - } catch (e) { - self.logger.error(e) - reject(new ResourceNotFoundError(e.message, url, e)) - } - }) + if (!url) throw new IllegalArgumentError('source file URL is required', url) + if (!destPath) throw new IllegalArgumentError('destination path is required', destPath) + if (!this.isValidURL(url)) { + throw new IllegalArgumentError('source URL is invalid', url) } - /** + return new Promise(async (resolve, reject) => { + if (!await this.urlExists(url)) { + reject(new ResourceNotFoundError('source URL does not exist', url)) + } + + try { + await streamPipeline( + got.stream(url), + fs.createWriteStream(destPath) + ) + resolve(destPath) + } catch (e) { + self.logger.error(e) + reject(new ResourceNotFoundError(e.message, url, e)) + } + }) + } + + /** * Compute hash of the file contents * @param filePath path of the file * @param algo hash algorithm * @returns {Promise} returns hex digest of the computed hash * @throws Error if the file cannot be read */ - async computeFileHash(filePath, algo = 'sha384') { - const self = this - - return new Promise((resolve, reject) => { - try { - self.logger.debug(`Computing checksum for '${filePath}' using algo '${algo}'`) - const checksum = crypto.createHash(algo); - const s = fs.createReadStream(filePath) - s.on('data', function (d) { - checksum.update(d); - }); - s.on('end', function () { - const d = checksum.digest('hex'); - self.logger.debug(`Computed checksum '${d}' for '${filePath}' using algo '${algo}'`) - resolve(d) - }) - } catch (e) { - reject(new FullstackTestingError('failed to compute checksum', e, {filePath, algo})) - } + async computeFileHash (filePath, algo = 'sha384') { + const self = this + + return new Promise((resolve, reject) => { + try { + self.logger.debug(`Computing checksum for '${filePath}' using algo '${algo}'`) + const checksum = crypto.createHash(algo) + const s = fs.createReadStream(filePath) + s.on('data', function (d) { + checksum.update(d) }) - } + s.on('end', function () { + const d = checksum.digest('hex') + self.logger.debug(`Computed checksum '${d}' for '${filePath}' using algo '${algo}'`) + resolve(d) + }) + } catch (e) { + reject(new FullstackTestingError('failed to compute checksum', e, { filePath, algo })) + } + }) + } - /** + /** * Verifies that the checksum of the sourceFile matches with the contents of the checksumFile * * It throws error if the checksum doesn't match. @@ -140,12 +140,12 @@ export class PackageDownloader { * @param algo hash algorithm to be used to compute checksum * @throws DataValidationError if the checksum doesn't match */ - async verifyChecksum(sourceFile, checksum, algo = 'sha384') { - const computed = await this.computeFileHash(sourceFile, algo) - if (checksum !== computed) throw new DataValidationError('checksum', checksum, computed) - } + async verifyChecksum (sourceFile, checksum, algo = 'sha384') { + const computed = await this.computeFileHash(sourceFile, algo) + if (checksum !== computed) throw new DataValidationError('checksum', checksum, computed) + } - /** + /** * Fetch platform release artifact * * It fetches the build.zip file containing the release from a URL like: https://builds.hedera.com/node/software/v0.40/build-v0.40.4.zip @@ -155,45 +155,45 @@ export class PackageDownloader { * @param force whether to download even if the file exists * @returns {Promise} full path to the downloaded file */ - async fetchPlatform(tag, destDir, force = false) { - const self = this - const releaseDir = Templates.prepareReleasePrefix(tag) + async fetchPlatform (tag, destDir, force = false) { + const self = this + const releaseDir = Templates.prepareReleasePrefix(tag) - if (!destDir) throw new Error('destination directory path is required') + if (!destDir) throw new Error('destination directory path is required') - if (!fs.existsSync(destDir)) { - throw new IllegalArgumentError(`destDir (${destDir}) does not exist`, destDir) - } else if(!fs.statSync(destDir).isDirectory()) { - throw new IllegalArgumentError(`destDir (${destDir}) is not a directory`, destDir) + if (!fs.existsSync(destDir)) { + throw new IllegalArgumentError(`destDir (${destDir}) does not exist`, destDir) + } else if (!fs.statSync(destDir).isDirectory()) { + throw new IllegalArgumentError(`destDir (${destDir}) is not a directory`, destDir) + } + + const downloadDir = `${destDir}/${releaseDir}` + const packageURL = `${constants.HEDERA_BUILDS_URL}/node/software/${releaseDir}/build-${tag}.zip` + const packageFile = `${downloadDir}/build-${tag}.zip` + const checksumURL = `${constants.HEDERA_BUILDS_URL}/node/software/${releaseDir}/build-${tag}.sha384` + const checksumPath = `${downloadDir}/build-${tag}.sha384` + + return new Promise(async (resolve, reject) => { + try { + if (fs.existsSync(packageFile) && !force) { + resolve(packageFile) + return } - const downloadDir = `${destDir}/${releaseDir}` - const packageURL = `${constants.HEDERA_BUILDS_URL}/node/software/${releaseDir}/build-${tag}.zip` - const packageFile = `${downloadDir}/build-${tag}.zip` - const checksumURL = `${constants.HEDERA_BUILDS_URL}/node/software/${releaseDir}/build-${tag}.sha384` - const checksumPath = `${downloadDir}/build-${tag}.sha384` - - return new Promise(async (resolve, reject) => { - try { - if (fs.existsSync(packageFile) && !force) { - resolve(packageFile) - return - } - - if (!fs.existsSync(downloadDir)) { - fs.mkdirSync(downloadDir, {recursive: true}) - } - - await this.fetchFile(packageURL, packageFile) - await this.fetchFile(checksumURL, checksumPath) - - const checksum = fs.readFileSync(checksumPath).toString().split(" ")[0] - await this.verifyChecksum(packageFile, checksum) - resolve(packageFile) - } catch (e) { - self.logger.error(e) - reject(new FullstackTestingError(e.message, e, {tag, destDir})) - } - }) - } + if (!fs.existsSync(downloadDir)) { + fs.mkdirSync(downloadDir, { recursive: true }) + } + + await this.fetchFile(packageURL, packageFile) + await this.fetchFile(checksumURL, checksumPath) + + const checksum = fs.readFileSync(checksumPath).toString().split(' ')[0] + await this.verifyChecksum(packageFile, checksum) + resolve(packageFile) + } catch (e) { + self.logger.error(e) + reject(new FullstackTestingError(e.message, e, { tag, destDir })) + } + }) + } } diff --git a/fullstack-network-manager/src/core/platform_installer.mjs b/fullstack-network-manager/src/core/platform_installer.mjs index e48181a33..8834438f5 100644 --- a/fullstack-network-manager/src/core/platform_installer.mjs +++ b/fullstack-network-manager/src/core/platform_installer.mjs @@ -1,245 +1,244 @@ -import {DataValidationError, FullstackTestingError, IllegalArgumentError, MissingArgumentError} from "./errors.mjs"; -import chalk from "chalk"; -import * as fs from "fs"; -import {constants} from "./constants.mjs"; -import {Templates} from "./templates.mjs"; -import * as path from "path"; +import { DataValidationError, FullstackTestingError, IllegalArgumentError, MissingArgumentError } from './errors.mjs' +import chalk from 'chalk' +import * as fs from 'fs' +import { constants } from './constants.mjs' +import { Templates } from './templates.mjs' +import * as path from 'path' /** * PlatformInstaller install platform code in the root-container of a network pod */ export class PlatformInstaller { + constructor (logger, kubectl) { + if (!logger) throw new MissingArgumentError('an instance of core/Logger is required') + if (!kubectl) throw new MissingArgumentError('an instance of core/Kubectl is required') - constructor(logger, kubectl) { - if (!logger) throw new MissingArgumentError("an instance of core/Logger is required") - if (!kubectl) throw new MissingArgumentError("an instance of core/Kubectl is required") + this.logger = logger + this.kubectl = kubectl + } - this.logger = logger - this.kubectl = kubectl - } - - async setupHapiDirectories(podName, containerName = constants.ROOT_CONTAINER) { - const self = this + async setupHapiDirectories (podName, containerName = constants.ROOT_CONTAINER) { + const self = this - if (!podName) throw new MissingArgumentError('podName is required') - return new Promise(async (resolve, reject) => { - try { - // reset HAPI_PATH - await this.kubectl.execContainer(podName, containerName, `rm -rf ${constants.HGCAPP_SERVICES_HEDERA_PATH}`) + if (!podName) throw new MissingArgumentError('podName is required') + return new Promise(async (resolve, reject) => { + try { + // reset HAPI_PATH + await this.kubectl.execContainer(podName, containerName, `rm -rf ${constants.HGCAPP_SERVICES_HEDERA_PATH}`) - const paths = [ + const paths = [ `${constants.HAPI_PATH}/data/keys`, - `${constants.HAPI_PATH}/data/config`, - ] - - for (const p of paths) { - await this.kubectl.execContainer(podName, containerName, `mkdir -p ${p}`) - } - - await this.setPathPermission(podName, constants.HGCAPP_SERVICES_HEDERA_PATH) - - resolve(true) - } catch (e) { - reject(new FullstackTestingError(`failed to setup directories in pod '${podName}' at ${constants.HAPI_PATH}`, e)) - } - }) - } + `${constants.HAPI_PATH}/data/config` + ] - async validatePlatformReleaseDir(releaseDir) { - if (!releaseDir) throw new MissingArgumentError('releaseDir is required') - if (!fs.existsSync(releaseDir)) { - throw new IllegalArgumentError('releaseDir does not exists', releaseDir) + for (const p of paths) { + await this.kubectl.execContainer(podName, containerName, `mkdir -p ${p}`) } - const dataDir = `${releaseDir}/data` - const appsDir = `${releaseDir}/${constants.DATA_APPS_DIR}` - const libDir = `${releaseDir}/${constants.DATA_LIB_DIR}` + await this.setPathPermission(podName, constants.HGCAPP_SERVICES_HEDERA_PATH) - if (!fs.existsSync(dataDir)) { - throw new IllegalArgumentError('releaseDir does not have data directory', releaseDir) - } + resolve(true) + } catch (e) { + reject(new FullstackTestingError(`failed to setup directories in pod '${podName}' at ${constants.HAPI_PATH}`, e)) + } + }) + } - if (!fs.existsSync(appsDir)) { - throw new IllegalArgumentError(`'${constants.DATA_APPS_DIR}' missing in '${releaseDir}'`, releaseDir) - } + async validatePlatformReleaseDir (releaseDir) { + if (!releaseDir) throw new MissingArgumentError('releaseDir is required') + if (!fs.existsSync(releaseDir)) { + throw new IllegalArgumentError('releaseDir does not exists', releaseDir) + } - if (!fs.existsSync(libDir)) { - throw new IllegalArgumentError(`'${constants.DATA_LIB_DIR}' missing in '${releaseDir}'`, releaseDir) - } + const dataDir = `${releaseDir}/data` + const appsDir = `${releaseDir}/${constants.DATA_APPS_DIR}` + const libDir = `${releaseDir}/${constants.DATA_LIB_DIR}` - if (!fs.statSync(`${releaseDir}/data/apps`).isEmpty()) { - throw new IllegalArgumentError(`'${constants.DATA_APPS_DIR}' is empty in releaseDir: ${releaseDir}`, releaseDir) - } + if (!fs.existsSync(dataDir)) { + throw new IllegalArgumentError('releaseDir does not have data directory', releaseDir) + } - if (!fs.statSync(`${releaseDir}/data/lib`).isEmpty()) { - throw new IllegalArgumentError(`'${constants.DATA_LIB_DIR}' is empty in releaseDir: ${releaseDir}`, releaseDir) - } + if (!fs.existsSync(appsDir)) { + throw new IllegalArgumentError(`'${constants.DATA_APPS_DIR}' missing in '${releaseDir}'`, releaseDir) } - async copyPlatform(podName, buildZipFile) { - const self = this - if (!podName) throw new MissingArgumentError('podName is required') - if (!buildZipFile) throw new MissingArgumentError('buildZipFile is required') - if (!fs.statSync(buildZipFile).isFile()) throw new IllegalArgumentError('buildZipFile does not exists', buildZipFile) + if (!fs.existsSync(libDir)) { + throw new IllegalArgumentError(`'${constants.DATA_LIB_DIR}' missing in '${releaseDir}'`, releaseDir) + } - return new Promise(async (resolve, reject) => { - try { - await this.kubectl.copy(podName, - buildZipFile, + if (!fs.statSync(`${releaseDir}/data/apps`).isEmpty()) { + throw new IllegalArgumentError(`'${constants.DATA_APPS_DIR}' is empty in releaseDir: ${releaseDir}`, releaseDir) + } + + if (!fs.statSync(`${releaseDir}/data/lib`).isEmpty()) { + throw new IllegalArgumentError(`'${constants.DATA_LIB_DIR}' is empty in releaseDir: ${releaseDir}`, releaseDir) + } + } + + async copyPlatform (podName, buildZipFile) { + const self = this + if (!podName) throw new MissingArgumentError('podName is required') + if (!buildZipFile) throw new MissingArgumentError('buildZipFile is required') + if (!fs.statSync(buildZipFile).isFile()) throw new IllegalArgumentError('buildZipFile does not exists', buildZipFile) + + return new Promise(async (resolve, reject) => { + try { + await this.kubectl.copy(podName, + buildZipFile, `${podName}:${constants.HEDERA_USER_HOME_DIR}`, - '-c root-container', - ) + '-c root-container' + ) - await this.setupHapiDirectories(podName) - await this.kubectl.execContainer(podName, constants.ROOT_CONTAINER, + await this.setupHapiDirectories(podName) + await this.kubectl.execContainer(podName, constants.ROOT_CONTAINER, `cd ${constants.HAPI_PATH} && jar xvf /home/hedera/build-*`) - resolve(true) - } catch (e) { - resolve(new FullstackTestingError('failed to copy platform code into pods', e)) - } - }) - } - - async copyFiles(podName, srcFiles, destDir, container = constants.ROOT_CONTAINER) { - const self = this - return new Promise(async (resolve, reject) => { - try { - for (const srcPath of srcFiles) { - self.logger.debug(`Copying files into ${podName}: ${srcPath} -> ${destDir}`) - await this.kubectl.copy(podName, - srcPath, + resolve(true) + } catch (e) { + resolve(new FullstackTestingError('failed to copy platform code into pods', e)) + } + }) + } + + async copyFiles (podName, srcFiles, destDir, container = constants.ROOT_CONTAINER) { + const self = this + return new Promise(async (resolve, reject) => { + try { + for (const srcPath of srcFiles) { + self.logger.debug(`Copying files into ${podName}: ${srcPath} -> ${destDir}`) + await this.kubectl.copy(podName, + srcPath, `${podName}:${destDir}`, - `-c ${container}`, - ) - } + `-c ${container}` + ) + } - const fileList = await this.kubectl.execContainer(podName, container, `ls ${destDir}`) + const fileList = await this.kubectl.execContainer(podName, container, `ls ${destDir}`) - // create full path - const fullPaths = [] - fileList.forEach(filePath => fullPaths.push(`${destDir}/${filePath}`)) + // create full path + const fullPaths = [] + fileList.forEach(filePath => fullPaths.push(`${destDir}/${filePath}`)) - resolve(fullPaths) - } catch (e) { - reject(new FullstackTestingError(`failed to copy files to pod '${podName}'`, e)) - } - }) - } + resolve(fullPaths) + } catch (e) { + reject(new FullstackTestingError(`failed to copy files to pod '${podName}'`, e)) + } + }) + } - async copyGossipKeys(podName, stagingDir) { - const self = this + async copyGossipKeys (podName, stagingDir) { + const self = this - if (!podName) throw new MissingArgumentError('podName is required') - if (!stagingDir) throw new MissingArgumentError('stagingDir is required') + if (!podName) throw new MissingArgumentError('podName is required') + if (!stagingDir) throw new MissingArgumentError('stagingDir is required') - return new Promise(async (resolve, reject) => { - try { - const keysDir = `${constants.HAPI_PATH}/data/keys` - const nodeId = Templates.extractNodeIdFromPodName(podName) - const srcFiles = [ + return new Promise(async (resolve, reject) => { + try { + const keysDir = `${constants.HAPI_PATH}/data/keys` + const nodeId = Templates.extractNodeIdFromPodName(podName) + const srcFiles = [ `${stagingDir}/templates/node-keys/private-${nodeId}.pfx`, - `${stagingDir}/templates/node-keys/public.pfx`, - ] - - resolve(await self.copyFiles(podName, srcFiles, keysDir)) - } catch (e) { - reject(new FullstackTestingError(`failed to copy gossip keys to pod '${podName}'`, e)) - } - }) - } + `${stagingDir}/templates/node-keys/public.pfx` + ] - async copyPlatformConfigFiles(podName, stagingDir) { - const self = this + resolve(await self.copyFiles(podName, srcFiles, keysDir)) + } catch (e) { + reject(new FullstackTestingError(`failed to copy gossip keys to pod '${podName}'`, e)) + } + }) + } - if (!podName) throw new MissingArgumentError('podName is required') - if (!stagingDir) throw new MissingArgumentError('stagingDir is required') + async copyPlatformConfigFiles (podName, stagingDir) { + const self = this - return new Promise(async (resolve, reject) => { - try { - const srcFilesSet1 = [ + if (!podName) throw new MissingArgumentError('podName is required') + if (!stagingDir) throw new MissingArgumentError('stagingDir is required') + + return new Promise(async (resolve, reject) => { + try { + const srcFilesSet1 = [ `${stagingDir}/config.txt`, `${stagingDir}/templates/log4j2.xml`, - `${stagingDir}/templates/settings.txt`, - ] + `${stagingDir}/templates/settings.txt` + ] - const fileList1 = await self.copyFiles(podName, srcFilesSet1, constants.HAPI_PATH) + const fileList1 = await self.copyFiles(podName, srcFilesSet1, constants.HAPI_PATH) - const srcFilesSet2 = [ + const srcFilesSet2 = [ `${stagingDir}/templates/properties/api-permission.properties`, `${stagingDir}/templates/properties/application.properties`, - `${stagingDir}/templates/properties/bootstrap.properties`, - ] + `${stagingDir}/templates/properties/bootstrap.properties` + ] - const fileList2 = await self.copyFiles(podName, srcFilesSet2, `${constants.HAPI_PATH}/data/config`) + const fileList2 = await self.copyFiles(podName, srcFilesSet2, `${constants.HAPI_PATH}/data/config`) - resolve(fileList1.concat(fileList2)) - } catch (e) { - reject(new FullstackTestingError(`failed to copy config files to pod '${podName}'`, e)) - } - }) - } + resolve(fileList1.concat(fileList2)) + } catch (e) { + reject(new FullstackTestingError(`failed to copy config files to pod '${podName}'`, e)) + } + }) + } - async copyTLSKeys(podName, stagingDir) { - const self = this + async copyTLSKeys (podName, stagingDir) { + const self = this - if (!podName) throw new MissingArgumentError('podName is required') - if (!stagingDir) throw new MissingArgumentError('stagingDir is required') + if (!podName) throw new MissingArgumentError('podName is required') + if (!stagingDir) throw new MissingArgumentError('stagingDir is required') - return new Promise(async (resolve, reject) => { - try { - const destDir = constants.HAPI_PATH - const srcFiles = [ + return new Promise(async (resolve, reject) => { + try { + const destDir = constants.HAPI_PATH + const srcFiles = [ `${stagingDir}/templates/hedera.key`, - `${stagingDir}/templates/hedera.crt`, - ] - - resolve(await self.copyFiles(podName, srcFiles, destDir)) - } catch (e) { - reject(new FullstackTestingError(`failed to copy TLS keys to pod '${podName}'`, e)) - } - }) - } - - async setPathPermission(podName, destPath, mode = '0755', recursive = true, container = constants.ROOT_CONTAINER) { - const self = this - if (!podName) throw new MissingArgumentError('podName is required') - if (!destPath) throw new MissingArgumentError('destPath is required') - - return new Promise(async (resolve, reject) => { - try { - const recursiveFlag = recursive ? '-R' : '' - await this.kubectl.execContainer(podName, container, `chown ${recursiveFlag} hedera:hedera ${destPath}`) - await this.kubectl.execContainer(podName, container, `chmod ${recursiveFlag} ${mode} ${destPath}`) - resolve(true) - } catch (e) { - reject(new FullstackTestingError(`failed to set permission in '${podName}': ${destPath}`, e)) - } - }) - } + `${stagingDir}/templates/hedera.crt` + ] + + resolve(await self.copyFiles(podName, srcFiles, destDir)) + } catch (e) { + reject(new FullstackTestingError(`failed to copy TLS keys to pod '${podName}'`, e)) + } + }) + } + + async setPathPermission (podName, destPath, mode = '0755', recursive = true, container = constants.ROOT_CONTAINER) { + const self = this + if (!podName) throw new MissingArgumentError('podName is required') + if (!destPath) throw new MissingArgumentError('destPath is required') + + return new Promise(async (resolve, reject) => { + try { + const recursiveFlag = recursive ? '-R' : '' + await this.kubectl.execContainer(podName, container, `chown ${recursiveFlag} hedera:hedera ${destPath}`) + await this.kubectl.execContainer(podName, container, `chmod ${recursiveFlag} ${mode} ${destPath}`) + resolve(true) + } catch (e) { + reject(new FullstackTestingError(`failed to set permission in '${podName}': ${destPath}`, e)) + } + }) + } + + async setPlatformDirPermissions (podName) { + const self = this + if (!podName) throw new MissingArgumentError('podName is required') + + return new Promise(async (resolve, reject) => { + try { + const destPaths = [ + constants.HAPI_PATH + ] + + for (const destPath of destPaths) { + await self.setPathPermission(podName, destPath) + } - async setPlatformDirPermissions(podName) { - const self = this - if (!podName) throw new MissingArgumentError('podName is required') - - return new Promise(async (resolve, reject) => { - try { - const destPaths = [ - constants.HAPI_PATH - ] - - for (const destPath of destPaths) { - await self.setPathPermission(podName, destPath) - } - - resolve(true) - } catch (e) { - reject(new FullstackTestingError(`failed to set permission in '${podName}': ${destPath}`, e)) - } - }) - } + resolve(true) + } catch (e) { + reject(new FullstackTestingError(`failed to set permission in '${podName}': ${destPath}`, e)) + } + }) + } - /** + /** * Prepares config.txt file for the node * @param nodeIDs node IDs * @param destPath path where config.txt should be written @@ -247,129 +246,127 @@ export class PlatformInstaller { * @param template path to the confit.template file * @returns {Promise} */ - async prepareConfigTxt(nodeIDs, destPath, releaseTag, template = `${constants.RESOURCES_DIR}/templates/config.template`) { - const self = this - - if (!nodeIDs || nodeIDs.length === 0) throw new MissingArgumentError('list of node IDs is required') - if (!destPath) throw new MissingArgumentError('destPath is required') - if (!template) throw new MissingArgumentError('config templatePath is required') - if (!releaseTag) throw new MissingArgumentError('release tag is required') - - if (!fs.existsSync(path.dirname(destPath))) throw new IllegalArgumentError(`destPath does not exist: ${destPath}`, destPath) - if (!fs.existsSync(template)) throw new IllegalArgumentError(`config templatePath does not exist: ${template}`, destPath) - - const accountIdPrefix = process.env.FST_NODE_ACCOUNT_ID_PREFIX || '0.0' - const accountIdStart = process.env.FST_NODE_ACCOUNT_ID_START || '3' - const internalPort = process.env.FST_NODE_INTERNAL_GOSSIP_PORT || '50111' - const externalPort = process.env.FST_NODE_EXTERNAL_GOSSIP_PORT || '50111' - const ledgerName = process.env.FST_LEDGER_NAME || constants.CLUSTER_NAME - const appName = process.env.FST_HEDERA_APP_NAME || constants.HEDERA_APP_JAR - const nodeStakeAmount = process.env.FST_NODE_DEFAULT_STAKE_AMOUNT || constants.HEDERA_NODE_DEFAULT_STAKE_AMOUNT - - const releaseTagParts = releaseTag.split(".") - if (releaseTagParts.length !== 3) throw new FullstackTestingError(`release tag must have form v.., found ${releaseTagParts}`, 'v..', releaseTag) - const minorVersion = parseInt(releaseTagParts[1], 10) - - - return new Promise(async (resolve, reject) => { - try { - const configLines = [] - configLines.push(`swirld, ${ledgerName}`) - configLines.push(`app, ${appName}`) - - let nodeSeq = 0 - let accountIdSeq = parseInt(accountIdStart, 10) - for (const nodeId of nodeIDs) { - const podName = Templates.renderNetworkPodName(nodeId) - const svcName = Templates.renderNetworkSvcName(nodeId) - - const nodeName = nodeId - const nodeNickName = nodeId - - const internalIP = await self.kubectl.getPodIP(podName) - const externalIP = await self.kubectl.getClusterIP(svcName) - - const account = `${accountIdPrefix}.${accountIdSeq}` - if (minorVersion >= 40) { - configLines.push(`address, ${nodeSeq}, ${nodeNickName}, ${nodeName}, ${nodeStakeAmount}, ${internalIP}, ${internalPort}, ${externalIP}, ${externalPort}, ${account}`) - } else { - configLines.push(`address, ${nodeSeq}, ${nodeName}, ${nodeStakeAmount}, ${internalIP}, ${internalPort}, ${externalIP}, ${externalPort}, ${account}`) - } - - nodeSeq += 1 - accountIdSeq += 1 - } - - if (minorVersion >= 41) { - configLines.push(`nextNodeId, ${nodeSeq}`) - } - - fs.writeFileSync(destPath, configLines.join("\n")) - - resolve(configLines) - } catch (e) { - reject(new FullstackTestingError('failed to generate config.txt', e)) - } - - }) - } - - async prepareStaging(nodeIDs, stagingDir, releaseTag, force = false) { - const self = this - return new Promise(async (resolve, reject) => { - try { - if (!fs.existsSync(stagingDir)) { - fs.mkdirSync(stagingDir, {recursive: true}) - } - - const configTxtPath = `${stagingDir}/config.txt` - - // copy a templates from fsnetman resources directory - fs.cpSync(`${constants.RESOURCES_DIR}/templates/`, `${stagingDir}/templates`, {recursive: true}) + async prepareConfigTxt (nodeIDs, destPath, releaseTag, template = `${constants.RESOURCES_DIR}/templates/config.template`) { + const self = this + + if (!nodeIDs || nodeIDs.length === 0) throw new MissingArgumentError('list of node IDs is required') + if (!destPath) throw new MissingArgumentError('destPath is required') + if (!template) throw new MissingArgumentError('config templatePath is required') + if (!releaseTag) throw new MissingArgumentError('release tag is required') + + if (!fs.existsSync(path.dirname(destPath))) throw new IllegalArgumentError(`destPath does not exist: ${destPath}`, destPath) + if (!fs.existsSync(template)) throw new IllegalArgumentError(`config templatePath does not exist: ${template}`, destPath) + + const accountIdPrefix = process.env.FST_NODE_ACCOUNT_ID_PREFIX || '0.0' + const accountIdStart = process.env.FST_NODE_ACCOUNT_ID_START || '3' + const internalPort = process.env.FST_NODE_INTERNAL_GOSSIP_PORT || '50111' + const externalPort = process.env.FST_NODE_EXTERNAL_GOSSIP_PORT || '50111' + const ledgerName = process.env.FST_LEDGER_NAME || constants.CLUSTER_NAME + const appName = process.env.FST_HEDERA_APP_NAME || constants.HEDERA_APP_JAR + const nodeStakeAmount = process.env.FST_NODE_DEFAULT_STAKE_AMOUNT || constants.HEDERA_NODE_DEFAULT_STAKE_AMOUNT + + const releaseTagParts = releaseTag.split('.') + if (releaseTagParts.length !== 3) throw new FullstackTestingError(`release tag must have form v.., found ${releaseTagParts}`, 'v..', releaseTag) + const minorVersion = parseInt(releaseTagParts[1], 10) + + return new Promise(async (resolve, reject) => { + try { + const configLines = [] + configLines.push(`swirld, ${ledgerName}`) + configLines.push(`app, ${appName}`) + + let nodeSeq = 0 + let accountIdSeq = parseInt(accountIdStart, 10) + for (const nodeId of nodeIDs) { + const podName = Templates.renderNetworkPodName(nodeId) + const svcName = Templates.renderNetworkSvcName(nodeId) + + const nodeName = nodeId + const nodeNickName = nodeId + + const internalIP = await self.kubectl.getPodIP(podName) + const externalIP = await self.kubectl.getClusterIP(svcName) + + const account = `${accountIdPrefix}.${accountIdSeq}` + if (minorVersion >= 40) { + configLines.push(`address, ${nodeSeq}, ${nodeNickName}, ${nodeName}, ${nodeStakeAmount}, ${internalIP}, ${internalPort}, ${externalIP}, ${externalPort}, ${account}`) + } else { + configLines.push(`address, ${nodeSeq}, ${nodeName}, ${nodeStakeAmount}, ${internalIP}, ${internalPort}, ${externalIP}, ${externalPort}, ${account}`) + } + + nodeSeq += 1 + accountIdSeq += 1 + } - // prepare address book - await this.prepareConfigTxt(nodeIDs, configTxtPath, releaseTag, `${stagingDir}/templates/config.template`) - self.logger.showUser(chalk.green('OK'), `Prepared config.txt: ${configTxtPath}`) + if (minorVersion >= 41) { + configLines.push(`nextNodeId, ${nodeSeq}`) + } - resolve(true) - } catch (e) { - reject(new FullstackTestingError('failed to preparing staging area', e)) - } - }) - } + fs.writeFileSync(destPath, configLines.join('\n')) + + resolve(configLines) + } catch (e) { + reject(new FullstackTestingError('failed to generate config.txt', e)) + } + }) + } + + async prepareStaging (nodeIDs, stagingDir, releaseTag, force = false) { + const self = this + return new Promise(async (resolve, reject) => { + try { + if (!fs.existsSync(stagingDir)) { + fs.mkdirSync(stagingDir, { recursive: true }) + } - async install(podName, buildZipFile, stagingDir, force = false, homeDir = constants.FST_HOME_DIR) { - const self = this - return new Promise(async (resolve, reject) => { - try { - self.logger.showUser(constants.LOG_GROUP_DIVIDER) - self.logger.showUser(chalk.cyan(`Installing platform to ${podName}`)) - - self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying platform: ${buildZipFile} ...`) - await this.copyPlatform(podName, buildZipFile) - self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied platform into network-node: ${buildZipFile}`) - - self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying gossip keys ...`) - await this.copyGossipKeys(podName, stagingDir) - self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied gossip keys`) - - self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying TLS keys ...`) - await this.copyTLSKeys(podName, stagingDir) - self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied TLS keys`) - - self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying auxiliary config files ...`) - await this.copyPlatformConfigFiles(podName, stagingDir) - self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied auxiliary config keys`) - - self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Setting file permissions ...`) - await this.setPlatformDirPermissions(podName) - self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Set file permissions`) - - resolve(true) - } catch (e) { - self.logger.showUserError(e) - reject(e) - } - }) - } -} \ No newline at end of file + const configTxtPath = `${stagingDir}/config.txt` + + // copy a templates from fsnetman resources directory + fs.cpSync(`${constants.RESOURCES_DIR}/templates/`, `${stagingDir}/templates`, { recursive: true }) + + // prepare address book + await this.prepareConfigTxt(nodeIDs, configTxtPath, releaseTag, `${stagingDir}/templates/config.template`) + self.logger.showUser(chalk.green('OK'), `Prepared config.txt: ${configTxtPath}`) + + resolve(true) + } catch (e) { + reject(new FullstackTestingError('failed to preparing staging area', e)) + } + }) + } + + async install (podName, buildZipFile, stagingDir, force = false, homeDir = constants.FST_HOME_DIR) { + const self = this + return new Promise(async (resolve, reject) => { + try { + self.logger.showUser(constants.LOG_GROUP_DIVIDER) + self.logger.showUser(chalk.cyan(`Installing platform to ${podName}`)) + + self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying platform: ${buildZipFile} ...`) + await this.copyPlatform(podName, buildZipFile) + self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied platform into network-node: ${buildZipFile}`) + + self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying gossip keys ...`) + await this.copyGossipKeys(podName, stagingDir) + self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied gossip keys`) + + self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying TLS keys ...`) + await this.copyTLSKeys(podName, stagingDir) + self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied TLS keys`) + + self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Copying auxiliary config files ...`) + await this.copyPlatformConfigFiles(podName, stagingDir) + self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Copied auxiliary config keys`) + + self.logger.showUser(constants.LOG_STATUS_PROGRESS, `[POD=${podName}] Setting file permissions ...`) + await this.setPlatformDirPermissions(podName) + self.logger.showUser(constants.LOG_STATUS_DONE, `[POD=${podName}] Set file permissions`) + + resolve(true) + } catch (e) { + self.logger.showUserError(e) + reject(e) + } + }) + } +} diff --git a/fullstack-network-manager/src/core/shell_runner.mjs b/fullstack-network-manager/src/core/shell_runner.mjs index eb9eb50fb..5fde240e9 100644 --- a/fullstack-network-manager/src/core/shell_runner.mjs +++ b/fullstack-network-manager/src/core/shell_runner.mjs @@ -1,62 +1,61 @@ -import {spawn} from "child_process"; -import chalk from "chalk"; +import { spawn } from 'child_process' +import chalk from 'chalk' export class ShellRunner { - constructor(logger) { - if (!logger ) throw new Error("An instance of core/Logger is required") - this.logger = logger - } + constructor (logger) { + if (!logger) throw new Error('An instance of core/Logger is required') + this.logger = logger + } - /** + /** * Returns a promise that invokes the shell command * @param cmd shell command string * @returns {Promise} console output as an array of strings */ - async run(cmd) { - const self = this - const callStack= new Error().stack // capture the callstack to be included in error - - return new Promise((resolve, reject) => { - const child = spawn(cmd, { - shell: true, - }) - - const output = [] - child.stdout.on('data', d => { - const items = d.toString().split(/\r?\n/) - items.forEach(item => { - if (item) { - output.push(item.trim()) - } - }) - }) - - const errOutput= [] - child.stderr.on('data', d => { - const items = d.toString().split(/\r?\n/) - items.forEach(item => { - if (item) { - errOutput.push(item.trim()) - } - }) - }) - - - child.on('exit', (code, signal) => { - if (code) { - const 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) - }) + async run (cmd) { + const self = this + const callStack = new Error().stack // capture the callstack to be included in error + + return new Promise((resolve, reject) => { + const child = spawn(cmd, { + shell: true + }) + + const output = [] + child.stdout.on('data', d => { + const items = d.toString().split(/\r?\n/) + items.forEach(item => { + if (item) { + output.push(item.trim()) + } }) - } -} \ No newline at end of file + }) + + const errOutput = [] + child.stderr.on('data', d => { + const items = d.toString().split(/\r?\n/) + items.forEach(item => { + if (item) { + errOutput.push(item.trim()) + } + }) + }) + + child.on('exit', (code, signal) => { + if (code) { + const 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/core/templates.mjs b/fullstack-network-manager/src/core/templates.mjs index 232c1fc94..17ce0f288 100644 --- a/fullstack-network-manager/src/core/templates.mjs +++ b/fullstack-network-manager/src/core/templates.mjs @@ -1,24 +1,23 @@ -import {DataValidationError} from "./errors.mjs"; +import { DataValidationError } from './errors.mjs' export class Templates { - static renderNetworkPodName(nodeId) { - return `network-${nodeId}-0` - } + static renderNetworkPodName (nodeId) { + return `network-${nodeId}-0` + } - static renderNetworkSvcName(nodeId) { - return `network-${nodeId}-svc` - } + static renderNetworkSvcName (nodeId) { + return `network-${nodeId}-svc` + } - static extractNodeIdFromPodName(podName) { - const parts = podName.split('-') - if (parts.length !== 3) throw new DataValidationError(`pod name is malformed : ${podName}`, 3, parts.length) - return parts[1].trim() - } - - static prepareReleasePrefix(tag) { - const parsed = tag.split('.') - if (parsed.length < 3) throw new Error(`tag (${tag}) must include major, minor and patch fields (e.g. v0.40.4)`) - return `${parsed[0]}.${parsed[1]}` - } + static extractNodeIdFromPodName (podName) { + const parts = podName.split('-') + if (parts.length !== 3) throw new DataValidationError(`pod name is malformed : ${podName}`, 3, parts.length) + return parts[1].trim() + } + static prepareReleasePrefix (tag) { + const parsed = tag.split('.') + if (parsed.length < 3) throw new Error(`tag (${tag}) must include major, minor and patch fields (e.g. v0.40.4)`) + return `${parsed[0]}.${parsed[1]}` + } } diff --git a/fullstack-network-manager/src/core/zippy.mjs b/fullstack-network-manager/src/core/zippy.mjs index cc76f3310..361d5f8c2 100644 --- a/fullstack-network-manager/src/core/zippy.mjs +++ b/fullstack-network-manager/src/core/zippy.mjs @@ -1,78 +1,78 @@ -import {FullstackTestingError, IllegalArgumentError, MissingArgumentError} from "./errors.mjs"; -import fs from "fs"; -import AdmZip from "adm-zip"; -import chalk from "chalk"; -import path from "path"; +import { FullstackTestingError, IllegalArgumentError, MissingArgumentError } from './errors.mjs' +import fs from 'fs' +import AdmZip from 'adm-zip' +import chalk from 'chalk' +import path from 'path' export class Zippy { - constructor(logger) { - if (!logger ) throw new Error("An instance of core/Logger is required") - this.logger = logger - } + constructor (logger) { + if (!logger) throw new Error('An instance of core/Logger is required') + this.logger = logger + } - /** + /** * Zip a file or directory * @param srcPath path to a file or directory * @param destPath path to the output zip file * @returns {Promise} */ - async zip(srcPath, destPath, verbose = false) { - const self = this + async zip (srcPath, destPath, verbose = false) { + const self = this - if (!srcPath) throw new MissingArgumentError('srcPath is required') - if (!destPath) throw new MissingArgumentError('destPath is required') - if (!destPath.endsWith('.zip')) throw new MissingArgumentError('destPath must be a path to a zip file') + if (!srcPath) throw new MissingArgumentError('srcPath is required') + if (!destPath) throw new MissingArgumentError('destPath is required') + if (!destPath.endsWith('.zip')) throw new MissingArgumentError('destPath must be a path to a zip file') - return new Promise(async (resolve, reject) => { - try { - const zip = AdmZip('', {}) + return new Promise(async (resolve, reject) => { + try { + const zip = AdmZip('', {}) - const stat = fs.statSync(srcPath) - if (stat.isDirectory()) { - zip.addLocalFolder(srcPath, '') - } else { - zip.addFile(path.basename(srcPath), fs.readFileSync(srcPath), '', stat) - } + const stat = fs.statSync(srcPath) + if (stat.isDirectory()) { + zip.addLocalFolder(srcPath, '') + } else { + zip.addFile(path.basename(srcPath), fs.readFileSync(srcPath), '', stat) + } - await zip.writeZipPromise(destPath, {overwrite: true}) + await zip.writeZipPromise(destPath, { overwrite: true }) - resolve(destPath) - } catch (e) { - reject(new FullstackTestingError(`failed to unzip ${srcPath}: ${e.message}`, e)) - } - }) - } - - async unzip(srcPath, destPath, verbose = false) { - const self = this + resolve(destPath) + } catch (e) { + reject(new FullstackTestingError(`failed to unzip ${srcPath}: ${e.message}`, e)) + } + }) + } - if (!srcPath) throw new MissingArgumentError('srcPath is required') - if (!destPath) throw new MissingArgumentError('destPath is required') + async unzip (srcPath, destPath, verbose = false) { + const self = this - if (!fs.existsSync(srcPath)) throw new IllegalArgumentError('srcPath does not exists', srcPath) + if (!srcPath) throw new MissingArgumentError('srcPath is required') + if (!destPath) throw new MissingArgumentError('destPath is required') - return new Promise((resolve, reject) => { - try { - const zip = AdmZip(srcPath, {readEntries: true}) + if (!fs.existsSync(srcPath)) throw new IllegalArgumentError('srcPath does not exists', srcPath) - zip.getEntries().forEach(function (zipEntry) { - if (verbose) { - self.logger.debug(`Extracting file: ${zipEntry.entryName} -> ${destPath}/${zipEntry.entryName} ...`, { - src: zipEntry.entryName, - dst: `${destPath}/${zipEntry.entryName}` - }) - } + return new Promise((resolve, reject) => { + try { + const zip = AdmZip(srcPath, { readEntries: true }) - zip.extractEntryTo(zipEntry, destPath, true, true, true, zipEntry.entryName) - if (verbose) { - self.logger.showUser(chalk.green('OK'), `Extracted: ${zipEntry.entryName} -> ${destPath}/${zipEntry.entryName}`) - } - }); + zip.getEntries().forEach(function (zipEntry) { + if (verbose) { + self.logger.debug(`Extracting file: ${zipEntry.entryName} -> ${destPath}/${zipEntry.entryName} ...`, { + src: zipEntry.entryName, + dst: `${destPath}/${zipEntry.entryName}` + }) + } - resolve(destPath) - } catch (e) { - reject(new FullstackTestingError(`failed to unzip ${srcPath}: ${e.message}`, e)) - } + zip.extractEntryTo(zipEntry, destPath, true, true, true, zipEntry.entryName) + if (verbose) { + self.logger.showUser(chalk.green('OK'), `Extracted: ${zipEntry.entryName} -> ${destPath}/${zipEntry.entryName}`) + } }) - } + + resolve(destPath) + } catch (e) { + reject(new FullstackTestingError(`failed to unzip ${srcPath}: ${e.message}`, e)) + } + }) + } } diff --git a/fullstack-network-manager/src/index.mjs b/fullstack-network-manager/src/index.mjs index 89f8027a9..3565dd7fb 100644 --- a/fullstack-network-manager/src/index.mjs +++ b/fullstack-network-manager/src/index.mjs @@ -1,39 +1,39 @@ -import yargs from 'yargs'; -import {hideBin} from 'yargs/helpers' +import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' import * as commands from './commands/index.mjs' import * as core from './core/index.mjs' -import {ChartManager, ConfigManager} from "./core/index.mjs"; +import { ChartManager, ConfigManager } from './core/index.mjs' -export function main(argv) { - const logger = core.logging.NewLogger('debug') - const kind = new core.Kind(logger) - const helm = new core.Helm(logger) - const kubectl = new core.Kubectl(logger) - const downloader = new core.PackageDownloader(logger) - const platformInstaller = new core.PlatformInstaller(logger, kubectl) - const chartManager = new ChartManager(helm, logger) - const configManager = new ConfigManager(logger) +export function main (argv) { + const logger = core.logging.NewLogger('debug') + const kind = new core.Kind(logger) + const helm = new core.Helm(logger) + const kubectl = new core.Kubectl(logger) + const downloader = new core.PackageDownloader(logger) + const platformInstaller = new core.PlatformInstaller(logger, kubectl) + const chartManager = new ChartManager(helm, logger) + const configManager = new ConfigManager(logger) - const opts = { - logger: logger, - kind: kind, - helm: helm, - kubectl: kubectl, - downloader: downloader, - platformInstaller: platformInstaller, - chartManager: chartManager, - configManager: configManager, - } + const opts = { + logger, + kind, + helm, + kubectl, + downloader, + platformInstaller, + chartManager, + configManager + } - logger.debug("Constants: %s", JSON.stringify(core.constants)) + logger.debug('Constants: %s', JSON.stringify(core.constants)) - return yargs(hideBin(argv)) - .usage(`Usage:\n $0 [options]`) - .alias('h', 'help') - .alias('v', 'version') - .command(commands.Initialize(opts)) - .strict() - .wrap(120) - .demand(1, 'Select a command') - .parse() + return yargs(hideBin(argv)) + .usage('Usage:\n $0 [options]') + .alias('h', 'help') + .alias('v', 'version') + .command(commands.Initialize(opts)) + .strict() + .wrap(120) + .demand(1, 'Select a command') + .parse() }