@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
234 lines • 10.6 kB
JavaScript
/**
* SPDX-License-Identifier: Apache-2.0
*/
import { Listr } from 'listr2';
import { SoloError } from '../core/errors.js';
import { BaseCommand } from './base.js';
import { Flags as flags } from './flags.js';
import * as constants from '../core/constants.js';
import chalk from 'chalk';
import { ListrRemoteConfig } from '../core/config/remote/listr_config_tasks.js';
import { ClusterCommandTasks } from './cluster/tasks.js';
import { ErrorMessages } from '../core/error_messages.js';
import { splitFlagInput } from '../core/helpers.js';
import { container } from 'tsyringe-neo';
import { InjectTokens } from '../core/dependency_injection/inject_tokens.js';
export class DeploymentCommand extends BaseCommand {
tasks;
constructor(opts) {
super(opts);
this.tasks = new ClusterCommandTasks(this, this.k8Factory);
}
static get DEPLOY_FLAGS_LIST() {
return [
flags.quiet,
flags.context,
flags.namespace,
flags.clusterRef,
flags.userEmailAddress,
flags.deployment,
flags.deploymentClusters,
flags.nodeAliasesUnparsed,
];
}
static get LIST_DEPLOYMENTS_FLAGS_LIST() {
return [flags.quiet, flags.clusterRef];
}
async create(argv) {
const self = this;
const tasks = new Listr([
{
title: 'Initialize',
task: async (ctx, task) => {
self.configManager.update(argv);
self.logger.debug('Updated config with argv', { config: self.configManager.config });
await self.configManager.executePrompt(task, [
flags.namespace,
flags.deployment,
flags.deploymentClusters,
flags.nodeAliasesUnparsed,
]);
const deploymentName = self.configManager.getFlag(flags.deployment);
if (self.localConfig.deployments && self.localConfig.deployments[deploymentName]) {
throw new SoloError(ErrorMessages.DEPLOYMENT_NAME_ALREADY_EXISTS(deploymentName));
}
ctx.config = {
namespace: self.configManager.getFlag(flags.namespace),
deployment: self.configManager.getFlag(flags.deployment),
deploymentClusters: splitFlagInput(self.configManager.getFlag(flags.deploymentClusters)),
nodeAliases: splitFlagInput(self.configManager.getFlag(flags.nodeAliasesUnparsed)),
clusterRef: self.configManager.getFlag(flags.clusterRef),
context: self.configManager.getFlag(flags.context),
email: self.configManager.getFlag(flags.userEmailAddress),
};
self.logger.debug('Prepared config', { config: ctx.config, cachedConfig: self.configManager.config });
},
},
this.setupHomeDirectoryTask(),
this.localConfig.promptLocalConfigTask(self.k8Factory),
{
title: 'Add new deployment to local config',
task: async (ctx, task) => {
const { deployments } = this.localConfig;
const { deployment, namespace: configNamespace, deploymentClusters } = ctx.config;
deployments[deployment] = {
namespace: configNamespace.name,
clusters: deploymentClusters,
};
this.localConfig.setDeployments(deployments);
await this.localConfig.write();
},
},
this.tasks.selectContext(),
{
title: 'Validate context',
task: async (ctx, task) => {
ctx.config.context = ctx.config.context ?? self.configManager.getFlag(flags.context);
const availableContexts = self.k8Factory.default().contexts().list();
if (availableContexts.includes(ctx.config.context)) {
task.title += chalk.green(`- validated context ${ctx.config.context}`);
return;
}
throw new SoloError(`Context with name ${ctx.config.context} not found, available contexts include ${availableContexts.join(', ')}`);
},
},
this.tasks.updateLocalConfig(),
{
title: 'Validate cluster connections',
task: async (ctx, task) => {
const subTasks = [];
for (const cluster of self.localConfig.deployments[ctx.config.deployment].clusters) {
const context = self.localConfig.clusterRefs?.[cluster];
if (!context)
continue;
subTasks.push({
title: `Testing connection to cluster: ${chalk.cyan(cluster)}`,
task: async (_, task) => {
if (!(await self.k8Factory.default().contexts().testContextConnection(context))) {
task.title = `${task.title} - ${chalk.red('Cluster connection failed')}`;
throw new SoloError(`Cluster connection failed for: ${cluster}`);
}
},
});
}
return task.newListr(subTasks, {
concurrent: true,
rendererOptions: { collapseSubtasks: false },
});
},
},
ListrRemoteConfig.createRemoteConfigInMultipleClusters(this, argv),
], {
concurrent: false,
rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
});
try {
await tasks.run();
}
catch (e) {
throw new SoloError('Error creating deployment', e);
}
return true;
}
async list(argv) {
const self = this;
const tasks = new Listr([
{
title: 'Initialize',
task: async (ctx, task) => {
self.configManager.update(argv);
self.logger.debug('Updated config with argv', { config: self.configManager.config });
await self.configManager.executePrompt(task, [flags.clusterRef]);
ctx.config = {
clusterName: self.configManager.getFlag(flags.clusterRef),
};
self.logger.debug('Prepared config', { config: ctx.config, cachedConfig: self.configManager.config });
},
},
{
title: 'Validate context',
task: async (ctx) => {
const clusterName = ctx.config.clusterName;
const context = self.localConfig.clusterRefs[clusterName];
self.k8Factory.default().contexts().updateCurrent(context);
const namespaces = await self.k8Factory.default().namespaces().list();
const namespacesWithRemoteConfigs = [];
for (const namespace of namespaces) {
const isFound = await container
.resolve(InjectTokens.ClusterChecks)
.isRemoteConfigPresentInNamespace(namespace);
if (isFound)
namespacesWithRemoteConfigs.push(namespace.name);
}
self.logger.showList(`Deployments inside cluster: ${chalk.cyan(clusterName)}`, namespacesWithRemoteConfigs);
},
},
], {
concurrent: false,
rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
});
try {
await tasks.run();
}
catch (e) {
throw new SoloError(`Error installing chart ${constants.SOLO_DEPLOYMENT_CHART}`, e);
}
return true;
}
getCommandDefinition() {
const self = this;
return {
command: 'deployment',
desc: 'Manage solo network deployment',
builder: yargs => {
return yargs
.command({
command: 'create',
desc: 'Creates solo deployment',
builder: y => flags.setCommandFlags(y, ...DeploymentCommand.DEPLOY_FLAGS_LIST),
handler: argv => {
self.logger.info("==== Running 'deployment create' ===");
self.logger.info(argv);
self
.create(argv)
.then(r => {
self.logger.info('==== Finished running `deployment create`====');
if (!r)
process.exit(1);
})
.catch(err => {
self.logger.showUserError(err);
process.exit(1);
});
},
})
.command({
command: 'list',
desc: 'List solo deployments inside a cluster',
builder: y => flags.setCommandFlags(y, ...DeploymentCommand.LIST_DEPLOYMENTS_FLAGS_LIST),
handler: argv => {
self.logger.info("==== Running 'deployment list' ===");
self.logger.info(argv);
self
.list(argv)
.then(r => {
self.logger.info('==== Finished running `deployment list`====');
if (!r)
process.exit(1);
})
.catch(err => {
self.logger.showUserError(err);
process.exit(1);
});
},
})
.demandCommand(1, 'Select a chart command');
},
};
}
close() {
// no-op
return Promise.resolve();
}
}
//# sourceMappingURL=deployment.js.map