Skip to content

Commit 59b156c

Browse files
committed
polishing and cleaning up the profile_manager, adding comments, aliases and keeping it D.R.Y.
Signed-off-by: instamenta <[email protected]>
1 parent 45c69a0 commit 59b156c

File tree

3 files changed

+83
-91
lines changed

3 files changed

+83
-91
lines changed

src/commands/network.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,6 @@ export class NetworkCommand extends BaseCommand {
201201
valuesArg += this.prepareValuesFiles(config.valuesFile);
202202
}
203203

204-
valuesArg += `--set "hedera.configMaps.genesisNetworkJson=${config.genesisNetworkData.toJSON()}"`;
205-
206204
this.logger.debug('Prepared helm chart values', {valuesArg});
207205
return valuesArg;
208206
}
@@ -687,14 +685,6 @@ export class NetworkCommand extends BaseCommand {
687685
return true;
688686
}
689687

690-
prepareGenesisNetworkJson(config: NetworkDeployConfigClass): string {
691-
const data = {network: {nodes: []}};
692-
693-
// TODO
694-
695-
return JSON.stringify(data);
696-
}
697-
698688
getCommandDefinition(): {command: string; desc: string; builder: CommandBuilder} {
699689
const self = this;
700690
return {
@@ -777,6 +767,7 @@ export class NetworkCommand extends BaseCommand {
777767
},
778768
};
779769
}
770+
780771
/** Adds the consensus node, envoy and haproxy components to remote config. */
781772
public addNodesAndProxies(): ListrTask<any, any, any> {
782773
return {

src/core/profile_manager.ts

Lines changed: 78 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ import {SoloError, IllegalArgumentError, MissingArgumentError} from './errors.js
2020
import * as yaml from 'yaml';
2121
import dot from 'dot-object';
2222
import * as semver from 'semver';
23-
import type {SemVer} from 'semver';
2423
import {readFile, writeFile} from 'fs/promises';
2524

2625
import {Flags as flags} from '../commands/flags.js';
2726
import {Templates} from './templates.js';
2827
import * as constants from './constants.js';
2928
import {type ConfigManager} from './config_manager.js';
3029
import * as helpers from './helpers.js';
31-
import {getNodeAccountMap, parseIpAddressToUint8Array} from './helpers.js';
30+
import {getNodeAccountMap} from './helpers.js';
31+
import type {SemVer} from 'semver';
3232
import type {SoloLogger} from './logging.js';
33-
import type {NodeAlias, NodeAliases} from '../types/aliases.js';
34-
import {type GenesisNetworkDataConstructor} from './models/genesisNetworkDataConstructor.js';
33+
import type {AnyObject, NodeAlias, NodeAliases, Path} from '../types/aliases.js';
34+
import type {GenesisNetworkDataConstructor} from './models/genesisNetworkDataConstructor.js';
3535

3636
const consensusSidecars = [
3737
'recordStreamUploader',
@@ -46,10 +46,10 @@ export class ProfileManager {
4646
private readonly configManager: ConfigManager;
4747
private readonly cacheDir: string;
4848

49-
private profiles: Map<string, object>;
49+
private profiles: Map<string, AnyObject>;
5050
private profileFile: string | undefined;
5151

52-
constructor(logger: SoloLogger, configManager: ConfigManager, cacheDir: string = constants.SOLO_VALUES_DIR) {
52+
constructor(logger: SoloLogger, configManager: ConfigManager, cacheDir = constants.SOLO_VALUES_DIR) {
5353
if (!logger) throw new MissingArgumentError('An instance of core/SoloLogger is required');
5454
if (!configManager) throw new MissingArgumentError('An instance of core/ConfigManager is required');
5555

@@ -62,7 +62,15 @@ export class ProfileManager {
6262
this.cacheDir = cacheDir;
6363
}
6464

65-
loadProfiles(forceReload = false): Map<string, object> {
65+
/**
66+
* Load profiles from a profile file and populate the profiles map.
67+
*
68+
* @param [forceReload = false] - forces the profiles map to override even if it exists.
69+
* @returns reference to the populated profiles map.
70+
*
71+
* @throws {IllegalArgumentError} if the profile file is not found.
72+
*/
73+
loadProfiles(forceReload = false): Map<string, AnyObject> {
6674
const profileFile = this.configManager.getFlag<string>(flags.profileFile);
6775
if (!profileFile) throw new MissingArgumentError('profileFile is required');
6876

@@ -76,7 +84,7 @@ export class ProfileManager {
7684
// load profile file
7785
this.profiles = new Map();
7886
const yamlData = fs.readFileSync(profileFile, 'utf8');
79-
const profileItems = yaml.parse(yamlData) as Record<string, object>;
87+
const profileItems = yaml.parse(yamlData) as Record<string, AnyObject>;
8088

8189
// add profiles
8290
for (const key in profileItems) {
@@ -89,15 +97,25 @@ export class ProfileManager {
8997
return this.profiles;
9098
}
9199

92-
getProfile(profileName: string): object {
100+
/**
101+
* Get profile from the profiles map, loads them on demand if they are not loaded already.
102+
*
103+
* @param profileName - profile name (key in the map).
104+
* @returns the profile.
105+
*
106+
* @throws {IllegalArgumentError} if profiles can't be loaded or the profile name is not found in the map.
107+
*/
108+
getProfile(profileName: string): AnyObject {
93109
if (!profileName) throw new MissingArgumentError('profileName is required');
94110
if (!this.profiles || this.profiles.size <= 0) {
95111
this.loadProfiles();
96112
}
97113

98-
if (!this.profiles || !this.profiles.has(profileName))
114+
if (!this.profiles || !this.profiles.has(profileName)) {
99115
throw new IllegalArgumentError(`Profile does not exists with name: ${profileName}`);
100-
return this.profiles.get(profileName) as object;
116+
}
117+
118+
return this.profiles.get(profileName) as AnyObject;
101119
}
102120

103121
/**
@@ -107,7 +125,7 @@ export class ProfileManager {
107125
* @param yamlRoot - root of the YAML object
108126
* @returns
109127
*/
110-
_setValue(itemPath: string, value: any, yamlRoot: object): object {
128+
_setValue(itemPath: string, value: any, yamlRoot: AnyObject): AnyObject {
111129
// find the location where to set the value in the YAML
112130
const itemPathParts: string[] = itemPath.split('.');
113131
let parent = yamlRoot;
@@ -116,7 +134,7 @@ export class ProfileManager {
116134
for (let itemPathPart of itemPathParts) {
117135
if (helpers.isNumeric(itemPathPart)) {
118136
// @ts-ignore
119-
itemPathPart = Number.parseInt(itemPathPart); // numeric path part can only be array index i.e. an integer
137+
itemPathPart = Number.parseInt(itemPathPart); // numeric path part can only be array index i.e., an integer
120138
if (!Array.isArray(parent[prevItemPath])) {
121139
parent[prevItemPath] = [];
122140
}
@@ -145,12 +163,12 @@ export class ProfileManager {
145163

146164
/**
147165
* Set items for the chart
148-
* @param itemPath - item path in the yaml, if empty then root of the yaml object will be used
166+
* @param itemPath - item path in the YAML, if empty then root of the YAML object will be used
149167
* @param items - the element object
150-
* @param yamlRoot - root of the yaml object to update
168+
* @param yamlRoot - root of the YAML object to update
151169
* @private
152170
*/
153-
_setChartItems(itemPath: string, items: any, yamlRoot: object) {
171+
_setChartItems(itemPath: string, items: any, yamlRoot: AnyObject) {
154172
if (!items) return;
155173

156174
const dotItems = dot.dot(items);
@@ -172,11 +190,11 @@ export class ProfileManager {
172190
}
173191

174192
resourcesForConsensusPod(
175-
profile: any,
193+
profile: AnyObject,
176194
nodeAliases: NodeAliases,
177-
yamlRoot: object,
195+
yamlRoot: AnyObject,
178196
genesisNetworkData?: GenesisNetworkDataConstructor,
179-
): object {
197+
): AnyObject {
180198
if (!profile) throw new MissingArgumentError('profile is required');
181199

182200
const accountMap = getNodeAccountMap(nodeAliases);
@@ -266,26 +284,26 @@ export class ProfileManager {
266284
return yamlRoot;
267285
}
268286

269-
resourcesForHaProxyPod(profile: any, yamlRoot: object) {
287+
private resourcesForHaProxyPod(profile: AnyObject, yamlRoot: AnyObject) {
270288
if (!profile) throw new MissingArgumentError('profile is required');
271289
if (!profile.haproxy) return; // use chart defaults
272290

273291
return this._setChartItems('defaults.haproxy', profile.haproxy, yamlRoot);
274292
}
275293

276-
resourcesForEnvoyProxyPod(profile: any, yamlRoot: object) {
294+
private resourcesForEnvoyProxyPod(profile: AnyObject, yamlRoot: AnyObject) {
277295
if (!profile) throw new MissingArgumentError('profile is required');
278296
if (!profile.envoyProxy) return; // use chart defaults
279297
return this._setChartItems('defaults.envoyProxy', profile.envoyProxy, yamlRoot);
280298
}
281299

282-
resourcesForHederaExplorerPod(profile: any, yamlRoot: object) {
300+
private resourcesForHederaExplorerPod(profile: AnyObject, yamlRoot: AnyObject) {
283301
if (!profile) throw new MissingArgumentError('profile is required');
284302
if (!profile.explorer) return;
285303
return this._setChartItems('', profile.explorer, yamlRoot);
286304
}
287305

288-
resourcesForMinioTenantPod(profile: any, yamlRoot: object) {
306+
private resourcesForMinioTenantPod(profile: AnyObject, yamlRoot: AnyObject) {
289307
if (!profile) throw new MissingArgumentError('profile is required');
290308
// @ts-ignore
291309
if (!profile.minio || !profile.minio.tenant) return; // use chart defaults
@@ -306,11 +324,11 @@ export class ProfileManager {
306324

307325
/**
308326
* Prepare a values file for Solo Helm chart
309-
* @param profileName resource profile name
310-
* @param genesisNetworkData
327+
* @param profileName - resource profile name
328+
* @param genesisNetworkData - reference to the constructor
311329
* @returns return the full path to the values file
312330
*/
313-
prepareValuesForSoloChart(profileName: string, genesisNetworkData?: GenesisNetworkDataConstructor) {
331+
public async prepareValuesForSoloChart(profileName: string, genesisNetworkData?: GenesisNetworkDataConstructor) {
314332
if (!profileName) throw new MissingArgumentError('profileName is required');
315333
const profile = this.getProfile(profileName);
316334

@@ -324,20 +342,11 @@ export class ProfileManager {
324342
this.resourcesForEnvoyProxyPod(profile, yamlRoot);
325343
this.resourcesForMinioTenantPod(profile, yamlRoot);
326344

327-
// write the YAML
328345
const cachedValuesFile = path.join(this.cacheDir, `solo-${profileName}.yaml`);
329-
return new Promise<string>((resolve, reject) => {
330-
fs.writeFile(cachedValuesFile, yaml.stringify(yamlRoot), err => {
331-
if (err) {
332-
reject(err);
333-
}
334-
335-
resolve(cachedValuesFile);
336-
});
337-
});
346+
return this.writeToYaml(cachedValuesFile, yamlRoot);
338347
}
339348

340-
async bumpHederaConfigVersion(applicationPropertiesPath: string) {
349+
private async bumpHederaConfigVersion(applicationPropertiesPath: string) {
341350
const lines = (await readFile(applicationPropertiesPath, 'utf-8')).split('\n');
342351

343352
for (const line of lines) {
@@ -351,62 +360,59 @@ export class ProfileManager {
351360
await writeFile(applicationPropertiesPath, lines.join('\n'));
352361
}
353362

354-
async prepareValuesForNodeAdd(configTxtPath: string, applicationPropertiesPath: string) {
363+
public async prepareValuesForNodeAdd(configTxtPath: string, applicationPropertiesPath: string) {
355364
const yamlRoot = {};
356365
this._setFileContentsAsValue('hedera.configMaps.configTxt', configTxtPath, yamlRoot);
357366
await this.bumpHederaConfigVersion(applicationPropertiesPath);
358367
this._setFileContentsAsValue('hedera.configMaps.applicationProperties', applicationPropertiesPath, yamlRoot);
359368

360-
// write the yaml
361369
const cachedValuesFile = path.join(this.cacheDir, 'solo-node-add.yaml');
362-
return new Promise<string>((resolve, reject) => {
363-
fs.writeFile(cachedValuesFile, yaml.stringify(yamlRoot), err => {
364-
if (err) {
365-
reject(err);
366-
}
370+
return this.writeToYaml(cachedValuesFile, yamlRoot);
371+
}
367372

368-
resolve(cachedValuesFile);
369-
});
370-
});
373+
public setValueForGenesisNetwork(path: string) {
374+
const yamlRoot = {};
375+
376+
this._setFileContentsAsValue('hedera.configMaps.genesisNetworkJson', path, yamlRoot);
371377
}
372378

373379
/**
374380
* Prepare a values file for rpc-relay Helm chart
375381
* @param profileName - resource profile name
376382
* @returns return the full path to the values file
377383
*/
378-
prepareValuesForRpcRelayChart(profileName: string) {
384+
public async prepareValuesForRpcRelayChart(profileName: string) {
379385
if (!profileName) throw new MissingArgumentError('profileName is required');
380-
const profile = this.getProfile(profileName) as any;
386+
const profile = this.getProfile(profileName) as AnyObject;
381387
if (!profile.rpcRelay) return Promise.resolve(); // use chart defaults
382388

383389
// generate the YAML
384390
const yamlRoot = {};
385391
this._setChartItems('', profile.rpcRelay, yamlRoot);
386392

387-
// write the YAML
388393
const cachedValuesFile = path.join(this.cacheDir, `rpcRelay-${profileName}.yaml`);
389-
return new Promise<string>((resolve, reject) => {
390-
fs.writeFile(cachedValuesFile, yaml.stringify(yamlRoot), err => {
391-
if (err) {
392-
reject(err);
393-
}
394-
395-
resolve(cachedValuesFile);
396-
});
397-
});
394+
return this.writeToYaml(cachedValuesFile, yamlRoot);
398395
}
399396

400-
prepareValuesHederaExplorerChart(profileName: string) {
397+
public async prepareValuesHederaExplorerChart(profileName: string) {
401398
if (!profileName) throw new MissingArgumentError('profileName is required');
402-
const profile = this.getProfile(profileName) as any;
399+
const profile = this.getProfile(profileName) as AnyObject;
403400
// generate the YAML
404401
const yamlRoot = {};
405402
this.resourcesForHederaExplorerPod(profile, yamlRoot);
406403

407-
// write the YAML
408404
const cachedValuesFile = path.join(this.cacheDir, `explorer-${profileName}.yaml`);
409-
return new Promise<string>((resolve, reject) => {
405+
return this.writeToYaml(cachedValuesFile, yamlRoot);
406+
}
407+
408+
/**
409+
* Writes the YAML to file.
410+
*
411+
* @param cachedValuesFile - the target file to write the YAML root to.
412+
* @param yamlRoot - object to turn into YAML and write to file.
413+
*/
414+
private async writeToYaml(cachedValuesFile: Path, yamlRoot: AnyObject) {
415+
return await new Promise<string>((resolve, reject) => {
410416
fs.writeFile(cachedValuesFile, yaml.stringify(yamlRoot), err => {
411417
if (err) {
412418
reject(err);
@@ -422,9 +428,9 @@ export class ProfileManager {
422428
* @param profileName - resource profile name
423429
* @returns the full path to the values file
424430
*/
425-
prepareValuesForMirrorNodeChart(profileName: string) {
431+
public async prepareValuesForMirrorNodeChart(profileName: string) {
426432
if (!profileName) throw new MissingArgumentError('profileName is required');
427-
const profile = this.getProfile(profileName) as any;
433+
const profile = this.getProfile(profileName) as AnyObject;
428434
if (!profile.mirror) return Promise.resolve(); // use chart defaults
429435

430436
// generate the YAML
@@ -443,26 +449,17 @@ export class ProfileManager {
443449
this._setChartItems('grpc', profile.mirror.grpc, yamlRoot);
444450
this._setChartItems('monitor', profile.mirror.monitor, yamlRoot);
445451

446-
// write the YAML
447452
const cachedValuesFile = path.join(this.cacheDir, `mirror-${profileName}.yaml`);
448-
return new Promise<string>((resolve, reject) => {
449-
fs.writeFile(cachedValuesFile, yaml.stringify(yamlRoot), err => {
450-
if (err) {
451-
reject(err);
452-
}
453-
454-
resolve(cachedValuesFile);
455-
});
456-
});
453+
return this.writeToYaml(cachedValuesFile, yamlRoot);
457454
}
458455

459456
/**
460-
* Writes the contents of a file as a value for the given nested item path in the yaml object
461-
* @param itemPath - nested item path in the yaml object to store the file contents
462-
* @param valueFilePath - path to the file whose contents will be stored in the yaml object
463-
* @param yamlRoot - root of the yaml object
457+
* Writes the contents of a file as a value for the given nested item path in the YAML object
458+
* @param itemPath - nested item path in the YAML object to store the file contents
459+
* @param valueFilePath - path to the file whose contents will be stored in the YAML object
460+
* @param yamlRoot - root of the YAML object
464461
*/
465-
private _setFileContentsAsValue(itemPath: string, valueFilePath: string, yamlRoot: object) {
462+
private _setFileContentsAsValue(itemPath: string, valueFilePath: string, yamlRoot: AnyObject) {
466463
const fileContents = fs.readFileSync(valueFilePath, 'utf8');
467464
this._setValue(itemPath, fileContents, yamlRoot);
468465
}
@@ -548,7 +545,7 @@ export class ProfileManager {
548545
fs.writeFileSync(configFilePath, configLines.join('\n'));
549546

550547
return configFilePath;
551-
} catch (e: Error | any) {
548+
} catch (e: Error | unknown) {
552549
throw new SoloError('failed to generate config.txt', e);
553550
}
554551
}

src/types/aliases.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,7 @@ export type Nullable<T> = T | null;
4444
export type IP = string;
4545

4646
export type JsonString = string;
47+
48+
export type Path = string;
49+
50+
export type AnyObject = Record<any, any>;

0 commit comments

Comments
 (0)