Skip to content

Commit

Permalink
fix(cli): init command should add FST helm chart repository (#527)
Browse files Browse the repository at this point in the history
Signed-off-by: Lenin Mehedy <lenin.mehedy@swirldslabs.com>
  • Loading branch information
leninmehedy authored Nov 13, 2023
1 parent 460a68e commit dc1e748
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 111 deletions.
73 changes: 3 additions & 70 deletions fullstack-network-manager/src/commands/base.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import * as core from "../core/index.mjs"
import chalk from "chalk";
import {ShellRunner} from "../core/shell_runner.mjs";
import {constants} from "../core/index.mjs";

export class BaseCommand extends ShellRunner {
async checkDep(cmd) {
Expand Down Expand Up @@ -71,87 +72,19 @@ export class BaseCommand extends ShellRunner {
return true
}

/**
* List available clusters
* @returns {Promise<string[]>}
*/
async getInstalledCharts(namespaceName) {
try {
return await this.helm.list(`-n ${namespaceName}`, '-q')
} catch (e) {
this.logger.showUserError(e)
}

return []
}

async chartInstall(namespaceName, chartName, chartPath, valuesArg = '') {
try {
const charts = await this.getInstalledCharts(namespaceName)
if (!charts.includes(chartName)) {
this.logger.showUser(chalk.cyan('> running helm dependency update for chart:'), chalk.yellow(`${chartName} ...`))
await this.helm.dependency('update', chartPath)

this.logger.showUser(chalk.cyan('> installing chart:'), chalk.yellow(`${chartName}`))
await this.helm.install(`-n ${namespaceName} ${chartName} ${chartPath} ${valuesArg}`)
this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is installed`)
} else {
this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is already installed`)
}

return true
} catch (e) {
this.logger.showUserError(e)
}

return false
}

async chartUninstall(namespaceName, chartName) {
try {
this.logger.showUser(chalk.cyan('> checking chart:'), chalk.yellow(`${chartName}`))
const charts = await this.getInstalledCharts(namespaceName)
if (charts.includes(chartName)) {
this.logger.showUser(chalk.cyan('> uninstalling chart:'), chalk.yellow(`${chartName}`))
await this.helm.uninstall(`-n ${namespaceName} ${chartName}`)
this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is uninstalled`)
} else {
this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is already uninstalled`)
}

return true
} catch (e) {
this.logger.showUserError(e)
}

return false
}

async chartUpgrade(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 true
} catch (e) {
this.logger.showUserError(e)
}

return false
}

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')

super(opts.logger);

this.kind = opts.kind
this.helm = opts.helm
this.kubectl = opts.kubectl
this.chartManager = opts.chartManager

// map of dependency checks
this.checks = new Map()
Expand Down
24 changes: 15 additions & 9 deletions fullstack-network-manager/src/commands/chart.mjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {BaseCommand} from "./base.mjs";
import * as core from "../core/index.mjs";
import * as flags from "./flags.mjs";
import {constants} from "../core/index.mjs";


export class ChartCommand extends BaseCommand {
chartPath = `${core.constants.FST_HOME_DIR}/full-stack-testing/charts/fullstack-deployment`
chartName = "fullstack-deployment"
chartPath = `full-stack-testing/fullstack-deployment`

prepareValuesArg(argv) {
const {valuesFile, mirrorNode, hederaExplorer} = argv
let valuesArg = `--values ${this.chartPath}/values.yaml`

let valuesArg = ''
if (valuesFile) {
valuesArg += ` --values ${valuesFile}`
valuesArg += `--values ${valuesFile}`
}

valuesArg += ` --set hedera-mirror-node.enabled=${mirrorNode} --set hedera-explorer.enabled=${hederaExplorer}`
Expand All @@ -21,23 +21,29 @@ export class ChartCommand extends BaseCommand {
}

async install(argv) {
const namespace = argv.namespace
const valuesArg = this.prepareValuesArg(argv)
try {
const namespace = argv.namespace
const valuesArg = this.prepareValuesArg(argv)

return await this.chartInstall(namespace, this.chartName, this.chartPath, valuesArg)
await this.chartManager.install(namespace, constants.FST_CHART_DEPLOYMENT_NAME, this.chartPath, valuesArg)

this.logger.showList('charts', await this.chartManager.getInstalledCharts(namespace))
} catch (e) {
this.logger.showUserError(e)
}
}

async uninstall(argv) {
const namespace = argv.namespace

return await this.chartUninstall(namespace, this.chartName)
return await this.chartManager.uninstall(namespace, constants.FST_CHART_DEPLOYMENT_NAME)
}

async upgrade(argv) {
const namespace = argv.namespace
const valuesArg = this.prepareValuesArg(argv)

return await this.chartUpgrade(namespace, this.chartName, this.chartPath, valuesArg)
return await this.chartManager.upgrade(namespace, constants.FST_CHART_DEPLOYMENT_NAME, this.chartPath, valuesArg)
}

static getCommandDefinition(chartCmd) {
Expand Down
29 changes: 8 additions & 21 deletions fullstack-network-manager/src/commands/cluster.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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";

/**
* Define the core functionalities of 'cluster' command
Expand All @@ -21,21 +22,8 @@ export class ClusterCommand extends BaseCommand {
return []
}

showList(itemType, items = []) {
this.logger.showUser(chalk.green(`\n *** List of available ${itemType} ***`))
this.logger.showUser(chalk.green(`---------------------------------------`))
if (items.length > 0) {
items.forEach(name => this.logger.showUser(chalk.yellow(` - ${name}`)))
} else {
this.logger.showUser(chalk.blue(`[ None ]`))
}

this.logger.showUser("\n")
return true
}

async showClusterList(argv) {
this.showList("clusters", await this.getClusters())
this.logger.showList("clusters", await this.getClusters())
return true
}

Expand Down Expand Up @@ -91,7 +79,7 @@ export class ClusterCommand extends BaseCommand {

await this.kubectl.config(`set-context --current --namespace="${namespace}"`)

this.showList("namespaces", await this.getNameSpaces())
this.logger.showList("namespaces", await this.getNameSpaces())

return true
} catch (e) {
Expand Down Expand Up @@ -156,7 +144,7 @@ export class ClusterCommand extends BaseCommand {
this.logger.showUser(chalk.green('OK'), `cluster '${clusterName}' is already deleted`)
}

this.showList('clusters', await this.getClusters())
this.logger.showList('clusters', await this.getClusters())

return true
} catch (e) {
Expand All @@ -168,7 +156,7 @@ export class ClusterCommand extends BaseCommand {


async showInstalledChartList(namespace) {
this.showList("charts installed", await this.getInstalledCharts(namespace))
this.logger.showList("charts installed", await this.chartManager.getInstalledCharts(namespace))
}

/**
Expand All @@ -182,14 +170,13 @@ export class ClusterCommand extends BaseCommand {
await this.create(argv)

const clusterName = argv.clusterName
const chartName = "fullstack-cluster-setup"
const namespace = argv.namespace
const chartPath = `${core.constants.FST_HOME_DIR}/full-stack-testing/charts/${chartName}`
const chartPath = `full-stack-testing/fullstack-cluster-setup`
const valuesArg = this.prepareValuesArg(argv.prometheusStack,
argv.minio, argv.envoyGateway, argv.certManager, argv.certManagerCrds)

this.logger.showUser(chalk.cyan('> setting up cluster:'), chalk.yellow(`${clusterName}`))
await this.chartInstall(namespace, chartName, chartPath, valuesArg)
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, valuesArg)
await this.showInstalledChartList(namespace)

return true
Expand Down
3 changes: 2 additions & 1 deletion fullstack-network-manager/src/commands/init.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export class InitCommand extends BaseCommand {

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))

return status
} catch (e) {
Expand Down
112 changes: 112 additions & 0 deletions fullstack-network-manager/src/core/chart_manager.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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')

this.logger = logger
this.helm = helm
}

/**
* Setup chart repositories
*
* This must be invoked before calling other methods
*
* @param repoURLs a map of name and chart repository URLs
* @param force whether or not to update the repo
* @returns {Promise<boolean>}
*/
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<string[]>}
*/
async getInstalledCharts(namespaceName) {
try {
return await this.helm.list(`-n ${namespaceName}`, '-q')
} catch (e) {
this.logger.showUserError(e)
}

return []
}

async install(namespaceName, chartName, chartPath, valuesArg = '') {
try {
const charts = await this.getInstalledCharts(namespaceName)
if (!charts.includes(chartName)) {
this.logger.showUser(chalk.cyan('> installing chart:'), chalk.yellow(`${chartPath}`))
await this.helm.install(`${chartName} ${chartPath} -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
}

async uninstall(namespaceName, chartName) {
try {
this.logger.showUser(chalk.cyan('> checking chart:'), chalk.yellow(`${chartName}`))
const charts = await this.getInstalledCharts(namespaceName)
if (charts.includes(chartName)) {
this.logger.showUser(chalk.cyan('> uninstalling chart:'), chalk.yellow(`${chartName}`))
await this.helm.uninstall(`-n ${namespaceName} ${chartName}`)
this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is uninstalled`)
} else {
this.logger.showUser(chalk.green('OK'), `chart '${chartName}' is already uninstalled`)
}

return true
} catch (e) {
this.logger.showUserError(e)
}

return false
}

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 true
} catch (e) {
this.logger.showUserError(e)
}

return false
}
}
5 changes: 4 additions & 1 deletion fullstack-network-manager/src/core/constants.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,8 @@ export const constants = {
HEDERA_BUILDS_URL: 'https://builds.hedera.com',
LOG_STATUS_PROGRESS: chalk.cyan('>>'),
LOG_STATUS_DONE: chalk.green('OK'),
LOG_GROUP_DIVIDER: chalk.yellow('----------------------------------------------------------------------------')
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',
}
12 changes: 11 additions & 1 deletion fullstack-network-manager/src/core/helm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,14 @@ export class Helm extends ShellRunner {
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<Array>} console output as an array of strings
*/
async repo(subCommand, ...args) {
return this.run(this.prepareCommand('repo', subCommand, ...args))
}
}
2 changes: 2 additions & 0 deletions fullstack-network-manager/src/core/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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";

// Expose components from the core module
export {
Expand All @@ -19,4 +20,5 @@ export {
PlatformInstaller,
Zippy,
Templates,
ChartManager,
}
Loading

0 comments on commit dc1e748

Please sign in to comment.