Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): allow deploying different number network nodes #726

Merged
merged 22 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 301 additions & 28 deletions solo/README.md

Large diffs are not rendered by default.

19 changes: 15 additions & 4 deletions solo/src/commands/cluster.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ export class ClusterCommand extends BaseCommand {
{
title: 'Initialize',
task: async (ctx, task) => {
self.configManager.load(argv)
self.configManager.load()
const prevNamespace = self.configManager.getFlag(flags.namespace)

await prompts.execute(task, self.configManager, [
flags.chartDirectory,
flags.fstChartVersion,
Expand All @@ -65,7 +67,7 @@ export class ClusterCommand extends BaseCommand {

// prepare config
ctx.config = {
namespace: self.configManager.getFlag(flags.namespace) || constants.DEFAULT_NAMESPACE,
namespace: argv[flags.namespace.name] || constants.DEFAULT_NAMESPACE,
chartDir: self.configManager.getFlag(flags.chartDirectory),
deployPrometheusStack: self.configManager.getFlag(flags.deployPrometheusStack),
deployMinio: self.configManager.getFlag(flags.deployMinio),
Expand All @@ -74,6 +76,13 @@ export class ClusterCommand extends BaseCommand {
fstChartVersion: self.configManager.getFlag(flags.fstChartVersion)
}

if (prevNamespace && prevNamespace !== argv[flags.namespace.name]) {
// reset to previous namespace
// this is needed to restore the namespace which user might have set during init
self.configManager.setFlag(flags.namespace, prevNamespace)
self.configManager.persist()
}

self.logger.debug('Prepare ctx.config', { config: ctx.config, argv })

ctx.isChartInstalled = await this.chartManager.isChartInstalled(ctx.config.namespace, constants.FULLSTACK_CLUSTER_SETUP_CHART)
Expand Down Expand Up @@ -116,7 +125,9 @@ export class ClusterCommand extends BaseCommand {
throw e
}

await self.showInstalledChartList(namespace)
if (argv.dev) {
await self.showInstalledChartList(namespace)
}
},
skip: (ctx, _) => ctx.isChartInstalled
}
Expand Down Expand Up @@ -158,7 +169,7 @@ export class ClusterCommand extends BaseCommand {

self.configManager.load(argv)
const clusterName = self.configManager.getFlag(flags.clusterName)
const namespace = argv.namespace || constants.DEFAULT_NAMESPACE
const namespace = argv[flags.namespace.name] || constants.DEFAULT_NAMESPACE
ctx.config = {
clusterName,
namespace
Expand Down
22 changes: 17 additions & 5 deletions solo/src/commands/flags.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@ import * as helpers from '../core/helpers.mjs'
*/
export function setCommandFlags (y, ...commandFlags) {
commandFlags.forEach(flag => {
y.option(flag.name, flag.definition)
setCommandFlag(y, flag, false)
})
}

export function setCommandFlag (y, flag, demand = false) {
if (demand) {
const def = JSON.parse(JSON.stringify(flag.definition))
delete (def.default)
def.demand = demand
y.option(flag.name, def)
} else {
y.option(flag.name, flag.definition)
}
}

export function withDefaultValue (f, value) {
const clone = JSON.parse(JSON.stringify(f))
clone.definition.default = value
Expand Down Expand Up @@ -152,7 +163,7 @@ export const releaseTag = {
name: 'release-tag',
definition: {
describe: 'Release tag to be used (e.g. v0.42.5)',
default: 'v0.42.5',
default: '',
alias: 't',
type: 'string'
}
Expand Down Expand Up @@ -180,7 +191,7 @@ export const nodeIDs = {
name: 'node-ids',
definition: {
describe: 'Comma separated node IDs (empty means all nodes)',
default: 'node0,node1,node2',
default: '',
alias: 'i',
type: 'string'
}
Expand Down Expand Up @@ -268,7 +279,8 @@ export const keyFormat = {
name: 'key-format',
definition: {
describe: 'Public and Private key file format (pem or pfx)',
default: 'pfx'
default: 'pfx',
type: 'string'
}
}

Expand Down Expand Up @@ -311,7 +323,7 @@ export const hederaExplorerTlsHostName = {
export const deletePvcs = {
name: 'delete-pvcs',
definition: {
describe: 'Delete the persistent volume claims, defaults to false',
describe: 'Delete the persistent volume claims',
default: false,
type: 'boolean'
}
Expand Down
20 changes: 11 additions & 9 deletions solo/src/commands/init.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,11 @@ export class InitCommand extends BaseCommand {
title: 'Setup chart manager',
task: async (ctx, _) => {
ctx.repoURLs = await this.chartManager.setup()
}
},
{
title: 'Generate status report',
task: (ctx, _) => {
self.logger.showList('Home Directories', ctx.dirs)
self.logger.showList('Chart Repository', ctx.repoURLs)
self.logger.showJSON('Cached Config', ctx.config)
if (argv.dev) {
self.logger.showList('Home Directories', ctx.dirs)
self.logger.showList('Chart Repository', ctx.repoURLs)
self.logger.showJSON('Cached Config', ctx.config)
}
}
}
], {
Expand All @@ -111,8 +108,13 @@ export class InitCommand extends BaseCommand {
command: 'init',
desc: 'Initialize local environment and default flags',
builder: y => {
const requiredFlags = [flags.namespace.name, flags.releaseTag.name, flags.nodeIDs.name]
for (const flag of flags.allFlags) {
flags.setCommandFlags(y, flag)
if (requiredFlags.includes(flag.name)) {
flags.setCommandFlag(y, flag, true)
} else {
flags.setCommandFlag(y, flag, false)
}
}
},
handler: (argv) => {
Expand Down
42 changes: 22 additions & 20 deletions solo/src/commands/network.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,35 @@ export class NetworkCommand extends BaseCommand {
return valuesArg
}

prepareValuesArg (chartDir, valuesFile, deployMirrorNode, deployHederaExplorer, tlsClusterIssuerType,
enableHederaExplorerTls, namespace, hederaExplorerTlsLoadBalancerIp, hederaExplorerTlsHostName,
enablePrometheusSvcMonitor, releaseTag) {
prepareValuesArg (config = {}) {
let valuesArg = ''
if (chartDir) {
valuesArg = `-f ${chartDir}/fullstack-deployment/values.yaml`
if (config.chartDir) {
valuesArg = `-f ${config.chartDir}/fullstack-deployment/values.yaml`
}

valuesArg += this.prepareValuesFiles(valuesFile)
valuesArg += this.prepareValuesFiles(config.valuesFile)

valuesArg += ` --set hedera-mirror-node.enabled=${deployMirrorNode} --set hedera-explorer.enabled=${deployHederaExplorer}`
valuesArg += ` --set telemetry.prometheus.svcMonitor.enabled=${enablePrometheusSvcMonitor}`
valuesArg += ` --set hedera-mirror-node.enabled=${config.deployMirrorNode} --set hedera-explorer.enabled=${config.deployHederaExplorer}`
valuesArg += ` --set telemetry.prometheus.svcMonitor.enabled=${config.enablePrometheusSvcMonitor}`

if (enableHederaExplorerTls) {
valuesArg += this.getTlsValueArguments(tlsClusterIssuerType, enableHederaExplorerTls, namespace,
hederaExplorerTlsLoadBalancerIp, hederaExplorerTlsHostName)
if (config.enableHederaExplorerTls) {
valuesArg += this.getTlsValueArguments(config.tlsClusterIssuerType, config.enableHederaExplorerTls, config.namespace,
config.hederaExplorerTlsLoadBalancerIp, config.hederaExplorerTlsHostName)
}

if (releaseTag) {
const rootImage = helpers.getRootImageRepository(releaseTag)
if (config.releaseTag) {
const rootImage = helpers.getRootImageRepository(config.releaseTag)
valuesArg += ` --set defaults.root.image.repository=${rootImage}`
}

// prepare name and account IDs for nodes
let i = 0
let accountId = Number.parseInt(constants.HEDERA_NODE_ACCOUNT_ID_START)
config.nodeIds.forEach(nodeId => {
valuesArg += ` --set hedera.nodes[${i}].name=${nodeId},hedera.nodes[${i++}].accountId=0.0.${accountId++}`
})

this.logger.debug('Prepared helm chart values', { valuesArg })
return valuesArg
}

Expand All @@ -95,13 +101,14 @@ export class NetworkCommand extends BaseCommand {
]

this.configManager.load(argv)
this.logger.debug('Loaded cached config', { config: this.configManager.config })
await prompts.execute(task, this.configManager, flagList)

// create a config object for subsequent steps
const config = {
releaseTag: this.configManager.getFlag(flags.releaseTag),
namespace: this.configManager.getFlag(flags.namespace),
nodeIds: this.configManager.getFlag(flags.nodeIDs),
nodeIds: helpers.parseNodeIDs(this.configManager.getFlag(flags.nodeIDs)),
chartDir: this.configManager.getFlag(flags.chartDirectory),
fstChartVersion: this.configManager.getFlag(flags.fstChartVersion),
valuesFile: this.configManager.getFlag(flags.valuesFile),
Expand All @@ -118,12 +125,7 @@ export class NetworkCommand extends BaseCommand {
config.chartPath = await this.prepareChartPath(config.chartDir,
constants.FULLSTACK_TESTING_CHART, constants.FULLSTACK_DEPLOYMENT_CHART)

config.valuesArg = this.prepareValuesArg(config.chartDir,
config.valuesFile, config.deployMirrorNode, config.deployHederaExplorer,
config.tlsClusterIssuerType, config.enableHederaExplorerTls, config.namespace,
config.hederaExplorerTlsLoadBalancerIp, config.hederaExplorerTlsHostName, config.enablePrometheusSvcMonitor,
config.releaseTag
)
config.valuesArg = this.prepareValuesArg(config)

this.logger.debug('Prepared config', { config, cachedConfig: this.configManager.config })
return config
Expand Down
61 changes: 33 additions & 28 deletions solo/src/commands/node.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as fs from 'fs'
import { Listr } from 'listr2'
import path from 'path'
import { FullstackTestingError, IllegalArgumentError } from '../core/errors.mjs'
import * as helpers from '../core/helpers.mjs'
import { sleep } from '../core/helpers.mjs'
import { constants, Templates } from '../core/index.mjs'
import { BaseCommand } from './base.mjs'
Expand Down Expand Up @@ -156,7 +157,7 @@ export class NodeCommand extends BaseCommand {

const config = {
namespace: self.configManager.getFlag(flags.namespace),
nodeIds: self.configManager.getFlag(flags.nodeIDs),
nodeIds: helpers.parseNodeIDs(self.configManager.getFlag(flags.nodeIDs)),
releaseTag: self.configManager.getFlag(flags.releaseTag),
cacheDir: self.configManager.getFlag(flags.cacheDir),
force: self.configManager.getFlag(flags.force),
Expand Down Expand Up @@ -394,7 +395,7 @@ export class NodeCommand extends BaseCommand {

ctx.config = {
namespace: self.configManager.getFlag(flags.namespace),
nodeIds: self.configManager.getFlag(flags.nodeIDs)
nodeIds: helpers.parseNodeIDs(self.configManager.getFlag(flags.nodeIDs))
}

if (!await this.k8.hasNamespace(ctx.config.namespace)) {
Expand Down Expand Up @@ -480,7 +481,7 @@ export class NodeCommand extends BaseCommand {

ctx.config = {
namespace: self.configManager.getFlag(flags.namespace),
nodeIds: self.configManager.getFlag(flags.nodeIDs)
nodeIds: helpers.parseNodeIDs(self.configManager.getFlag(flags.nodeIDs))
}

if (!await this.k8.hasNamespace(ctx.config.namespace)) {
Expand Down Expand Up @@ -544,7 +545,7 @@ export class NodeCommand extends BaseCommand {
])

const config = {
nodeIds: self.configManager.getFlag(flags.nodeIDs),
nodeIds: helpers.parseNodeIDs(self.configManager.getFlag(flags.nodeIDs)),
cacheDir: self.configManager.getFlag(flags.cacheDir),
generateGossipKeys: self.configManager.getFlag(flags.generateGossipKeys),
generateTlsKeys: self.configManager.getFlag(flags.generateTlsKeys),
Expand Down Expand Up @@ -584,21 +585,23 @@ export class NodeCommand extends BaseCommand {
})
}

self.logger.showUser(chalk.green('*** Generated Node Gossip Keys ***'))
for (const entry of nodeKeyFiles.entries()) {
const nodeId = entry[0]
const fileList = entry[1]
if (argv.dev) {
self.logger.showUser(chalk.green('*** Generated Node Gossip Keys ***'))
for (const entry of nodeKeyFiles.entries()) {
const nodeId = entry[0]
const fileList = entry[1]
self.logger.showUser(chalk.cyan('---------------------------------------------------------------------------------------------'))
self.logger.showUser(chalk.cyan(`Node ID: ${nodeId}`))
self.logger.showUser(chalk.cyan('==========================='))
self.logger.showUser(chalk.green('Signing key\t\t:'), chalk.yellow(fileList.signingKeyFiles.privateKeyFile))
self.logger.showUser(chalk.green('Signing certificate\t:'), chalk.yellow(fileList.signingKeyFiles.certificateFile))
self.logger.showUser(chalk.green('Agreement key\t\t:'), chalk.yellow(fileList.agreementKeyFiles.privateKeyFile))
self.logger.showUser(chalk.green('Agreement certificate\t:'), chalk.yellow(fileList.agreementKeyFiles.certificateFile))
self.logger.showUser(chalk.blue('Inspect certificate\t: '), chalk.yellow(`openssl storeutl -noout -text -certs ${fileList.agreementKeyFiles.certificateFile}`))
self.logger.showUser(chalk.blue('Verify certificate\t: '), chalk.yellow(`openssl verify -CAfile ${fileList.signingKeyFiles.certificateFile} ${fileList.agreementKeyFiles.certificateFile}`))
}
self.logger.showUser(chalk.cyan('---------------------------------------------------------------------------------------------'))
self.logger.showUser(chalk.cyan(`Node ID: ${nodeId}`))
self.logger.showUser(chalk.cyan('==========================='))
self.logger.showUser(chalk.green('Signing key\t\t:'), chalk.yellow(fileList.signingKeyFiles.privateKeyFile))
self.logger.showUser(chalk.green('Signing certificate\t:'), chalk.yellow(fileList.signingKeyFiles.certificateFile))
self.logger.showUser(chalk.green('Agreement key\t\t:'), chalk.yellow(fileList.agreementKeyFiles.privateKeyFile))
self.logger.showUser(chalk.green('Agreement certificate\t:'), chalk.yellow(fileList.agreementKeyFiles.certificateFile))
self.logger.showUser(chalk.blue('Inspect certificate\t: '), chalk.yellow(`openssl storeutl -noout -text -certs ${fileList.agreementKeyFiles.certificateFile}`))
self.logger.showUser(chalk.blue('Verify certificate\t: '), chalk.yellow(`openssl verify -CAfile ${fileList.signingKeyFiles.certificateFile} ${fileList.agreementKeyFiles.certificateFile}`))
}
self.logger.showUser(chalk.cyan('---------------------------------------------------------------------------------------------'))
}
},
skip: (ctx, _) => !ctx.config.generateGossipKeys
Expand All @@ -617,19 +620,21 @@ export class NodeCommand extends BaseCommand {
})
}

self.logger.showUser(chalk.green('*** Generated Node TLS Keys ***'))
for (const entry of nodeKeyFiles.entries()) {
const nodeId = entry[0]
const fileList = entry[1]
if (argv.dev) {
self.logger.showUser(chalk.green('*** Generated Node TLS Keys ***'))
for (const entry of nodeKeyFiles.entries()) {
const nodeId = entry[0]
const fileList = entry[1]
self.logger.showUser(chalk.cyan('---------------------------------------------------------------------------------------------'))
self.logger.showUser(chalk.cyan(`Node ID: ${nodeId}`))
self.logger.showUser(chalk.cyan('==========================='))
self.logger.showUser(chalk.green('TLS key\t\t:'), chalk.yellow(fileList.tlsKeyFiles.privateKeyFile))
self.logger.showUser(chalk.green('TLS certificate\t:'), chalk.yellow(fileList.tlsKeyFiles.certificateFile))
self.logger.showUser(chalk.blue('Inspect certificate\t: '), chalk.yellow(`openssl storeutl -noout -text -certs ${fileList.tlsKeyFiles.certificateFile}`))
self.logger.showUser(chalk.blue('Verify certificate\t: '), chalk.yellow(`openssl verify -CAfile ${fileList.tlsKeyFiles.certificateFile} ${fileList.tlsKeyFiles.certificateFile}`))
}
self.logger.showUser(chalk.cyan('---------------------------------------------------------------------------------------------'))
self.logger.showUser(chalk.cyan(`Node ID: ${nodeId}`))
self.logger.showUser(chalk.cyan('==========================='))
self.logger.showUser(chalk.green('TLS key\t\t:'), chalk.yellow(fileList.tlsKeyFiles.privateKeyFile))
self.logger.showUser(chalk.green('TLS certificate\t:'), chalk.yellow(fileList.tlsKeyFiles.certificateFile))
self.logger.showUser(chalk.blue('Inspect certificate\t: '), chalk.yellow(`openssl storeutl -noout -text -certs ${fileList.tlsKeyFiles.certificateFile}`))
self.logger.showUser(chalk.blue('Verify certificate\t: '), chalk.yellow(`openssl verify -CAfile ${fileList.tlsKeyFiles.certificateFile} ${fileList.tlsKeyFiles.certificateFile}`))
}
self.logger.showUser(chalk.cyan('---------------------------------------------------------------------------------------------'))
}
},
skip: (ctx, _) => !ctx.config.generateTlsKeys
Expand Down
13 changes: 1 addition & 12 deletions solo/src/commands/prompts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,15 @@ export async function promptNamespace (task, input) {

export async function promptNodeIds (task, input) {
try {
let nodeIds = []
if (!input) {
nodeIds = []
input = await task.prompt(ListrEnquirerPromptAdapter).run({
type: 'input',
default: 'node0,node1,node2',
message: 'Enter list of node IDs (comma separated list):'
})
}

if (input) {
input.split(',').forEach(item => {
const nodeId = item.trim()
if (nodeId) {
nodeIds.push(nodeId)
}
})
}

return nodeIds
return input
} catch (e) {
throw new FullstackTestingError(`input failed: ${flags.nodeIDs.name}`, e)
}
Expand Down
Loading
Loading