Skip to content

Commit e8fe739

Browse files
instamentaIvo-Yankovjeromy-cannon
authored
feat: Create remote config (#862)
Signed-off-by: Ivo Yankov <[email protected]> Signed-off-by: instamenta <[email protected]> Signed-off-by: Jeromy Cannon <[email protected]> Co-authored-by: Ivo Yankov <[email protected]> Co-authored-by: Jeromy Cannon <[email protected]>
1 parent 275a6a4 commit e8fe739

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3275
-158
lines changed

package-lock.json

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
"inquirer": "^12.1.0",
6464
"ip": "^2.0.1",
6565
"js-base64": "^3.7.7",
66-
"js-yaml": "^4.1.0",
6766
"jsdoc": "^4.0.4",
6867
"listr2": "^8.2.5",
6968
"semver": "^7.6.3",
@@ -98,6 +97,7 @@
9897
"@types/stream-buffers": "^3.0.7",
9998
"@types/tar": "^6.1.13",
10099
"@types/uuid": "^10.0.0",
100+
"@types/ws": "^8.5.13",
101101
"@types/yargs": "^17.0.33",
102102
"@typescript-eslint/utils": "^8.17.0",
103103
"c8": "^10.1.2",

src/commands/base.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@
1818
import paths from 'path';
1919
import {MissingArgumentError} from '../core/errors.js';
2020
import {ShellRunner} from '../core/shell_runner.js';
21-
import type {ChartManager, ConfigManager, Helm, K8, DependencyManager, LeaseManager} from '../core/index.js';
21+
import type {
22+
ChartManager,
23+
ConfigManager,
24+
Helm,
25+
K8,
26+
DependencyManager,
27+
LeaseManager,
28+
RemoteConfigManager,
29+
LocalConfig,
30+
} from '../core/index.js';
2231
import type {CommandFlag, Opts} from '../types/index.js';
23-
import {type LocalConfig} from '../core/config/local_config.js';
2432

2533
export class BaseCommand extends ShellRunner {
2634
protected readonly helm: Helm;
@@ -31,6 +39,7 @@ export class BaseCommand extends ShellRunner {
3139
protected readonly leaseManager: LeaseManager;
3240
protected readonly _configMaps = new Map<string, any>();
3341
protected readonly localConfig: LocalConfig;
42+
protected readonly remoteConfigManager: RemoteConfigManager;
3443

3544
constructor(opts: Opts) {
3645
if (!opts || !opts.logger) throw new Error('An instance of core/SoloLogger is required');
@@ -40,6 +49,8 @@ export class BaseCommand extends ShellRunner {
4049
if (!opts || !opts.configManager) throw new Error('An instance of core/ConfigManager is required');
4150
if (!opts || !opts.depManager) throw new Error('An instance of core/DependencyManager is required');
4251
if (!opts || !opts.localConfig) throw new Error('An instance of core/LocalConfig is required');
52+
if (!opts || !opts.remoteConfigManager)
53+
throw new Error('An instance of core/config/RemoteConfigManager is required');
4354

4455
super(opts.logger);
4556

@@ -50,6 +61,7 @@ export class BaseCommand extends ShellRunner {
5061
this.depManager = opts.depManager;
5162
this.leaseManager = opts.leaseManager;
5263
this.localConfig = opts.localConfig;
64+
this.remoteConfigManager = opts.remoteConfigManager;
5365
}
5466

5567
async prepareChartPath(chartDir: string, chartRepo: string, chartReleaseName: string) {

src/commands/context/handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class ContextCommandHandlers implements CommandHandlers {
3636
const action = helpers.commandActionBuilder(
3737
[
3838
this.tasks.initialize(argv),
39-
this.parent.getLocalConfig().promptLocalConfigTask(this.parent.getK8(), argv),
39+
this.parent.getLocalConfig().promptLocalConfigTask(),
4040
this.tasks.updateLocalConfig(argv),
4141
],
4242
{

src/commands/context/tasks.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,25 @@ export class ContextCommandTasks {
3535
const isQuiet = !!argv[flags.quiet.name];
3636

3737
let currentDeploymentName = argv[flags.namespace.name];
38-
let clusterAliases = Templates.parseClusterAliases(argv[flags.clusterName.name]);
38+
let clusters = Templates.parseClusterAliases(argv[flags.clusterName.name]);
3939
let contextName = argv[flags.context.name];
4040

4141
const kubeContexts = await this.parent.getK8().getContexts();
4242

4343
if (isQuiet) {
4444
const currentCluster = await this.parent.getK8().getKubeConfig().getCurrentCluster();
45-
if (!clusterAliases.length) clusterAliases = [currentCluster.name];
45+
if (!clusters.length) clusters = [currentCluster.name];
4646
if (!contextName) contextName = await this.parent.getK8().getKubeConfig().getCurrentContext();
4747

4848
if (!currentDeploymentName) {
4949
const selectedContext = kubeContexts.find(e => e.name === contextName);
5050
currentDeploymentName = selectedContext && selectedContext.namespace ? selectedContext.namespace : 'default';
5151
}
5252
} else {
53-
if (!clusterAliases.length) {
53+
if (!clusters.length) {
5454
const prompt = this.promptMap.get(flags.clusterName.name);
55-
const unparsedClusterAliases = await prompt(task, clusterAliases);
56-
clusterAliases = Templates.parseClusterAliases(unparsedClusterAliases);
55+
const unparsedClusterAliases = await prompt(task, clusters);
56+
clusters = Templates.parseClusterAliases(unparsedClusterAliases);
5757
}
5858
if (!contextName) {
5959
const prompt = this.promptMap.get(flags.context.name);
@@ -74,13 +74,13 @@ export class ContextCommandTasks {
7474

7575
// Set clusters for active deployment
7676
const deployments = this.parent.getLocalConfig().deployments;
77-
deployments[currentDeploymentName].clusterAliases = clusterAliases;
77+
deployments[currentDeploymentName].clusters = clusters;
7878
this.parent.getLocalConfig().setDeployments(deployments);
7979

8080
this.parent.getK8().getKubeConfig().setCurrentContext(contextName);
8181

8282
this.parent.logger.info(
83-
`Save LocalConfig file: [currentDeploymentName: ${currentDeploymentName}, contextName: ${contextName}, clusterAliases: ${clusterAliases.join(' ')}]`,
83+
`Save LocalConfig file: [currentDeploymentName: ${currentDeploymentName}, contextName: ${contextName}, clusters: ${clusters.join(' ')}]`,
8484
);
8585
await this.parent.getLocalConfig().write();
8686
});

src/commands/deployment.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* Copyright (C) 2024 Hedera Hashgraph, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the ""License"");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an ""AS IS"" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
import {Listr, type ListrTaskWrapper} from 'listr2';
18+
import {SoloError} from '../core/errors.js';
19+
import {BaseCommand} from './base.js';
20+
import * as flags from './flags.js';
21+
import {constants, Templates} from '../core/index.js';
22+
import * as prompts from './prompts.js';
23+
import chalk from 'chalk';
24+
import {RemoteConfigTasks} from '../core/config/remote/remote_config_tasks.js';
25+
import {ListrLease} from '../core/lease/listr_lease.js';
26+
import type {Namespace} from '../core/config/remote/types.js';
27+
import type {CommandFlag, ContextClusterStructure} from '../types/index.js';
28+
29+
export class DeploymentCommand extends BaseCommand {
30+
private static get DEPLOY_FLAGS_LIST(): CommandFlag[] {
31+
return [
32+
flags.quiet,
33+
flags.namespace,
34+
flags.userEmailAddress,
35+
flags.deploymentClusters,
36+
flags.contextClusterUnparsed,
37+
];
38+
}
39+
40+
private async create(argv: any): Promise<boolean> {
41+
const self = this;
42+
const lease = await self.leaseManager.create();
43+
44+
interface Config {
45+
namespace: Namespace;
46+
contextClusterUnparsed: string;
47+
contextCluster: ContextClusterStructure;
48+
}
49+
interface Context {
50+
config: Config;
51+
}
52+
53+
const tasks = new Listr<Context>(
54+
[
55+
{
56+
title: 'Initialize',
57+
task: async (ctx, task): Promise<Listr<Context, any, any>> => {
58+
self.configManager.update(argv);
59+
self.logger.debug('Loaded cached config', {config: self.configManager.config});
60+
61+
await prompts.execute(task, self.configManager, DeploymentCommand.DEPLOY_FLAGS_LIST);
62+
63+
ctx.config = {
64+
contextClusterUnparsed: self.configManager.getFlag<string>(flags.contextClusterUnparsed),
65+
namespace: self.configManager.getFlag<Namespace>(flags.namespace),
66+
} as Config;
67+
68+
ctx.config.contextCluster = Templates.parseContextCluster(ctx.config.contextClusterUnparsed);
69+
70+
const namespace = ctx.config.namespace;
71+
72+
if (!(await self.k8.hasNamespace(namespace))) {
73+
await self.k8.createNamespace(namespace);
74+
}
75+
76+
self.logger.debug('Prepared config', {config: ctx.config, cachedConfig: self.configManager.config});
77+
78+
return ListrLease.newAcquireLeaseTask(lease, task);
79+
},
80+
},
81+
this.localConfig.promptLocalConfigTask(),
82+
{
83+
title: 'Validate cluster connections',
84+
task: async (ctx, task): Promise<Listr<Context, any, any>> => {
85+
const subTasks = [];
86+
87+
for (const cluster of Object.keys(ctx.config.contextCluster)) {
88+
subTasks.push({
89+
title: `Testing connection to cluster: ${chalk.cyan(cluster)}`,
90+
task: async (_: Context, task: ListrTaskWrapper<Context, any, any>) => {
91+
if (!(await self.k8.testClusterConnection(cluster))) {
92+
task.title = `${task.title} - ${chalk.red('Cluster connection failed')}`;
93+
94+
throw new SoloError(`Cluster connection failed for: ${cluster}`);
95+
}
96+
},
97+
});
98+
}
99+
100+
return task.newListr(subTasks, {
101+
concurrent: true,
102+
rendererOptions: {collapseSubtasks: false},
103+
});
104+
},
105+
},
106+
RemoteConfigTasks.createRemoteConfig.bind(this)(),
107+
],
108+
{
109+
concurrent: false,
110+
rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION,
111+
},
112+
);
113+
114+
try {
115+
await tasks.run();
116+
} catch (e: Error | any) {
117+
throw new SoloError(`Error installing chart ${constants.SOLO_DEPLOYMENT_CHART}`, e);
118+
} finally {
119+
await lease.release();
120+
}
121+
122+
return true;
123+
}
124+
125+
public getCommandDefinition(): {command: string; desc: string; builder: Function} {
126+
const self = this;
127+
return {
128+
command: 'deployment',
129+
desc: 'Manage solo network deployment',
130+
builder: (yargs: any): any => {
131+
return yargs
132+
.command({
133+
command: 'create',
134+
desc: 'Creates solo deployment',
135+
builder: (y: any) => flags.setCommandFlags(y, ...DeploymentCommand.DEPLOY_FLAGS_LIST),
136+
handler: (argv: any) => {
137+
self.logger.debug("==== Running 'deployment create' ===");
138+
self.logger.debug(argv);
139+
140+
self
141+
.create(argv)
142+
.then(r => {
143+
self.logger.debug('==== Finished running `deployment create`====');
144+
145+
if (!r) process.exit(1);
146+
})
147+
.catch(err => {
148+
self.logger.showUserError(err);
149+
process.exit(1);
150+
});
151+
},
152+
})
153+
.demandCommand(1, 'Select a chart command');
154+
},
155+
};
156+
}
157+
}

src/commands/flags.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,6 @@ export const userEmailAddress: CommandFlag = {
745745
name: 'email',
746746
definition: {
747747
describe: 'User email address used for local configuration',
748-
defaultValue: '',
749748
type: 'string',
750749
},
751750
};
@@ -760,22 +759,11 @@ export const context: CommandFlag = {
760759
},
761760
};
762761

763-
export const deploymentName: CommandFlag = {
764-
constName: 'deploymentName',
765-
name: 'deployment-name',
766-
definition: {
767-
describe: 'Solo deployment name',
768-
defaultValue: '',
769-
type: 'string',
770-
},
771-
};
772-
773762
export const deploymentClusters: CommandFlag = {
774763
constName: 'deploymentClusters',
775764
name: 'deployment-clusters',
776765
definition: {
777766
describe: 'Solo deployment cluster list (comma separated)',
778-
defaultValue: '',
779767
type: 'string',
780768
},
781769
};
@@ -855,6 +843,17 @@ export const stakeAmounts: CommandFlag = {
855843
},
856844
};
857845

846+
export const contextClusterUnparsed: CommandFlag = {
847+
constName: 'contextClusterUnparsed',
848+
name: 'context-cluster',
849+
definition: {
850+
describe:
851+
'Context cluster mapping where context is key = value is cluster and comma delimited if more than one, ' +
852+
'(e.g.: --context-cluster kind-solo=kind-solo,kind-solo-2=kind-solo-2)',
853+
type: 'string',
854+
},
855+
};
856+
858857
export const allFlags: CommandFlag[] = [
859858
accountId,
860859
amount,
@@ -869,14 +868,14 @@ export const allFlags: CommandFlag[] = [
869868
chartDirectory,
870869
clusterName,
871870
clusterSetupNamespace,
871+
context,
872872
deletePvcs,
873873
deleteSecrets,
874874
deployCertManager,
875875
deployCertManagerCrds,
876876
deployHederaExplorer,
877877
deployJsonRpcRelay,
878878
deploymentClusters,
879-
deploymentName,
880879
deployMinio,
881880
deployPrometheusStack,
882881
devMode,
@@ -931,6 +930,7 @@ export const allFlags: CommandFlag[] = [
931930
grpcWebTlsCertificatePath,
932931
grpcTlsKeyPath,
933932
grpcWebTlsKeyPath,
933+
contextClusterUnparsed,
934934
];
935935

936936
/** Resets the definition.disablePrompt for all flags */

0 commit comments

Comments
 (0)