From 6a29265999ac0c22079b55715c707d18cf7a7886 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 2 Apr 2025 10:04:14 +0300 Subject: [PATCH 01/70] preparing new command BlockNodesCommand Signed-off-by: Zhan Milenkov --- src/commands/block-nodes.ts | 303 ++++++++++++++++++++++++++++++++++++ src/commands/relay.ts | 4 +- src/core/lock/listr-lock.ts | 6 +- src/index.ts | 2 +- src/types/flag-types.ts | 5 + src/types/index.ts | 12 +- 6 files changed, 323 insertions(+), 9 deletions(-) create mode 100644 src/commands/block-nodes.ts diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts new file mode 100644 index 000000000..e2f594026 --- /dev/null +++ b/src/commands/block-nodes.ts @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {Listr, ListrTask} from 'listr2'; +import {SoloError} from '../core/errors/solo-error.js'; +import * as helpers from '../core/helpers.js'; +import * as constants from '../core/constants.js'; +import {type AccountManager} from '../core/account-manager.js'; +import {BaseCommand, type Options} from './base.js'; +import {Flags as flags} from './flags.js'; +import {showVersionBanner} from '../core/helpers.js'; +import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; +import {AnyListrContext, type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; +import {ListrLock} from '../core/lock/listr-lock.js'; +import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; +import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; +import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; +import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; +import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; +import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; +import {type Lock} from '../core/lock/lock.js'; +import {K8} from '../integration/kube/k8.js'; + +interface BlockNodesDeployConfigClass { + chainId: string; + chartDirectory: string; + namespace: NamespaceName; + deployment: string; + nodeAliasesUnparsed: string; + operatorId: string; + operatorKey: string; + profileFile: string; + profileName: string; + replicaCount: number; + valuesFile: string; + isChartInstalled: boolean; + nodeAliases: NodeAliases; + releaseName: string; + valuesArg: string; + clusterRef: Optional; + domainName: Optional; + context: Optional; +} + +interface BlockNodesDeployContext { + config: BlockNodesDeployConfigClass; +} + +export class BlockNodesCommand extends BaseCommand { + private readonly accountManager: AccountManager; + + public constructor(options: Options) { + super(options); + this.accountManager = options.accountManager; + } + + public static readonly COMMAND_NAME: string = 'block-nodes'; + + private static readonly DEPLOY_CONFIGS_NAME: string = 'deployConfigs'; + + private static readonly DEPLOY_FLAGS_LIST: CommandFlags = { + required: [], + optional: [ + flags.chainId, + flags.chartDirectory, + flags.clusterRef, + flags.deployment, + flags.nodeAliasesUnparsed, + flags.operatorId, + flags.operatorKey, + flags.profileFile, + flags.profileName, + flags.quiet, + flags.replicaCount, + flags.valuesFile, + flags.domainName, + ], + }; + + private async prepareValuesArgForBlockNodes(valuesFile: string): Promise { + let valuesArgument: string = ''; + + // if (blockNodesRelease) valuesArgument += ` --set image.tag=${blockNodesRelease.replace(/^v/, '')}`; + + if (valuesFile) { + valuesArgument += helpers.prepareValuesFiles(valuesFile); + } + + return valuesArgument; + } + + private prepareReleaseName(nodeAliases: NodeAliases = []): string { + let releaseName: string = 'block nodes'; // TODO + + for (const nodeAlias of nodeAliases) { + releaseName += `-${nodeAlias}`; + } + + return releaseName; + } + + private async deploy(argv: ArgvStruct): Promise { + const lease: Lock = await this.leaseManager.create(); + + const tasks: Listr = new Listr( + [ + { + title: 'Initialize', + task: async (context_, task): Promise> => { + // reset nodeAlias + this.configManager.setFlag(flags.nodeAliasesUnparsed, ''); + + this.configManager.update(argv); + + flags.disablePrompts([ + flags.operatorId, + flags.operatorKey, + flags.clusterRef, + flags.profileFile, + flags.profileName, + ]); + + const allFlags: CommandFlag[] = [ + ...BlockNodesCommand.DEPLOY_FLAGS_LIST.required, + ...BlockNodesCommand.DEPLOY_FLAGS_LIST.optional, + ]; + + await this.configManager.executePrompt(task, allFlags); + + // prompt if inputs are empty and set it in the context + context_.config = this.configManager.getConfig(BlockNodesCommand.DEPLOY_CONFIGS_NAME, allFlags, [ + 'nodeAliases', + ]) as BlockNodesDeployConfigClass; + + context_.config.namespace = await resolveNamespaceFromDeployment( + this.localConfig, + this.configManager, + task, + ); + context_.config.nodeAliases = helpers.parseNodeAliases( + context_.config.nodeAliasesUnparsed, + this.remoteConfigManager.getConsensusNodes(), + this.configManager, + ); + context_.config.releaseName = this.prepareReleaseName(context_.config.nodeAliases); + + if (context_.config.clusterRef) { + const context: string = this.remoteConfigManager.getClusterRefs()[context_.config.clusterRef]; + if (context) context_.config.context = context; + } + + this.logger.debug('Initialized config', {config: context_.config}); + + return ListrLock.newAcquireLockTask(lease, task); + }, + }, + { + title: 'Check chart is installed', + task: async (context_): Promise => { + const config: BlockNodesDeployConfigClass = context_.config; + + config.isChartInstalled = await this.chartManager.isChartInstalled( + config.namespace, + config.releaseName, + config.context, + ); + }, + }, + { + title: 'Prepare chart values', + task: async (context_): Promise => { + const config: BlockNodesDeployConfigClass = context_.config; + + await this.accountManager.loadNodeClient( + context_.config.namespace, + this.remoteConfigManager.getClusterRefs(), + this.configManager.getFlag(flags.deployment), + this.configManager.getFlag(flags.forcePortForward), + ); + + config.valuesArg = await this.prepareValuesArgForBlockNodes(config.valuesFile); + }, + }, + { + title: 'Deploy JSON RPC BlockNodes', + task: async (context_): Promise => { + const config: BlockNodesDeployConfigClass = context_.config; + + await this.chartManager.install( + config.namespace, + config.releaseName, + JSON_RPC_RELAY_CHART, + JSON_RPC_RELAY_CHART, + '', + config.valuesArg, + config.context, + ); + + showVersionBanner(this.logger, config.releaseName, HEDERA_JSON_RPC_RELAY_VERSION); + }, + }, + { + title: 'Check block nodes are running', + task: async (context_): Promise => { + const config: BlockNodesDeployConfigClass = context_.config; + + await this.k8Factory + .getK8(config.context) + .pods() + .waitForRunningPhase( + config.namespace, + ['app=hedera-json-rpc-block nodes', `app.kubernetes.io/instance=${config.releaseName}`], // TODO + constants.RELAY_PODS_RUNNING_MAX_ATTEMPTS, + constants.RELAY_PODS_RUNNING_DELAY, + ); + }, + }, + { + title: 'Check block nodes is ready', + task: async (context_): Promise => { + const config: BlockNodesDeployConfigClass = context_.config; + try { + await this.k8Factory + .getK8(config.context) + .pods() + .waitForReadyStatus( + config.namespace, + ['app=hedera-json-rpc-relay', `app.kubernetes.io/instance=${config.releaseName}`], + constants.RELAY_PODS_READY_MAX_ATTEMPTS, + constants.RELAY_PODS_READY_DELAY, + ); + } catch (error) { + throw new SoloError(`BlockNodes ${config.releaseName} is not ready: ${error.message}`, error); + } + }, + }, + this.addBlockNodesComponent(), + ], + { + concurrent: false, + rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION, + }, + ); + + try { + await tasks.run(); + } catch (error) { + throw new SoloError(`Error deploying block nodes: ${error.message}`, error); + } finally { + await lease.release(); + await this.accountManager.close(); + } + + return true; + } + + public getCommandDefinition(): CommandDefinition { + return { + command: BlockNodesCommand.COMMAND_NAME, + desc: 'Manage block nodes in solo network', + builder: (yargs: AnyYargs) => { + return yargs + .command({ + command: 'deploy', + desc: 'Deploy block nodes', + builder: (y: AnyYargs): void => { + flags.setRequiredCommandFlags(y, ...BlockNodesCommand.DEPLOY_FLAGS_LIST.required); + flags.setOptionalCommandFlags(y, ...BlockNodesCommand.DEPLOY_FLAGS_LIST.optional); + }, + handler: async (argv: ArgvStruct): Promise => { + this.logger.info("==== Running 'relay deploy' ===", {argv}); + this.logger.info(argv); + + await this.deploy(argv).then((r): void => { + this.logger.info('==== Finished running `relay deploy`===='); + if (!r) throw new SoloError('Error deploying relay, expected return value to be true'); + }); + }, + }) + .demandCommand(1, 'Select a relay command'); + }, + }; + } + + /** Adds the relay component to remote config. */ + public addBlockNodesComponent(): SoloListrTask { + return { + title: 'Add relay component in remote config', + skip: (): boolean => !this.remoteConfigManager.isLoaded(), + task: async (context_): Promise => { + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + const { + config: {namespace, nodeAliases}, + } = context_; + const cluster = this.remoteConfigManager.currentCluster; + + // remoteConfig.components.add(new BlockNodesComponent('relay', cluster, namespace.name, nodeAliases)); // TODO + }); + }, + }; + } + + public async close(): Promise {} // no-op +} diff --git a/src/commands/relay.ts b/src/commands/relay.ts index de4230c8b..12d7f2489 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -328,8 +328,6 @@ export class RelayCommand extends BaseCommand { task: async context_ => { const config = context_.config; - const kubeContext = self.k8Factory.getK8(config.context).contexts().readCurrent(); - await self.chartManager.install( config.namespace, config.releaseName, @@ -337,7 +335,7 @@ export class RelayCommand extends BaseCommand { JSON_RPC_RELAY_CHART, '', config.valuesArg, - kubeContext, + config.context, ); showVersionBanner(self.logger, config.releaseName, HEDERA_JSON_RPC_RELAY_VERSION); diff --git a/src/core/lock/listr-lock.ts b/src/core/lock/listr-lock.ts index 290a15e26..9a28f23f6 100644 --- a/src/core/lock/listr-lock.ts +++ b/src/core/lock/listr-lock.ts @@ -5,6 +5,8 @@ import {type Lock} from './lock.js'; import {LockAcquisitionError} from './lock-acquisition-error.js'; import {type SoloListrTaskWrapper} from '../../types/index.js'; +import {type AnyListrContext} from '../../types/aliases.js'; +import {type Listr} from 'listr2'; /** * A utility class for managing lock acquisition tasks in Listr2 based workflows. @@ -33,12 +35,12 @@ export class ListrLock { * @param task - the parent task to which the lock acquisition task will be added. * @returns a new Listr2 task for acquiring a lock with retry logic. */ - public static newAcquireLockTask(lock: Lock, task: SoloListrTaskWrapper) { + public static newAcquireLockTask(lock: Lock, task: SoloListrTaskWrapper): Listr { return task.newListr( [ { title: ListrLock.ACQUIRE_LOCK_TASK_TITLE, - task: async (_, task) => { + task: async (_, task): Promise => { await ListrLock.acquireWithRetry(lock, task); }, }, diff --git a/src/index.ts b/src/index.ts index 85b687ae1..509e16d5d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,7 +45,7 @@ export async function main(argv: string[], context?: {logger: SoloLogger}) { throw new SoloError('Error initializing container'); } - const logger = container.resolve(InjectTokens.SoloLogger); + const logger: SoloLogger = container.resolve(InjectTokens.SoloLogger); if (context) { // save the logger so that solo.ts can use it to properly flush the logs and exit diff --git a/src/types/flag-types.ts b/src/types/flag-types.ts index b9414a342..086c9e0e4 100644 --- a/src/types/flag-types.ts +++ b/src/types/flag-types.ts @@ -19,3 +19,8 @@ export interface Definition { disablePrompt?: boolean; dataMask?: string; } + +export interface CommandFlags { + required: CommandFlag[]; + optional: CommandFlag[]; +} diff --git a/src/types/index.ts b/src/types/index.ts index bc1a72aaf..a110f43bb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,12 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 import type * as x509 from '@peculiar/x509'; -import type net from 'net'; +import type net from 'node:net'; import type * as WebSocket from 'ws'; -import type crypto from 'crypto'; +import type crypto from 'node:crypto'; import {type ListrTask, type ListrTaskWrapper} from 'listr2'; import {type PublicKey} from '@hashgraph/sdk'; -import {type JsonString} from './aliases.js'; +import {type AnyYargs, type JsonString} from './aliases.js'; import {type Listr} from 'listr2'; // NOTE: DO NOT add any Solo imports in this file to avoid circular dependencies @@ -121,3 +121,9 @@ export interface GossipEndpoint { hostname: string; port: number; } + +export interface CommandDefinition { + command: string; + desc: string; + builder: (yargs: AnyYargs) => any; +} From c792f9d609bffac7eb7e4a7370a82cf642f4cb01 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 2 Apr 2025 12:04:20 +0300 Subject: [PATCH 02/70] fixed the interfaces for the new command, made the command flag definition, added the new flag about the block nodes chart version together with the initial constants Signed-off-by: Zhan Milenkov --- src/commands/base.ts | 2 +- src/commands/block-nodes.ts | 79 +++++++++++------------------------ src/commands/flags.ts | 82 +++++++++++++++++++++++-------------- src/core/constants.ts | 5 +++ version.ts | 1 + 5 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/commands/base.ts b/src/commands/base.ts index 80214db39..04f2e0272 100644 --- a/src/commands/base.ts +++ b/src/commands/base.ts @@ -51,7 +51,7 @@ export abstract class BaseCommand extends ShellRunner { public readonly localConfig: LocalConfig; protected readonly remoteConfigManager: RemoteConfigManager; - constructor(options: Options) { + public constructor(options: Options) { if (!options || !options.helm) throw new Error('An instance of core/Helm is required'); if (!options || !options.k8Factory) throw new Error('An instance of core/K8Factory is required'); if (!options || !options.chartManager) throw new Error('An instance of core/ChartManager is required'); diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index e2f594026..f6c4a9128 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -1,44 +1,33 @@ // SPDX-License-Identifier: Apache-2.0 -import {Listr, ListrTask} from 'listr2'; +import {Listr} from 'listr2'; import {SoloError} from '../core/errors/solo-error.js'; import * as helpers from '../core/helpers.js'; import * as constants from '../core/constants.js'; -import {type AccountManager} from '../core/account-manager.js'; -import {BaseCommand, type Options} from './base.js'; +import {BaseCommand} from './base.js'; import {Flags as flags} from './flags.js'; import {showVersionBanner} from '../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; -import {AnyListrContext, type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; +import {type AnyListrContext, type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; -import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; -import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; +import * as versions from '../../version.js'; import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; -import {K8} from '../integration/kube/k8.js'; +import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; interface BlockNodesDeployConfigClass { - chainId: string; + chartVersion: string; chartDirectory: string; - namespace: NamespaceName; - deployment: string; - nodeAliasesUnparsed: string; - operatorId: string; - operatorKey: string; - profileFile: string; - profileName: string; - replicaCount: number; - valuesFile: string; - isChartInstalled: boolean; - nodeAliases: NodeAliases; - releaseName: string; - valuesArg: string; - clusterRef: Optional; + clusterRef: ClusterReference; + deployment: DeploymentName; + devMode: boolean; domainName: Optional; - context: Optional; + enableIngress: boolean; + quiet: boolean; + valuesFile: Optional; + namespace: NamespaceName; } interface BlockNodesDeployContext { @@ -46,13 +35,6 @@ interface BlockNodesDeployContext { } export class BlockNodesCommand extends BaseCommand { - private readonly accountManager: AccountManager; - - public constructor(options: Options) { - super(options); - this.accountManager = options.accountManager; - } - public static readonly COMMAND_NAME: string = 'block-nodes'; private static readonly DEPLOY_CONFIGS_NAME: string = 'deployConfigs'; @@ -60,19 +42,15 @@ export class BlockNodesCommand extends BaseCommand { private static readonly DEPLOY_FLAGS_LIST: CommandFlags = { required: [], optional: [ - flags.chainId, + flags.blockNodesChartVersion, flags.chartDirectory, flags.clusterRef, flags.deployment, - flags.nodeAliasesUnparsed, - flags.operatorId, - flags.operatorKey, - flags.profileFile, - flags.profileName, + flags.devMode, + flags.domainName, + flags.enableIngress, flags.quiet, - flags.replicaCount, flags.valuesFile, - flags.domainName, ], }; @@ -112,11 +90,10 @@ export class BlockNodesCommand extends BaseCommand { this.configManager.update(argv); flags.disablePrompts([ - flags.operatorId, - flags.operatorKey, + flags.valuesFile, + flags.chartDirectory, flags.clusterRef, - flags.profileFile, - flags.profileName, + ]); const allFlags: CommandFlag[] = [ @@ -170,32 +147,25 @@ export class BlockNodesCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodesDeployConfigClass = context_.config; - await this.accountManager.loadNodeClient( - context_.config.namespace, - this.remoteConfigManager.getClusterRefs(), - this.configManager.getFlag(flags.deployment), - this.configManager.getFlag(flags.forcePortForward), - ); - config.valuesArg = await this.prepareValuesArgForBlockNodes(config.valuesFile); }, }, { - title: 'Deploy JSON RPC BlockNodes', + title: 'Deploy BlockNodes', task: async (context_): Promise => { const config: BlockNodesDeployConfigClass = context_.config; await this.chartManager.install( config.namespace, config.releaseName, - JSON_RPC_RELAY_CHART, - JSON_RPC_RELAY_CHART, + constants.BLOCK_NODE_CHART, + constants.BLOCK_NODE_CHART, '', config.valuesArg, config.context, ); - showVersionBanner(this.logger, config.releaseName, HEDERA_JSON_RPC_RELAY_VERSION); + showVersionBanner(this.logger, config.releaseName, versions.BLOCK_NODE_VERSION); }, }, { @@ -247,7 +217,6 @@ export class BlockNodesCommand extends BaseCommand { throw new SoloError(`Error deploying block nodes: ${error.message}`, error); } finally { await lease.release(); - await this.accountManager.close(); } return true; diff --git a/src/commands/flags.ts b/src/commands/flags.ts index 05fee125a..b090e4b88 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -2,8 +2,8 @@ import * as constants from '../core/constants.js'; import * as version from '../../version.js'; -import {type CommandFlag} from '../types/flag-types.js'; -import fs from 'fs'; +import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; +import fs from 'node:fs'; import {IllegalArgumentError} from '../core/errors/illegal-argument-error.js'; import {SoloError} from '../core/errors/solo-error.js'; import {ListrInquirerPromptAdapter} from '@listr2/prompt-adapter-inquirer'; @@ -19,9 +19,11 @@ import {type ClusterReference} from '../core/config/remote/types.js'; import {type Optional, type SoloListrTaskWrapper} from '../types/index.js'; import chalk from 'chalk'; import {PathEx} from '../business/utils/path-ex.js'; +import {BLOCK_NODE_CHART_URL} from '../core/constants.js'; +import {BLOCK_NODE_VERSION} from '../../version.js'; export class Flags { - public static KEY_COMMON = '_COMMON_'; + public static KEY_COMMON: string = '_COMMON_'; private static async prompt( type: 'toggle' | 'input' | 'number', @@ -36,7 +38,7 @@ export class Flags { // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { try { - let needsPrompt = type === 'toggle' ? input === undefined || typeof input !== 'boolean' : !input; + let needsPrompt: boolean = type === 'toggle' ? input === undefined || typeof input !== 'boolean' : !input; needsPrompt = type === 'number' ? typeof input !== 'number' : needsPrompt; if (needsPrompt) { @@ -46,15 +48,17 @@ export class Flags { throw new SoloError('Cannot prompt for input in non-interactive mode'); } - const promptOptions = {default: defaultValue, message: promptMessage}; + const promptOptions: {default: any; message: string} = {default: defaultValue, message: promptMessage}; switch (type) { - case 'input': + case 'input': { input = await task.prompt(ListrInquirerPromptAdapter).run(inputPrompt, promptOptions); break; - case 'toggle': + } + case 'toggle': { input = await task.prompt(ListrInquirerPromptAdapter).run(confirmPrompt, promptOptions); break; + } case 'number': { input = await task.prompt(ListrInquirerPromptAdapter).run(numberPrompt, promptOptions); break; @@ -114,9 +118,9 @@ export class Flags { * */ public static setRequiredCommandFlags(y: AnyYargs, ...commandFlags: CommandFlag[]) { - commandFlags.forEach(flag => { + for (const flag of commandFlags) { y.option(flag.name, {...flag.definition, demandOption: true}); - }); + } } /** @@ -126,14 +130,14 @@ export class Flags { * */ public static setOptionalCommandFlags(y: AnyYargs, ...commandFlags: CommandFlag[]) { - commandFlags.forEach(flag => { - let defaultValue = flag.definition.defaultValue !== '' ? flag.definition.defaultValue : undefined; + for (const flag of commandFlags) { + let defaultValue = flag.definition.defaultValue === '' ? undefined : flag.definition.defaultValue; defaultValue = defaultValue && flag.definition.dataMask ? flag.definition.dataMask : defaultValue; y.option(flag.name, { ...flag.definition, default: defaultValue, }); - }); + } } public static readonly devMode: CommandFlag = { @@ -263,25 +267,25 @@ export class Flags { const valuesFiles: Record> = {}; if (input) { const inputItems = input.split(','); - inputItems.forEach(v => { + for (const v of inputItems) { const parts = v.split('='); let clusterReference: string; let valuesFile: string; - if (parts.length !== 2) { - valuesFile = PathEx.resolve(v); - clusterReference = Flags.KEY_COMMON; - } else { + if (parts.length === 2) { clusterReference = parts[0]; valuesFile = PathEx.resolve(parts[1]); + } else { + valuesFile = PathEx.resolve(v); + clusterReference = Flags.KEY_COMMON; } if (!valuesFiles[clusterReference]) { valuesFiles[clusterReference] = []; } valuesFiles[clusterReference].push(valuesFile); - }); + } } return valuesFiles; @@ -1022,6 +1026,17 @@ export class Flags { }, }; + public static readonly blockNodesChartVersion: CommandFlag = { + constName: 'chartVersion', + name: 'chart-version', + definition: { + describe: 'Block nodes chart version', + defaultValue: version.BLOCK_NODE_VERSION, + type: 'string', + }, + prompt: undefined, + }; + public static readonly applicationProperties: CommandFlag = { constName: 'applicationProperties', name: 'application-properties', @@ -2267,7 +2282,7 @@ export class Flags { type: 'number', }, prompt: async function (task: SoloListrTaskWrapper, input: number): Promise { - const promptForInput = (): Promise => + const promptForInput: () => Promise = (): Promise => Flags.prompt( 'number', task, @@ -2455,20 +2470,23 @@ export class Flags { Flags.dnsConsensusNodePattern, Flags.domainName, Flags.domainNames, + Flags.blockNodesChartVersion, ]; /** Resets the definition.disablePrompt for all flags */ - private static resetDisabledPrompts() { - Flags.allFlags.forEach(f => { - if (f.definition.disablePrompt) { - delete f.definition.disablePrompt; + private static resetDisabledPrompts(): void { + for (const flag of Flags.allFlags) { + if (flag.definition.disablePrompt) { + delete flag.definition.disablePrompt; } - }); + } } - public static readonly allFlagsMap = new Map(Flags.allFlags.map(f => [f.name, f])); + public static readonly allFlagsMap: Map = new Map( + Flags.allFlags.map((flag): [string, CommandFlag] => [flag.name, flag]), + ); - public static readonly nodeConfigFileFlags = new Map( + public static readonly nodeConfigFileFlags: Map = new Map( [ Flags.apiPermissionProperties, Flags.applicationEnv, @@ -2476,12 +2494,14 @@ export class Flags { Flags.bootstrapProperties, Flags.log4j2Xml, Flags.settingTxt, - ].map(f => [f.name, f]), + ].map((flag): [string, CommandFlag] => [flag.name, flag]), ); - public static readonly integerFlags = new Map([Flags.replicaCount].map(f => [f.name, f])); + public static readonly integerFlags: Map = new Map( + [Flags.replicaCount].map((flag): [string, CommandFlag] => [flag.name, flag]), + ); - public static readonly DEFAULT_FLAGS = { + public static readonly DEFAULT_FLAGS: CommandFlags = { required: [], optional: [Flags.namespace, Flags.cacheDir, Flags.releaseTag, Flags.devMode, Flags.quiet], }; @@ -2503,12 +2523,12 @@ export class Flags { } // remove flags that use the default value - const flag = Flags.allFlags.find(flag => flag.name === name); + const flag: CommandFlag = Flags.allFlags.find((flag): boolean => flag.name === name); if (!flag || (flag.definition.defaultValue && flag.definition.defaultValue === value)) { continue; } - const flagName = flag.name; + const flagName: string = flag.name; // if the flag is boolean based, render it without value if (value === true) { diff --git a/src/core/constants.ts b/src/core/constants.ts index cfcf5e94e..186e4fec2 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -80,6 +80,11 @@ export const INGRESS_CONTROLLER_CHART_URL = export const INGRESS_CONTROLLER_RELEASE_NAME = 'haproxy-ingress'; export const INGRESS_CONTROLLER_NAME = 'haproxy-ingress.github.io/controller'; +export const BLOCK_NODE_CHART_URL = + process.env.BLOCK_NODE_CHART_URL ?? 'https://hiero-ledger.github.io/hiero-block-node/charts'; +export const BLOCK_NODE_CHART = 'block-node-server'; +export const BLOCK_NODE_RELEASE_NAME = 'block-node'; + export const CERT_MANAGER_NAME_SPACE = 'cert-manager'; export const SOLO_HEDERA_MIRROR_IMPORTER = [ 'app.kubernetes.io/component=importer', diff --git a/version.ts b/version.ts index 0fbff0c5a..3c7a8c175 100644 --- a/version.ts +++ b/version.ts @@ -17,6 +17,7 @@ export const MIRROR_NODE_VERSION = 'v0.126.0'; export const HEDERA_EXPLORER_VERSION = '24.12.1'; export const HEDERA_JSON_RPC_RELAY_VERSION = 'v0.66.0'; export const INGRESS_CONTROLLER_VERSION = '0.14.5'; +export const BLOCK_NODE_VERSION = 'v0.14.5'; // TODO export function getSoloVersion(): Version { if (process.env.npm_package_version) { From d03c7a2c2d76c9272176ee1c57e7866c068c9eb3 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 2 Apr 2025 13:36:37 +0300 Subject: [PATCH 03/70] added new values file for block nodes and cleaned up the tasks Signed-off-by: Zhan Milenkov --- resources/block-nodes-values.yaml | 25 ++++++++++++++++ src/commands/block-nodes.ts | 47 +++++++++++++++++-------------- src/commands/flags.ts | 2 -- 3 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 resources/block-nodes-values.yaml diff --git a/resources/block-nodes-values.yaml b/resources/block-nodes-values.yaml new file mode 100644 index 000000000..dca65eb98 --- /dev/null +++ b/resources/block-nodes-values.yaml @@ -0,0 +1,25 @@ +resources: + requests: + cpu: "3" + memory: "8Gi" + +blockNode: + config: + JAVA_TOOL_OPTIONS: "-Djava.util.logging.config.file=/app/logs/config/logging.properties" + JAVA_OPTS: "-Xms8G -Xmx8G" + MEDIATOR_RING_BUFFER_SIZE: "2048" + persistence: + archive: + size: 6Gi + live: + size: 1Gi + + +kubepromstack: + enabled: false + +loki: + enabled: false + +promtail: + enabled: false diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index f6c4a9128..56570cc1d 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -8,7 +8,13 @@ import {BaseCommand} from './base.js'; import {Flags as flags} from './flags.js'; import {showVersionBanner} from '../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; -import {type AnyListrContext, type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; +import { + type AnyListrContext, + type AnyYargs, + type ArgvStruct, + type NodeAlias, + type NodeAliases, +} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; @@ -28,6 +34,11 @@ interface BlockNodesDeployConfigClass { quiet: boolean; valuesFile: Optional; namespace: NamespaceName; + nodeAliases: NodeAliases; // from remote config + releaseName: string; + context: string; + isChartInstalled: boolean; + valuesArg: string; } interface BlockNodesDeployContext { @@ -84,17 +95,9 @@ export class BlockNodesCommand extends BaseCommand { { title: 'Initialize', task: async (context_, task): Promise> => { - // reset nodeAlias - this.configManager.setFlag(flags.nodeAliasesUnparsed, ''); - this.configManager.update(argv); - flags.disablePrompts([ - flags.valuesFile, - flags.chartDirectory, - flags.clusterRef, - - ]); + flags.disablePrompts([flags.valuesFile, flags.chartDirectory, flags.clusterRef]); const allFlags: CommandFlag[] = [ ...BlockNodesCommand.DEPLOY_FLAGS_LIST.required, @@ -104,27 +107,29 @@ export class BlockNodesCommand extends BaseCommand { await this.configManager.executePrompt(task, allFlags); // prompt if inputs are empty and set it in the context - context_.config = this.configManager.getConfig(BlockNodesCommand.DEPLOY_CONFIGS_NAME, allFlags, [ - 'nodeAliases', - ]) as BlockNodesDeployConfigClass; + context_.config = this.configManager.getConfig( + BlockNodesCommand.DEPLOY_CONFIGS_NAME, + allFlags, + ) as BlockNodesDeployConfigClass; context_.config.namespace = await resolveNamespaceFromDeployment( this.localConfig, this.configManager, task, ); - context_.config.nodeAliases = helpers.parseNodeAliases( - context_.config.nodeAliasesUnparsed, - this.remoteConfigManager.getConsensusNodes(), - this.configManager, - ); + + context_.config.nodeAliases = this.remoteConfigManager + .getConsensusNodes() + .map((node): NodeAlias => node.name); + context_.config.releaseName = this.prepareReleaseName(context_.config.nodeAliases); - if (context_.config.clusterRef) { - const context: string = this.remoteConfigManager.getClusterRefs()[context_.config.clusterRef]; - if (context) context_.config.context = context; + if (!context_.config.clusterRef) { + context_.config.clusterRef = this.k8Factory.default().clusters().readCurrent(); } + context_.config.context = this.remoteConfigManager.getClusterRefs()[context_.config.clusterRef]; + this.logger.debug('Initialized config', {config: context_.config}); return ListrLock.newAcquireLockTask(lease, task); diff --git a/src/commands/flags.ts b/src/commands/flags.ts index b090e4b88..f8f830e6d 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -19,8 +19,6 @@ import {type ClusterReference} from '../core/config/remote/types.js'; import {type Optional, type SoloListrTaskWrapper} from '../types/index.js'; import chalk from 'chalk'; import {PathEx} from '../business/utils/path-ex.js'; -import {BLOCK_NODE_CHART_URL} from '../core/constants.js'; -import {BLOCK_NODE_VERSION} from '../../version.js'; export class Flags { public static KEY_COMMON: string = '_COMMON_'; From 3aae36b69abda841b8e60a4338dd7e308bd1abc8 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 2 Apr 2025 16:12:59 +0300 Subject: [PATCH 04/70] add new command to command list and fix getCommandDefinition methods by using new types Signed-off-by: Zhan Milenkov --- src/commands/account.ts | 6 +++--- src/commands/block-nodes.ts | 34 ++++++++++++++++++++++------------ src/commands/cluster/index.ts | 3 ++- src/commands/deployment.ts | 6 +++--- src/commands/explorer.ts | 6 +++--- src/commands/flags.ts | 23 +++++++++++++++++++++++ src/commands/index.ts | 35 ++++++++++++++--------------------- src/commands/init.ts | 7 ++++--- src/commands/mirror-node.ts | 6 +++--- src/commands/network.ts | 8 ++++---- src/commands/node/index.ts | 6 +++--- src/commands/relay.ts | 6 +++--- src/core/constants.ts | 1 + src/types/index.ts | 3 ++- 14 files changed, 90 insertions(+), 60 deletions(-) diff --git a/src/commands/account.ts b/src/commands/account.ts index 3a6c559ab..7dd40452a 100644 --- a/src/commands/account.ts +++ b/src/commands/account.ts @@ -16,7 +16,7 @@ import {type ArgvStruct, type AnyYargs, type NodeAliases} from '../types/aliases import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; -import {type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type SoloListrTask} from '../types/index.js'; import {Templates} from '../core/templates.js'; import {SecretType} from '../integration/kube/resources/secret/secret-type.js'; import {Base64} from 'js-base64'; @@ -724,8 +724,8 @@ export class AccountCommand extends BaseCommand { return true; } - public getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: AccountCommand.COMMAND_NAME, desc: 'Manage Hedera accounts in solo network', diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index 56570cc1d..f2fe9b3b6 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -65,20 +65,29 @@ export class BlockNodesCommand extends BaseCommand { ], }; - private async prepareValuesArgForBlockNodes(valuesFile: string): Promise { + private async prepareValuesArgForBlockNodes(config: BlockNodesDeployConfigClass): Promise { let valuesArgument: string = ''; - // if (blockNodesRelease) valuesArgument += ` --set image.tag=${blockNodesRelease.replace(/^v/, '')}`; + valuesArgument += helpers.prepareValuesFiles(constants.BLOCK_NODES_VALUES_FILE); - if (valuesFile) { - valuesArgument += helpers.prepareValuesFiles(valuesFile); + if (config.valuesFile) { + valuesArgument += helpers.prepareValuesFiles(config.valuesFile); + } + + if (config.domainName) { + valuesArgument += helpers.populateHelmArguments({ + 'ingress.enabled': true, + 'ingress.hosts[0].host': config.domainName, + 'ingress.hosts[0].paths[0].path': '/', + 'ingress.hosts[0].paths[0].pathType': 'ImplementationSpecific', + }); } return valuesArgument; } private prepareReleaseName(nodeAliases: NodeAliases = []): string { - let releaseName: string = 'block nodes'; // TODO + let releaseName: string = 'block-nodes'; // TODO for (const nodeAlias of nodeAliases) { releaseName += `-${nodeAlias}`; @@ -97,7 +106,7 @@ export class BlockNodesCommand extends BaseCommand { task: async (context_, task): Promise> => { this.configManager.update(argv); - flags.disablePrompts([flags.valuesFile, flags.chartDirectory, flags.clusterRef]); + flags.disablePrompts(BlockNodesCommand.DEPLOY_FLAGS_LIST.optional); const allFlags: CommandFlag[] = [ ...BlockNodesCommand.DEPLOY_FLAGS_LIST.required, @@ -152,7 +161,7 @@ export class BlockNodesCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodesDeployConfigClass = context_.config; - config.valuesArg = await this.prepareValuesArgForBlockNodes(config.valuesFile); + config.valuesArg = await this.prepareValuesArgForBlockNodes(config); }, }, { @@ -228,10 +237,11 @@ export class BlockNodesCommand extends BaseCommand { } public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: BlockNodesCommand.COMMAND_NAME, desc: 'Manage block nodes in solo network', - builder: (yargs: AnyYargs) => { + builder: (yargs: AnyYargs): any => { return yargs .command({ command: 'deploy', @@ -241,11 +251,11 @@ export class BlockNodesCommand extends BaseCommand { flags.setOptionalCommandFlags(y, ...BlockNodesCommand.DEPLOY_FLAGS_LIST.optional); }, handler: async (argv: ArgvStruct): Promise => { - this.logger.info("==== Running 'relay deploy' ===", {argv}); - this.logger.info(argv); + self.logger.info("==== Running 'relay deploy' ===", {argv}); + self.logger.info(argv); - await this.deploy(argv).then((r): void => { - this.logger.info('==== Finished running `relay deploy`===='); + await self.deploy(argv).then((r): void => { + self.logger.info('==== Finished running `relay deploy`===='); if (!r) throw new SoloError('Error deploying relay, expected return value to be true'); }); }, diff --git a/src/commands/cluster/index.ts b/src/commands/cluster/index.ts index 18d1960bf..c0f10dda3 100644 --- a/src/commands/cluster/index.ts +++ b/src/commands/cluster/index.ts @@ -7,6 +7,7 @@ import {type ClusterCommandHandlers} from './handlers.js'; import {patchInject} from '../../core/dependency-injection/container-helper.js'; import {InjectTokens} from '../../core/dependency-injection/inject-tokens.js'; import {type AnyYargs} from '../../types/aliases.js'; +import {type CommandDefinition} from '../../types/index.js'; /** * Defines the core functionalities of 'node' command @@ -22,7 +23,7 @@ export class ClusterCommand extends BaseCommand { public static readonly COMMAND_NAME = 'cluster-ref'; - getCommandDefinition() { + public getCommandDefinition(): CommandDefinition { return { command: ClusterCommand.COMMAND_NAME, desc: 'Manage solo testing cluster', diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index b74cdbca3..032dbd32e 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -8,7 +8,7 @@ import * as constants from '../core/constants.js'; import chalk from 'chalk'; import {ClusterCommandTasks} from './cluster/tasks.js'; import {type ClusterReference, type DeploymentName, type NamespaceNameAsString} from '../core/config/remote/types.js'; -import {type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type SoloListrTask} from '../types/index.js'; import {ErrorMessages} from '../core/error-messages.js'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterChecks} from '../core/cluster-checks.js'; @@ -330,8 +330,8 @@ export class DeploymentCommand extends BaseCommand { return true; } - public getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: DeploymentCommand.COMMAND_NAME, desc: 'Manage solo network deployment', diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 3d94ff453..4276052c4 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -16,7 +16,7 @@ import {ListrLock} from '../core/lock/listr-lock.js'; import {ComponentType} from '../core/config/remote/enumerations.js'; import {MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; -import {type Optional, type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterChecks} from '../core/cluster-checks.js'; @@ -508,8 +508,8 @@ export class ExplorerCommand extends BaseCommand { return true; } - public getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: ExplorerCommand.COMMAND_NAME, desc: 'Manage Explorer in solo network', diff --git a/src/commands/flags.ts b/src/commands/flags.ts index f8f830e6d..28267f0c1 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -1605,6 +1605,29 @@ export class Flags { }, }; + public static readonly blockNodesVersion: CommandFlag = { + constName: 'blockNodesVersion', + name: 'block-nodes-version', + definition: { + describe: 'Block nodes chart version', + defaultValue: version.MIRROR_NODE_VERSION, + type: 'string', + }, + prompt: async function promptMirrorNodeVersion( + task: SoloListrTaskWrapper, + input: boolean, + ): Promise { + return await Flags.promptToggle( + task, + input, + Flags.mirrorNodeVersion.definition.defaultValue as boolean, + 'Would you like to choose mirror node version? ', + null, + Flags.mirrorNodeVersion.name, + ); + }, + }; + public static readonly enableIngress: CommandFlag = { constName: 'enableIngress', name: 'enable-ingress', diff --git a/src/commands/index.ts b/src/commands/index.ts index 4ad00b156..f63b6e9d0 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -9,33 +9,26 @@ import {RelayCommand} from './relay.js'; import {AccountCommand} from './account.js'; import {DeploymentCommand} from './deployment.js'; import {ExplorerCommand} from './explorer.js'; +import {BlockNodesCommand} from './block-nodes.js'; import {type Options} from './base.js'; +import {type CommandDefinition} from '../types/index.js'; /** * Return a list of Yargs command builder to be exposed through CLI - * @param opts it is an Options object containing logger + * @param options it is an Options object containing logger * @returns an array of Yargs command builder */ -export function Initialize(options: Options) { - const initCmd = new InitCommand(options); - const clusterCmd = new ClusterCommand(options); - const networkCommand = new NetworkCommand(options); - const nodeCmd = new NodeCommand(options); - const relayCmd = new RelayCommand(options); - const accountCmd = new AccountCommand(options); - const mirrorNodeCmd = new MirrorNodeCommand(options); - const explorerCommand = new ExplorerCommand(options); - const deploymentCommand = new DeploymentCommand(options); - +export function Initialize(options: Options): CommandDefinition[] { return [ - initCmd.getCommandDefinition(), - accountCmd.getCommandDefinition(), - clusterCmd.getCommandDefinition(), - networkCommand.getCommandDefinition(), - nodeCmd.getCommandDefinition(), - relayCmd.getCommandDefinition(), - mirrorNodeCmd.getCommandDefinition(), - explorerCommand.getCommandDefinition(), - deploymentCommand.getCommandDefinition(), + new InitCommand(options).getCommandDefinition(), + new ClusterCommand(options).getCommandDefinition(), + new NetworkCommand(options).getCommandDefinition(), + new NodeCommand(options).getCommandDefinition(), + new RelayCommand(options).getCommandDefinition(), + new AccountCommand(options).getCommandDefinition(), + new MirrorNodeCommand(options).getCommandDefinition(), + new ExplorerCommand(options).getCommandDefinition(), + new DeploymentCommand(options).getCommandDefinition(), + new BlockNodesCommand(options).getCommandDefinition(), ]; } diff --git a/src/commands/init.ts b/src/commands/init.ts index 2e8d16c93..0f7a4338d 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -2,7 +2,7 @@ import {Listr} from 'listr2'; import {BaseCommand} from './base.js'; -import fs from 'fs'; +import fs from 'node:fs'; import * as constants from '../core/constants.js'; import {SoloError} from '../core/errors/solo-error.js'; import {Flags as flags} from './flags.js'; @@ -10,6 +10,7 @@ import chalk from 'chalk'; import {type EmailAddress} from '../core/config/remote/types.js'; import {PathEx} from '../business/utils/path-ex.js'; import {getSoloVersion} from '../../version.js'; +import {type CommandDefinition} from '../types/index.js'; /** * Defines the core functionalities of 'init' command @@ -140,8 +141,8 @@ export class InitCommand extends BaseCommand { * Return Yargs command definition for 'init' command * @returns A object representing the Yargs command definition */ - getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: InitCommand.COMMAND_NAME, desc: 'Initialize local environment', diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 245192f36..f353170d1 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -20,7 +20,7 @@ import {ListrLock} from '../core/lock/listr-lock.js'; import {ComponentType} from '../core/config/remote/enumerations.js'; import {MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; import * as fs from 'node:fs'; -import {type Optional, type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import * as Base64 from 'js-base64'; import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; import {INGRESS_CONTROLLER_NAME} from '../core/constants.js'; @@ -801,8 +801,8 @@ export class MirrorNodeCommand extends BaseCommand { return true; } - public getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: MirrorNodeCommand.COMMAND_NAME, desc: 'Manage Hedera Mirror Node in solo network', diff --git a/src/commands/network.ts b/src/commands/network.ts index 81bd8e3cd..b66ed7978 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -20,7 +20,7 @@ import { showVersionBanner, } from '../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; -import fs from 'fs'; +import fs from 'node:fs'; import {type KeyManager} from '../core/key-manager.js'; import {type PlatformInstaller} from '../core/platform-installer.js'; import {type ProfileManager} from '../core/profile-manager.js'; @@ -32,7 +32,7 @@ import {ConsensusNodeStates} from '../core/config/remote/enumerations.js'; import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; import {HaProxyComponent} from '../core/config/remote/components/ha-proxy-component.js'; import {v4 as uuidv4} from 'uuid'; -import {type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js'; +import {type CommandDefinition, type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js'; import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js'; @@ -1246,8 +1246,8 @@ export class NetworkCommand extends BaseCommand { return networkDestroySuccess; } - getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: NetworkCommand.COMMAND_NAME, desc: 'Manage solo network deployment', diff --git a/src/commands/node/index.ts b/src/commands/node/index.ts index 20f36e530..74f84c265 100644 --- a/src/commands/node/index.ts +++ b/src/commands/node/index.ts @@ -8,7 +8,7 @@ import * as NodeFlags from './flags.js'; import {type NodeCommandHandlers} from './handlers.js'; import {patchInject} from '../../core/dependency-injection/container-helper.js'; import {InjectTokens} from '../../core/dependency-injection/inject-tokens.js'; -import {type ExtendedNetServer} from '../../types/index.js'; +import {type CommandDefinition, type ExtendedNetServer} from '../../types/index.js'; import {type AnyYargs} from '../../types/aliases.js'; /** @@ -64,8 +64,8 @@ export class NodeCommand extends BaseCommand { return this.handlers.getUnusedConfigs(configName); } - public getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: NodeCommand.COMMAND_NAME, desc: 'Manage Hedera platform node in solo network', diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 12d7f2489..78dc22cb3 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -18,7 +18,7 @@ import {ComponentType} from '../core/config/remote/enumerations.js'; import * as Base64 from 'js-base64'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; -import {type Optional, type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; @@ -482,8 +482,8 @@ export class RelayCommand extends BaseCommand { return true; } - public getCommandDefinition() { - const self = this; + public getCommandDefinition(): CommandDefinition { + const self: this = this; return { command: RelayCommand.COMMAND_NAME, desc: 'Manage JSON RPC relays in solo network', diff --git a/src/core/constants.ts b/src/core/constants.ts index 186e4fec2..b16b8574e 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -135,6 +135,7 @@ export const POD_CONDITION_STATUS_TRUE = 'True'; export const EXPLORER_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'hedera-explorer-values.yaml'); export const MIRROR_NODE_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'mirror-node-values.yaml'); +export const BLOCK_NODES_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'block-nodes-values.yaml'); export const NODE_LOG_FAILURE_MSG = 'failed to download logs from pod'; /** diff --git a/src/types/index.ts b/src/types/index.ts index a110f43bb..a47af150f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -6,7 +6,7 @@ import type * as WebSocket from 'ws'; import type crypto from 'node:crypto'; import {type ListrTask, type ListrTaskWrapper} from 'listr2'; import {type PublicKey} from '@hashgraph/sdk'; -import {type AnyYargs, type JsonString} from './aliases.js'; +import {type AnyYargs, ArgvStruct, type JsonString} from './aliases.js'; import {type Listr} from 'listr2'; // NOTE: DO NOT add any Solo imports in this file to avoid circular dependencies @@ -126,4 +126,5 @@ export interface CommandDefinition { command: string; desc: string; builder: (yargs: AnyYargs) => any; + handler?: (argv: ArgvStruct) => Promise; } From fc213e89dfa694386303ee91c9d0bdc5a990ba0b Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 2 Apr 2025 16:17:28 +0300 Subject: [PATCH 05/70] remove unused code Signed-off-by: Zhan Milenkov --- src/commands/block-nodes.ts | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index f2fe9b3b6..81b837e34 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -17,7 +17,7 @@ import { } from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; -import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type Optional} from '../types/index.js'; import * as versions from '../../version.js'; import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; @@ -217,7 +217,6 @@ export class BlockNodesCommand extends BaseCommand { } }, }, - this.addBlockNodesComponent(), ], { concurrent: false, @@ -265,23 +264,5 @@ export class BlockNodesCommand extends BaseCommand { }; } - /** Adds the relay component to remote config. */ - public addBlockNodesComponent(): SoloListrTask { - return { - title: 'Add relay component in remote config', - skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (context_): Promise => { - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { - const { - config: {namespace, nodeAliases}, - } = context_; - const cluster = this.remoteConfigManager.currentCluster; - - // remoteConfig.components.add(new BlockNodesComponent('relay', cluster, namespace.name, nodeAliases)); // TODO - }); - }, - }; - } - public async close(): Promise {} // no-op } From 612742d1b78dc81decdd075cef09f4e2f2dc161a Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 2 Apr 2025 16:32:27 +0300 Subject: [PATCH 06/70] set block nodes version to v7.0.0 Signed-off-by: Zhan Milenkov --- version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.ts b/version.ts index 3c7a8c175..17f0f95bd 100644 --- a/version.ts +++ b/version.ts @@ -17,7 +17,7 @@ export const MIRROR_NODE_VERSION = 'v0.126.0'; export const HEDERA_EXPLORER_VERSION = '24.12.1'; export const HEDERA_JSON_RPC_RELAY_VERSION = 'v0.66.0'; export const INGRESS_CONTROLLER_VERSION = '0.14.5'; -export const BLOCK_NODE_VERSION = 'v0.14.5'; // TODO +export const BLOCK_NODE_VERSION = 'v0.7.0'; export function getSoloVersion(): Version { if (process.env.npm_package_version) { From 7a7661ec88b4c7b271d1ae16030a0a08ff58d07a Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 10:33:15 +0300 Subject: [PATCH 07/70] fixing issues with installing chart, urls and constants for new chart and initial setup for m4 apple chip compatibility Signed-off-by: Zhan Milenkov --- resources/block-nodes-values.yaml | 2 +- src/commands/block-nodes.ts | 47 ++++++++++++++++--------------- src/core/constants.ts | 8 ++++-- version.ts | 2 +- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/resources/block-nodes-values.yaml b/resources/block-nodes-values.yaml index dca65eb98..eccfa6490 100644 --- a/resources/block-nodes-values.yaml +++ b/resources/block-nodes-values.yaml @@ -6,7 +6,7 @@ resources: blockNode: config: JAVA_TOOL_OPTIONS: "-Djava.util.logging.config.file=/app/logs/config/logging.properties" - JAVA_OPTS: "-Xms8G -Xmx8G" + JAVA_OPTS: "-Xms8G -Xmx8G -XX:UseSVE=0" MEDIATOR_RING_BUFFER_SIZE: "2048" persistence: archive: diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index 81b837e34..9cb83523d 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -35,7 +35,6 @@ interface BlockNodesDeployConfigClass { valuesFile: Optional; namespace: NamespaceName; nodeAliases: NodeAliases; // from remote config - releaseName: string; context: string; isChartInstalled: boolean; valuesArg: string; @@ -86,14 +85,8 @@ export class BlockNodesCommand extends BaseCommand { return valuesArgument; } - private prepareReleaseName(nodeAliases: NodeAliases = []): string { - let releaseName: string = 'block-nodes'; // TODO - - for (const nodeAlias of nodeAliases) { - releaseName += `-${nodeAlias}`; - } - - return releaseName; + private getReleaseName(blockNodeId: string): string { + return constants.BLOCK_NODE_RELEASE_NAME + '-' + blockNodeId; } private async deploy(argv: ArgvStruct): Promise { @@ -131,8 +124,6 @@ export class BlockNodesCommand extends BaseCommand { .getConsensusNodes() .map((node): NodeAlias => node.name); - context_.config.releaseName = this.prepareReleaseName(context_.config.nodeAliases); - if (!context_.config.clusterRef) { context_.config.clusterRef = this.k8Factory.default().clusters().readCurrent(); } @@ -151,7 +142,7 @@ export class BlockNodesCommand extends BaseCommand { config.isChartInstalled = await this.chartManager.isChartInstalled( config.namespace, - config.releaseName, + this.getReleaseName('33') /* TODO */, config.context, ); }, @@ -169,17 +160,24 @@ export class BlockNodesCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodesDeployConfigClass = context_.config; + // TODO CHECK OS if M4 + // # Fix for M4 chips + // ARCH="$(uname -p)" + // if [[ "${ARCH}" == "arm64" || "${ARCH}" == "aarch64" ]]; then + // JAVA_OPTS="${JAVA_OPTS} -XX:UseSVE=0" + // fi + await this.chartManager.install( config.namespace, - config.releaseName, - constants.BLOCK_NODE_CHART, + this.getReleaseName('33') /* TODO */, constants.BLOCK_NODE_CHART, - '', + constants.BLOCK_NODE_CHART_URL, + config.chartVersion, config.valuesArg, config.context, ); - showVersionBanner(this.logger, config.releaseName, versions.BLOCK_NODE_VERSION); + showVersionBanner(this.logger, this.getReleaseName('33') /* TODO */, versions.BLOCK_NODE_VERSION); }, }, { @@ -192,9 +190,9 @@ export class BlockNodesCommand extends BaseCommand { .pods() .waitForRunningPhase( config.namespace, - ['app=hedera-json-rpc-block nodes', `app.kubernetes.io/instance=${config.releaseName}`], // TODO - constants.RELAY_PODS_RUNNING_MAX_ATTEMPTS, - constants.RELAY_PODS_RUNNING_DELAY, + [`app.kubernetes.io/instance=${this.getReleaseName('33') /* TODO */}`], + constants.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS, + constants.BLOCK_NODES_PODS_RUNNING_DELAY, ); }, }, @@ -208,12 +206,15 @@ export class BlockNodesCommand extends BaseCommand { .pods() .waitForReadyStatus( config.namespace, - ['app=hedera-json-rpc-relay', `app.kubernetes.io/instance=${config.releaseName}`], - constants.RELAY_PODS_READY_MAX_ATTEMPTS, - constants.RELAY_PODS_READY_DELAY, + [`app.kubernetes.io/instance=${this.getReleaseName('33') /* TODO */}`], + constants.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS, + constants.BLOCK_NODES_PODS_RUNNING_DELAY, ); } catch (error) { - throw new SoloError(`BlockNodes ${config.releaseName} is not ready: ${error.message}`, error); + throw new SoloError( + `BlockNodes ${this.getReleaseName('33') /* TODO */} is not ready: ${error.message}`, + error, + ); } }, }, diff --git a/src/core/constants.ts b/src/core/constants.ts index b16b8574e..cbfbc3bc3 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -80,9 +80,8 @@ export const INGRESS_CONTROLLER_CHART_URL = export const INGRESS_CONTROLLER_RELEASE_NAME = 'haproxy-ingress'; export const INGRESS_CONTROLLER_NAME = 'haproxy-ingress.github.io/controller'; -export const BLOCK_NODE_CHART_URL = - process.env.BLOCK_NODE_CHART_URL ?? 'https://hiero-ledger.github.io/hiero-block-node/charts'; -export const BLOCK_NODE_CHART = 'block-node-server'; +export const BLOCK_NODE_CHART_URL = process.env.BLOCK_NODE_CHART_URL ?? 'oci://ghcr.io/hiero-ledger/hiero-block-node'; +export const BLOCK_NODE_CHART = 'block-node-helm-chart'; export const BLOCK_NODE_RELEASE_NAME = 'block-node'; export const CERT_MANAGER_NAME_SPACE = 'cert-manager'; @@ -223,6 +222,9 @@ export const RELAY_PODS_RUNNING_MAX_ATTEMPTS = +process.env.RELAY_PODS_RUNNING_M export const RELAY_PODS_RUNNING_DELAY = +process.env.RELAY_PODS_RUNNING_DELAY || 1_000; export const RELAY_PODS_READY_MAX_ATTEMPTS = +process.env.RELAY_PODS_READY_MAX_ATTEMPTS || 100; export const RELAY_PODS_READY_DELAY = +process.env.RELAY_PODS_READY_DELAY || 1_000; +export const BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS = +process.env.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS || 900; +export const BLOCK_NODES_PODS_RUNNING_DELAY = +process.env.BLOCK_NODES_PODS_RUNNING_DELAY || 1_000; + export const GRPC_PORT = +process.env.GRPC_PORT || 50_211; export const LOCAL_BUILD_COPY_RETRY = +process.env.LOCAL_BUILD_COPY_RETRY || 3; diff --git a/version.ts b/version.ts index 17f0f95bd..6ecad60ed 100644 --- a/version.ts +++ b/version.ts @@ -17,7 +17,7 @@ export const MIRROR_NODE_VERSION = 'v0.126.0'; export const HEDERA_EXPLORER_VERSION = '24.12.1'; export const HEDERA_JSON_RPC_RELAY_VERSION = 'v0.66.0'; export const INGRESS_CONTROLLER_VERSION = '0.14.5'; -export const BLOCK_NODE_VERSION = 'v0.7.0'; +export const BLOCK_NODE_VERSION = '0.7.0'; export function getSoloVersion(): Version { if (process.env.npm_package_version) { From ae621cff83205d8ec252b151bc8eb2ad47bc3a1d Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 11:32:02 +0300 Subject: [PATCH 08/70] adding new component block node to remote config, to keep track Signed-off-by: Zhan Milenkov --- resources/block-nodes-values.yaml | 3 +- src/commands/block-nodes.ts | 20 +++++---- src/core/config/remote/cluster.ts | 3 ++ .../config/remote/components-data-wrapper.ts | 44 +++++++------------ .../remote/components/block-node-component.ts | 20 +++++++++ src/core/config/remote/enumerations.ts | 1 + 6 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 src/core/config/remote/components/block-node-component.ts diff --git a/resources/block-nodes-values.yaml b/resources/block-nodes-values.yaml index eccfa6490..45747ce39 100644 --- a/resources/block-nodes-values.yaml +++ b/resources/block-nodes-values.yaml @@ -6,7 +6,7 @@ resources: blockNode: config: JAVA_TOOL_OPTIONS: "-Djava.util.logging.config.file=/app/logs/config/logging.properties" - JAVA_OPTS: "-Xms8G -Xmx8G -XX:UseSVE=0" + JAVA_OPTS: "-Xms8G -Xmx8G" # if modifying this line also change in BlockNodesCommand MEDIATOR_RING_BUFFER_SIZE: "2048" persistence: archive: @@ -14,7 +14,6 @@ blockNode: live: size: 1Gi - kubepromstack: enabled: false diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index 9cb83523d..c3bf30893 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -22,6 +22,7 @@ import * as versions from '../../version.js'; import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; +import os from 'node:os'; interface BlockNodesDeployConfigClass { chartVersion: string; @@ -82,6 +83,14 @@ export class BlockNodesCommand extends BaseCommand { }); } + // Fix for M4 chips (ARM64) + const arch: string = os.arch(); + if (arch === 'arm64' || arch === 'aarch64') { + valuesArgument += helpers.populateHelmArguments({ + JAVA_OPTS: '-Xms8G -Xmx8G -XX:UseSVE=0', + }); + } + return valuesArgument; } @@ -160,13 +169,6 @@ export class BlockNodesCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodesDeployConfigClass = context_.config; - // TODO CHECK OS if M4 - // # Fix for M4 chips - // ARCH="$(uname -p)" - // if [[ "${ARCH}" == "arm64" || "${ARCH}" == "aarch64" ]]; then - // JAVA_OPTS="${JAVA_OPTS} -XX:UseSVE=0" - // fi - await this.chartManager.install( config.namespace, this.getReleaseName('33') /* TODO */, @@ -256,7 +258,9 @@ export class BlockNodesCommand extends BaseCommand { await self.deploy(argv).then((r): void => { self.logger.info('==== Finished running `relay deploy`===='); - if (!r) throw new SoloError('Error deploying relay, expected return value to be true'); + if (!r) { + throw new SoloError('Error deploying relay, expected return value to be true'); + } }); }, }) diff --git a/src/core/config/remote/cluster.ts b/src/core/config/remote/cluster.ts index 6d099159c..9149d82e8 100644 --- a/src/core/config/remote/cluster.ts +++ b/src/core/config/remote/cluster.ts @@ -15,6 +15,7 @@ export class Cluster implements ICluster, ToObject { if (!name) { throw new SoloError('name is required'); } + if (typeof name !== 'string') { throw new SoloError(`Invalid type for name: ${typeof name}`); } @@ -22,6 +23,7 @@ export class Cluster implements ICluster, ToObject { if (!namespace) { throw new SoloError('namespace is required'); } + if (typeof namespace !== 'string') { throw new SoloError(`Invalid type for namespace: ${typeof namespace}`); } @@ -29,6 +31,7 @@ export class Cluster implements ICluster, ToObject { if (!deployment) { throw new SoloError('deployment is required'); } + if (typeof deployment !== 'string') { throw new SoloError(`Invalid type for deployment: ${typeof deployment}`); } diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index f0c7324e3..3925e88bd 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -22,6 +22,7 @@ import {type ToObject, type Validate} from '../../../types/index.js'; import {Templates} from '../../templates.js'; import {type NodeAliases} from '../../../types/aliases.js'; import {type CloneTrait} from '../../../types/traits/clone-trait.js'; +import {BlockNodeComponent} from './components/block-node-component.js'; /** * Represent the components in the remote config and handles: @@ -32,14 +33,6 @@ import {type CloneTrait} from '../../../types/traits/clone-trait.js'; export class ComponentsDataWrapper implements Validate, ToObject, CloneTrait { - /** - * @param relays - Relay record mapping service name to relay components - * @param haProxies - HA Proxies record mapping service name to ha proxies components - * @param mirrorNodes - Mirror Nodes record mapping service name to mirror nodes components - * @param envoyProxies - Envoy Proxies record mapping service name to envoy proxies components - * @param consensusNodes - Consensus Nodes record mapping service name to consensus nodes components - * @param mirrorNodeExplorers - Mirror Node Explorers record mapping service name to mirror node explorers components - */ private constructor( public readonly relays: Record = {}, public readonly haProxies: Record = {}, @@ -47,6 +40,7 @@ export class ComponentsDataWrapper public readonly envoyProxies: Record = {}, public readonly consensusNodes: Record = {}, public readonly mirrorNodeExplorers: Record = {}, + public readonly blockNodes: Record = {}, ) { this.validate(); } @@ -55,55 +49,49 @@ export class ComponentsDataWrapper /** Used to add new component to their respective group. */ public add(component: BaseComponent): void { - const self = this; - - const serviceName = component.name; + const serviceName: string = component.name; if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } if (!(component instanceof BaseComponent)) { - throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent); + throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); } - function addComponentCallback(components: Record): void { - if (self.exists(components, component)) { - throw new SoloError('Component exists', null, component.toObject()); + const addComponentCallback: (components: Record) => void = (components): void => { + if (this.exists(components, component)) { + throw new SoloError('Component exists', undefined, component.toObject()); } components[serviceName] = component; - } + }; - self.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); + this.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); } /** Used to edit an existing component from their respective group. */ public edit(component: BaseComponent): void { - const self = this; - - const serviceName = component.name; + const serviceName: string = component.name; if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } if (!(component instanceof BaseComponent)) { - throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent); + throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); } - function editComponentCallback(components: Record): void { + const editComponentCallback: (components: Record) => void = (components): void => { if (!components[serviceName]) { - throw new SoloError(`Component doesn't exist, name: ${serviceName}`, null, {component}); + throw new SoloError(`Component doesn't exist, name: ${serviceName}`, undefined, {component}); } components[serviceName] = component; - } + }; - self.applyCallbackToComponentGroup(component.type, serviceName, editComponentCallback); + this.applyCallbackToComponentGroup(component.type, serviceName, editComponentCallback); } /** Used to remove specific component from their respective group. */ public remove(serviceName: ComponentName, type: ComponentType): void { - const self = this; - if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } @@ -118,7 +106,7 @@ export class ComponentsDataWrapper delete components[serviceName]; } - self.applyCallbackToComponentGroup(type, serviceName, deleteComponentCallback); + this.applyCallbackToComponentGroup(type, serviceName, deleteComponentCallback); } /* -------- Utilities -------- */ diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts new file mode 100644 index 000000000..cfb89c0ef --- /dev/null +++ b/src/core/config/remote/components/block-node-component.ts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {ComponentType} from '../enumerations.js'; +import {BaseComponent} from './base-component.js'; +import {type Component, type NamespaceNameAsString} from '../types.js'; + +export class BlockNodeComponent extends BaseComponent { + public constructor(name: string, cluster: string, namespace: NamespaceNameAsString) { + super(ComponentType.BlockNode, name, cluster, namespace); + this.validate(); + } + + /* -------- Utilities -------- */ + + /** Handles creating instance of the class from plain object. */ + public static fromObject(component: Component): BlockNodeComponent { + const {name, cluster, namespace} = component; + return new BlockNodeComponent(name, cluster, namespace); + } +} diff --git a/src/core/config/remote/enumerations.ts b/src/core/config/remote/enumerations.ts index 2e452e2ec..e4e9ca4a8 100644 --- a/src/core/config/remote/enumerations.ts +++ b/src/core/config/remote/enumerations.ts @@ -5,6 +5,7 @@ */ export enum ComponentType { ConsensusNode = 'consensusNodes', + BlockNode = 'blockNode', HaProxy = 'haProxies', EnvoyProxy = 'envoyProxies', MirrorNode = 'mirrorNodes', From 5c159949e4dca89be7b8a1bd69b27e6f8b3be7f8 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 12:06:44 +0300 Subject: [PATCH 09/70] added better names for ComponentsDataWrapper's methods, removed warnings in the remote config related classes and worked on adding support for the new component type 'BlockNodeComponents' Signed-off-by: Zhan Milenkov --- src/commands/deployment.ts | 2 +- src/commands/explorer.ts | 4 +- src/commands/mirror-node.ts | 4 +- src/commands/network.ts | 6 +- src/commands/node/handlers.ts | 8 +- src/commands/node/tasks.ts | 6 +- src/commands/relay.ts | 4 +- .../config/remote/components-data-wrapper.ts | 159 ++++++++++-------- src/core/config/remote/metadata.ts | 2 +- .../remote/remote-config-data-wrapper.ts | 8 +- .../config/remote/remote-config-manager.ts | 98 ++++++----- .../remote/components-data-wrapper.test.ts | 14 +- 12 files changed, 177 insertions(+), 138 deletions(-) diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index f0363bfad..7313b17a5 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -697,7 +697,7 @@ export class DeploymentCommand extends BaseCommand { //* add the new nodes to components for (const nodeAlias of nodeAliases) { - remoteConfig.components.add( + remoteConfig.components.addNewComponent( new ConsensusNodeComponent( nodeAlias, clusterRef, diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index a27f887a6..3bcf63493 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -576,7 +576,7 @@ export class ExplorerCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('mirrorNodeExplorer', ComponentType.MirrorNodeExplorer); + remoteConfig.components.removeComponent('mirrorNodeExplorer', ComponentType.MirrorNodeExplorer); }); }, }; @@ -593,7 +593,7 @@ export class ExplorerCommand extends BaseCommand { config: {namespace}, } = context_; const cluster = this.remoteConfigManager.currentCluster; - remoteConfig.components.add(new MirrorNodeExplorerComponent('mirrorNodeExplorer', cluster, namespace.name)); + remoteConfig.components.addNewComponent(new MirrorNodeExplorerComponent('mirrorNodeExplorer', cluster, namespace.name)); }); }, }; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index bdee14b68..9e49458f1 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -881,7 +881,7 @@ export class MirrorNodeCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('mirrorNode', ComponentType.MirrorNode); + remoteConfig.components.removeComponent('mirrorNode', ComponentType.MirrorNode); }); }, }; @@ -898,7 +898,7 @@ export class MirrorNodeCommand extends BaseCommand { config: {namespace, clusterRef}, } = context_; - remoteConfig.components.add(new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name)); + remoteConfig.components.addNewComponent(new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name)); }); }, }; diff --git a/src/commands/network.ts b/src/commands/network.ts index 868e81fd5..a4c09009b 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -1332,7 +1332,7 @@ export class NetworkCommand extends BaseCommand { await this.remoteConfigManager.modify(async remoteConfig => { for (const consensusNode of context_.config.consensusNodes) { - remoteConfig.components.edit( + remoteConfig.components.editComponent( new ConsensusNodeComponent( consensusNode.name, consensusNode.cluster, @@ -1342,11 +1342,11 @@ export class NetworkCommand extends BaseCommand { ), ); - remoteConfig.components.add( + remoteConfig.components.addNewComponent( new EnvoyProxyComponent(`envoy-proxy-${consensusNode.name}`, consensusNode.cluster, namespace.name), ); - remoteConfig.components.add( + remoteConfig.components.addNewComponent( new HaProxyComponent(`haproxy-${consensusNode.name}`, consensusNode.cluster, namespace.name), ); } diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index c85bde18c..14c425f12 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -905,9 +905,9 @@ export class NodeCommandHandlers extends CommandHandler { title: 'Remove node and proxies from remote config', task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('Consensus node name', ComponentType.ConsensusNode); - remoteConfig.components.remove('Envoy proxy name', ComponentType.EnvoyProxy); - remoteConfig.components.remove('HaProxy name', ComponentType.HaProxy); + remoteConfig.components.removeComponent('Consensus node name', ComponentType.ConsensusNode); + remoteConfig.components.removeComponent('Envoy proxy name', ComponentType.EnvoyProxy); + remoteConfig.components.removeComponent('HaProxy name', ComponentType.HaProxy); }); }, }; @@ -933,7 +933,7 @@ export class NodeCommandHandlers extends CommandHandler { } = context_; for (const consensusNode of context_.config.consensusNodes) { - remoteConfig.components.edit( + remoteConfig.components.editComponent( new ConsensusNodeComponent( consensusNode.name, consensusNode.cluster, diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 98de6b83e..84ff2ea58 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -2523,7 +2523,7 @@ export class NodeCommandTasks { task.title += `: ${nodeAlias}`; await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.add( + remoteConfig.components.addNewComponent( new ConsensusNodeComponent( nodeAlias, clusterReference, @@ -2533,9 +2533,9 @@ export class NodeCommandTasks { ), ); - remoteConfig.components.add(new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace)); + remoteConfig.components.addNewComponent(new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace)); - remoteConfig.components.add(new HaProxyComponent(`haproxy-${nodeAlias}`, clusterReference, namespace)); + remoteConfig.components.addNewComponent(new HaProxyComponent(`haproxy-${nodeAlias}`, clusterReference, namespace)); }); context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes(); diff --git a/src/commands/relay.ts b/src/commands/relay.ts index fc5ebae68..7a8c9c78c 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -550,7 +550,7 @@ export class RelayCommand extends BaseCommand { } = context_; const cluster = this.remoteConfigManager.currentCluster; - remoteConfig.components.add(new RelayComponent('relay', cluster, namespace.name, nodeAliases)); + remoteConfig.components.addNewComponent(new RelayComponent('relay', cluster, namespace.name, nodeAliases)); }); }, }; @@ -563,7 +563,7 @@ export class RelayCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('relay', ComponentType.Relay); + remoteConfig.components.removeComponent('relay', ComponentType.Relay); }); }, }; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 3925e88bd..064696bb9 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -5,6 +5,7 @@ import {SoloError} from '../../errors/solo-error.js'; import {BaseComponent} from './components/base-component.js'; import {RelayComponent} from './components/relay-component.js'; import {HaProxyComponent} from './components/ha-proxy-component.js'; +import {BlockNodeComponent} from './components/block-node-component.js'; import {MirrorNodeComponent} from './components/mirror-node-component.js'; import {EnvoyProxyComponent} from './components/envoy-proxy-component.js'; import {ConsensusNodeComponent} from './components/consensus-node-component.js'; @@ -22,7 +23,6 @@ import {type ToObject, type Validate} from '../../../types/index.js'; import {Templates} from '../../templates.js'; import {type NodeAliases} from '../../../types/aliases.js'; import {type CloneTrait} from '../../../types/traits/clone-trait.js'; -import {BlockNodeComponent} from './components/block-node-component.js'; /** * Represent the components in the remote config and handles: @@ -48,7 +48,7 @@ export class ComponentsDataWrapper /* -------- Modifiers -------- */ /** Used to add new component to their respective group. */ - public add(component: BaseComponent): void { + public addNewComponent(component: BaseComponent): void { const serviceName: string = component.name; if (!serviceName || typeof serviceName !== 'string') { @@ -60,7 +60,7 @@ export class ComponentsDataWrapper } const addComponentCallback: (components: Record) => void = (components): void => { - if (this.exists(components, component)) { + if (this.checkComponentExists(components, component)) { throw new SoloError('Component exists', undefined, component.toObject()); } components[serviceName] = component; @@ -70,12 +70,13 @@ export class ComponentsDataWrapper } /** Used to edit an existing component from their respective group. */ - public edit(component: BaseComponent): void { + public editComponent(component: BaseComponent): void { const serviceName: string = component.name; if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } + if (!(component instanceof BaseComponent)) { throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); } @@ -91,20 +92,21 @@ export class ComponentsDataWrapper } /** Used to remove specific component from their respective group. */ - public remove(serviceName: ComponentName, type: ComponentType): void { + public removeComponent(serviceName: ComponentName, type: ComponentType): void { if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } + if (!Object.values(ComponentType).includes(type)) { throw new SoloError(`Invalid component type ${type}`); } - function deleteComponentCallback(components: Record): void { + const deleteComponentCallback: (components: Record) => void = (components): void => { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`); } delete components[serviceName]; - } + }; this.applyCallbackToComponentGroup(type, serviceName, deleteComponentCallback); } @@ -114,13 +116,12 @@ export class ComponentsDataWrapper public getComponent(type: ComponentType, serviceName: ComponentName): T { let component: T; - function getComponentCallback(components: Record): void { + const getComponentCallback: (components: Record) => void = (components): void => { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to read`); } - component = components[serviceName] as T; - } + }; this.applyCallbackToComponentGroup(type, serviceName, getComponentCallback); @@ -132,37 +133,48 @@ export class ComponentsDataWrapper * and pass it to a callback to apply modifications */ private applyCallbackToComponentGroup( - type: ComponentType, + componentType: ComponentType, serviceName: ComponentName, callback: (components: Record) => void, ): void { - switch (type) { + switch (componentType) { case ComponentType.Relay: { callback(this.relays); break; } + case ComponentType.HaProxy: { callback(this.haProxies); break; } + case ComponentType.MirrorNode: { callback(this.mirrorNodes); break; } + case ComponentType.EnvoyProxy: { callback(this.envoyProxies); break; } + case ComponentType.ConsensusNode: { callback(this.consensusNodes); break; } + case ComponentType.MirrorNodeExplorer: { callback(this.mirrorNodeExplorers); break; } + + case ComponentType.BlockNode: { + callback(this.blockNodes); + break; + } + default: { - throw new SoloError(`Unknown component type ${type}, service name: ${serviceName}`); + throw new SoloError(`Unknown component type ${componentType}, service name: ${serviceName}`); } } @@ -181,53 +193,61 @@ export class ComponentsDataWrapper const envoyProxies: Record = {}; const consensusNodes: Record = {}; const mirrorNodeExplorers: Record = {}; + const blockNodes: Record = {}; - for (const [type, subComponents] of Object.entries(components)) { - switch (type) { + for (const [componentType, subComponents] of Object.entries(components)) { + switch (componentType) { case ComponentType.Relay: { - for (const [name, component] of Object.entries(subComponents)) { - relays[name] = RelayComponent.fromObject(component as IRelayComponent); + for (const [serviceName, component] of Object.entries(subComponents)) { + relays[serviceName] = RelayComponent.fromObject(component as IRelayComponent); } break; } case ComponentType.HaProxy: { - for (const [name, component] of Object.entries(subComponents)) { - haProxies[name] = HaProxyComponent.fromObject(component); + for (const [serviceName, component] of Object.entries(subComponents)) { + haProxies[serviceName] = HaProxyComponent.fromObject(component); } break; } case ComponentType.MirrorNode: { - for (const [name, component] of Object.entries(subComponents)) { - mirrorNodes[name] = MirrorNodeComponent.fromObject(component); + for (const [serviceName, component] of Object.entries(subComponents)) { + mirrorNodes[serviceName] = MirrorNodeComponent.fromObject(component); } break; } case ComponentType.EnvoyProxy: { - for (const [name, component] of Object.entries(subComponents)) { - envoyProxies[name] = EnvoyProxyComponent.fromObject(component); + for (const [serviceName, component] of Object.entries(subComponents)) { + envoyProxies[serviceName] = EnvoyProxyComponent.fromObject(component); } break; } case ComponentType.ConsensusNode: { - for (const [name, component] of Object.entries(subComponents)) { - consensusNodes[name] = ConsensusNodeComponent.fromObject(component as IConsensusNodeComponent); + for (const [serviceName, component] of Object.entries(subComponents)) { + consensusNodes[serviceName] = ConsensusNodeComponent.fromObject(component as IConsensusNodeComponent); } break; } case ComponentType.MirrorNodeExplorer: { - for (const [name, component] of Object.entries(subComponents)) { - mirrorNodeExplorers[name] = MirrorNodeExplorerComponent.fromObject(component); + for (const [serviceName, component] of Object.entries(subComponents)) { + mirrorNodeExplorers[serviceName] = MirrorNodeExplorerComponent.fromObject(component); + } + break; + } + + case ComponentType.BlockNode: { + for (const [serviceName, component] of Object.entries(subComponents)) { + blockNodes[serviceName] = BlockNodeComponent.fromObject(component); } break; } default: { - throw new SoloError(`Unknown component type ${type}`); + throw new SoloError(`Unknown component type ${componentType}`); } } } @@ -257,63 +277,68 @@ export class ComponentsDataWrapper ); } - return new ComponentsDataWrapper(undefined, undefined, undefined, undefined, consensusNodeComponents, undefined); + return new ComponentsDataWrapper(undefined, undefined, undefined, undefined, consensusNodeComponents); } /** checks if component exists in the respective group */ - private exists(components: Record, newComponent: BaseComponent): boolean { - return Object.values(components).some(component => BaseComponent.compare(component, newComponent)); + private checkComponentExists(components: Record, newComponent: BaseComponent): boolean { + return Object.values(components).some((component): boolean => BaseComponent.compare(component, newComponent)); } - public validate(): void { - function testComponentsObject(components: Record, expectedInstance: any): void { - for (const [name, component] of Object.entries(components)) { - if (!name || typeof name !== 'string') { - throw new SoloError(`Invalid component service name ${{[name]: component?.constructor?.name}}`); - } + /** Validates that the component group mapping has only components from the expected instance */ + private validateComponentTypes(components: Record, expectedInstance: any): void { + for (const [serviceName, component] of Object.entries(components)) { + if (!serviceName || typeof serviceName !== 'string') { + throw new SoloError(`Invalid component service name ${{[serviceName]: component?.constructor?.name}}`); + } - if (!(component instanceof expectedInstance)) { - throw new SoloError( - `Invalid component type, service name: ${name}, ` + - `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`, - null, - {component}, - ); - } + if (!(component instanceof expectedInstance)) { + throw new SoloError( + `Invalid component type, service name: ${serviceName}, ` + + `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`, + undefined, + {component}, + ); } } - - testComponentsObject(this.relays, RelayComponent); - testComponentsObject(this.haProxies, HaProxyComponent); - testComponentsObject(this.mirrorNodes, MirrorNodeComponent); - testComponentsObject(this.envoyProxies, EnvoyProxyComponent); - testComponentsObject(this.consensusNodes, ConsensusNodeComponent); - testComponentsObject(this.mirrorNodeExplorers, MirrorNodeExplorerComponent); } - public toObject(): ComponentsDataStructure { - function transform(components: Record): Record { - const transformedComponents: Record = {}; + public validate(): void { + this.validateComponentTypes(this.relays, RelayComponent); + this.validateComponentTypes(this.haProxies, HaProxyComponent); + this.validateComponentTypes(this.mirrorNodes, MirrorNodeComponent); + this.validateComponentTypes(this.envoyProxies, EnvoyProxyComponent); + this.validateComponentTypes(this.consensusNodes, ConsensusNodeComponent); + this.validateComponentTypes(this.mirrorNodeExplorers, MirrorNodeExplorerComponent); + this.validateComponentTypes(this.blockNodes, BlockNodeComponent); + } - for (const [name, component] of Object.entries(components)) { - transformedComponents[name] = component.toObject() as Component; - } + private transformComponentGroupToObject( + components: Record, + ): Record { + const transformedComponents: Record = {}; - return transformedComponents; + for (const [serviceName, component] of Object.entries(components)) { + transformedComponents[serviceName] = component.toObject() as Component; } + return transformedComponents; + } + + public toObject(): ComponentsDataStructure { return { - [ComponentType.Relay]: transform(this.relays), - [ComponentType.HaProxy]: transform(this.haProxies), - [ComponentType.MirrorNode]: transform(this.mirrorNodes), - [ComponentType.EnvoyProxy]: transform(this.envoyProxies), - [ComponentType.ConsensusNode]: transform(this.consensusNodes), - [ComponentType.MirrorNodeExplorer]: transform(this.mirrorNodeExplorers), + [ComponentType.Relay]: this.transformComponentGroupToObject(this.relays), + [ComponentType.HaProxy]: this.transformComponentGroupToObject(this.haProxies), + [ComponentType.MirrorNode]: this.transformComponentGroupToObject(this.mirrorNodes), + [ComponentType.EnvoyProxy]: this.transformComponentGroupToObject(this.envoyProxies), + [ComponentType.ConsensusNode]: this.transformComponentGroupToObject(this.consensusNodes), + [ComponentType.MirrorNodeExplorer]: this.transformComponentGroupToObject(this.mirrorNodeExplorers), + [ComponentType.BlockNode]: this.transformComponentGroupToObject(this.blockNodes), }; } - public clone() { - const data = this.toObject(); + public clone(): ComponentsDataWrapper { + const data: ComponentsDataStructure = this.toObject(); return ComponentsDataWrapper.fromObject(data); } diff --git a/src/core/config/remote/metadata.ts b/src/core/config/remote/metadata.ts index f9095b776..9d6ef2458 100644 --- a/src/core/config/remote/metadata.ts +++ b/src/core/config/remote/metadata.ts @@ -120,7 +120,7 @@ export class RemoteConfigMetadata } public toObject(): RemoteConfigMetadataStructure { - const data = { + const data: RemoteConfigMetadataStructure = { namespace: this.namespace, deploymentName: this.deploymentName, state: this.state, diff --git a/src/core/config/remote/remote-config-data-wrapper.ts b/src/core/config/remote/remote-config-data-wrapper.ts index c5522f266..d10817347 100644 --- a/src/core/config/remote/remote-config-data-wrapper.ts +++ b/src/core/config/remote/remote-config-data-wrapper.ts @@ -16,7 +16,7 @@ import {type ConfigMap} from '../../../integration/kube/resources/config-map/con export class RemoteConfigDataWrapper implements Validate, ToObject { private readonly _version: Version = '1.0.0'; private _metadata: RemoteConfigMetadata; - private _clusters: Record; + private readonly _clusters: Record; private _components: ComponentsDataWrapper; private _commandHistory: string[]; private _lastExecutedCommand: string; @@ -96,14 +96,14 @@ export class RemoteConfigDataWrapper implements Validate, ToObject typeof c !== 'string')) { + if (!Array.isArray(this.commandHistory) || this.commandHistory.some((c): boolean => typeof c !== 'string')) { throw new SoloError(`Invalid remote config command history: ${this.commandHistory}`); } diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index b59eb1923..60944142c 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -9,7 +9,14 @@ import * as yaml from 'yaml'; import {ComponentsDataWrapper} from './components-data-wrapper.js'; import {RemoteConfigValidator} from './remote-config-validator.js'; import {type K8Factory} from '../../../integration/kube/k8-factory.js'; -import {type ClusterReference, type ClusterReferences, type DeploymentName, type Version} from './types.js'; +import { + type ClusterReference, + type ClusterReferences, + type Context, + type DeploymentName, + type EmailAddress, + type Version, +} from './types.js'; import {type SoloLogger} from '../../logging/solo-logger.js'; import {type ConfigManager} from '../../config-manager.js'; import {type LocalConfig} from '../local/local-config.js'; @@ -28,6 +35,7 @@ import {promptTheUserForDeployment, resolveNamespaceFromDeployment} from '../../ import {type DeploymentStates} from './enumerations.js'; import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; import {getSoloVersion} from '../../../../version.js'; +import {DeploymentStructure} from '../local/local-config-data.js'; /** * Uses Kubernetes ConfigMaps to manage the remote configuration data by creating, loading, modifying, @@ -107,7 +115,7 @@ export class RemoteConfigManager { namespace: NamespaceName, deployment: DeploymentName, clusterReference: ClusterReference, - context: string, + context: Context, dnsBaseDomain: string, dnsConsensusNodePattern: string, ): Promise { @@ -121,10 +129,10 @@ export class RemoteConfigManager { ), }; - const lastUpdatedAt = new Date(); - const email = this.localConfig.userEmailAddress; - const soloVersion = getSoloVersion(); - const currentCommand = argv._.join(' '); + const lastUpdatedAt: Date = new Date(); + const email: EmailAddress = this.localConfig.userEmailAddress; + const soloVersion: string = getSoloVersion(); + const currentCommand: string = argv._.join(' '); this.remoteConfig = new RemoteConfigDataWrapper({ clusters, @@ -156,12 +164,12 @@ export class RemoteConfigManager { * @param context - The context to use for the Kubernetes client. * @returns true if the configuration is loaded successfully. */ - private async load(namespace?: NamespaceName, context?: string): Promise { + private async load(namespace?: NamespaceName, context?: Context): Promise { if (this.remoteConfig) { return; } try { - const configMap = await this.getConfigMap(namespace, context); + const configMap: ConfigMap = await this.getConfigMap(namespace, context); this.remoteConfig = RemoteConfigDataWrapper.fromConfigmap(this.configManager, configMap); } catch (error) { @@ -173,8 +181,9 @@ export class RemoteConfigManager { * Loads the remote configuration, performs a validation and returns it * @returns RemoteConfigDataWrapper */ - public async get(context?: string): Promise { - const namespace = this.configManager.getFlag(flags.namespace) ?? (await this.getNamespace()); + public async get(context?: Context): Promise { + const namespace: NamespaceName = + this.configManager.getFlag(flags.namespace) ?? (await this.getNamespace()); await this.load(namespace, context); try { @@ -201,8 +210,8 @@ export class RemoteConfigManager { public static compare(remoteConfig1: RemoteConfigDataWrapper, remoteConfig2: RemoteConfigDataWrapper): boolean { // Compare clusters - const clusters1 = Object.keys(remoteConfig1.clusters); - const clusters2 = Object.keys(remoteConfig2.clusters); + const clusters1: string[] = Object.keys(remoteConfig1.clusters); + const clusters2: string[] = Object.keys(remoteConfig2.clusters); if (clusters1.length !== clusters2.length) { return false; } @@ -249,8 +258,8 @@ export class RemoteConfigManager { skipConsensusNodesValidation, ); - const currentCommand = argv._?.join(' '); - const commandArguments = flags.stringifyArgv(argv); + const currentCommand: string = argv._?.join(' '); + const commandArguments: string = flags.stringifyArgv(argv); this.remoteConfig!.addCommandToHistory( `Executed by ${this.localConfig.userEmailAddress}: ${currentCommand} ${commandArguments}`.trim(), @@ -267,7 +276,7 @@ export class RemoteConfigManager { const command: string = argv._[0]; const subcommand: string = argv._[1]; - const isCommandUsingSoloChartVersionFlag = + const isCommandUsingSoloChartVersionFlag: boolean = (command === 'network' && subcommand === 'deploy') || (command === 'network' && subcommand === 'refresh') || (command === 'node' && subcommand === 'update') || @@ -283,7 +292,7 @@ export class RemoteConfigManager { this.remoteConfig.metadata.soloChartVersion = flags.soloChartVersion.definition.defaultValue as Version; } - const isCommandUsingReleaseTagVersionFlag = + const isCommandUsingReleaseTagVersionFlag: boolean = (command === 'node' && subcommand !== 'keys' && subcommand !== 'logs' && subcommand !== 'states') || (command === 'network' && subcommand === 'deploy'); @@ -319,7 +328,7 @@ export class RemoteConfigManager { /** Empties the component data inside the remote config */ public async deleteComponents(): Promise { - await this.modify(async remoteConfig => { + await this.modify(async (remoteConfig): Promise => { remoteConfig.components = ComponentsDataWrapper.initializeEmpty(); }); } @@ -336,7 +345,7 @@ export class RemoteConfigManager { * @returns the remote configuration data. * @throws if the ConfigMap could not be read and the error is not a 404 status, will throw a SoloError {@link SoloError} */ - public async getConfigMap(namespace?: NamespaceName, context?: string): Promise { + public async getConfigMap(namespace?: NamespaceName, context?: Context): Promise { if (!namespace) { namespace = await this.getNamespace(); } @@ -345,7 +354,7 @@ export class RemoteConfigManager { } try { - const configMap = await this.k8Factory + const configMap: ConfigMap = await this.k8Factory .getK8(context) .configMaps() .read(namespace, constants.SOLO_REMOTE_CONFIGMAP_NAME); @@ -366,11 +375,11 @@ export class RemoteConfigManager { /** * Creates a new ConfigMap entry in the Kubernetes cluster with the remote configuration data. */ - public async createConfigMap(context?: string): Promise { - const namespace = await this.getNamespace(); - const name = constants.SOLO_REMOTE_CONFIGMAP_NAME; - const labels = constants.SOLO_REMOTE_CONFIGMAP_LABELS; - const data = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; + public async createConfigMap(context?: Context): Promise { + const namespace: NamespaceName = await this.getNamespace(); + const name: string = constants.SOLO_REMOTE_CONFIGMAP_NAME; + const labels: Record = constants.SOLO_REMOTE_CONFIGMAP_LABELS; + const data: {'remote-config-data': string} = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; await this.k8Factory.getK8(context).configMaps().create(namespace, name, labels, data); } @@ -379,12 +388,12 @@ export class RemoteConfigManager { * Replaces an existing ConfigMap in the Kubernetes cluster with the current remote configuration data. */ private async replaceConfigMap(): Promise { - const namespace = await this.getNamespace(); - const name = constants.SOLO_REMOTE_CONFIGMAP_NAME; - const labels = constants.SOLO_REMOTE_CONFIGMAP_LABELS; - const data = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; + const namespace: NamespaceName = await this.getNamespace(); + const name: string = constants.SOLO_REMOTE_CONFIGMAP_NAME; + const labels: Record = constants.SOLO_REMOTE_CONFIGMAP_LABELS; + const data: {'remote-config-data': string} = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; - const deploymentName = this.configManager.getFlag(flags.deployment); + const deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment); if (!deploymentName) { throw new SoloError('Failed to get deployment'); @@ -396,10 +405,15 @@ export class RemoteConfigManager { throw new SoloError(`Failed to get get cluster refs from local config for deployment ${deploymentName}`); } - const contexts = clusterReferences.map(clusterReference => this.localConfig.clusterRefs[clusterReference]); + const contexts: Context[] = clusterReferences.map( + (clusterReference): string => this.localConfig.clusterRefs[clusterReference], + ); await Promise.all( - contexts.map(context => this.k8Factory.getK8(context).configMaps().replace(namespace, name, labels, data)), + contexts.map( + (context): Promise => + this.k8Factory.getK8(context).configMaps().replace(namespace, name, labels, data), + ), ); } @@ -409,8 +423,8 @@ export class RemoteConfigManager { } // TODO: Current quick fix for commands where namespace is not passed - let deploymentName = this.configManager.getFlag(flags.deployment); - let currentDeployment = this.localConfig.deployments[deploymentName]; + let deploymentName: string = this.configManager.getFlag(flags.deployment); + let currentDeployment: DeploymentStructure = this.localConfig.deployments[deploymentName]; if (!deploymentName) { deploymentName = await promptTheUserForDeployment(this.configManager); @@ -429,7 +443,7 @@ export class RemoteConfigManager { throw new SoloError(`Selected deployment name is not set in local config - ${deploymentName}`); } - const namespace = currentDeployment.namespace; + const namespace: string = currentDeployment.namespace; this.logger.warn(`Namespace not found in flags, setting it to: ${namespace}`); this.configManager.setFlag(flags.namespace, namespace); @@ -441,7 +455,7 @@ export class RemoteConfigManager { return; } - const context = this.getContextForFirstCluster() ?? this.k8Factory.default().contexts().readCurrent(); + const context: Context = this.getContextForFirstCluster() ?? this.k8Factory.default().contexts().readCurrent(); if (!context) { throw new SoloError("Context is not passed and default one can't be acquired"); @@ -475,8 +489,8 @@ export class RemoteConfigManager { for (const node of Object.values(this.components.consensusNodes)) { this.logger.debug(`Adding consensus node ${node.name} , node.cluster = ${node.cluster}`); - const cluster = this.clusters[node.cluster]; - const context = this.localConfig.clusterRefs[node.cluster]; + const cluster: Cluster = this.clusters[node.cluster]; + const context: Context = this.localConfig.clusterRefs[node.cluster]; consensusNodes.push( new ConsensusNode( @@ -507,8 +521,8 @@ export class RemoteConfigManager { * Gets a list of distinct contexts from the consensus nodes. * @returns an array of context strings. */ - public getContexts(): string[] { - return [...new Set(this.getConsensusNodes().map(node => node.context))]; + public getContexts(): Context[] { + return [...new Set(this.getConsensusNodes().map((node): Context => node.context))]; } /** @@ -516,7 +530,7 @@ export class RemoteConfigManager { * @returns an object of cluster references. */ public getClusterRefs(): ClusterReferences { - const nodes = this.getConsensusNodes(); + const nodes: ConsensusNode[] = this.getConsensusNodes(); const accumulator: ClusterReferences = {}; for (const node of nodes) { @@ -527,11 +541,11 @@ export class RemoteConfigManager { } private getContextForFirstCluster(): string { - const deploymentName = this.configManager.getFlag(flags.deployment); + const deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment); const clusterReference: ClusterReference = this.localConfig.deployments[deploymentName].clusters[0]; - const context = this.localConfig.clusterRefs[clusterReference]; + const context: Context = this.localConfig.clusterRefs[clusterReference]; this.logger.debug(`Using context ${context} for cluster ${clusterReference} for deployment ${deploymentName}`); diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index aa61c7833..232d50d5d 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -99,7 +99,7 @@ describe('ComponentsDataWrapper', () => { const existingComponent = consensusNodes[serviceName]; - expect(() => componentsDataWrapper.add(existingComponent)).to.throw(SoloError, 'Component exists'); + expect(() => componentsDataWrapper.addNewComponent(existingComponent)).to.throw(SoloError, 'Component exists'); }); it('should be able to add new component with the .add() method', () => { @@ -115,7 +115,7 @@ describe('ComponentsDataWrapper', () => { }; const newComponent = new EnvoyProxyComponent(name, cluster, namespace); - componentsDataWrapper.add(newComponent); + componentsDataWrapper.addNewComponent(newComponent); const componentDataWrapperObject = componentsDataWrapper.toObject(); @@ -139,13 +139,13 @@ describe('ComponentsDataWrapper', () => { } = createComponentsDataWrapper(); const relayComponent = relays[serviceName]; - componentsDataWrapper.edit(relayComponent); + componentsDataWrapper.editComponent(relayComponent); const newCluster = 'newCluster'; const newReplayComponent = new RelayComponent(relayComponent.name, newCluster, namespace); - componentsDataWrapper.edit(newReplayComponent); + componentsDataWrapper.editComponent(newReplayComponent); expect(componentsDataWrapper.toObject()[ComponentType.Relay][relayComponent.name].cluster).to.equal(newCluster); }); @@ -160,7 +160,7 @@ describe('ComponentsDataWrapper', () => { const relay = relays[serviceName]; relay.name = notFoundServiceName; - expect(() => componentsDataWrapper.edit(relay)).to.throw( + expect(() => componentsDataWrapper.editComponent(relay)).to.throw( SoloError, `Component doesn't exist, name: ${notFoundServiceName}`, ); @@ -172,7 +172,7 @@ describe('ComponentsDataWrapper', () => { serviceName, } = createComponentsDataWrapper(); - componentsDataWrapper.remove(serviceName, ComponentType.Relay); + componentsDataWrapper.removeComponent(serviceName, ComponentType.Relay); expect(componentsDataWrapper.relays).not.to.have.own.property(serviceName); }); @@ -184,7 +184,7 @@ describe('ComponentsDataWrapper', () => { const notFoundServiceName = 'not_found'; - expect(() => componentsDataWrapper.remove(notFoundServiceName, ComponentType.Relay)).to.throw( + expect(() => componentsDataWrapper.removeComponent(notFoundServiceName, ComponentType.Relay)).to.throw( SoloError, `Component ${notFoundServiceName} of type ${ComponentType.Relay} not found while attempting to remove`, ); From 9315010668a550815813f14b431dc5afc27f3108 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 13:32:09 +0300 Subject: [PATCH 10/70] checkpoint while refactoring components validation to reduce redundancy and make it less error prone Signed-off-by: Zhan Milenkov --- .../config/remote/remote-config-validator.ts | 336 +++++++++++------- 1 file changed, 216 insertions(+), 120 deletions(-) diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 79063ebfb..7e1a75a8b 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -10,12 +10,81 @@ import {type BaseComponent} from './components/base-component.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {type LocalConfig} from '../local/local-config.js'; import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; +import {type Context} from './types.js'; +import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; + +type validationCallback = ( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, +) => Promise[]; /** * Static class is used to validate that components in the remote config * are present in the kubernetes cluster, and throw errors if there is mismatch. */ export class RemoteConfigValidator { + private static componentValidations: Record< + string, + {label: string[] | ((c: BaseComponent) => string[]); type: string; skipCondition?: (c: BaseComponent) => boolean} + > = { + relays: {label: [constants.SOLO_RELAY_LABEL], type: 'Relay'}, + haProxies: {label: (c: BaseComponent): string[] => [`app=${c.name}`], type: 'HaProxy'}, + mirrorNodes: {label: constants.SOLO_HEDERA_MIRROR_IMPORTER, type: 'Block nodes'}, + envoyProxies: {label: (c: BaseComponent): string[] => [`app=${c.name}`], type: 'Envoy proxy'}, + mirrorNodeExplorers: {label: [constants.SOLO_HEDERA_EXPLORER_LABEL], type: 'Mirror node explorer'}, + blockNodes: {label: (c: BaseComponent): string[] => [`app.kubernetes.io/instance=${c.name}`], type: 'Block nodes'}, + consensusNodes: { + label: (c: BaseComponent): string[] => [`app=network-${c.name}`], + type: 'Consensus node', + skipCondition(c: ConsensusNodeComponent): boolean { + return c.state === ConsensusNodeStates.REQUESTED || c.state === ConsensusNodeStates.NON_DEPLOYED; + }, + }, + }; + + public static async validateComponents( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + skipConsensusNodes: boolean + ): Promise { + const validations = Object.entries(this.componentValidations) + .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) + .flatMap(([key, { label, type, skipCondition }]) => + this.validateComponentList(namespace, components[key as keyof ComponentsDataWrapper], k8Factory, localConfig, label, type, skipCondition) + ) + + await Promise.all(validations) + } + + private static validateComponentList( + namespace: NamespaceName, + components: Record, + k8Factory: K8Factory, + localConfig: LocalConfig, + label: string[] | ((c: BaseComponent) => string[]), + type: string, + skipCondition?: (component: BaseComponent) => boolean + ): Promise[] { + return Object.values(components).map(async (component) => { + if (skipCondition?.(component)) return + + await this.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + typeof label === 'function' ? label(component) : label, + type + ) + }) + } + + // + /** * Gathers and handles validation of all components. * @@ -32,146 +101,173 @@ export class RemoteConfigValidator { localConfig: LocalConfig, skipConsensusNodes: boolean, ): Promise { - await Promise.all([ - ...RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig), - ...RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig), - ...RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig), - ...RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig), - ...RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), - ...(skipConsensusNodes - ? [] - : RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig)), - ]); + const validationCallbacks: validationCallback[] = [ + RemoteConfigValidator.validateRelays, + RemoteConfigValidator.validateHaProxies, + RemoteConfigValidator.validateMirrorNodes, + RemoteConfigValidator.validateEnvoyProxies, + RemoteConfigValidator.validateMirrorNodeExplorers, + RemoteConfigValidator.validateBlockNodes, + ]; + + if (!skipConsensusNodes) { + validationCallbacks.push(RemoteConfigValidator.validateConsensusNodes); + } + + await Promise.all( + validationCallbacks.flatMap((validator): Promise[] => + validator(namespace, components, k8Factory, localConfig), + ), + ); } - private static validateRelays( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, - ): Promise[] { - return Object.values(components.relays).map(async component => { - const context = localConfig.clusterRefs[component.cluster]; - const labels = [constants.SOLO_RELAY_LABEL]; - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - - if (pods.length === 0) { - throw new Error('Pod not found'); - } // to return the generic error message - } catch (error) { - RemoteConfigValidator.throwValidationError('Relay', component, error); - } + private static validateRelays: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.relays).map(async (component): Promise => { + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + [constants.SOLO_RELAY_LABEL], + 'Relay', + ); }); - } + }; - private static validateHaProxies( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, - ): Promise[] { - return Object.values(components.haProxies).map(async component => { - const context = localConfig.clusterRefs[component.cluster]; - const labels = [`app=${component.name}`]; - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - - if (pods.length === 0) { - throw new Error('Pod not found'); - } // to return the generic error message - } catch (error) { - RemoteConfigValidator.throwValidationError('HaProxy', component, error); - } + private static validateHaProxies: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.haProxies).map(async (component): Promise => { + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + [`app=${component.name}`], + 'HaProxy', + ); }); - } + }; - private static validateMirrorNodes( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, - ): Promise[] { - return Object.values(components.mirrorNodes).map(async component => { - const context = localConfig.clusterRefs[component.cluster]; - const labels = constants.SOLO_HEDERA_MIRROR_IMPORTER; - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - - if (pods.length === 0) { - throw new Error('Pod not found'); - } // to return the generic error message - } catch (error) { - RemoteConfigValidator.throwValidationError('Mirror node', component, error); - } + private static validateMirrorNodes: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.mirrorNodes).map(async (component): Promise => { + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + constants.SOLO_HEDERA_MIRROR_IMPORTER, + 'Block nodes', + ); }); - } + }; - private static validateEnvoyProxies( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, - ): Promise[] { - return Object.values(components.envoyProxies).map(async component => { - const context = localConfig.clusterRefs[component.cluster]; - const labels = [`app=${component.name}`]; - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - - if (pods.length === 0) { - throw new Error('Pod not found'); - } // to return the generic error message - } catch (error) { - RemoteConfigValidator.throwValidationError('Envoy proxy', component, error); - } + private static validateEnvoyProxies: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.envoyProxies).map(async (component): Promise => { + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + [`app=${component.name}`], + 'Envoy proxy', + ); }); - } + }; - private static validateConsensusNodes( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, - ): Promise[] { - return Object.values(components.consensusNodes).map(async component => { + private static validateConsensusNodes: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.consensusNodes).map(async (component): Promise => { if (component.state === ConsensusNodeStates.REQUESTED || component.state === ConsensusNodeStates.NON_DEPLOYED) { return; } - const context = localConfig.clusterRefs[component.cluster]; - const labels = [`app=network-${component.name}`]; - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + [`app=network-${component.name}`], + 'Consensus node', + ); + }); + }; - if (pods.length === 0) { - throw new Error('Pod not found'); - } // to return the generic error message - } catch (error) { - RemoteConfigValidator.throwValidationError('Consensus node', component, error); - } + private static validateMirrorNodeExplorers: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.mirrorNodeExplorers).map(async (component): Promise => { + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + [constants.SOLO_HEDERA_EXPLORER_LABEL], + 'Mirror node explorer', + ); }); - } + }; - private static validateMirrorNodeExplorers( + private static validateBlockNodes: validationCallback = ( + namespace, + components, + k8Factory, + localConfig, + ): Promise[] => { + return Object.values(components.blockNodes).map(async (component): Promise => { + await RemoteConfigValidator.validateComponent( + namespace, + component, + k8Factory, + localConfig.clusterRefs[component.cluster], + [`app.kubernetes.io/instance=${component.name}`], + 'Block nodes', + ); + }); + }; + + private static async validateComponent( namespace: NamespaceName, - components: ComponentsDataWrapper, + component: BaseComponent, k8Factory: K8Factory, - localConfig: LocalConfig, - ): Promise[] { - return Object.values(components.mirrorNodeExplorers).map(async component => { - const context = localConfig.clusterRefs[component.cluster]; - const labels = [constants.SOLO_HEDERA_EXPLORER_LABEL]; - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - - if (pods.length === 0) { - throw new Error('Pod not found'); - } // to return the generic error message - } catch (error) { - RemoteConfigValidator.throwValidationError('Mirror node explorer', component, error); + context: Context, + labels: string[], + errorType: string, + ): Promise { + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); + + if (pods.length === 0) { + throw new Error('Pod not found'); // to return the generic error message } - }); + } catch (error) { + RemoteConfigValidator.throwValidationError(errorType, component, error); + } } /** From 6548a0e529d276eb94a4622001002fc701080ced Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 13:43:36 +0300 Subject: [PATCH 11/70] refactored RemoteConfigValidator Signed-off-by: Zhan Milenkov --- .../config/remote/remote-config-validator.ts | 212 ++---------------- 1 file changed, 19 insertions(+), 193 deletions(-) diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 7e1a75a8b..b91ca39c8 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -3,7 +3,6 @@ import * as constants from '../../constants.js'; import {SoloError} from '../../errors/solo-error.js'; import {ConsensusNodeStates} from './enumerations.js'; - import {type K8Factory} from '../../../integration/kube/k8-factory.js'; import {type ComponentsDataWrapper} from './components-data-wrapper.js'; import {type BaseComponent} from './components/base-component.js'; @@ -13,13 +12,6 @@ import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; -type validationCallback = ( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, -) => Promise[]; - /** * Static class is used to validate that components in the remote config * are present in the kubernetes cluster, and throw errors if there is mismatch. @@ -27,7 +19,11 @@ type validationCallback = ( export class RemoteConfigValidator { private static componentValidations: Record< string, - {label: string[] | ((c: BaseComponent) => string[]); type: string; skipCondition?: (c: BaseComponent) => boolean} + { + label: string[] | ((c: BaseComponent) => string[]); + type: string; + skipCondition?: (c: BaseComponent) => boolean; + } > = { relays: {label: [constants.SOLO_RELAY_LABEL], type: 'Relay'}, haProxies: {label: (c: BaseComponent): string[] => [`app=${c.name}`], type: 'HaProxy'}, @@ -49,15 +45,15 @@ export class RemoteConfigValidator { components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, - skipConsensusNodes: boolean + skipConsensusNodes: boolean, ): Promise { - const validations = Object.entries(this.componentValidations) - .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) - .flatMap(([key, { label, type, skipCondition }]) => - this.validateComponentList(namespace, components[key as keyof ComponentsDataWrapper], k8Factory, localConfig, label, type, skipCondition) - ) + const validations: Promise[] = Object.entries(this.componentValidations) + .filter(([key]): boolean => key !== 'consensusNodes' || !skipConsensusNodes) + .flatMap(([key, {label, type, skipCondition}]): Promise[] => + this.validateComponentList(namespace, components[key], k8Factory, localConfig, label, type, skipCondition), + ); - await Promise.all(validations) + await Promise.all(validations); } private static validateComponentList( @@ -67,189 +63,19 @@ export class RemoteConfigValidator { localConfig: LocalConfig, label: string[] | ((c: BaseComponent) => string[]), type: string, - skipCondition?: (component: BaseComponent) => boolean + skipCondition?: (component: BaseComponent) => boolean, ): Promise[] { - return Object.values(components).map(async (component) => { - if (skipCondition?.(component)) return - - await this.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - typeof label === 'function' ? label(component) : label, - type - ) - }) - } - - // - - /** - * Gathers and handles validation of all components. - * - * @param namespace - namespace to validate the components in. - * @param components - components to validate. - * @param k8Factory - to validate the elements. - * @param localConfig - to get the context from cluster - * @param skipConsensusNodes - whether to validate consensus nodes - */ - public static async validateComponents( - namespace: NamespaceName, - components: ComponentsDataWrapper, - k8Factory: K8Factory, - localConfig: LocalConfig, - skipConsensusNodes: boolean, - ): Promise { - const validationCallbacks: validationCallback[] = [ - RemoteConfigValidator.validateRelays, - RemoteConfigValidator.validateHaProxies, - RemoteConfigValidator.validateMirrorNodes, - RemoteConfigValidator.validateEnvoyProxies, - RemoteConfigValidator.validateMirrorNodeExplorers, - RemoteConfigValidator.validateBlockNodes, - ]; - - if (!skipConsensusNodes) { - validationCallbacks.push(RemoteConfigValidator.validateConsensusNodes); - } - - await Promise.all( - validationCallbacks.flatMap((validator): Promise[] => - validator(namespace, components, k8Factory, localConfig), - ), - ); - } - - private static validateRelays: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.relays).map(async (component): Promise => { - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - [constants.SOLO_RELAY_LABEL], - 'Relay', - ); - }); - }; - - private static validateHaProxies: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.haProxies).map(async (component): Promise => { - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - [`app=${component.name}`], - 'HaProxy', - ); - }); - }; - - private static validateMirrorNodes: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.mirrorNodes).map(async (component): Promise => { - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - constants.SOLO_HEDERA_MIRROR_IMPORTER, - 'Block nodes', - ); - }); - }; - - private static validateEnvoyProxies: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.envoyProxies).map(async (component): Promise => { - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - [`app=${component.name}`], - 'Envoy proxy', - ); - }); - }; - - private static validateConsensusNodes: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.consensusNodes).map(async (component): Promise => { - if (component.state === ConsensusNodeStates.REQUESTED || component.state === ConsensusNodeStates.NON_DEPLOYED) { + return Object.values(components).map(async (component): Promise => { + if (skipCondition?.(component)) { return; } - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - [`app=network-${component.name}`], - 'Consensus node', - ); - }); - }; - - private static validateMirrorNodeExplorers: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.mirrorNodeExplorers).map(async (component): Promise => { - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - [constants.SOLO_HEDERA_EXPLORER_LABEL], - 'Mirror node explorer', - ); - }); - }; + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = typeof label === 'function' ? label(component) : label; - private static validateBlockNodes: validationCallback = ( - namespace, - components, - k8Factory, - localConfig, - ): Promise[] => { - return Object.values(components.blockNodes).map(async (component): Promise => { - await RemoteConfigValidator.validateComponent( - namespace, - component, - k8Factory, - localConfig.clusterRefs[component.cluster], - [`app.kubernetes.io/instance=${component.name}`], - 'Block nodes', - ); + await this.validateComponent(namespace, component, k8Factory, context, labels, type); }); - }; + } private static async validateComponent( namespace: NamespaceName, From 7d9611e613a042fd84f7ffdeb4d3082217047e99 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 14:18:18 +0300 Subject: [PATCH 12/70] add logic for creating the new block node component and figuring out the releaseName Signed-off-by: Zhan Milenkov --- src/commands/block-nodes.ts | 60 ++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/commands/block-nodes.ts b/src/commands/block-nodes.ts index c3bf30893..34f9f1f28 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-nodes.ts @@ -17,12 +17,13 @@ import { } from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; -import {type CommandDefinition, type Optional} from '../types/index.js'; +import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import * as versions from '../../version.js'; import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import os from 'node:os'; +import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; interface BlockNodesDeployConfigClass { chartVersion: string; @@ -39,6 +40,8 @@ interface BlockNodesDeployConfigClass { context: string; isChartInstalled: boolean; valuesArg: string; + blockNodeName: string; + releaseName: string; } interface BlockNodesDeployContext { @@ -144,6 +147,30 @@ export class BlockNodesCommand extends BaseCommand { return ListrLock.newAcquireLockTask(lease, task); }, }, + { + title: 'Prepare release name and block node name', + task: async (context_): Promise => { + const config: BlockNodesDeployConfigClass = context_.config; + + let newBlockNodeNumber: number = 1; + + if ( + this.remoteConfigManager.components.blockNodes && + Object.values(this.remoteConfigManager.components.blockNodes).length > 0 + ) { + for (const blockNodeComponent of Object.values(this.remoteConfigManager.components.blockNodes)) { + const blockNodeNumber: number = +blockNodeComponent.name.split('-').at(-1); + if (blockNodeNumber >= newBlockNodeNumber) { + newBlockNodeNumber = blockNodeNumber + 1; + } + } + } + + const releaseName: string = this.getReleaseName(newBlockNodeNumber.toString()); + config.blockNodeName = releaseName; + config.releaseName = releaseName; + }, + }, { title: 'Check chart is installed', task: async (context_): Promise => { @@ -151,7 +178,7 @@ export class BlockNodesCommand extends BaseCommand { config.isChartInstalled = await this.chartManager.isChartInstalled( config.namespace, - this.getReleaseName('33') /* TODO */, + config.releaseName, config.context, ); }, @@ -171,7 +198,7 @@ export class BlockNodesCommand extends BaseCommand { await this.chartManager.install( config.namespace, - this.getReleaseName('33') /* TODO */, + config.releaseName, constants.BLOCK_NODE_CHART, constants.BLOCK_NODE_CHART_URL, config.chartVersion, @@ -179,7 +206,7 @@ export class BlockNodesCommand extends BaseCommand { config.context, ); - showVersionBanner(this.logger, this.getReleaseName('33') /* TODO */, versions.BLOCK_NODE_VERSION); + showVersionBanner(this.logger, config.releaseName, versions.BLOCK_NODE_VERSION); }, }, { @@ -192,7 +219,7 @@ export class BlockNodesCommand extends BaseCommand { .pods() .waitForRunningPhase( config.namespace, - [`app.kubernetes.io/instance=${this.getReleaseName('33') /* TODO */}`], + [`app.kubernetes.io/instance=${config.releaseName}`], constants.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS, constants.BLOCK_NODES_PODS_RUNNING_DELAY, ); @@ -208,18 +235,16 @@ export class BlockNodesCommand extends BaseCommand { .pods() .waitForReadyStatus( config.namespace, - [`app.kubernetes.io/instance=${this.getReleaseName('33') /* TODO */}`], + [`app.kubernetes.io/instance=${config.releaseName}`], constants.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS, constants.BLOCK_NODES_PODS_RUNNING_DELAY, ); } catch (error) { - throw new SoloError( - `BlockNodes ${this.getReleaseName('33') /* TODO */} is not ready: ${error.message}`, - error, - ); + throw new SoloError(`BlockNodes ${config.releaseName} is not ready: ${error.message}`, error); } }, }, + this.addBlockNodeComponent(), ], { concurrent: false, @@ -238,6 +263,21 @@ export class BlockNodesCommand extends BaseCommand { return true; } + /** Adds the relay component to remote config. */ + public addBlockNodeComponent(): SoloListrTask { + return { + title: 'Add relay component in remote config', + skip: (): boolean => !this.remoteConfigManager.isLoaded(), + task: async (context_): Promise => { + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + const {namespace, clusterRef, blockNodeName} = context_.config; + + remoteConfig.components.addNewComponent(new BlockNodeComponent(blockNodeName, clusterRef, namespace.name)); + }); + }, + }; + } + public getCommandDefinition(): CommandDefinition { const self: this = this; return { From 1baf5f8f2bb4aa5a8f51ca13f17f91596ac5b8cf Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 15:04:33 +0300 Subject: [PATCH 13/70] rename 'block nodes' to 'block node' ( plural to singular ) Signed-off-by: Zhan Milenkov --- ...des-values.yaml => block-node-values.yaml} | 0 .../{block-nodes.ts => block-node.ts} | 85 +++++++++---------- src/commands/flags.ts | 11 +-- src/commands/index.ts | 4 +- src/core/constants.ts | 6 +- 5 files changed, 53 insertions(+), 53 deletions(-) rename resources/{block-nodes-values.yaml => block-node-values.yaml} (100%) rename src/commands/{block-nodes.ts => block-node.ts} (75%) diff --git a/resources/block-nodes-values.yaml b/resources/block-node-values.yaml similarity index 100% rename from resources/block-nodes-values.yaml rename to resources/block-node-values.yaml diff --git a/src/commands/block-nodes.ts b/src/commands/block-node.ts similarity index 75% rename from src/commands/block-nodes.ts rename to src/commands/block-node.ts index 34f9f1f28..9133adebd 100644 --- a/src/commands/block-nodes.ts +++ b/src/commands/block-node.ts @@ -25,7 +25,7 @@ import {type NamespaceName} from '../integration/kube/resources/namespace/namesp import os from 'node:os'; import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; -interface BlockNodesDeployConfigClass { +interface BlockNodeDeployConfigClass { chartVersion: string; chartDirectory: string; clusterRef: ClusterReference; @@ -44,19 +44,19 @@ interface BlockNodesDeployConfigClass { releaseName: string; } -interface BlockNodesDeployContext { - config: BlockNodesDeployConfigClass; +interface BlockNodeDeployContext { + config: BlockNodeDeployConfigClass; } -export class BlockNodesCommand extends BaseCommand { - public static readonly COMMAND_NAME: string = 'block-nodes'; +export class BlockNodeCommand extends BaseCommand { + public static readonly COMMAND_NAME: string = 'block-node'; private static readonly DEPLOY_CONFIGS_NAME: string = 'deployConfigs'; private static readonly DEPLOY_FLAGS_LIST: CommandFlags = { required: [], optional: [ - flags.blockNodesChartVersion, + flags.blockNodeChartVersion, flags.chartDirectory, flags.clusterRef, flags.deployment, @@ -68,10 +68,10 @@ export class BlockNodesCommand extends BaseCommand { ], }; - private async prepareValuesArgForBlockNodes(config: BlockNodesDeployConfigClass): Promise { + private async prepareValuesArgForBlockNode(config: BlockNodeDeployConfigClass): Promise { let valuesArgument: string = ''; - valuesArgument += helpers.prepareValuesFiles(constants.BLOCK_NODES_VALUES_FILE); + valuesArgument += helpers.prepareValuesFiles(constants.BLOCK_NODE_VALUES_FILE); if (config.valuesFile) { valuesArgument += helpers.prepareValuesFiles(config.valuesFile); @@ -90,7 +90,7 @@ export class BlockNodesCommand extends BaseCommand { const arch: string = os.arch(); if (arch === 'arm64' || arch === 'aarch64') { valuesArgument += helpers.populateHelmArguments({ - JAVA_OPTS: '-Xms8G -Xmx8G -XX:UseSVE=0', + JAVA_OPTS: '"-Xms8G -Xmx8G -XX:UseSVE=0"', }); } @@ -104,27 +104,26 @@ export class BlockNodesCommand extends BaseCommand { private async deploy(argv: ArgvStruct): Promise { const lease: Lock = await this.leaseManager.create(); - const tasks: Listr = new Listr( + const tasks: Listr = new Listr( [ { title: 'Initialize', task: async (context_, task): Promise> => { this.configManager.update(argv); - flags.disablePrompts(BlockNodesCommand.DEPLOY_FLAGS_LIST.optional); + flags.disablePrompts(BlockNodeCommand.DEPLOY_FLAGS_LIST.optional); const allFlags: CommandFlag[] = [ - ...BlockNodesCommand.DEPLOY_FLAGS_LIST.required, - ...BlockNodesCommand.DEPLOY_FLAGS_LIST.optional, + ...BlockNodeCommand.DEPLOY_FLAGS_LIST.required, + ...BlockNodeCommand.DEPLOY_FLAGS_LIST.optional, ]; await this.configManager.executePrompt(task, allFlags); - // prompt if inputs are empty and set it in the context context_.config = this.configManager.getConfig( - BlockNodesCommand.DEPLOY_CONFIGS_NAME, + BlockNodeCommand.DEPLOY_CONFIGS_NAME, allFlags, - ) as BlockNodesDeployConfigClass; + ) as BlockNodeDeployConfigClass; context_.config.namespace = await resolveNamespaceFromDeployment( this.localConfig, @@ -150,7 +149,7 @@ export class BlockNodesCommand extends BaseCommand { { title: 'Prepare release name and block node name', task: async (context_): Promise => { - const config: BlockNodesDeployConfigClass = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; let newBlockNodeNumber: number = 1; @@ -174,7 +173,7 @@ export class BlockNodesCommand extends BaseCommand { { title: 'Check chart is installed', task: async (context_): Promise => { - const config: BlockNodesDeployConfigClass = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; config.isChartInstalled = await this.chartManager.isChartInstalled( config.namespace, @@ -186,15 +185,15 @@ export class BlockNodesCommand extends BaseCommand { { title: 'Prepare chart values', task: async (context_): Promise => { - const config: BlockNodesDeployConfigClass = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; - config.valuesArg = await this.prepareValuesArgForBlockNodes(config); + config.valuesArg = await this.prepareValuesArgForBlockNode(config); }, }, { - title: 'Deploy BlockNodes', + title: 'Deploy block node', task: async (context_): Promise => { - const config: BlockNodesDeployConfigClass = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; await this.chartManager.install( config.namespace, @@ -210,9 +209,9 @@ export class BlockNodesCommand extends BaseCommand { }, }, { - title: 'Check block nodes are running', + title: 'Check block node is running', task: async (context_): Promise => { - const config: BlockNodesDeployConfigClass = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; await this.k8Factory .getK8(config.context) @@ -220,15 +219,15 @@ export class BlockNodesCommand extends BaseCommand { .waitForRunningPhase( config.namespace, [`app.kubernetes.io/instance=${config.releaseName}`], - constants.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS, - constants.BLOCK_NODES_PODS_RUNNING_DELAY, + constants.BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS, + constants.BLOCK_NODE_PODS_RUNNING_DELAY, ); }, }, { - title: 'Check block nodes is ready', + title: 'Check block node is ready', task: async (context_): Promise => { - const config: BlockNodesDeployConfigClass = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; try { await this.k8Factory .getK8(config.context) @@ -236,11 +235,11 @@ export class BlockNodesCommand extends BaseCommand { .waitForReadyStatus( config.namespace, [`app.kubernetes.io/instance=${config.releaseName}`], - constants.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS, - constants.BLOCK_NODES_PODS_RUNNING_DELAY, + constants.BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS, + constants.BLOCK_NODE_PODS_RUNNING_DELAY, ); } catch (error) { - throw new SoloError(`BlockNodes ${config.releaseName} is not ready: ${error.message}`, error); + throw new SoloError(`Block node ${config.releaseName} is not ready: ${error.message}`, error); } }, }, @@ -255,7 +254,7 @@ export class BlockNodesCommand extends BaseCommand { try { await tasks.run(); } catch (error) { - throw new SoloError(`Error deploying block nodes: ${error.message}`, error); + throw new SoloError(`Error deploying block node: ${error.message}`, error); } finally { await lease.release(); } @@ -263,10 +262,10 @@ export class BlockNodesCommand extends BaseCommand { return true; } - /** Adds the relay component to remote config. */ - public addBlockNodeComponent(): SoloListrTask { + /** Adds the block node component to remote config. */ + public addBlockNodeComponent(): SoloListrTask { return { - title: 'Add relay component in remote config', + title: 'Add block node component in remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { @@ -281,30 +280,30 @@ export class BlockNodesCommand extends BaseCommand { public getCommandDefinition(): CommandDefinition { const self: this = this; return { - command: BlockNodesCommand.COMMAND_NAME, + command: BlockNodeCommand.COMMAND_NAME, desc: 'Manage block nodes in solo network', builder: (yargs: AnyYargs): any => { return yargs .command({ command: 'deploy', - desc: 'Deploy block nodes', + desc: 'Deploy block node', builder: (y: AnyYargs): void => { - flags.setRequiredCommandFlags(y, ...BlockNodesCommand.DEPLOY_FLAGS_LIST.required); - flags.setOptionalCommandFlags(y, ...BlockNodesCommand.DEPLOY_FLAGS_LIST.optional); + flags.setRequiredCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.required); + flags.setOptionalCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.optional); }, handler: async (argv: ArgvStruct): Promise => { - self.logger.info("==== Running 'relay deploy' ===", {argv}); + self.logger.info("==== Running 'block node deploy' ===", {argv}); self.logger.info(argv); await self.deploy(argv).then((r): void => { - self.logger.info('==== Finished running `relay deploy`===='); + self.logger.info('==== Finished running `block node deploy`===='); if (!r) { - throw new SoloError('Error deploying relay, expected return value to be true'); + throw new SoloError('Error deploying block node, expected return value to be true'); } }); }, }) - .demandCommand(1, 'Select a relay command'); + .demandCommand(1, 'Select a block node command'); }, }; } diff --git a/src/commands/flags.ts b/src/commands/flags.ts index d0990cd39..17445984a 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -1028,7 +1028,7 @@ export class Flags { }, }; - public static readonly blockNodesChartVersion: CommandFlag = { + public static readonly blockNodeChartVersion: CommandFlag = { constName: 'chartVersion', name: 'chart-version', definition: { @@ -1609,9 +1609,9 @@ export class Flags { }, }; - public static readonly blockNodesVersion: CommandFlag = { - constName: 'blockNodesVersion', - name: 'block-nodes-version', + public static readonly blockNodeVersion: CommandFlag = { + constName: 'blockNodeVersion', + name: 'block-node-version', definition: { describe: 'Block nodes chart version', defaultValue: version.MIRROR_NODE_VERSION, @@ -2534,7 +2534,8 @@ export class Flags { Flags.dnsConsensusNodePattern, Flags.domainName, Flags.domainNames, - Flags.blockNodesChartVersion, + Flags.blockNodeChartVersion, + Flags.blockNodeVersion, ]; /** Resets the definition.disablePrompt for all flags */ diff --git a/src/commands/index.ts b/src/commands/index.ts index f63b6e9d0..60124b447 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -9,7 +9,7 @@ import {RelayCommand} from './relay.js'; import {AccountCommand} from './account.js'; import {DeploymentCommand} from './deployment.js'; import {ExplorerCommand} from './explorer.js'; -import {BlockNodesCommand} from './block-nodes.js'; +import {BlockNodeCommand} from './block-node.js'; import {type Options} from './base.js'; import {type CommandDefinition} from '../types/index.js'; @@ -29,6 +29,6 @@ export function Initialize(options: Options): CommandDefinition[] { new MirrorNodeCommand(options).getCommandDefinition(), new ExplorerCommand(options).getCommandDefinition(), new DeploymentCommand(options).getCommandDefinition(), - new BlockNodesCommand(options).getCommandDefinition(), + new BlockNodeCommand(options).getCommandDefinition(), ]; } diff --git a/src/core/constants.ts b/src/core/constants.ts index f1abb6b28..8cbcf22ed 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -134,7 +134,7 @@ export const POD_CONDITION_STATUS_TRUE = 'True'; export const EXPLORER_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'hedera-explorer-values.yaml'); export const MIRROR_NODE_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'mirror-node-values.yaml'); -export const BLOCK_NODES_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'block-nodes-values.yaml'); +export const BLOCK_NODE_VALUES_FILE = PathEx.joinWithRealPath(RESOURCES_DIR, 'block-node-values.yaml'); export const NODE_LOG_FAILURE_MSG = 'failed to download logs from pod'; /** @@ -222,8 +222,8 @@ export const RELAY_PODS_RUNNING_MAX_ATTEMPTS = +process.env.RELAY_PODS_RUNNING_M export const RELAY_PODS_RUNNING_DELAY = +process.env.RELAY_PODS_RUNNING_DELAY || 1000; export const RELAY_PODS_READY_MAX_ATTEMPTS = +process.env.RELAY_PODS_READY_MAX_ATTEMPTS || 100; export const RELAY_PODS_READY_DELAY = +process.env.RELAY_PODS_READY_DELAY || 1000; -export const BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS = +process.env.BLOCK_NODES_PODS_RUNNING_MAX_ATTEMPTS || 900; -export const BLOCK_NODES_PODS_RUNNING_DELAY = +process.env.BLOCK_NODES_PODS_RUNNING_DELAY || 1000; +export const BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS = +process.env.BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS || 900; +export const BLOCK_NODE_PODS_RUNNING_DELAY = +process.env.BLOCK_NODE_PODS_RUNNING_DELAY || 1000; export const GRPC_PORT = +process.env.GRPC_PORT || 50_211; export const LOCAL_BUILD_COPY_RETRY = +process.env.LOCAL_BUILD_COPY_RETRY || 3; From d42863be1c66ba485c189f9abe50c601a9a480f4 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 15:42:50 +0300 Subject: [PATCH 14/70] add 'nameOverride' for the block node chart and fix the logic for supporting m4 apple chips Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 9133adebd..99f823764 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -77,6 +77,8 @@ export class BlockNodeCommand extends BaseCommand { valuesArgument += helpers.prepareValuesFiles(config.valuesFile); } + valuesArgument += helpers.populateHelmArguments({nameOverride: config.blockNodeName}); + if (config.domainName) { valuesArgument += helpers.populateHelmArguments({ 'ingress.enabled': true, @@ -90,7 +92,7 @@ export class BlockNodeCommand extends BaseCommand { const arch: string = os.arch(); if (arch === 'arm64' || arch === 'aarch64') { valuesArgument += helpers.populateHelmArguments({ - JAVA_OPTS: '"-Xms8G -Xmx8G -XX:UseSVE=0"', + 'blockNode.config.JAVA_OPTS': '"-Xms8G -Xmx8G -XX:UseSVE=0"', }); } From 3053fceccc264a9ab59dd29a617ec4b2a2e23a0c Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 15:49:12 +0300 Subject: [PATCH 15/70] moved each remote config enumerations into their individual own file and renamed ComponentType to ComponentTypes to follow convention Signed-off-by: Zhan Milenkov --- src/commands/deployment.ts | 3 +- src/commands/explorer.ts | 4 +- src/commands/mirror-node.ts | 4 +- src/commands/network.ts | 2 +- src/commands/node/handlers.ts | 11 ++-- src/commands/node/tasks.ts | 2 +- src/commands/relay.ts | 4 +- .../config/remote/components-data-wrapper.ts | 53 ++++++++++--------- .../remote/components/base-component.ts | 6 +-- .../remote/components/block-node-component.ts | 4 +- .../components/consensus-node-component.ts | 5 +- .../components/envoy-proxy-component.ts | 4 +- .../remote/components/ha-proxy-component.ts | 4 +- .../components/mirror-node-component.ts | 4 +- .../mirror-node-explorer-component.ts | 4 +- .../remote/components/relay-component.ts | 4 +- src/core/config/remote/enumerations.ts | 32 ----------- .../remote/enumerations/component-types.ts | 14 +++++ .../enumerations/consensus-node-states.ts | 14 +++++ .../remote/enumerations/deployment-states.ts | 6 +++ src/core/config/remote/metadata.ts | 3 +- .../config/remote/remote-config-manager.ts | 2 +- .../config/remote/remote-config-validator.ts | 2 +- src/core/config/remote/types.ts | 6 ++- src/core/enumerations.ts | 2 +- .../core/remote-config-validator.test.ts | 2 +- .../remote/components-data-wrapper.test.ts | 19 +++---- .../remote/components/components.test.ts | 2 +- test/unit/core/config/remote/metadata.test.ts | 3 +- 29 files changed, 118 insertions(+), 107 deletions(-) delete mode 100644 src/core/config/remote/enumerations.ts create mode 100644 src/core/config/remote/enumerations/component-types.ts create mode 100644 src/core/config/remote/enumerations/consensus-node-states.ts create mode 100644 src/core/config/remote/enumerations/deployment-states.ts diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 7313b17a5..aea4f159c 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -15,11 +15,12 @@ import {type ClusterChecks} from '../core/cluster-checks.js'; import {container} from 'tsyringe-neo'; import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; import {type ArgvStruct, type AnyYargs, type NodeAliases} from '../types/aliases.js'; -import {ConsensusNodeStates, DeploymentStates} from '../core/config/remote/enumerations.js'; import {Templates} from '../core/templates.js'; import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; +import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; +import {DeploymentStates} from '../core/config/remote/enumerations/deployment-states.js'; interface DeploymentAddClusterConfig { quiet: boolean; diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 3bcf63493..f23e398cf 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -13,7 +13,6 @@ import {Flags as flags} from './flags.js'; import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {ComponentType} from '../core/config/remote/enumerations.js'; import {MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; @@ -25,6 +24,7 @@ import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; import {HEDERA_EXPLORER_CHART_URL, INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; import * as helpers from '../core/helpers.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface ExplorerDeployConfigClass { chartDirectory: string; @@ -576,7 +576,7 @@ export class ExplorerCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('mirrorNodeExplorer', ComponentType.MirrorNodeExplorer); + remoteConfig.components.removeComponent('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer); }); }, }; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 9e49458f1..5cdd66675 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -17,7 +17,6 @@ import * as helpers from '../core/helpers.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {type PodName} from '../integration/kube/resources/pod/pod-name.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {ComponentType} from '../core/config/remote/enumerations.js'; import {MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; import * as fs from 'node:fs'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; @@ -36,6 +35,7 @@ import {type ClusterReference, type DeploymentName} from '../core/config/remote/ import {showVersionBanner} from '../core/helpers.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface MirrorNodeDeployConfigClass { chartDirectory: string; @@ -881,7 +881,7 @@ export class MirrorNodeCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('mirrorNode', ComponentType.MirrorNode); + remoteConfig.components.removeComponent('mirrorNode', ComponentTypes.MirrorNode); }); }, }; diff --git a/src/commands/network.ts b/src/commands/network.ts index a4c09009b..08d8fa149 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -28,7 +28,6 @@ import {type CertificateManager} from '../core/certificate-manager.js'; import {type AnyYargs, type IP, type NodeAlias, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; -import {ConsensusNodeStates} from '../core/config/remote/enumerations.js'; import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; import {HaProxyComponent} from '../core/config/remote/components/ha-proxy-component.js'; import {v4 as uuidv4} from 'uuid'; @@ -45,6 +44,7 @@ import {type PodReference} from '../integration/kube/resources/pod/pod-reference import {SOLO_DEPLOYMENT_CHART} from '../core/constants.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; +import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; export interface NetworkDeployConfigClass { applicationEnv: string; diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 14c425f12..640a59d6d 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -7,7 +7,6 @@ import * as constants from '../../core/constants.js'; import {type LockManager} from '../../core/lock/lock-manager.js'; import {type RemoteConfigManager} from '../../core/config/remote/remote-config-manager.js'; import {SoloError} from '../../core/errors/solo-error.js'; -import {ComponentType, ConsensusNodeStates} from '../../core/config/remote/enumerations.js'; import {type Lock} from '../../core/lock/lock.js'; import {type NodeCommandTasks} from './tasks.js'; import {NodeSubcommandType} from '../../core/enumerations.js'; @@ -28,6 +27,8 @@ import {type NodeDeleteContext} from './config-interfaces/node-delete-context.js import {type NodeAddContext} from './config-interfaces/node-add-context.js'; import {type NodeUpdateContext} from './config-interfaces/node-update-context.js'; import {type NodeUpgradeContext} from './config-interfaces/node-upgrade-context.js'; +import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js'; +import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; @injectable() export class NodeCommandHandlers extends CommandHandler { @@ -905,9 +906,9 @@ export class NodeCommandHandlers extends CommandHandler { title: 'Remove node and proxies from remote config', task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('Consensus node name', ComponentType.ConsensusNode); - remoteConfig.components.removeComponent('Envoy proxy name', ComponentType.EnvoyProxy); - remoteConfig.components.removeComponent('HaProxy name', ComponentType.HaProxy); + remoteConfig.components.removeComponent('Consensus node name', ComponentTypes.ConsensusNode); + remoteConfig.components.removeComponent('Envoy proxy name', ComponentTypes.EnvoyProxy); + remoteConfig.components.removeComponent('HaProxy name', ComponentTypes.HaProxy); }); }, }; @@ -1037,7 +1038,7 @@ export class NodeCommandHandlers extends CommandHandler { ): ConsensusNodeStates { let nodeComponent: ConsensusNodeComponent; try { - nodeComponent = components.getComponent(ComponentType.ConsensusNode, nodeAlias); + nodeComponent = components.getComponent(ComponentTypes.ConsensusNode, nodeAlias); } catch { throw new SoloError(`${nodeAlias} not found in remote config`); } diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 84ff2ea58..1071a56dc 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -91,7 +91,6 @@ import {type RemoteConfigManager} from '../../core/config/remote/remote-config-m import {type LocalConfig} from '../../core/config/local/local-config.js'; import {BaseCommand} from '../base.js'; import {ConsensusNodeComponent} from '../../core/config/remote/components/consensus-node-component.js'; -import {ConsensusNodeStates} from '../../core/config/remote/enumerations.js'; import {EnvoyProxyComponent} from '../../core/config/remote/components/envoy-proxy-component.js'; import {HaProxyComponent} from '../../core/config/remote/components/ha-proxy-component.js'; import {HEDERA_PLATFORM_VERSION} from '../../../version.js'; @@ -117,6 +116,7 @@ import {type NodeKeysConfigClass} from './config-interfaces/node-keys-config-cla import {type NodeStartConfigClass} from './config-interfaces/node-start-config-class.js'; import {type CheckedNodesConfigClass, type CheckedNodesContext} from './config-interfaces/node-common-config-class.js'; import {type NetworkNodeServices} from '../../core/network-node-services.js'; +import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; @injectable() export class NodeCommandTasks { diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 7a8c9c78c..e68181e5f 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -14,13 +14,13 @@ import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {RelayComponent} from '../core/config/remote/components/relay-component.js'; -import {ComponentType} from '../core/config/remote/enumerations.js'; import * as Base64 from 'js-base64'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface RelayDestroyConfigClass { chartDirectory: string; @@ -563,7 +563,7 @@ export class RelayCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('relay', ComponentType.Relay); + remoteConfig.components.removeComponent('relay', ComponentTypes.Relay); }); }, }; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 064696bb9..8c61ec488 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -import {ComponentType, ConsensusNodeStates} from './enumerations.js'; import {SoloError} from '../../errors/solo-error.js'; import {BaseComponent} from './components/base-component.js'; import {RelayComponent} from './components/relay-component.js'; @@ -23,6 +22,8 @@ import {type ToObject, type Validate} from '../../../types/index.js'; import {Templates} from '../../templates.js'; import {type NodeAliases} from '../../../types/aliases.js'; import {type CloneTrait} from '../../../types/traits/clone-trait.js'; +import {ComponentTypes} from './enumerations/component-types.js'; +import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; /** * Represent the components in the remote config and handles: @@ -92,12 +93,12 @@ export class ComponentsDataWrapper } /** Used to remove specific component from their respective group. */ - public removeComponent(serviceName: ComponentName, type: ComponentType): void { + public removeComponent(serviceName: ComponentName, type: ComponentTypes): void { if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } - if (!Object.values(ComponentType).includes(type)) { + if (!Object.values(ComponentTypes).includes(type)) { throw new SoloError(`Invalid component type ${type}`); } @@ -113,7 +114,7 @@ export class ComponentsDataWrapper /* -------- Utilities -------- */ - public getComponent(type: ComponentType, serviceName: ComponentName): T { + public getComponent(type: ComponentTypes, serviceName: ComponentName): T { let component: T; const getComponentCallback: (components: Record) => void = (components): void => { @@ -133,42 +134,42 @@ export class ComponentsDataWrapper * and pass it to a callback to apply modifications */ private applyCallbackToComponentGroup( - componentType: ComponentType, + componentType: ComponentTypes, serviceName: ComponentName, callback: (components: Record) => void, ): void { switch (componentType) { - case ComponentType.Relay: { + case ComponentTypes.Relay: { callback(this.relays); break; } - case ComponentType.HaProxy: { + case ComponentTypes.HaProxy: { callback(this.haProxies); break; } - case ComponentType.MirrorNode: { + case ComponentTypes.MirrorNode: { callback(this.mirrorNodes); break; } - case ComponentType.EnvoyProxy: { + case ComponentTypes.EnvoyProxy: { callback(this.envoyProxies); break; } - case ComponentType.ConsensusNode: { + case ComponentTypes.ConsensusNode: { callback(this.consensusNodes); break; } - case ComponentType.MirrorNodeExplorer: { + case ComponentTypes.MirrorNodeExplorer: { callback(this.mirrorNodeExplorers); break; } - case ComponentType.BlockNode: { + case ComponentTypes.BlockNode: { callback(this.blockNodes); break; } @@ -197,49 +198,49 @@ export class ComponentsDataWrapper for (const [componentType, subComponents] of Object.entries(components)) { switch (componentType) { - case ComponentType.Relay: { + case ComponentTypes.Relay: { for (const [serviceName, component] of Object.entries(subComponents)) { relays[serviceName] = RelayComponent.fromObject(component as IRelayComponent); } break; } - case ComponentType.HaProxy: { + case ComponentTypes.HaProxy: { for (const [serviceName, component] of Object.entries(subComponents)) { haProxies[serviceName] = HaProxyComponent.fromObject(component); } break; } - case ComponentType.MirrorNode: { + case ComponentTypes.MirrorNode: { for (const [serviceName, component] of Object.entries(subComponents)) { mirrorNodes[serviceName] = MirrorNodeComponent.fromObject(component); } break; } - case ComponentType.EnvoyProxy: { + case ComponentTypes.EnvoyProxy: { for (const [serviceName, component] of Object.entries(subComponents)) { envoyProxies[serviceName] = EnvoyProxyComponent.fromObject(component); } break; } - case ComponentType.ConsensusNode: { + case ComponentTypes.ConsensusNode: { for (const [serviceName, component] of Object.entries(subComponents)) { consensusNodes[serviceName] = ConsensusNodeComponent.fromObject(component as IConsensusNodeComponent); } break; } - case ComponentType.MirrorNodeExplorer: { + case ComponentTypes.MirrorNodeExplorer: { for (const [serviceName, component] of Object.entries(subComponents)) { mirrorNodeExplorers[serviceName] = MirrorNodeExplorerComponent.fromObject(component); } break; } - case ComponentType.BlockNode: { + case ComponentTypes.BlockNode: { for (const [serviceName, component] of Object.entries(subComponents)) { blockNodes[serviceName] = BlockNodeComponent.fromObject(component); } @@ -327,13 +328,13 @@ export class ComponentsDataWrapper public toObject(): ComponentsDataStructure { return { - [ComponentType.Relay]: this.transformComponentGroupToObject(this.relays), - [ComponentType.HaProxy]: this.transformComponentGroupToObject(this.haProxies), - [ComponentType.MirrorNode]: this.transformComponentGroupToObject(this.mirrorNodes), - [ComponentType.EnvoyProxy]: this.transformComponentGroupToObject(this.envoyProxies), - [ComponentType.ConsensusNode]: this.transformComponentGroupToObject(this.consensusNodes), - [ComponentType.MirrorNodeExplorer]: this.transformComponentGroupToObject(this.mirrorNodeExplorers), - [ComponentType.BlockNode]: this.transformComponentGroupToObject(this.blockNodes), + [ComponentTypes.Relay]: this.transformComponentGroupToObject(this.relays), + [ComponentTypes.HaProxy]: this.transformComponentGroupToObject(this.haProxies), + [ComponentTypes.MirrorNode]: this.transformComponentGroupToObject(this.mirrorNodes), + [ComponentTypes.EnvoyProxy]: this.transformComponentGroupToObject(this.envoyProxies), + [ComponentTypes.ConsensusNode]: this.transformComponentGroupToObject(this.consensusNodes), + [ComponentTypes.MirrorNodeExplorer]: this.transformComponentGroupToObject(this.mirrorNodeExplorers), + [ComponentTypes.BlockNode]: this.transformComponentGroupToObject(this.blockNodes), }; } diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index a618749fc..1fc70d53b 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -import {ComponentType} from '../enumerations.js'; import {SoloError} from '../../../errors/solo-error.js'; import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject, type Validate} from '../../../../types/index.js'; +import {ComponentTypes} from '../enumerations/component-types.js'; /** * Represents the base structure and common functionality for all components within the system. @@ -17,7 +17,7 @@ export abstract class BaseComponent implements Component, Validate, ToObject { /** @@ -20,7 +20,7 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To namespace: NamespaceNameAsString, public readonly consensusNodeAliases: NodeAliases = [], ) { - super(ComponentType.Relay, name, cluster, namespace); + super(ComponentTypes.Relay, name, cluster, namespace); this.validate(); } diff --git a/src/core/config/remote/enumerations.ts b/src/core/config/remote/enumerations.ts deleted file mode 100644 index e4e9ca4a8..000000000 --- a/src/core/config/remote/enumerations.ts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -/** - * Enumerations that represent the component types used in remote config - */ -export enum ComponentType { - ConsensusNode = 'consensusNodes', - BlockNode = 'blockNode', - HaProxy = 'haProxies', - EnvoyProxy = 'envoyProxies', - MirrorNode = 'mirrorNodes', - MirrorNodeExplorer = 'mirrorNodeExplorers', - Relay = 'relays', -} - -/** - * Enumerations that represent the state of consensus node in remote config - */ -export enum ConsensusNodeStates { - NON_DEPLOYED = 'non-deployed', - REQUESTED = 'requested', - INITIALIZED = 'initialized', - SETUP = 'setup', - STARTED = 'started', - FROZEN = 'frozen', - STOPPED = 'stopped', -} - -export enum DeploymentStates { - PRE_GENESIS = 'pre-genesis', - POST_GENESIS = 'post-genesis', -} diff --git a/src/core/config/remote/enumerations/component-types.ts b/src/core/config/remote/enumerations/component-types.ts new file mode 100644 index 000000000..0a209b7d4 --- /dev/null +++ b/src/core/config/remote/enumerations/component-types.ts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 + +/** + * Enumerations that represent the component types used in remote config + */ +export enum ComponentTypes { + ConsensusNode = 'consensusNodes', + BlockNode = 'blockNode', + HaProxy = 'haProxies', + EnvoyProxy = 'envoyProxies', + MirrorNode = 'mirrorNodes', + MirrorNodeExplorer = 'mirrorNodeExplorers', + Relay = 'relays', +} diff --git a/src/core/config/remote/enumerations/consensus-node-states.ts b/src/core/config/remote/enumerations/consensus-node-states.ts new file mode 100644 index 000000000..b429d8944 --- /dev/null +++ b/src/core/config/remote/enumerations/consensus-node-states.ts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 + +/** + * Enumerations that represent the state of consensus node in remote config + */ +export enum ConsensusNodeStates { + NON_DEPLOYED = 'non-deployed', + REQUESTED = 'requested', + INITIALIZED = 'initialized', + SETUP = 'setup', + STARTED = 'started', + FROZEN = 'frozen', + STOPPED = 'stopped', +} diff --git a/src/core/config/remote/enumerations/deployment-states.ts b/src/core/config/remote/enumerations/deployment-states.ts new file mode 100644 index 000000000..2b0c9dd55 --- /dev/null +++ b/src/core/config/remote/enumerations/deployment-states.ts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 + +export enum DeploymentStates { + PRE_GENESIS = 'pre-genesis', + POST_GENESIS = 'post-genesis', +} diff --git a/src/core/config/remote/metadata.ts b/src/core/config/remote/metadata.ts index 9d6ef2458..836f55801 100644 --- a/src/core/config/remote/metadata.ts +++ b/src/core/config/remote/metadata.ts @@ -10,7 +10,8 @@ import { type Version, } from './types.js'; import {type Optional, type ToObject, type Validate} from '../../../types/index.js'; -import {DeploymentStates} from './enumerations.js'; + +import {DeploymentStates} from './enumerations/deployment-states.js'; /** * Represent the remote config metadata object and handles: diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index 60944142c..fc4d8d7a4 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -32,10 +32,10 @@ import {Cluster} from './cluster.js'; import {ConsensusNode} from '../../model/consensus-node.js'; import {Templates} from '../../templates.js'; import {promptTheUserForDeployment, resolveNamespaceFromDeployment} from '../../resolvers.js'; -import {type DeploymentStates} from './enumerations.js'; import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; import {getSoloVersion} from '../../../../version.js'; import {DeploymentStructure} from '../local/local-config-data.js'; +import {DeploymentStates} from './enumerations/deployment-states.js'; /** * Uses Kubernetes ConfigMaps to manage the remote configuration data by creating, loading, modifying, diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index b91ca39c8..df84fde49 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -2,7 +2,6 @@ import * as constants from '../../constants.js'; import {SoloError} from '../../errors/solo-error.js'; -import {ConsensusNodeStates} from './enumerations.js'; import {type K8Factory} from '../../../integration/kube/k8-factory.js'; import {type ComponentsDataWrapper} from './components-data-wrapper.js'; import {type BaseComponent} from './components/base-component.js'; @@ -11,6 +10,7 @@ import {type LocalConfig} from '../local/local-config.js'; import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; +import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; /** * Static class is used to validate that components in the remote config diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index bdc54e683..8c64912cf 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -1,7 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 import {type NodeAliases} from '../../../types/aliases.js'; -import {type ComponentType, type ConsensusNodeStates, type DeploymentStates} from './enumerations.js'; +import {ComponentTypes} from './enumerations/component-types.js'; +import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; +import {DeploymentStates} from './enumerations/deployment-states.js'; export type EmailAddress = `${string}@${string}.${string}`; export type Version = string; @@ -43,7 +45,7 @@ export interface ICluster { dnsConsensusNodePattern: string; } -export type ComponentsDataStructure = Record>; +export type ComponentsDataStructure = Record>; export type RemoteConfigCommonFlagsStruct = { releaseTag?: string; diff --git a/src/core/enumerations.ts b/src/core/enumerations.ts index 4e65139c1..7e22db657 100644 --- a/src/core/enumerations.ts +++ b/src/core/enumerations.ts @@ -14,7 +14,7 @@ export enum NodeStatusCodes { CATASTROPHIC_FAILURE = 11, } -export const NodeStatusEnums = { +export const NodeStatusEnums: Record = { 0: 'NO_VALUE', 1: 'STARTING_UP', 2: 'ACTIVE', diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index 40c575a86..ea6531cd7 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -8,7 +8,6 @@ import {type ConfigManager} from '../../../../src/core/config-manager.js'; import {Templates} from '../../../../src/core/templates.js'; import {Flags as flags} from '../../../../src/commands/flags.js'; import {RemoteConfigValidator} from '../../../../src/core/config/remote/remote-config-validator.js'; -import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations.js'; import {ComponentsDataWrapper} from '../../../../src/core/config/remote/components-data-wrapper.js'; import {SoloError} from '../../../../src/core/errors/solo-error.js'; import {RelayComponent} from '../../../../src/core/config/remote/components/relay-component.js'; @@ -30,6 +29,7 @@ import {LocalConfig} from '../../../../src/core/config/local/local-config.js'; import {getTestCacheDirectory} from '../../../test-utility.js'; import {Duration} from '../../../../src/core/time/duration.js'; import {LocalConfigDataWrapper} from '../../../../src/core/config/local/local-config-data-wrapper.js'; +import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations/consensus-node-states.js'; describe('RemoteConfigValidator', () => { const namespace = NamespaceName.of('remote-config-validator'); diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 232d50d5d..434a7e5b1 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -10,9 +10,10 @@ import {EnvoyProxyComponent} from '../../../../../src/core/config/remote/compone import {ConsensusNodeComponent} from '../../../../../src/core/config/remote/components/consensus-node-component.js'; import {MirrorNodeExplorerComponent} from '../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; import {RelayComponent} from '../../../../../src/core/config/remote/components/relay-component.js'; -import {ComponentType, ConsensusNodeStates} from '../../../../../src/core/config/remote/enumerations.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {type NodeAliases} from '../../../../../src/types/aliases.js'; +import {ComponentTypes} from '../../../../../src/core/config/remote/enumerations/component-types.js'; +import {ConsensusNodeStates} from '../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; export function createComponentsDataWrapper() { const name = 'name'; @@ -83,7 +84,7 @@ describe('ComponentsDataWrapper', () => { expect(componentsDataWrapperObject).to.deep.equal(newComponentsDataWrapper.toObject()); - for (const type of Object.values(ComponentType)) { + for (const type of Object.values(ComponentTypes)) { expect(componentsDataWrapperObject).to.have.ownProperty(type); } @@ -119,15 +120,15 @@ describe('ComponentsDataWrapper', () => { const componentDataWrapperObject = componentsDataWrapper.toObject(); - expect(componentDataWrapperObject[ComponentType.EnvoyProxy]).has.own.property(newServiceName); + expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newServiceName); - expect(componentDataWrapperObject[ComponentType.EnvoyProxy][newServiceName]).to.deep.equal({ + expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy][newServiceName]).to.deep.equal({ name, cluster, namespace, }); - expect(Object.values(componentDataWrapperObject[ComponentType.EnvoyProxy])).to.have.lengthOf(3); + expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(3); }); it('should be able to edit component with the .edit()', () => { @@ -147,7 +148,7 @@ describe('ComponentsDataWrapper', () => { componentsDataWrapper.editComponent(newReplayComponent); - expect(componentsDataWrapper.toObject()[ComponentType.Relay][relayComponent.name].cluster).to.equal(newCluster); + expect(componentsDataWrapper.toObject()[ComponentTypes.Relay][relayComponent.name].cluster).to.equal(newCluster); }); it("should not be able to edit component with the .edit() if it doesn't exist ", () => { @@ -172,7 +173,7 @@ describe('ComponentsDataWrapper', () => { serviceName, } = createComponentsDataWrapper(); - componentsDataWrapper.removeComponent(serviceName, ComponentType.Relay); + componentsDataWrapper.removeComponent(serviceName, ComponentTypes.Relay); expect(componentsDataWrapper.relays).not.to.have.own.property(serviceName); }); @@ -184,9 +185,9 @@ describe('ComponentsDataWrapper', () => { const notFoundServiceName = 'not_found'; - expect(() => componentsDataWrapper.removeComponent(notFoundServiceName, ComponentType.Relay)).to.throw( + expect(() => componentsDataWrapper.removeComponent(notFoundServiceName, ComponentTypes.Relay)).to.throw( SoloError, - `Component ${notFoundServiceName} of type ${ComponentType.Relay} not found while attempting to remove`, + `Component ${notFoundServiceName} of type ${ComponentTypes.Relay} not found while attempting to remove`, ); }); }); diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 40277aa9f..a87fbce6c 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -11,9 +11,9 @@ import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/comp import {MirrorNodeComponent} from '../../../../../../src/core/config/remote/components/mirror-node-component.js'; import {MirrorNodeExplorerComponent} from '../../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; import {SoloError} from '../../../../../../src/core/errors/solo-error.js'; -import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enumerations.js'; import {type NodeAliases} from '../../../../../../src/types/aliases.js'; import {Templates} from '../../../../../../src/core/templates.js'; +import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; function testBaseComponentData(classComponent: any) { const validNamespace = 'valid'; diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index 88599b548..e7ccfafb5 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -10,7 +10,8 @@ import { type NamespaceNameAsString, type Version, } from '../../../../../src/core/config/remote/types.js'; -import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations.js'; + +import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; export function createMetadata() { const namespace: NamespaceNameAsString = 'namespace'; From 7c98a59423aa448ce5a0b2700c1dda9f19fa6356 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 16:54:48 +0300 Subject: [PATCH 16/70] adding new field state to the BaseComponent used inside the remote config Signed-off-by: Zhan Milenkov --- src/commands/explorer.ts | 17 +- src/commands/mirror-node.ts | 15 +- src/commands/node/handlers.ts | 25 +-- src/commands/relay.ts | 15 +- .../config/remote/components-data-wrapper.ts | 10 +- .../remote/components/base-component.ts | 8 + .../remote/components/block-node-component.ts | 9 +- .../components/consensus-node-component.ts | 19 +- .../components/envoy-proxy-component.ts | 9 +- .../remote/components/ha-proxy-component.ts | 9 +- .../components/mirror-node-component.ts | 9 +- .../mirror-node-explorer-component.ts | 9 +- .../remote/components/relay-component.ts | 9 +- .../remote/enumerations/component-states.ts | 9 + src/core/config/remote/types.ts | 10 +- .../remote/components-data-wrapper.test.ts | 11 +- .../remote/components/components.test.ts | 174 +++++++++++------- 17 files changed, 226 insertions(+), 141 deletions(-) create mode 100644 src/core/config/remote/enumerations/component-states.ts diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index f23e398cf..942cd343c 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -25,6 +25,7 @@ import {HEDERA_EXPLORER_CHART_URL, INGRESS_CONTROLLER_NAME} from '../core/consta import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; import * as helpers from '../core/helpers.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface ExplorerDeployConfigClass { chartDirectory: string; @@ -489,7 +490,7 @@ export class ExplorerCommand extends BaseCommand { }); }, }, - this.removeMirrorNodeExplorerComponents(), + this.disableMirrorNodeExplorerComponents(), ], { concurrent: false, @@ -570,13 +571,13 @@ export class ExplorerCommand extends BaseCommand { } /** Removes the explorer components from remote config. */ - private removeMirrorNodeExplorerComponents(): SoloListrTask { + private disableMirrorNodeExplorerComponents(): SoloListrTask { return { title: 'Remove explorer from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { - await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer); + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + remoteConfig.components.disableComponent('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer); }); }, }; @@ -588,12 +589,14 @@ export class ExplorerCommand extends BaseCommand { title: 'Add explorer to remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - await this.remoteConfigManager.modify(async remoteConfig => { + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { const { config: {namespace}, } = context_; - const cluster = this.remoteConfigManager.currentCluster; - remoteConfig.components.addNewComponent(new MirrorNodeExplorerComponent('mirrorNodeExplorer', cluster, namespace.name)); + const cluster = this.remoteConfigManager.currentCluster; // TODO: <--- + remoteConfig.components.addNewComponent( + new MirrorNodeExplorerComponent('mirrorNodeExplorer', cluster, namespace.name, ComponentStates.ACTIVE), + ); }); }, }; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 5cdd66675..4980978a9 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -8,12 +8,14 @@ import {MissingArgumentError} from '../core/errors/missing-argument-error.js'; import {SoloError} from '../core/errors/solo-error.js'; import {UserBreak} from '../core/errors/user-break.js'; import * as constants from '../core/constants.js'; +import {INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {type AccountManager} from '../core/account-manager.js'; import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import * as helpers from '../core/helpers.js'; +import {showVersionBanner} from '../core/helpers.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {type PodName} from '../integration/kube/resources/pod/pod-name.js'; import {ListrLock} from '../core/lock/listr-lock.js'; @@ -22,7 +24,6 @@ import * as fs from 'node:fs'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import * as Base64 from 'js-base64'; import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; -import {INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {PodReference} from '../integration/kube/resources/pod/pod-reference.js'; import {ContainerName} from '../integration/kube/resources/container/container-name.js'; @@ -32,10 +33,10 @@ import {type CommandFlag} from '../types/flag-types.js'; import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js'; import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; -import {showVersionBanner} from '../core/helpers.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface MirrorNodeDeployConfigClass { chartDirectory: string; @@ -788,7 +789,7 @@ export class MirrorNodeCommand extends BaseCommand { }); }, }, - this.removeMirrorNodeComponents(), + this.disableMirrorNodeComponents(), ], { concurrent: false, @@ -875,13 +876,13 @@ export class MirrorNodeCommand extends BaseCommand { } /** Removes the mirror node components from remote config. */ - public removeMirrorNodeComponents(): SoloListrTask { + public disableMirrorNodeComponents(): SoloListrTask { // TODO return { title: 'Remove mirror node from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('mirrorNode', ComponentTypes.MirrorNode); + remoteConfig.components.disableComponent('mirrorNode', ComponentTypes.MirrorNode); }); }, }; @@ -898,7 +899,9 @@ export class MirrorNodeCommand extends BaseCommand { config: {namespace, clusterRef}, } = context_; - remoteConfig.components.addNewComponent(new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name)); + remoteConfig.components.addNewComponent( + new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name, ComponentStates.ACTIVE), + ); }); }, }; diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 640a59d6d..461d640e4 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -11,7 +11,7 @@ import {type Lock} from '../../core/lock/lock.js'; import {type NodeCommandTasks} from './tasks.js'; import {NodeSubcommandType} from '../../core/enumerations.js'; import {NodeHelper} from './helper.js'; -import {type ArgvStruct, type NodeAlias, type NodeAliases} from '../../types/aliases.js'; +import {AnyListrContext, type ArgvStruct, type NodeAlias, type NodeAliases} from '../../types/aliases.js'; import {ConsensusNodeComponent} from '../../core/config/remote/components/consensus-node-component.js'; import {type Listr} from 'listr2'; import chalk from 'chalk'; @@ -29,6 +29,7 @@ import {type NodeUpdateContext} from './config-interfaces/node-update-context.js import {type NodeUpgradeContext} from './config-interfaces/node-upgrade-context.js'; import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentStates} from '../../core/config/remote/enumerations/component-states.js'; @injectable() export class NodeCommandHandlers extends CommandHandler { @@ -900,15 +901,16 @@ export class NodeCommandHandlers extends CommandHandler { // TODO MOVE TO TASKS /** Removes the consensus node, envoy and haproxy components from remote config. */ - public removeNodeAndProxies(): SoloListrTask { + public removeNodeAndProxies(): SoloListrTask { return { skip: (): boolean => !this.remoteConfigManager.isLoaded(), title: 'Remove node and proxies from remote config', task: async (): Promise => { - await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('Consensus node name', ComponentTypes.ConsensusNode); - remoteConfig.components.removeComponent('Envoy proxy name', ComponentTypes.EnvoyProxy); - remoteConfig.components.removeComponent('HaProxy name', ComponentTypes.HaProxy); + // TODO + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + remoteConfig.components.disableComponent('Consensus node name', ComponentTypes.ConsensusNode); + remoteConfig.components.disableComponent('Envoy proxy name', ComponentTypes.EnvoyProxy); + remoteConfig.components.disableComponent('HaProxy name', ComponentTypes.HaProxy); }); }, }; @@ -917,15 +919,15 @@ export class NodeCommandHandlers extends CommandHandler { /** * Changes the state from all consensus nodes components in remote config. * - * @param state - to which to change the consensus node component + * @param nodeStates - to which to change the consensus node component */ - public changeAllNodeStates(state: ConsensusNodeStates): SoloListrTask { + public changeAllNodeStates(nodeStates: ConsensusNodeStates): SoloListrTask { interface Context { config: {namespace: NamespaceName; consensusNodes: ConsensusNode[]}; } return { - title: `Change node state to ${state} in remote config`, + title: `Change node state to ${nodeStates} in remote config`, skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_: Context): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { @@ -939,7 +941,8 @@ export class NodeCommandHandlers extends CommandHandler { consensusNode.name, consensusNode.cluster, namespace.name, - state, + ComponentStates.ACTIVE, + nodeStates, consensusNode.nodeId, ), ); @@ -1058,6 +1061,6 @@ export class NodeCommandHandlers extends CommandHandler { // throw new SoloError(`${nodeAlias} has invalid state - ` + errorMessageData); // } - return nodeComponent.state; + return nodeComponent.nodeState; } } diff --git a/src/commands/relay.ts b/src/commands/relay.ts index e68181e5f..1c38254b5 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -21,6 +21,7 @@ import {type CommandDefinition, type Optional, type SoloListrTask} from '../type import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface RelayDestroyConfigClass { chartDirectory: string; @@ -468,7 +469,7 @@ export class RelayCommand extends BaseCommand { }, skip: context_ => !context_.config.isChartInstalled, }, - this.removeRelayComponent(), + this.disableRelayComponent(), ], { concurrent: false, @@ -548,22 +549,24 @@ export class RelayCommand extends BaseCommand { const { config: {namespace, nodeAliases}, } = context_; - const cluster = this.remoteConfigManager.currentCluster; + const cluster = this.remoteConfigManager.currentCluster; // TODO - remoteConfig.components.addNewComponent(new RelayComponent('relay', cluster, namespace.name, nodeAliases)); + remoteConfig.components.addNewComponent( + new RelayComponent('relay', cluster, namespace.name, ComponentStates.ACTIVE, nodeAliases), + ); }); }, }; } /** Remove the relay component from remote config. */ - public removeRelayComponent(): SoloListrTask { + public disableRelayComponent(): SoloListrTask { return { title: 'Remove relay component from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { - await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.removeComponent('relay', ComponentTypes.Relay); + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + remoteConfig.components.disableComponent('relay', ComponentTypes.Relay); }); }, }; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 8c61ec488..42793dc04 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -24,6 +24,7 @@ import {type NodeAliases} from '../../../types/aliases.js'; import {type CloneTrait} from '../../../types/traits/clone-trait.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; +import {ComponentStates} from './enumerations/component-states.js'; /** * Represent the components in the remote config and handles: @@ -93,7 +94,7 @@ export class ComponentsDataWrapper } /** Used to remove specific component from their respective group. */ - public removeComponent(serviceName: ComponentName, type: ComponentTypes): void { + public disableComponent(serviceName: ComponentName, type: ComponentTypes): void { if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } @@ -102,14 +103,14 @@ export class ComponentsDataWrapper throw new SoloError(`Invalid component type ${type}`); } - const deleteComponentCallback: (components: Record) => void = (components): void => { + const disableComponentCallback: (components: Record) => void = (components): void => { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`); } - delete components[serviceName]; + components[serviceName].state = ComponentStates.DELETED; }; - this.applyCallbackToComponentGroup(type, serviceName, deleteComponentCallback); + this.applyCallbackToComponentGroup(type, serviceName, disableComponentCallback); } /* -------- Utilities -------- */ @@ -273,6 +274,7 @@ export class ComponentsDataWrapper nodeAlias, clusterReference, namespace, + ComponentStates.ACTIVE, ConsensusNodeStates.NON_DEPLOYED, Templates.nodeIdFromNodeAlias(nodeAlias), ); diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 1fc70d53b..74ea32a36 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -4,6 +4,7 @@ import {SoloError} from '../../../errors/solo-error.js'; import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; +import {ComponentStates} from '../enumerations/component-states.js'; /** * Represents the base structure and common functionality for all components within the system. @@ -15,12 +16,14 @@ export abstract class BaseComponent implements Component, Validate, ToObject { /** * @param name - to distinguish components. * @param cluster - in which the component is deployed. * @param namespace - associated with the component. + * @param state - the state of the component * @param consensusNodeAliases - list node aliases */ public constructor( name: string, cluster: string, namespace: NamespaceNameAsString, + state: ComponentStates, public readonly consensusNodeAliases: NodeAliases = [], ) { - super(ComponentTypes.Relay, name, cluster, namespace); + super(ComponentTypes.Relay, name, cluster, namespace, state); this.validate(); } @@ -28,8 +31,8 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To /** Handles creating instance of the class from plain object. */ public static fromObject(component: IRelayComponent): RelayComponent { - const {name, cluster, namespace, consensusNodeAliases} = component; - return new RelayComponent(name, cluster, namespace, consensusNodeAliases); + const {name, cluster, namespace, state, consensusNodeAliases} = component; + return new RelayComponent(name, cluster, namespace, state, consensusNodeAliases); } public validate(): void { diff --git a/src/core/config/remote/enumerations/component-states.ts b/src/core/config/remote/enumerations/component-states.ts new file mode 100644 index 000000000..af7b895fb --- /dev/null +++ b/src/core/config/remote/enumerations/component-states.ts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 + +/** + * Represents the possible states of a component in the remote config. + */ +export enum ComponentStates { + ACTIVE = 'active', + DELETED = 'deleted', +} diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index 8c64912cf..fbb0702dc 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 import {type NodeAliases} from '../../../types/aliases.js'; -import {ComponentTypes} from './enumerations/component-types.js'; -import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; -import {DeploymentStates} from './enumerations/deployment-states.js'; +import {type ComponentTypes} from './enumerations/component-types.js'; +import {type ConsensusNodeStates} from './enumerations/consensus-node-states.js'; +import {type DeploymentStates} from './enumerations/deployment-states.js'; +import {type ComponentStates} from './enumerations/component-states.js'; export type EmailAddress = `${string}@${string}.${string}`; export type Version = string; @@ -26,6 +27,7 @@ export interface Component { name: ComponentName; cluster: ClusterReference; namespace: NamespaceNameAsString; + state: ComponentStates; } export interface IRelayComponent extends Component { @@ -34,7 +36,7 @@ export interface IRelayComponent extends Component { export interface IConsensusNodeComponent extends Component { nodeId: number; - state: ConsensusNodeStates; + nodeState: ConsensusNodeStates; } export interface ICluster { diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 434a7e5b1..540f676be 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -157,7 +157,7 @@ describe('ComponentsDataWrapper', () => { components: {relays}, serviceName, } = createComponentsDataWrapper(); - const notFoundServiceName = 'not_found'; + const notFoundServiceName: string = 'not_found'; const relay = relays[serviceName]; relay.name = notFoundServiceName; @@ -167,25 +167,26 @@ describe('ComponentsDataWrapper', () => { ); }); - it('should be able to remove component with the .remove()', () => { + it('should be able to disable component with the .disableComponent()', () => { + // TODO 666 const { wrapper: {componentsDataWrapper}, serviceName, } = createComponentsDataWrapper(); - componentsDataWrapper.removeComponent(serviceName, ComponentTypes.Relay); + componentsDataWrapper.disableComponent(serviceName, ComponentTypes.Relay); expect(componentsDataWrapper.relays).not.to.have.own.property(serviceName); }); - it("should not be able to remove component with the .remove() if it doesn't exist ", () => { + it("should not be able to disable component with the .disableComponent() if it doesn't exist ", () => { const { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); const notFoundServiceName = 'not_found'; - expect(() => componentsDataWrapper.removeComponent(notFoundServiceName, ComponentTypes.Relay)).to.throw( + expect(() => componentsDataWrapper.disableComponent(notFoundServiceName, ComponentTypes.Relay)).to.throw( SoloError, `Component ${notFoundServiceName} of type ${ComponentTypes.Relay} not found while attempting to remove`, ); diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index a87fbce6c..7fb941fb2 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -14,22 +14,25 @@ import {SoloError} from '../../../../../../src/core/errors/solo-error.js'; import {type NodeAliases} from '../../../../../../src/types/aliases.js'; import {Templates} from '../../../../../../src/core/templates.js'; import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentStates} from '../../../../../../src/core/config/remote/enumerations/component-states.js'; +import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; -function testBaseComponentData(classComponent: any) { +function testBaseComponentData(classComponent: any): void { + const state = ComponentStates.ACTIVE; const validNamespace = 'valid'; it('should fail if name is not provided', () => { const name = ''; - expect(() => new classComponent(name, 'valid', validNamespace)).to.throw(SoloError, `Invalid name: ${name}`); + expect(() => new classComponent(name, 'valid', validNamespace, state)).to.throw(SoloError, `Invalid name: ${name}`); }); it('should fail if name is string', () => { const name = 1; // @ts-ignore - expect(() => new classComponent(name, 'valid', validNamespace)).to.throw(SoloError, `Invalid name: ${name}`); + expect(() => new classComponent(name, 'valid', validNamespace, state)).to.throw(SoloError, `Invalid name: ${name}`); }); it('should fail if cluster is not provided', () => { const cluster = ''; - expect(() => new classComponent('valid', cluster, validNamespace)).to.throw( + expect(() => new classComponent('valid', cluster, validNamespace, state)).to.throw( SoloError, `Invalid cluster: ${cluster}`, ); @@ -37,7 +40,7 @@ function testBaseComponentData(classComponent: any) { it('should fail if cluster is string', () => { const cluster = 1; - expect(() => new classComponent('valid', cluster, validNamespace)).to.throw( + expect(() => new classComponent('valid', cluster, validNamespace, state)).to.throw( SoloError, `Invalid cluster: ${cluster}`, ); @@ -45,7 +48,7 @@ function testBaseComponentData(classComponent: any) { it('should fail if namespace is not provided', () => { const namespace = ''; - expect(() => new classComponent('valid', 'valid', namespace)).to.throw( + expect(() => new classComponent('valid', 'valid', namespace, state)).to.throw( SoloError, `Invalid namespace: ${namespace}`, ); @@ -53,62 +56,76 @@ function testBaseComponentData(classComponent: any) { it('should fail if namespace is string', () => { const namespace = 1; - expect(() => new classComponent('valid', 'valid', namespace)).to.throw( + expect(() => new classComponent('valid', 'valid', namespace, state)).to.throw( SoloError, `Invalid namespace: ${namespace}`, ); }); it('should successfully create ', () => { - new classComponent('valid', 'valid', 'valid'); + new classComponent('valid', 'valid', 'valid', state); }); it('should be an instance of BaseComponent', () => { - const component = new classComponent('valid', 'valid', validNamespace); + const component = new classComponent('valid', 'valid', validNamespace, state); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { const {name, cluster, namespace} = {name: 'name', cluster: 'cluster', namespace: 'namespace'}; - const component = new classComponent(name, cluster, namespace); + const component = new classComponent(name, cluster, namespace, state); expect(component.toObject()).to.deep.equal({name, cluster, namespace}); }); } -describe('HaProxyComponent', () => testBaseComponentData(HaProxyComponent)); +describe('HaProxyComponent', (): void => testBaseComponentData(HaProxyComponent)); -describe('EnvoyProxyComponent', () => testBaseComponentData(EnvoyProxyComponent)); +describe('EnvoyProxyComponent', (): void => testBaseComponentData(EnvoyProxyComponent)); -describe('MirrorNodeComponent', () => testBaseComponentData(MirrorNodeComponent)); +describe('MirrorNodeComponent', (): void => testBaseComponentData(MirrorNodeComponent)); -describe('MirrorNodeExplorerComponent', () => testBaseComponentData(MirrorNodeExplorerComponent)); +describe('MirrorNodeExplorerComponent', (): void => testBaseComponentData(MirrorNodeExplorerComponent)); + +describe('BlockNodeComponent', (): void => testBaseComponentData(BlockNodeComponent)); describe('RelayComponent', () => { it('should fail if name is not provided', () => { const name = ''; - expect(() => new RelayComponent(name, 'valid', 'valid', [])).to.throw(SoloError, `Invalid name: ${name}`); + expect(() => new RelayComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, [])).to.throw( + SoloError, + `Invalid name: ${name}`, + ); }); it('should fail if name is string', () => { const name = 1; // @ts-expect-error - TS2345: Argument of type number is not assignable to parameter of type string - expect(() => new RelayComponent(name, 'valid', 'valid', [])).to.throw(SoloError, `Invalid name: ${name}`); + expect(() => new RelayComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, [])).to.throw( + SoloError, + `Invalid name: ${name}`, + ); }); it('should fail if cluster is not provided', () => { const cluster = ''; - expect(() => new RelayComponent('valid', cluster, 'valid', [])).to.throw(SoloError, `Invalid cluster: ${cluster}`); + expect(() => new RelayComponent('valid', cluster, 'valid', ComponentStates.ACTIVE, [])).to.throw( + SoloError, + `Invalid cluster: ${cluster}`, + ); }); it('should fail if cluster is string', () => { const cluster = 1; // @ts-expect-error - TS2345: Argument of type number is not assignable to parameter of type string - expect(() => new RelayComponent('valid', cluster, 'valid', [])).to.throw(SoloError, `Invalid cluster: ${cluster}`); + expect(() => new RelayComponent('valid', cluster, 'valid', ComponentStates.ACTIVE, [])).to.throw( + SoloError, + `Invalid cluster: ${cluster}`, + ); }); it('should fail if namespace is not provided', () => { const namespace = null; - expect(() => new RelayComponent('valid', 'valid', namespace, [])).to.throw( + expect(() => new RelayComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, [])).to.throw( SoloError, `Invalid namespace: ${namespace}`, ); @@ -117,7 +134,7 @@ describe('RelayComponent', () => { it('should fail if namespace is string', () => { const namespace = 1; // @ts-expect-error - forcefully provide namespace as a number to create an error - expect(() => new RelayComponent('valid', 'valid', namespace, [])).to.throw( + expect(() => new RelayComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, [])).to.throw( SoloError, `Invalid namespace: ${namespace}`, ); @@ -125,7 +142,7 @@ describe('RelayComponent', () => { it('should fail if consensusNodeAliases is not valid', () => { const consensusNodeAliases = [undefined] as NodeAliases; - expect(() => new RelayComponent('valid', 'valid', 'valid', consensusNodeAliases)).to.throw( + expect(() => new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, consensusNodeAliases)).to.throw( SoloError, `Invalid consensus node alias: ${consensusNodeAliases[0]}, aliases ${consensusNodeAliases}`, ); @@ -133,18 +150,18 @@ describe('RelayComponent', () => { it('should fail if consensusNodeAliases is not valid', () => { const consensusNodeAliases = ['node1', 1] as NodeAliases; - expect(() => new RelayComponent('valid', 'valid', 'valid', consensusNodeAliases)).to.throw( + expect(() => new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, consensusNodeAliases)).to.throw( SoloError, `Invalid consensus node alias: 1, aliases ${consensusNodeAliases}`, ); }); it('should successfully create ', () => { - new RelayComponent('valid', 'valid', 'valid'); + new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); }); it('should be an instance of BaseComponent', () => { - const component = new RelayComponent('valid', 'valid', 'valid'); + const component = new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); expect(component).to.be.instanceOf(BaseComponent); }); @@ -156,7 +173,7 @@ describe('RelayComponent', () => { consensusNodeAliases: ['node1'] as NodeAliases, }; - const component = new RelayComponent(name, cluster, namespace, consensusNodeAliases); + const component = new RelayComponent(name, cluster, namespace, ComponentStates.ACTIVE, consensusNodeAliases); expect(component.toObject()).to.deep.equal({name, cluster, namespace: namespace, consensusNodeAliases}); }); }); @@ -164,26 +181,25 @@ describe('RelayComponent', () => { describe('ConsensusNodeComponent', () => { it('should fail if name is not provided', () => { const name = ''; - expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( - SoloError, - `Invalid name: ${name}`, - ); + expect( + () => new ConsensusNodeComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), + ).to.throw(SoloError, `Invalid name: ${name}`); }); it('should fail if name is not a string', () => { - const name = 1; // @ts-ignore - expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( - SoloError, - `Invalid name: ${name}`, - ); + const name = 1; + expect( + // @ts-ignore + () => new ConsensusNodeComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), + ).to.throw(SoloError, `Invalid name: ${name}`); }); it('should fail if cluster is not provided', () => { const cluster = ''; - expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( - SoloError, - `Invalid cluster: ${cluster}`, - ); + expect( + () => + new ConsensusNodeComponent('valid', cluster, 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), + ).to.throw(SoloError, `Invalid cluster: ${cluster}`); }); it('should fail if cluster is not a string', () => { @@ -196,65 +212,89 @@ describe('ConsensusNodeComponent', () => { it('should fail if namespace is not provided', () => { const namespace = null; - expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED, 0)).to.throw( - SoloError, - `Invalid namespace: ${namespace}`, - ); + expect( + () => + new ConsensusNodeComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), + ).to.throw(SoloError, `Invalid namespace: ${namespace}`); }); it('should fail if namespace is not a string', () => { - const namespace = 1; // @ts-ignore - expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED, 0)).to.throw( - SoloError, - `Invalid namespace: ${namespace}`, - ); + const namespace = 1; + expect( + () => + // @ts-ignore + new ConsensusNodeComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), + ).to.throw(SoloError, `Invalid namespace: ${namespace}`); }); it('should fail if state is not valid', () => { - const state = 'invalid' as ConsensusNodeStates.STARTED; - expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', state, 0)).to.throw( + const nodeState = 'invalid' as ConsensusNodeStates.STARTED; + expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, nodeState, 0)).to.throw( SoloError, - `Invalid consensus node state: ${state}`, + `Invalid consensus node state: ${nodeState}`, ); }); it('should fail if nodeId is not a number', () => { - const nodeId = 'invalid'; // @ts-ignore - expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, nodeId)).to.throw( - SoloError, - `Invalid node id. It must be a number: ${nodeId}`, - ); + const nodeId = 'invalid'; + expect( + () => + new ConsensusNodeComponent( + 'valid', + 'valid', + 'valid', + ComponentStates.ACTIVE, + ConsensusNodeStates.STARTED, + // @ts-ignore + nodeId, + ), + ).to.throw(SoloError, `Invalid node id. It must be a number: ${nodeId}`); }); it('should fail if nodeId is negative', () => { - const nodeId = -1; // @ts-ignore - expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, nodeId)).to.throw( - SoloError, - `Invalid node id. It cannot be negative: ${nodeId}`, - ); + const nodeId = -1; + expect( + () => + new ConsensusNodeComponent( + 'valid', + 'valid', + 'valid', + ComponentStates.ACTIVE, + ConsensusNodeStates.STARTED, + // @ts-ignore + nodeId, + ), + ).to.throw(SoloError, `Invalid node id. It cannot be negative: ${nodeId}`); }); it('should successfully create ', () => { - new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, 0); + new ConsensusNodeComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0); }); - it('should be an instance of BaseComponent', () => { - const component = new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, 0); + it('should be an instance of BaseComponent', (): void => { + const component = new ConsensusNodeComponent( + 'valid', + 'valid', + 'valid', + ComponentStates.ACTIVE, + ConsensusNodeStates.STARTED, + 0, + ); expect(component).to.be.instanceOf(BaseComponent); }); - it('calling toObject() should return a valid data', () => { + it('calling toObject() should return a valid data', (): void => { const nodeAlias = 'node1'; const nodeInfo = { name: nodeAlias, cluster: 'cluster', namespace: 'namespace', - state: ConsensusNodeStates.STARTED, + nodeState: ConsensusNodeStates.STARTED, nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; - const {name, cluster, namespace, state, nodeId} = nodeInfo; - const component = new ConsensusNodeComponent(name, cluster, namespace, state, nodeId); + const {name, cluster, namespace, nodeState, nodeId} = nodeInfo; + const component = new ConsensusNodeComponent(name, cluster, namespace, ComponentStates.ACTIVE, nodeState, nodeId); expect(component.toObject()).to.deep.equal(nodeInfo); }); }); From ec669ebf1f9b39561aeff8ad422009ea5e1d153d Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 20:56:45 +0300 Subject: [PATCH 17/70] fixed typescript warnings and remote config component tests Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 7 +- src/commands/deployment.ts | 4 +- src/commands/network.ts | 26 ++-- src/commands/node/tasks.ts | 10 +- .../config/remote/remote-config-validator.ts | 2 +- .../core/remote-config-validator.test.ts | 25 ++-- .../remote/components-data-wrapper.test.ts | 125 ++++++++++-------- 7 files changed, 125 insertions(+), 74 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 99f823764..9b0e3a56b 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -3,10 +3,10 @@ import {Listr} from 'listr2'; import {SoloError} from '../core/errors/solo-error.js'; import * as helpers from '../core/helpers.js'; +import {showVersionBanner} from '../core/helpers.js'; import * as constants from '../core/constants.js'; import {BaseCommand} from './base.js'; import {Flags as flags} from './flags.js'; -import {showVersionBanner} from '../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import { type AnyListrContext, @@ -24,6 +24,7 @@ import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import os from 'node:os'; import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; +import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -273,7 +274,9 @@ export class BlockNodeCommand extends BaseCommand { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { const {namespace, clusterRef, blockNodeName} = context_.config; - remoteConfig.components.addNewComponent(new BlockNodeComponent(blockNodeName, clusterRef, namespace.name)); + remoteConfig.components.addNewComponent( + new BlockNodeComponent(blockNodeName, clusterRef, namespace.name, ComponentStates.ACTIVE), + ); }); }, }; diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index aea4f159c..e505bde40 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -14,13 +14,14 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {type ClusterChecks} from '../core/cluster-checks.js'; import {container} from 'tsyringe-neo'; import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; -import {type ArgvStruct, type AnyYargs, type NodeAliases} from '../types/aliases.js'; +import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {Templates} from '../core/templates.js'; import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; import {DeploymentStates} from '../core/config/remote/enumerations/deployment-states.js'; +import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface DeploymentAddClusterConfig { quiet: boolean; @@ -703,6 +704,7 @@ export class DeploymentCommand extends BaseCommand { nodeAlias, clusterRef, namespace.name, + ComponentStates.ACTIVE, ConsensusNodeStates.NON_DEPLOYED, Templates.nodeIdFromNodeAlias(nodeAlias), ), diff --git a/src/commands/network.ts b/src/commands/network.ts index 08d8fa149..4f40f7849 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -11,13 +11,14 @@ import {UserBreak} from '../core/errors/user-break.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; import * as constants from '../core/constants.js'; +import {SOLO_DEPLOYMENT_CHART} from '../core/constants.js'; import {Templates} from '../core/templates.js'; import { addDebugOptions, - resolveValidJsonFilePath, - sleep, parseNodeAliases, + resolveValidJsonFilePath, showVersionBanner, + sleep, } from '../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import fs from 'node:fs'; @@ -41,10 +42,10 @@ import {Base64} from 'js-base64'; import {SecretType} from '../integration/kube/resources/secret/secret-type.js'; import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; -import {SOLO_DEPLOYMENT_CHART} from '../core/constants.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; export interface NetworkDeployConfigClass { applicationEnv: string; @@ -1326,9 +1327,7 @@ export class NetworkCommand extends BaseCommand { title: 'Add node and proxies to remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - const { - config: {namespace}, - } = context_; + const {namespace} = context_.config; await this.remoteConfigManager.modify(async remoteConfig => { for (const consensusNode of context_.config.consensusNodes) { @@ -1337,17 +1336,28 @@ export class NetworkCommand extends BaseCommand { consensusNode.name, consensusNode.cluster, namespace.name, + ComponentStates.ACTIVE, ConsensusNodeStates.REQUESTED, consensusNode.nodeId, ), ); remoteConfig.components.addNewComponent( - new EnvoyProxyComponent(`envoy-proxy-${consensusNode.name}`, consensusNode.cluster, namespace.name), + new EnvoyProxyComponent( + `envoy-proxy-${consensusNode.name}`, + consensusNode.cluster, + namespace.name, + ComponentStates.ACTIVE, + ), ); remoteConfig.components.addNewComponent( - new HaProxyComponent(`haproxy-${consensusNode.name}`, consensusNode.cluster, namespace.name), + new HaProxyComponent( + `haproxy-${consensusNode.name}`, + consensusNode.cluster, + namespace.name, + ComponentStates.ACTIVE, + ), ); } }); diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 1071a56dc..4e716ea02 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -117,6 +117,7 @@ import {type NodeStartConfigClass} from './config-interfaces/node-start-config-c import {type CheckedNodesConfigClass, type CheckedNodesContext} from './config-interfaces/node-common-config-class.js'; import {type NetworkNodeServices} from '../../core/network-node-services.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentStates} from '../../core/config/remote/enumerations/component-states.js'; @injectable() export class NodeCommandTasks { @@ -2528,14 +2529,19 @@ export class NodeCommandTasks { nodeAlias, clusterReference, namespace, + ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, Templates.nodeIdFromNodeAlias(nodeAlias), ), ); - remoteConfig.components.addNewComponent(new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace)); + remoteConfig.components.addNewComponent( + new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace, ComponentStates.ACTIVE), + ); - remoteConfig.components.addNewComponent(new HaProxyComponent(`haproxy-${nodeAlias}`, clusterReference, namespace)); + remoteConfig.components.addNewComponent( + new HaProxyComponent(`haproxy-${nodeAlias}`, clusterReference, namespace, ComponentStates.ACTIVE), + ); }); context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes(); diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index df84fde49..fd0d9f42e 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -35,7 +35,7 @@ export class RemoteConfigValidator { label: (c: BaseComponent): string[] => [`app=network-${c.name}`], type: 'Consensus node', skipCondition(c: ConsensusNodeComponent): boolean { - return c.state === ConsensusNodeStates.REQUESTED || c.state === ConsensusNodeStates.NON_DEPLOYED; + return c.nodeState === ConsensusNodeStates.REQUESTED || c.nodeState === ConsensusNodeStates.NON_DEPLOYED; }, }, }; diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index ea6531cd7..0e2b2839b 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -import {it, describe} from 'mocha'; +import {describe, it} from 'mocha'; import {expect} from 'chai'; import * as constants from '../../../../src/core/constants.js'; @@ -30,6 +30,7 @@ import {getTestCacheDirectory} from '../../../test-utility.js'; import {Duration} from '../../../../src/core/time/duration.js'; import {LocalConfigDataWrapper} from '../../../../src/core/config/local/local-config-data-wrapper.js'; import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentStates} from '../../../../src/core/config/remote/enumerations/component-states.js'; describe('RemoteConfigValidator', () => { const namespace = NamespaceName.of('remote-config-validator'); @@ -55,7 +56,7 @@ describe('RemoteConfigValidator', () => { }); const cluster = 'cluster'; - const state = ConsensusNodeStates.STARTED; + const nodeState = ConsensusNodeStates.STARTED; const nodeAlias = 'node1' as NodeAlias; const haProxyName = Templates.renderHaProxyName(nodeAlias); @@ -68,20 +69,28 @@ describe('RemoteConfigValidator', () => { // @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private const components = new ComponentsDataWrapper( - {[relayName]: new RelayComponent(relayName, cluster, namespace.name, consensusNodeAliases)}, - {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name)}, - {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name)}, - {[envoyProxyName]: new EnvoyProxyComponent(envoyProxyName, cluster, namespace.name)}, + {[relayName]: new RelayComponent(relayName, cluster, namespace.name, ComponentStates.ACTIVE, consensusNodeAliases)}, + {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, + {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name, ComponentStates.ACTIVE)}, + {[envoyProxyName]: new EnvoyProxyComponent(envoyProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, { [nodeAlias]: new ConsensusNodeComponent( nodeAlias, cluster, namespace.name, - state, + ComponentStates.ACTIVE, + nodeState, Templates.nodeIdFromNodeAlias(nodeAlias), ), }, - {[mirrorNodeExplorerName]: new MirrorNodeExplorerComponent(mirrorNodeExplorerName, cluster, namespace.name)}, + { + [mirrorNodeExplorerName]: new MirrorNodeExplorerComponent( + mirrorNodeExplorerName, + cluster, + namespace.name, + ComponentStates.ACTIVE, + ), + }, ); async function createPod(name: string, labels: Record) { diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 540f676be..af6eca5c5 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -11,56 +11,72 @@ import {ConsensusNodeComponent} from '../../../../../src/core/config/remote/comp import {MirrorNodeExplorerComponent} from '../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; import {RelayComponent} from '../../../../../src/core/config/remote/components/relay-component.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; -import {type NodeAliases} from '../../../../../src/types/aliases.js'; import {ComponentTypes} from '../../../../../src/core/config/remote/enumerations/component-types.js'; import {ConsensusNodeStates} from '../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentStates} from '../../../../../src/core/config/remote/enumerations/component-states.js'; +import {type NodeAliases} from '../../../../../src/types/aliases.js'; +import { + type ClusterReference, + type ComponentsDataStructure, + type NamespaceNameAsString, +} from '../../../../../src/core/config/remote/types.js'; +import {BlockNodeComponent} from '../../../../../src/core/config/remote/components/block-node-component.js'; export function createComponentsDataWrapper() { - const name = 'name'; - const serviceName = name; - - const cluster = 'cluster'; - const namespace = 'namespace'; - const state = ConsensusNodeStates.STARTED; - const consensusNodeAliases = ['node1', 'node2'] as NodeAliases; - - const relays = {[serviceName]: new RelayComponent(name, cluster, namespace, consensusNodeAliases)}; - const haProxies = { - [serviceName]: new HaProxyComponent(name, cluster, namespace), - ['serviceName2']: new HaProxyComponent('name2', 'cluster2', namespace), + const name: string = 'name'; + const serviceName: string = name; + + const cluster: ClusterReference = 'cluster'; + const namespace: NamespaceNameAsString = 'namespace'; + const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; + const consensusNodeAliases: NodeAliases = ['node1', 'node2']; + const state: ComponentStates = ComponentStates.ACTIVE; + + const relays: Record = { + [serviceName]: new RelayComponent(name, cluster, namespace, state, consensusNodeAliases), }; - const mirrorNodes = {[serviceName]: new MirrorNodeComponent(name, cluster, namespace)}; - const envoyProxies = { - [serviceName]: new EnvoyProxyComponent(name, cluster, namespace), - ['serviceName2']: new EnvoyProxyComponent('name2', 'cluster2', namespace), + + const haProxies: Record = { + [serviceName]: new HaProxyComponent(name, cluster, namespace, state), + ['serviceName2']: new HaProxyComponent('name2', 'cluster2', namespace, state), }; - const consensusNodes = { - [serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, 0), - ['serviceName2']: new ConsensusNodeComponent('node2', 'cluster2', namespace, state, 1), + + const mirrorNodes: Record = { + [serviceName]: new MirrorNodeComponent(name, cluster, namespace, state), + }; + + const envoyProxies: Record = { + [serviceName]: new EnvoyProxyComponent(name, cluster, namespace, state), + ['serviceName2']: new EnvoyProxyComponent('name2', 'cluster2', namespace, state), + }; + + const consensusNodes: Record = { + [serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), + ['serviceName2']: new ConsensusNodeComponent('node2', 'cluster2', namespace, state, nodeState, 1), + }; + + const mirrorNodeExplorers: Record = { + [serviceName]: new MirrorNodeExplorerComponent(name, cluster, namespace, state), + }; + + const blockNodes: Record = { + [serviceName]: new BlockNodeComponent(name, cluster, namespace, state), }; - const mirrorNodeExplorers = {[serviceName]: new MirrorNodeExplorerComponent(name, cluster, namespace)}; // @ts-expect-error - TS267: to access private constructor - const componentsDataWrapper = new ComponentsDataWrapper( + const componentsDataWrapper: ComponentsDataWrapper = new ComponentsDataWrapper( relays, haProxies, mirrorNodes, envoyProxies, consensusNodes, mirrorNodeExplorers, + blockNodes, ); - /* - ? The class after calling the toObject() method - * RELAY: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' consensusNodeAliases: ['node1', 'node2'] } }, - * HAPROXY: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, - * MIRROR_NODE: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, - * ENVOY_PROXY: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, - * CONSENSUS_NODE: { serviceName: { state: 'started', name: 'name', cluster: 'cluster', namespace: 'namespace'} }, - * MIRROR_NODE_EXPLORER: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, - */ + return { - values: {name, cluster, namespace, state, consensusNodeAliases}, - components: {consensusNodes, haProxies, envoyProxies, mirrorNodes, mirrorNodeExplorers, relays}, + values: {name, cluster, namespace, nodeState, consensusNodeAliases, state}, + components: {consensusNodes, haProxies, envoyProxies, mirrorNodes, mirrorNodeExplorers, relays, blockNodes}, wrapper: {componentsDataWrapper}, serviceName, }; @@ -79,8 +95,11 @@ describe('ComponentsDataWrapper', () => { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const newComponentsDataWrapper = ComponentsDataWrapper.fromObject(componentsDataWrapper.toObject()); - const componentsDataWrapperObject = componentsDataWrapper.toObject(); + const newComponentsDataWrapper: ComponentsDataWrapper = ComponentsDataWrapper.fromObject( + componentsDataWrapper.toObject(), + ); + + const componentsDataWrapperObject: ComponentsDataStructure = componentsDataWrapper.toObject(); expect(componentsDataWrapperObject).to.deep.equal(newComponentsDataWrapper.toObject()); @@ -91,34 +110,35 @@ describe('ComponentsDataWrapper', () => { expect(componentsDataWrapper); }); - it('should not be able to add new component with the .add() method if it already exist', () => { + it('should not be able to add new component with the .addNewComponent() method if it already exist', () => { const { wrapper: {componentsDataWrapper}, components: {consensusNodes}, serviceName, } = createComponentsDataWrapper(); - const existingComponent = consensusNodes[serviceName]; + const existingComponent: ConsensusNodeComponent = consensusNodes[serviceName]; expect(() => componentsDataWrapper.addNewComponent(existingComponent)).to.throw(SoloError, 'Component exists'); }); - it('should be able to add new component with the .add() method', () => { + it('should be able to add new component with the .addNewComponent() method', () => { const { wrapper: {componentsDataWrapper}, + values: {state}, } = createComponentsDataWrapper(); - const newServiceName = 'envoy'; + const newServiceName: string = 'envoy'; const {name, cluster, namespace} = { name: newServiceName, cluster: 'cluster', namespace: 'new-namespace', }; - const newComponent = new EnvoyProxyComponent(name, cluster, namespace); + const newComponent: EnvoyProxyComponent = new EnvoyProxyComponent(name, cluster, namespace, state); componentsDataWrapper.addNewComponent(newComponent); - const componentDataWrapperObject = componentsDataWrapper.toObject(); + const componentDataWrapperObject: ComponentsDataStructure = componentsDataWrapper.toObject(); expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newServiceName); @@ -131,35 +151,34 @@ describe('ComponentsDataWrapper', () => { expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(3); }); - it('should be able to edit component with the .edit()', () => { + it('should be able to edit component with the .editComponent()', () => { const { wrapper: {componentsDataWrapper}, components: {relays}, - values: {cluster, namespace}, + values: {namespace, state}, serviceName, } = createComponentsDataWrapper(); - const relayComponent = relays[serviceName]; + const relayComponent: RelayComponent = relays[serviceName]; componentsDataWrapper.editComponent(relayComponent); - const newCluster = 'newCluster'; + const newCluster: ClusterReference = 'newCluster'; - const newReplayComponent = new RelayComponent(relayComponent.name, newCluster, namespace); + const newReplayComponent: RelayComponent = new RelayComponent(relayComponent.name, newCluster, namespace, state); componentsDataWrapper.editComponent(newReplayComponent); expect(componentsDataWrapper.toObject()[ComponentTypes.Relay][relayComponent.name].cluster).to.equal(newCluster); }); - it("should not be able to edit component with the .edit() if it doesn't exist ", () => { + it("should not be able to edit component with the .editComponent() if it doesn't exist ", () => { const { wrapper: {componentsDataWrapper}, - components: {relays}, + values: {cluster, namespace, state}, serviceName, } = createComponentsDataWrapper(); const notFoundServiceName: string = 'not_found'; - const relay = relays[serviceName]; - relay.name = notFoundServiceName; + const relay: RelayComponent = new RelayComponent(notFoundServiceName, cluster, namespace, state); expect(() => componentsDataWrapper.editComponent(relay)).to.throw( SoloError, @@ -168,14 +187,16 @@ describe('ComponentsDataWrapper', () => { }); it('should be able to disable component with the .disableComponent()', () => { - // TODO 666 const { wrapper: {componentsDataWrapper}, + components: {relays}, serviceName, } = createComponentsDataWrapper(); componentsDataWrapper.disableComponent(serviceName, ComponentTypes.Relay); + expect(relays[serviceName].state).to.equal(ComponentStates.DELETED); + expect(componentsDataWrapper.relays).not.to.have.own.property(serviceName); }); @@ -184,7 +205,7 @@ describe('ComponentsDataWrapper', () => { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const notFoundServiceName = 'not_found'; + const notFoundServiceName: string = 'not_found'; expect(() => componentsDataWrapper.disableComponent(notFoundServiceName, ComponentTypes.Relay)).to.throw( SoloError, From b500b8760b401596b62524edc2a8a6ae4e438397 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 3 Apr 2025 23:20:55 +0300 Subject: [PATCH 18/70] fix all unit tests Signed-off-by: Zhan Milenkov --- .../config/remote/components-data-wrapper.ts | 10 +- .../remote/components/relay-component.ts | 8 +- .../remote/enumerations/component-types.ts | 2 +- test/unit/core/config/remote/cluster.test.ts | 128 +++++---- .../remote/components-data-wrapper.test.ts | 31 +- .../remote/components/components.test.ts | 271 +++--------------- test/unit/core/config/remote/metadata.test.ts | 185 ++++-------- .../unit/core/config/remote/migration.test.ts | 52 +--- .../remote/remote-config-data-wrapper.test.ts | 78 ++--- 9 files changed, 254 insertions(+), 511 deletions(-) diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 42793dc04..602e6ffba 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -254,7 +254,15 @@ export class ComponentsDataWrapper } } - return new ComponentsDataWrapper(relays, haProxies, mirrorNodes, envoyProxies, consensusNodes, mirrorNodeExplorers); + return new ComponentsDataWrapper( + relays, + haProxies, + mirrorNodes, + envoyProxies, + consensusNodes, + mirrorNodeExplorers, + blockNodes, + ); } /** Used to create an empty instance used to keep the constructor private */ diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index 26b88e5b1..fe8801f71 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -2,7 +2,7 @@ import {SoloError} from '../../../errors/solo-error.js'; import {BaseComponent} from './base-component.js'; -import {type IRelayComponent, type NamespaceNameAsString} from '../types.js'; +import {ClusterReference, type IRelayComponent, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; @@ -11,19 +11,19 @@ import {type ComponentStates} from '../enumerations/component-states.js'; export class RelayComponent extends BaseComponent implements IRelayComponent, ToObject { /** * @param name - to distinguish components. - * @param cluster - in which the component is deployed. + * @param clusterReference - in which the component is deployed. * @param namespace - associated with the component. * @param state - the state of the component * @param consensusNodeAliases - list node aliases */ public constructor( name: string, - cluster: string, + clusterReference: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, public readonly consensusNodeAliases: NodeAliases = [], ) { - super(ComponentTypes.Relay, name, cluster, namespace, state); + super(ComponentTypes.Relay, name, clusterReference, namespace, state); this.validate(); } diff --git a/src/core/config/remote/enumerations/component-types.ts b/src/core/config/remote/enumerations/component-types.ts index 0a209b7d4..4c01bcc88 100644 --- a/src/core/config/remote/enumerations/component-types.ts +++ b/src/core/config/remote/enumerations/component-types.ts @@ -5,7 +5,7 @@ */ export enum ComponentTypes { ConsensusNode = 'consensusNodes', - BlockNode = 'blockNode', + BlockNode = 'blockNodes', HaProxy = 'haProxies', EnvoyProxy = 'envoyProxies', MirrorNode = 'mirrorNodes', diff --git a/test/unit/core/config/remote/cluster.test.ts b/test/unit/core/config/remote/cluster.test.ts index ed69e85c6..afcd46a9c 100644 --- a/test/unit/core/config/remote/cluster.test.ts +++ b/test/unit/core/config/remote/cluster.test.ts @@ -2,69 +2,93 @@ import {it} from 'mocha'; import {expect} from 'chai'; -import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {Cluster} from '../../../../../src/core/config/remote/cluster.js'; -import {type ClusterReference} from '../../../../../src/core/config/remote/types.js'; +import {type ClusterReference, type ICluster} from '../../../../../src/core/config/remote/types.js'; describe('Cluster', () => { - it('should fail if name is not provided', () => { - expect(() => new Cluster(null, 'valid', 'valid')).to.throw(SoloError, 'name is required'); - expect(() => new Cluster('', 'valid', 'valid')).to.throw(SoloError, 'name is required'); - }); + it('should convert to an object', () => { + const clusterData: ICluster = { + name: 'name', + namespace: 'namespace', + deployment: 'deployment', + dnsBaseDomain: 'cluster.world', + dnsConsensusNodePattern: 'network.svc', + }; - it('should fail if name is not a string', () => { - const name = 1; // @ts-ignore - expect(() => new Cluster(name, 'valid', 'valid')).to.throw(SoloError, 'Invalid type for name: number'); - }); + const cluster: Cluster = new Cluster( + clusterData.name, + clusterData.namespace, + clusterData.deployment, + clusterData.dnsBaseDomain, + clusterData.dnsConsensusNodePattern, + ); - it('should fail if namespace is not provided', () => { - expect(() => new Cluster('valid', null, 'valid')).to.throw(SoloError, 'namespace is required'); - expect(() => new Cluster('valid', '', 'valid')).to.throw(SoloError, 'namespace is required'); + const clusterObject: ICluster = cluster.toObject(); + expect(clusterObject.name).to.equal(clusterData.name); + expect(clusterObject.namespace).to.equal(clusterData.namespace); + expect(clusterObject.deployment).to.equal(clusterData.deployment); + expect(clusterObject.dnsBaseDomain).to.equal(clusterData.dnsBaseDomain); + expect(clusterObject.dnsConsensusNodePattern).to.equal(clusterData.dnsConsensusNodePattern); }); - it('should fail if namespace is not a string', () => { - const namespace = 1; // @ts-ignore - expect(() => new Cluster('valid', namespace, 'valid')).to.throw(SoloError, 'Invalid type for namespace: number'); - }); + it('should convert clusters map to an object', () => { + const clusterData1: ICluster = { + name: 'name1', + namespace: 'namespace1', + deployment: 'deployment1', + dnsBaseDomain: 'cluster1.world', + dnsConsensusNodePattern: 'network1.svc', + }; - it('should convert to an object', () => { - const c = new Cluster('name', 'namespace', 'deployment', 'cluster.world', 'network.svc'); - const o = c.toObject(); - expect(o.name).to.equal('name'); - expect(o.namespace).to.equal('namespace'); - expect(o.deployment).to.equal('deployment'); - expect(o.dnsBaseDomain).to.equal('cluster.world'); - expect(o.dnsConsensusNodePattern).to.equal('network.svc'); - }); + const clusterData2: ICluster = { + name: 'name2', + namespace: 'namespace2', + deployment: 'deployment2', + dnsBaseDomain: 'cluster2.world', + dnsConsensusNodePattern: 'network2.svc', + }; - it('should convert clusters map to an object', () => { - const map1: Record = { - cluster1: new Cluster('name1', 'namespace1', 'deployment1', 'cluster1.world', 'network1.svc'), - cluster2: new Cluster('name2', 'namespace2', 'deployment2', 'cluster2.world', 'network2.svc'), + const clusterMap1: Record = { + cluster1: new Cluster( + clusterData1.name, + clusterData1.namespace, + clusterData1.deployment, + clusterData1.dnsBaseDomain, + clusterData1.dnsConsensusNodePattern, + ), + cluster2: new Cluster( + clusterData2.name, + clusterData2.namespace, + clusterData2.deployment, + clusterData2.dnsBaseDomain, + clusterData2.dnsConsensusNodePattern, + ), }; - const o = Cluster.toClustersMapObject(map1); - expect(o.cluster1.name).to.equal('name1'); - expect(o.cluster1.namespace).to.equal('namespace1'); - expect(o.cluster1.deployment).to.equal('deployment1'); - expect(o.cluster1.dnsBaseDomain).to.equal('cluster1.world'); - expect(o.cluster1.dnsConsensusNodePattern).to.equal('network1.svc'); - expect(o.cluster2.name).to.equal('name2'); - expect(o.cluster2.namespace).to.equal('namespace2'); - expect(o.cluster2.deployment).to.equal('deployment2'); - expect(o.cluster2.dnsBaseDomain).to.equal('cluster2.world'); - expect(o.cluster2.dnsConsensusNodePattern).to.equal('network2.svc'); + const clustersMapObject: any = Cluster.toClustersMapObject(clusterMap1); + expect(clustersMapObject.cluster1.name).to.equal(clusterData1.name); + expect(clustersMapObject.cluster1.namespace).to.equal(clusterData1.namespace); + expect(clustersMapObject.cluster1.deployment).to.equal(clusterData1.deployment); + expect(clustersMapObject.cluster1.dnsBaseDomain).to.equal(clusterData1.dnsBaseDomain); + expect(clustersMapObject.cluster1.dnsConsensusNodePattern).to.equal(clusterData1.dnsConsensusNodePattern); + + expect(clustersMapObject.cluster2.name).to.equal(clusterData2.name); + expect(clustersMapObject.cluster2.namespace).to.equal(clusterData2.namespace); + expect(clustersMapObject.cluster2.deployment).to.equal(clusterData2.deployment); + expect(clustersMapObject.cluster2.dnsBaseDomain).to.equal(clusterData2.dnsBaseDomain); + expect(clustersMapObject.cluster2.dnsConsensusNodePattern).to.equal(clusterData2.dnsConsensusNodePattern); + + const clustersMap2: Record = Cluster.fromClustersMapObject(clustersMapObject); + expect(clustersMap2.cluster1.name).to.equal(clusterMap1.cluster1.name); + expect(clustersMap2.cluster1.namespace).to.equal(clusterMap1.cluster1.namespace); + expect(clustersMap2.cluster1.deployment).to.equal(clusterMap1.cluster1.deployment); + expect(clustersMap2.cluster1.dnsBaseDomain).to.equal(clusterMap1.cluster1.dnsBaseDomain); + expect(clustersMap2.cluster1.dnsConsensusNodePattern).to.equal(clusterMap1.cluster1.dnsConsensusNodePattern); - const map2 = Cluster.fromClustersMapObject(o); - expect(map2.cluster1.name).to.equal(map1.cluster1.name); - expect(map2.cluster1.namespace).to.equal(map1.cluster1.namespace); - expect(map2.cluster1.deployment).to.equal(map1.cluster1.deployment); - expect(map2.cluster1.dnsBaseDomain).to.equal(map1.cluster1.dnsBaseDomain); - expect(map2.cluster1.dnsConsensusNodePattern).to.equal(map1.cluster1.dnsConsensusNodePattern); - expect(map2.cluster2.name).to.equal(map1.cluster2.name); - expect(map2.cluster2.namespace).to.equal(map1.cluster2.namespace); - expect(map2.cluster2.deployment).to.equal(map1.cluster2.deployment); - expect(map2.cluster2.dnsBaseDomain).to.equal(map1.cluster2.dnsBaseDomain); - expect(map2.cluster2.dnsConsensusNodePattern).to.equal(map1.cluster2.dnsConsensusNodePattern); + expect(clustersMap2.cluster2.name).to.equal(clusterMap1.cluster2.name); + expect(clustersMap2.cluster2.namespace).to.equal(clusterMap1.cluster2.namespace); + expect(clustersMap2.cluster2.deployment).to.equal(clusterMap1.cluster2.deployment); + expect(clustersMap2.cluster2.dnsBaseDomain).to.equal(clusterMap1.cluster2.dnsBaseDomain); + expect(clustersMap2.cluster2.dnsConsensusNodePattern).to.equal(clusterMap1.cluster2.dnsConsensusNodePattern); }); }); diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index af6eca5c5..ec101f7a2 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -22,7 +22,27 @@ import { } from '../../../../../src/core/config/remote/types.js'; import {BlockNodeComponent} from '../../../../../src/core/config/remote/components/block-node-component.js'; -export function createComponentsDataWrapper() { +export function createComponentsDataWrapper(): { + values: { + name: string; + cluster: ClusterReference; + namespace: NamespaceNameAsString; + nodeState: ConsensusNodeStates; + consensusNodeAliases: NodeAliases; + state: ComponentStates; + }; + components: { + relays: Record; + haProxies: Record; + mirrorNodes: Record; + envoyProxies: Record; + consensusNodes: Record; + mirrorNodeExplorers: Record; + blockNodes: Record; + }; + wrapper: {componentsDataWrapper: ComponentsDataWrapper}; + serviceName: string; +} { const name: string = 'name'; const serviceName: string = name; @@ -87,7 +107,10 @@ describe('ComponentsDataWrapper', () => { it('should not be able to create a instance if wrong data is passed to constructor', () => { // @ts-expect-error - TS267: to access private constructor - expect(() => new ComponentsDataWrapper({serviceName: {}})).to.throw(SoloError, 'Invalid component type'); + expect((): ComponentsDataWrapper => new ComponentsDataWrapper({serviceName: {}})).to.throw( + SoloError, + 'Invalid component type', + ); }); it('toObject method should return a object that can be parsed with fromObject', () => { @@ -146,6 +169,7 @@ describe('ComponentsDataWrapper', () => { name, cluster, namespace, + state, }); expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(3); @@ -175,7 +199,6 @@ describe('ComponentsDataWrapper', () => { const { wrapper: {componentsDataWrapper}, values: {cluster, namespace, state}, - serviceName, } = createComponentsDataWrapper(); const notFoundServiceName: string = 'not_found'; const relay: RelayComponent = new RelayComponent(notFoundServiceName, cluster, namespace, state); @@ -196,8 +219,6 @@ describe('ComponentsDataWrapper', () => { componentsDataWrapper.disableComponent(serviceName, ComponentTypes.Relay); expect(relays[serviceName].state).to.equal(ComponentStates.DELETED); - - expect(componentsDataWrapper.relays).not.to.have.own.property(serviceName); }); it("should not be able to disable component with the .disableComponent() if it doesn't exist ", () => { diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 7fb941fb2..17ce5657c 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -10,269 +10,79 @@ import {HaProxyComponent} from '../../../../../../src/core/config/remote/compone import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/components/envoy-proxy-component.js'; import {MirrorNodeComponent} from '../../../../../../src/core/config/remote/components/mirror-node-component.js'; import {MirrorNodeExplorerComponent} from '../../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; -import {SoloError} from '../../../../../../src/core/errors/solo-error.js'; -import {type NodeAliases} from '../../../../../../src/types/aliases.js'; +import {type NodeAlias} from '../../../../../../src/types/aliases.js'; import {Templates} from '../../../../../../src/core/templates.js'; import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; import {ComponentStates} from '../../../../../../src/core/config/remote/enumerations/component-states.js'; import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; +import { + type Component, + type IConsensusNodeComponent, + type IRelayComponent, +} from '../../../../../../src/core/config/remote/types.js'; function testBaseComponentData(classComponent: any): void { - const state = ComponentStates.ACTIVE; - const validNamespace = 'valid'; - it('should fail if name is not provided', () => { - const name = ''; - expect(() => new classComponent(name, 'valid', validNamespace, state)).to.throw(SoloError, `Invalid name: ${name}`); - }); - - it('should fail if name is string', () => { - const name = 1; // @ts-ignore - expect(() => new classComponent(name, 'valid', validNamespace, state)).to.throw(SoloError, `Invalid name: ${name}`); - }); - - it('should fail if cluster is not provided', () => { - const cluster = ''; - expect(() => new classComponent('valid', cluster, validNamespace, state)).to.throw( - SoloError, - `Invalid cluster: ${cluster}`, - ); - }); - - it('should fail if cluster is string', () => { - const cluster = 1; - expect(() => new classComponent('valid', cluster, validNamespace, state)).to.throw( - SoloError, - `Invalid cluster: ${cluster}`, - ); - }); - - it('should fail if namespace is not provided', () => { - const namespace = ''; - expect(() => new classComponent('valid', 'valid', namespace, state)).to.throw( - SoloError, - `Invalid namespace: ${namespace}`, - ); - }); - - it('should fail if namespace is string', () => { - const namespace = 1; - expect(() => new classComponent('valid', 'valid', namespace, state)).to.throw( - SoloError, - `Invalid namespace: ${namespace}`, - ); - }); - - it('should successfully create ', () => { - new classComponent('valid', 'valid', 'valid', state); - }); - it('should be an instance of BaseComponent', () => { - const component = new classComponent('valid', 'valid', validNamespace, state); + const component: any = new classComponent('service-name', 'cluster-reference', 'namespace', ComponentStates.ACTIVE); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { - const {name, cluster, namespace} = {name: 'name', cluster: 'cluster', namespace: 'namespace'}; - const component = new classComponent(name, cluster, namespace, state); - expect(component.toObject()).to.deep.equal({name, cluster, namespace}); + const data: Component = {name: 'name', cluster: 'cluster', namespace: 'namespace', state: ComponentStates.ACTIVE}; + + const component: any = new classComponent(data.name, data.cluster, data.namespace, data.state); + expect(component.toObject()).to.deep.equal(data); }); } -describe('HaProxyComponent', (): void => testBaseComponentData(HaProxyComponent)); +describe('HaProxyComponent', () => testBaseComponentData(HaProxyComponent)); -describe('EnvoyProxyComponent', (): void => testBaseComponentData(EnvoyProxyComponent)); +describe('EnvoyProxyComponent', () => testBaseComponentData(EnvoyProxyComponent)); -describe('MirrorNodeComponent', (): void => testBaseComponentData(MirrorNodeComponent)); +describe('MirrorNodeComponent', () => testBaseComponentData(MirrorNodeComponent)); -describe('MirrorNodeExplorerComponent', (): void => testBaseComponentData(MirrorNodeExplorerComponent)); +describe('MirrorNodeExplorerComponent', () => testBaseComponentData(MirrorNodeExplorerComponent)); -describe('BlockNodeComponent', (): void => testBaseComponentData(BlockNodeComponent)); +describe('BlockNodeComponent', () => testBaseComponentData(BlockNodeComponent)); describe('RelayComponent', () => { - it('should fail if name is not provided', () => { - const name = ''; - expect(() => new RelayComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, [])).to.throw( - SoloError, - `Invalid name: ${name}`, - ); - }); - - it('should fail if name is string', () => { - const name = 1; - // @ts-expect-error - TS2345: Argument of type number is not assignable to parameter of type string - expect(() => new RelayComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, [])).to.throw( - SoloError, - `Invalid name: ${name}`, - ); - }); - - it('should fail if cluster is not provided', () => { - const cluster = ''; - expect(() => new RelayComponent('valid', cluster, 'valid', ComponentStates.ACTIVE, [])).to.throw( - SoloError, - `Invalid cluster: ${cluster}`, - ); - }); - - it('should fail if cluster is string', () => { - const cluster = 1; - // @ts-expect-error - TS2345: Argument of type number is not assignable to parameter of type string - expect(() => new RelayComponent('valid', cluster, 'valid', ComponentStates.ACTIVE, [])).to.throw( - SoloError, - `Invalid cluster: ${cluster}`, - ); - }); - - it('should fail if namespace is not provided', () => { - const namespace = null; - expect(() => new RelayComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, [])).to.throw( - SoloError, - `Invalid namespace: ${namespace}`, - ); - }); - - it('should fail if namespace is string', () => { - const namespace = 1; - // @ts-expect-error - forcefully provide namespace as a number to create an error - expect(() => new RelayComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, [])).to.throw( - SoloError, - `Invalid namespace: ${namespace}`, - ); - }); - - it('should fail if consensusNodeAliases is not valid', () => { - const consensusNodeAliases = [undefined] as NodeAliases; - expect(() => new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, consensusNodeAliases)).to.throw( - SoloError, - `Invalid consensus node alias: ${consensusNodeAliases[0]}, aliases ${consensusNodeAliases}`, - ); - }); - - it('should fail if consensusNodeAliases is not valid', () => { - const consensusNodeAliases = ['node1', 1] as NodeAliases; - expect(() => new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, consensusNodeAliases)).to.throw( - SoloError, - `Invalid consensus node alias: 1, aliases ${consensusNodeAliases}`, - ); - }); - it('should successfully create ', () => { new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); }); it('should be an instance of BaseComponent', () => { - const component = new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); + const component: RelayComponent = new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { - const {name, cluster, namespace, consensusNodeAliases} = { + const values: IRelayComponent = { name: 'name', cluster: 'cluster', namespace: 'namespace', - consensusNodeAliases: ['node1'] as NodeAliases, + state: ComponentStates.ACTIVE, + consensusNodeAliases: ['node1'], }; - const component = new RelayComponent(name, cluster, namespace, ComponentStates.ACTIVE, consensusNodeAliases); - expect(component.toObject()).to.deep.equal({name, cluster, namespace: namespace, consensusNodeAliases}); - }); -}); - -describe('ConsensusNodeComponent', () => { - it('should fail if name is not provided', () => { - const name = ''; - expect( - () => new ConsensusNodeComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), - ).to.throw(SoloError, `Invalid name: ${name}`); - }); - - it('should fail if name is not a string', () => { - const name = 1; - expect( - // @ts-ignore - () => new ConsensusNodeComponent(name, 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), - ).to.throw(SoloError, `Invalid name: ${name}`); - }); - - it('should fail if cluster is not provided', () => { - const cluster = ''; - expect( - () => - new ConsensusNodeComponent('valid', cluster, 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), - ).to.throw(SoloError, `Invalid cluster: ${cluster}`); - }); - - it('should fail if cluster is not a string', () => { - const cluster = 1; // @ts-ignore - expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( - SoloError, - `Invalid cluster: ${cluster}`, + const component: RelayComponent = new RelayComponent( + values.name, + values.cluster, + values.namespace, + values.state, + values.consensusNodeAliases, ); - }); - - it('should fail if namespace is not provided', () => { - const namespace = null; - expect( - () => - new ConsensusNodeComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), - ).to.throw(SoloError, `Invalid namespace: ${namespace}`); - }); - it('should fail if namespace is not a string', () => { - const namespace = 1; - expect( - () => - // @ts-ignore - new ConsensusNodeComponent('valid', 'valid', namespace, ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0), - ).to.throw(SoloError, `Invalid namespace: ${namespace}`); - }); - - it('should fail if state is not valid', () => { - const nodeState = 'invalid' as ConsensusNodeStates.STARTED; - expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, nodeState, 0)).to.throw( - SoloError, - `Invalid consensus node state: ${nodeState}`, - ); - }); - - it('should fail if nodeId is not a number', () => { - const nodeId = 'invalid'; - expect( - () => - new ConsensusNodeComponent( - 'valid', - 'valid', - 'valid', - ComponentStates.ACTIVE, - ConsensusNodeStates.STARTED, - // @ts-ignore - nodeId, - ), - ).to.throw(SoloError, `Invalid node id. It must be a number: ${nodeId}`); - }); - - it('should fail if nodeId is negative', () => { - const nodeId = -1; - expect( - () => - new ConsensusNodeComponent( - 'valid', - 'valid', - 'valid', - ComponentStates.ACTIVE, - ConsensusNodeStates.STARTED, - // @ts-ignore - nodeId, - ), - ).to.throw(SoloError, `Invalid node id. It cannot be negative: ${nodeId}`); + expect(component.toObject()).to.deep.equal(values); }); +}); +describe('ConsensusNodeComponent', () => { it('should successfully create ', () => { new ConsensusNodeComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0); }); - it('should be an instance of BaseComponent', (): void => { - const component = new ConsensusNodeComponent( + it('should be an instance of BaseComponent', () => { + const component: ConsensusNodeComponent = new ConsensusNodeComponent( 'valid', 'valid', 'valid', @@ -283,18 +93,25 @@ describe('ConsensusNodeComponent', () => { expect(component).to.be.instanceOf(BaseComponent); }); - it('calling toObject() should return a valid data', (): void => { - const nodeAlias = 'node1'; - const nodeInfo = { + it('calling toObject() should return a valid data', () => { + const nodeAlias: NodeAlias = 'node1'; + const values: IConsensusNodeComponent = { name: nodeAlias, cluster: 'cluster', namespace: 'namespace', + state: ComponentStates.ACTIVE, nodeState: ConsensusNodeStates.STARTED, nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; - const {name, cluster, namespace, nodeState, nodeId} = nodeInfo; - const component = new ConsensusNodeComponent(name, cluster, namespace, ComponentStates.ACTIVE, nodeState, nodeId); - expect(component.toObject()).to.deep.equal(nodeInfo); + const component: ConsensusNodeComponent = new ConsensusNodeComponent( + values.name, + values.cluster, + values.namespace, + values.state, + values.nodeState, + values.nodeId, + ); + expect(component.toObject()).to.deep.equal(values); }); }); diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index e7ccfafb5..778e74d34 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -5,58 +5,50 @@ import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {RemoteConfigMetadata} from '../../../../../src/core/config/remote/metadata.js'; -import { - type EmailAddress, - type NamespaceNameAsString, - type Version, -} from '../../../../../src/core/config/remote/types.js'; +import {type EmailAddress, type RemoteConfigMetadataStructure} from '../../../../../src/core/config/remote/types.js'; import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; -export function createMetadata() { - const namespace: NamespaceNameAsString = 'namespace'; - const deploymentName = 'kind-namespace'; - const state = DeploymentStates.PRE_GENESIS; +export function createMetadata(): { + metadata: RemoteConfigMetadata; + migration: Migration; + values: RemoteConfigMetadataStructure; +} { const lastUpdatedAt: Date = new Date(); const lastUpdateBy: EmailAddress = 'test@test.test'; - const soloVersion: Version = '0.0.1'; - const migration = new Migration(lastUpdatedAt, lastUpdateBy, '0.0.0'); - const soloChartVersion = ''; - const hederaPlatformVersion = ''; - const hederaMirrorNodeChartVersion = ''; - const hederaExplorerChartVersion = ''; - const hederaJsonRpcRelayChartVersion = ''; + + const values: RemoteConfigMetadataStructure = { + namespace: 'namespace', + deploymentName: 'kind-namespace', + state: DeploymentStates.PRE_GENESIS, + soloVersion: '0.0.1', + migration: new Migration(lastUpdatedAt, lastUpdateBy, '0.0.0'), + lastUpdatedAt, + lastUpdateBy, + soloChartVersion: '', + hederaPlatformVersion: '', + hederaMirrorNodeChartVersion: '', + hederaExplorerChartVersion: '', + hederaJsonRpcRelayChartVersion: '', + }; return { metadata: new RemoteConfigMetadata( - namespace, - deploymentName, - state, - lastUpdatedAt, - lastUpdateBy, - soloVersion, - '', - '', - '', - '', - '', - migration, + values.namespace, + values.deploymentName, + values.state, + values.lastUpdatedAt, + values.lastUpdateBy, + values.soloVersion, + values.soloChartVersion, + values.hederaPlatformVersion, + values.hederaMirrorNodeChartVersion, + values.hederaExplorerChartVersion, + values.hederaJsonRpcRelayChartVersion, + values.migration as Migration, ), - values: { - namespace, - deploymentName, - state, - lastUpdatedAt, - lastUpdateBy, - migration, - soloVersion, - soloChartVersion, - hederaPlatformVersion, - hederaMirrorNodeChartVersion, - hederaExplorerChartVersion, - hederaJsonRpcRelayChartVersion, - }, - migration, + migration: values.migration as Migration, + values, }; } @@ -66,37 +58,21 @@ describe('RemoteConfigMetadata', () => { }); it('toObject method should return a valid object', () => { - const { - metadata, - migration, - values: { - namespace, - deploymentName, - state, - lastUpdatedAt, - lastUpdateBy, - soloVersion, - soloChartVersion, - hederaPlatformVersion, - hederaMirrorNodeChartVersion, - hederaExplorerChartVersion, - hederaJsonRpcRelayChartVersion, - }, - } = createMetadata(); + const {metadata, migration, values} = createMetadata(); expect(metadata.toObject()).to.deep.equal({ - namespace, - deploymentName, - state, - lastUpdatedAt, - lastUpdateBy, - soloVersion, - soloChartVersion, - hederaPlatformVersion, - hederaMirrorNodeChartVersion, - hederaExplorerChartVersion, - hederaJsonRpcRelayChartVersion, - migration: migration.toObject(), + namespace: values.namespace, + deploymentName: values.deploymentName, + state: values.state, + lastUpdatedAt: values.lastUpdatedAt, + lastUpdateBy: values.lastUpdateBy, + soloVersion: values.soloVersion, + soloChartVersion: values.soloChartVersion, + hederaPlatformVersion: values.hederaPlatformVersion, + hederaMirrorNodeChartVersion: values.hederaMirrorNodeChartVersion, + hederaExplorerChartVersion: values.hederaExplorerChartVersion, + hederaJsonRpcRelayChartVersion: values.hederaJsonRpcRelayChartVersion, + migration: (migration as Migration).toObject(), }); }); @@ -109,7 +85,7 @@ describe('RemoteConfigMetadata', () => { // @ts-expect-error - TS234: to access private property delete metadata._migration; - const newMetadata = RemoteConfigMetadata.fromObject({ + const newMetadata: RemoteConfigMetadata = RemoteConfigMetadata.fromObject({ namespace, deploymentName, state, @@ -133,7 +109,7 @@ describe('RemoteConfigMetadata', () => { metadata, values: {lastUpdateBy}, } = createMetadata(); - const version = '0.0.1'; + const version: string = '0.0.1'; metadata.makeMigration(lastUpdateBy, version); @@ -143,60 +119,23 @@ describe('RemoteConfigMetadata', () => { }); describe('Values', () => { - const { - values: {namespace, deploymentName, lastUpdatedAt, lastUpdateBy, soloVersion, state}, - } = createMetadata(); - - it('should not be able to create new instance of the class with invalid name', () => { - expect( - () => new RemoteConfigMetadata(null, deploymentName, state, lastUpdatedAt, lastUpdateBy, soloVersion), - ).to.throw(SoloError, `Invalid namespace: ${null}`); - - expect( - // @ts-expect-error: TS2345 - to assign unexpected value - () => new RemoteConfigMetadata(1, deploymentName, state, lastUpdatedAt, lastUpdateBy, soloVersion), - ).to.throw(SoloError, `Invalid namespace: ${1}`); - }); - - it('should not be able to create new instance of the class with invalid lastUpdatedAt', () => { - expect( - () => new RemoteConfigMetadata(namespace, deploymentName, state, null, lastUpdateBy, soloVersion), - ).to.throw(SoloError, `Invalid lastUpdatedAt: ${null}`); - - // @ts-expect-error: TS2345 - to assign unexpected value - expect(() => new RemoteConfigMetadata(namespace, deploymentName, state, 1, lastUpdateBy, soloVersion)).to.throw( - SoloError, - `Invalid lastUpdatedAt: ${1}`, - ); - }); - - it('should not be able to create new instance of the class with invalid lastUpdateBy', () => { - expect( - () => new RemoteConfigMetadata(namespace, deploymentName, state, lastUpdatedAt, null, soloVersion), - ).to.throw(SoloError, `Invalid lastUpdateBy: ${null}`); - - // @ts-expect-error: TS2345 - to assign unexpected value - expect(() => new RemoteConfigMetadata(namespace, deploymentName, state, lastUpdatedAt, 1, soloVersion)).to.throw( - SoloError, - `Invalid lastUpdateBy: ${1}`, - ); - }); + const {values} = createMetadata(); it('should not be able to create new instance of the class with invalid migration', () => { expect( () => new RemoteConfigMetadata( - namespace, - deploymentName, - state, - lastUpdatedAt, - lastUpdateBy, - soloVersion, - '', - '', - '', - '', - '', + values.namespace, + values.deploymentName, + values.state, + values.lastUpdatedAt, + values.lastUpdateBy, + values.soloVersion, + values.soloChartVersion, + values.hederaPlatformVersion, + values.hederaMirrorNodeChartVersion, + values.hederaExplorerChartVersion, + values.hederaJsonRpcRelayChartVersion, // @ts-expect-error - TS2345: to inject wrong migration {}, ), diff --git a/test/unit/core/config/remote/migration.test.ts b/test/unit/core/config/remote/migration.test.ts index 088bb91c7..3f96bb261 100644 --- a/test/unit/core/config/remote/migration.test.ts +++ b/test/unit/core/config/remote/migration.test.ts @@ -3,57 +3,17 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; -import {type EmailAddress, type Version} from '../../../../../src/core/config/remote/types.js'; -import {SoloError} from '../../../../../src/core/errors/solo-error.js'; +import {type IMigration} from '../../../../../src/core/config/remote/types.js'; -function createMigration() { - const migratedAt = new Date(); - const migratedBy = 'test@test.test' as EmailAddress; - const fromVersion = '1.0.0' as Version; - - return { - migration: new Migration(migratedAt, migratedBy, fromVersion), - values: {migratedAt, migratedBy, fromVersion}, - }; -} describe('Migration', () => { - it('should be able to create new instance of the class with valid data', () => { - expect(() => createMigration()).not.to.throw(); + const values: IMigration = {migratedAt: new Date(), migratedBy: 'test@test.test', fromVersion: '1.0.0'}; + let migration: Migration; + + beforeEach(() => { + migration = new Migration(values.migratedAt, values.migratedBy, values.fromVersion); }); it('toObject method should return a valid object', () => { - const {migration, values} = createMigration(); - expect(migration.toObject()).to.deep.equal(values); }); - - describe('Values', () => { - const migratedAt = new Date(); - const migratedBy = 'test@test.test' as EmailAddress; - const fromVersion = '1.0.0' as Version; - - it('should not be able to create new instance of the class with invalid migratedAt', () => { - // @ts-ignore - expect(() => new Migration(null, migratedBy, fromVersion)).to.throw(SoloError, `Invalid migratedAt: ${null}`); - - // @ts-ignore - expect(() => new Migration(1, migratedBy, fromVersion)).to.throw(SoloError, `Invalid migratedAt: ${1}`); - }); - - it('should not be able to create new instance of the class with invalid migratedBy', () => { - // @ts-ignore - expect(() => new Migration(migratedAt, null, fromVersion)).to.throw(SoloError, `Invalid migratedBy: ${null}`); - - // @ts-ignore - expect(() => new Migration(migratedAt, 1, fromVersion)).to.throw(SoloError, `Invalid migratedBy: ${1}`); - }); - - it('should not be able to create new instance of the class with invalid fromVersion', () => { - // @ts-ignore - expect(() => new Migration(migratedAt, migratedBy, null)).to.throw(SoloError, `Invalid fromVersion: ${null}`); - - // @ts-ignore - expect(() => new Migration(migratedAt, migratedBy, 1)).to.throw(SoloError, `Invalid fromVersion: ${1}`); - }); - }); }); diff --git a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts index d6af7249e..638f22069 100644 --- a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts +++ b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts @@ -7,41 +7,39 @@ import * as yaml from 'yaml'; import {RemoteConfigDataWrapper} from '../../../../../src/core/config/remote/remote-config-data-wrapper.js'; import {createMetadata} from './metadata.test.js'; import {createComponentsDataWrapper} from './components-data-wrapper.test.js'; -import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import * as constants from '../../../../../src/core/constants.js'; import {CommonFlagsDataWrapper} from '../../../../../src/core/config/remote/common-flags-data-wrapper.js'; - -const configManagerMock = { - update: (...arguments_: any) => true, - getFlag: (...arguments_: any) => true, - hasFlag: (...arguments_: any) => true, - setFlag: (...arguments_: any) => true, +import {type RemoteConfigDataStructure} from '../../../../../src/core/config/remote/types.js'; +import {type RemoteConfigData} from '../../../../../src/core/config/remote/remote-config-data.js'; + +const configManagerMock: any = { + update: (..._arguments: any): true => true, + getFlag: (..._arguments: any): true => true, + hasFlag: (..._arguments: any): true => true, + setFlag: (..._arguments: any): true => true, }; -async function createRemoteConfigDataWrapper() { +async function createRemoteConfigDataWrapper(): Promise<{ + values: RemoteConfigData; + dataWrapper: RemoteConfigDataWrapper; +}> { const {metadata} = createMetadata(); const { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const clusters = {}; - const components = componentsDataWrapper; - const lastExecutedCommand = 'lastExecutedCommand'; - const commandHistory = []; - const flags = await CommonFlagsDataWrapper.initialize(configManagerMock as any, {}); - - const dataWrapper = new RemoteConfigDataWrapper({ + const values: RemoteConfigData = { metadata, - clusters, - components, - lastExecutedCommand, - commandHistory, - flags, - }); + clusters: {}, + components: componentsDataWrapper, + lastExecutedCommand: 'lastExecutedCommand', + commandHistory: [], + flags: await CommonFlagsDataWrapper.initialize(configManagerMock, {}), + }; return { - dataWrapper, - values: {metadata, clusters, components, lastExecutedCommand, commandHistory}, + values, + dataWrapper: new RemoteConfigDataWrapper(values), }; } @@ -51,7 +49,7 @@ describe('RemoteConfigDataWrapper', async () => { it('should be able to add new command to history with addCommandToHistory()', async () => { const {dataWrapper} = await createRemoteConfigDataWrapper(); - const command = 'command'; + const command: string = 'command'; dataWrapper.addCommandToHistory(command); @@ -59,7 +57,7 @@ describe('RemoteConfigDataWrapper', async () => { expect(dataWrapper.commandHistory).to.include(command); it('should be able to handle overflow', () => { - for (let index = 0; index < constants.SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY; index++) { + for (let index: number = 0; index < constants.SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY; index++) { dataWrapper.addCommandToHistory(command); } }); @@ -67,9 +65,9 @@ describe('RemoteConfigDataWrapper', async () => { it('should successfully be able to parse yaml and create instance with fromConfigmap()', async () => { const {dataWrapper} = await createRemoteConfigDataWrapper(); - const dataWrapperObject = dataWrapper.toObject(); + const dataWrapperObject: RemoteConfigDataStructure = dataWrapper.toObject(); - const yamlData = yaml.stringify({ + const yamlData: string = yaml.stringify({ metadata: dataWrapperObject.metadata, components: dataWrapperObject.components as any, clusters: dataWrapperObject.clusters, @@ -77,30 +75,6 @@ describe('RemoteConfigDataWrapper', async () => { lastExecutedCommand: dataWrapperObject.lastExecutedCommand, }); - RemoteConfigDataWrapper.fromConfigmap(configManagerMock as any, {data: {'remote-config-data': yamlData}} as any); - }); - - it('should fail if invalid data is passed to setters', async () => { - const {dataWrapper} = await createRemoteConfigDataWrapper(); - - // @ts-expect-error TS2322: Type string is not assignable to type string[] - expect(() => (dataWrapper.commandHistory = '')).to.throw(SoloError); - - // @ts-expect-error TS2341 Property lastExecutedCommand is private and only accessible within class RemoteConfigDataWrapper - expect(() => (dataWrapper.lastExecutedCommand = '')).to.throw(SoloError); - - // @ts-expect-error TS2341 Property lastExecutedCommand is private and only accessible within class RemoteConfigDataWrapper - expect(() => (dataWrapper.lastExecutedCommand = 1)).to.throw(SoloError); - - // @ts-expect-error TS2322 Type number is not assignable to type ComponentsDataWrapper - expect(() => (dataWrapper.components = 1)).to.throw(SoloError); - - // @ts-expect-error TS2322 Type string is not assignable to type ComponentsDataWrapper - expect(() => (dataWrapper.components = '')).to.throw(SoloError); - - expect(() => (dataWrapper.metadata = null)).to.throw(SoloError); - - // @ts-expect-error 2740: Type {} is missing the following properties from type RemoteConfigMetadata - expect(() => (dataWrapper.metadata = {})).to.throw(SoloError); + RemoteConfigDataWrapper.fromConfigmap(configManagerMock, {data: {'remote-config-data': yamlData}} as any); }); }); From 50e96f58746e3cd4b3bb3770868023c8bafc478b Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Fri, 4 Apr 2025 15:02:31 +0300 Subject: [PATCH 19/70] added new logic for creating a component names with unique indexes Signed-off-by: Zhan Milenkov --- src/commands/node/tasks.ts | 19 +++++++- src/commands/relay.ts | 19 ++++---- .../config/remote/components-data-wrapper.ts | 20 +++++++++ .../remote/components/base-component.ts | 31 ++++++++++++- .../remote/components/block-node-component.ts | 15 ++++++- .../components/envoy-proxy-component.ts | 16 ++++++- .../remote/components/ha-proxy-component.ts | 16 ++++++- .../components/mirror-node-component.ts | 15 ++++++- .../mirror-node-explorer-component.ts | 15 ++++++- .../remote/components/relay-component.ts | 8 +++- src/core/templates.ts | 44 ++++++++----------- .../core/remote-config-validator.test.ts | 4 +- test/unit/core/config/remote/metadata.test.ts | 44 +++++++++---------- 13 files changed, 193 insertions(+), 73 deletions(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 4e716ea02..140c1ebc7 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -78,7 +78,7 @@ import {NetworkNodes} from '../../core/network-nodes.js'; import {container, inject, injectable} from 'tsyringe-neo'; import {type Optional, type SoloListr, type SoloListrTask, type SoloListrTaskWrapper} from '../../types/index.js'; import { - type ClusterReference, + type ClusterReference, ComponentName, type DeploymentName, type NamespaceNameAsString, } from '../../core/config/remote/types.js'; @@ -118,6 +118,8 @@ import {type CheckedNodesConfigClass, type CheckedNodesContext} from './config-i import {type NetworkNodeServices} from '../../core/network-node-services.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; import {ComponentStates} from '../../core/config/remote/enumerations/component-states.js'; +import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js'; +import {RelayComponent} from '../../core/config/remote/components/relay-component.js'; @injectable() export class NodeCommandTasks { @@ -2535,12 +2537,25 @@ export class NodeCommandTasks { ), ); + const newEnvoyProxyIndex: number = this.remoteConfigManager.components.getNewComponentIndex( + ComponentTypes.HaProxy, + ); + + const newEnvoyProxyName: ComponentName = HaProxyComponent.renderHaProxyName(newEnvoyProxyIndex, nodeAlias); + + remoteConfig.components.addNewComponent( new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace, ComponentStates.ACTIVE), ); + const newHaProxyIndex: number = this.remoteConfigManager.components.getNewComponentIndex( + ComponentTypes.HaProxy, + ); + + const newHaProxyName: ComponentName = HaProxyComponent.renderHaProxyName(newHaProxyIndex, nodeAlias); + remoteConfig.components.addNewComponent( - new HaProxyComponent(`haproxy-${nodeAlias}`, clusterReference, namespace, ComponentStates.ACTIVE), + new HaProxyComponent(newHaProxyName, clusterReference, namespace, ComponentStates.ACTIVE), ); }); diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 1c38254b5..2b233e23c 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -4,22 +4,22 @@ import {Listr} from 'listr2'; import {SoloError} from '../core/errors/solo-error.js'; import {MissingArgumentError} from '../core/errors/missing-argument-error.js'; import * as helpers from '../core/helpers.js'; +import {getNodeAccountMap, showVersionBanner} from '../core/helpers.js'; import * as constants from '../core/constants.js'; +import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; import {type ProfileManager} from '../core/profile-manager.js'; import {type AccountManager} from '../core/account-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; -import {getNodeAccountMap, showVersionBanner} from '../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {RelayComponent} from '../core/config/remote/components/relay-component.js'; import * as Base64 from 'js-base64'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; -import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; +import {type ClusterReference, ComponentName, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; -import {JSON_RPC_RELAY_CHART} from '../core/constants.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; @@ -546,13 +546,16 @@ export class RelayCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const { - config: {namespace, nodeAliases}, - } = context_; - const cluster = this.remoteConfigManager.currentCluster; // TODO + const {namespace, nodeAliases, clusterRef} = context_.config; + + const newComponentIndex: number = this.remoteConfigManager.components.getNewComponentIndex( + ComponentTypes.Relay, + ); + + const componentName: ComponentName = RelayComponent.renderRelayName(newComponentIndex); remoteConfig.components.addNewComponent( - new RelayComponent('relay', cluster, namespace.name, ComponentStates.ACTIVE, nodeAliases), + new RelayComponent(componentName, clusterRef, namespace.name, ComponentStates.ACTIVE, nodeAliases), ); }); }, diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 602e6ffba..27c530adc 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -296,6 +296,26 @@ export class ComponentsDataWrapper return Object.values(components).some((component): boolean => BaseComponent.compare(component, newComponent)); } + /** + * Checks all existing components of specified type and gives you a new unique index + */ + public getNewComponentIndex(componentType: ComponentTypes): number { + let newComponentIndex: number = 1; + + const callback: (components: Record) => void = (components): void => { + for (const componentName of Object.keys(components)) { + const componentIndex: number = BaseComponent.parseComponentName(componentName); + if (newComponentIndex <= componentIndex) { + newComponentIndex = componentIndex + 1; + } + } + }; + + this.applyCallbackToComponentGroup(componentType, '', callback); + + return newComponentIndex; + } + /** Validates that the component group mapping has only components from the expected instance */ private validateComponentTypes(components: Record, expectedInstance: any): void { for (const [serviceName, component] of Object.entries(components)) { diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 74ea32a36..b557b5d19 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -10,7 +10,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; * Represents the base structure and common functionality for all components within the system. * This class provides validation, comparison, and serialization functionality for components. */ -export abstract class BaseComponent implements Component, Validate, ToObject { +export class BaseComponent implements Component, Validate, ToObject { /** * @param type - type for identifying. * @param name - the name to distinguish components. @@ -71,4 +71,33 @@ export abstract class BaseComponent implements Component, Validate, ToObject string = (nodeAlias): string => `envoy-proxy-${nodeAlias}}`; + + public constructor( + name: string, + cluster: ClusterReference, + namespace: NamespaceNameAsString, + state: ComponentStates, + ) { super(ComponentTypes.EnvoyProxy, name, cluster, namespace, state); this.validate(); } @@ -18,4 +26,8 @@ export class EnvoyProxyComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new EnvoyProxyComponent(name, cluster, namespace, state); } + + public static renderEnvoyProxyName(index: number, nodeAlias: NodeAlias): string { + return EnvoyProxyComponent.renderComponentName(EnvoyProxyComponent.BASE_NAME(nodeAlias), index); + } } diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 6ff70d49f..bc27e1a66 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -1,12 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type Component, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; +import {type NodeAlias} from '../../../../types/aliases.js'; export class HaProxyComponent extends BaseComponent { - public constructor(name: string, cluster: string, namespace: NamespaceNameAsString, state: ComponentStates) { + private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}}`; + + public constructor( + name: string, + cluster: ClusterReference, + namespace: NamespaceNameAsString, + state: ComponentStates, + ) { super(ComponentTypes.HaProxy, name, cluster, namespace, state); this.validate(); } @@ -18,4 +26,8 @@ export class HaProxyComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new HaProxyComponent(name, cluster, namespace, state); } + + public static renderHaProxyName(index: number, nodeAlias: NodeAlias): string { + return HaProxyComponent.renderComponentName(HaProxyComponent.BASE_NAME(nodeAlias), index); + } } diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index 48a9a564b..800c4ed63 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -1,12 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type Component, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; export class MirrorNodeComponent extends BaseComponent { - public constructor(name: string, cluster: string, namespace: NamespaceNameAsString, state: ComponentStates) { + private static readonly BASE_NAME: string = 'mirror-node'; + + public constructor( + name: string, + cluster: ClusterReference, + namespace: NamespaceNameAsString, + state: ComponentStates, + ) { super(ComponentTypes.MirrorNode, name, cluster, namespace, state); this.validate(); } @@ -18,4 +25,8 @@ export class MirrorNodeComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeComponent(name, cluster, namespace, state); } + + public static renderMirrorNodeName(index: number): string { + return MirrorNodeComponent.renderComponentName(MirrorNodeComponent.BASE_NAME, index); + } } diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 377c614b6..3687cf8d1 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -1,12 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type Component, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; export class MirrorNodeExplorerComponent extends BaseComponent { - public constructor(name: string, cluster: string, namespace: NamespaceNameAsString, state: ComponentStates) { + private static readonly BASE_NAME: string = 'mirror-node-explorer'; + + public constructor( + name: string, + cluster: ClusterReference, + namespace: NamespaceNameAsString, + state: ComponentStates, + ) { super(ComponentTypes.MirrorNodeExplorer, name, cluster, namespace, state); this.validate(); } @@ -18,4 +25,8 @@ export class MirrorNodeExplorerComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeExplorerComponent(name, cluster, namespace, state); } + + public static renderMirrorNodeExplorerName(index: number): string { + return MirrorNodeExplorerComponent.renderComponentName(MirrorNodeExplorerComponent.BASE_NAME, index); + } } diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index fe8801f71..40375ce65 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -2,13 +2,15 @@ import {SoloError} from '../../../errors/solo-error.js'; import {BaseComponent} from './base-component.js'; -import {ClusterReference, type IRelayComponent, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type IRelayComponent, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; export class RelayComponent extends BaseComponent implements IRelayComponent, ToObject { + private static readonly BASE_NAME: string = 'relay'; + /** * @param name - to distinguish components. * @param clusterReference - in which the component is deployed. @@ -51,4 +53,8 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To ...super.toObject(), }; } + + public static renderRelayName(index: number): string { + return RelayComponent.renderComponentName(RelayComponent.BASE_NAME, index); + } } diff --git a/src/core/templates.ts b/src/core/templates.ts index 2a82a6d22..186ae37a4 100644 --- a/src/core/templates.ts +++ b/src/core/templates.ts @@ -25,10 +25,6 @@ export class Templates { return `network-${nodeAlias}-svc`; } - private static nodeAliasFromNetworkSvcName(svcName: string): NodeAlias { - return svcName.split('-').slice(1, -1).join('-') as NodeAlias; - } - public static renderNetworkHeadlessSvcName(nodeAlias: NodeAlias): string { return `network-${nodeAlias}`; } @@ -69,7 +65,7 @@ export class Templates { return `${nodeAlias}-admin`; } - public static renderNodeFriendlyName(prefix: string, nodeAlias: NodeAlias, suffix = ''): string { + public static renderNodeFriendlyName(prefix: string, nodeAlias: NodeAlias, suffix: string = ''): string { const parts = [prefix, nodeAlias]; if (suffix) { parts.push(suffix); @@ -184,11 +180,6 @@ export class Templates { return `${Templates.renderNetworkSvcName(nodeAlias)}.${namespace.name}.svc.cluster.local`; } - private static nodeAliasFromFullyQualifiedNetworkSvcName(svcName: string): NodeAlias { - const parts = svcName.split('.'); - return this.nodeAliasFromNetworkSvcName(parts[0]); - } - public static nodeIdFromNodeAlias(nodeAlias: NodeAlias): NodeId { for (let index = nodeAlias.length - 1; index > 0; index--) { if (Number.isNaN(Number.parseInt(nodeAlias[index]))) { @@ -237,7 +228,10 @@ export class Templates { * * @returns the appropriate secret labels */ - static renderGrpcTlsCertificatesSecretLabelObject(nodeAlias: NodeAlias, type: GrpcProxyTlsEnums) { + public static renderGrpcTlsCertificatesSecretLabelObject( + nodeAlias: NodeAlias, + type: GrpcProxyTlsEnums, + ): Record { switch (type) { //? HAProxy Proxy case GrpcProxyTlsEnums.GRPC: { @@ -251,23 +245,20 @@ export class Templates { } } - public static renderEnvoyProxyName(nodeAlias: NodeAlias): string { - return `envoy-proxy-${nodeAlias}`; - } - - public static renderHaProxyName(nodeAlias: NodeAlias): string { - return `haproxy-${nodeAlias}`; - } - - public static renderFullyQualifiedHaProxyName(nodeAlias: NodeAlias, namespace: NamespaceName): string { - return `${Templates.renderHaProxyName(nodeAlias)}-svc.${namespace}.svc.cluster.local`; - } - public static parseNodeAliasToIpMapping(unparsed: string): Record { const mapping: Record = {}; for (const data of unparsed.split(',')) { const [nodeAlias, ip] = data.split('=') as [NodeAlias, IP]; + + if (!nodeAlias || typeof nodeAlias !== 'string') { + throw new SoloError(`Can't parse node alias: ${data}`); + } + + if (!ip || typeof ip !== 'string') { + throw new SoloError(`Can't parse ip: ${data}`); + } + mapping[nodeAlias] = ip; } @@ -283,6 +274,7 @@ export class Templates { if (!nodeAlias || typeof nodeAlias !== 'string') { throw new SoloError(`Can't parse node alias: ${data}`); } + if (!domainName || typeof domainName !== 'string') { throw new SoloError(`Can't parse domain name: ${data}`); } @@ -307,15 +299,15 @@ export class Templates { * @param dnsBaseDomain - the base domain of the cluster * @param dnsConsensusNodePattern - the pattern to use for the consensus node */ - static renderConsensusNodeFullyQualifiedDomainName( + public static renderConsensusNodeFullyQualifiedDomainName( nodeAlias: string, nodeId: number, namespace: NamespaceNameAsString, cluster: ClusterReference, dnsBaseDomain: string, dnsConsensusNodePattern: string, - ) { - const searchReplace = { + ): string { + const searchReplace: Record = { '{nodeAlias}': nodeAlias, '{nodeId}': nodeId.toString(), '{namespace}': namespace, diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index 0e2b2839b..0c4a78e9f 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -65,10 +65,10 @@ describe('RemoteConfigValidator', () => { const mirrorNodeName = 'mirror-node'; const mirrorNodeExplorerName = 'mirror-node-explorer'; - const consensusNodeAliases = [nodeAlias] as NodeAliases; + const consensusNodeAliases: NodeAliases = [nodeAlias]; // @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private - const components = new ComponentsDataWrapper( + const components: ComponentsDataWrapper = new ComponentsDataWrapper( {[relayName]: new RelayComponent(relayName, cluster, namespace.name, ComponentStates.ACTIVE, consensusNodeAliases)}, {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name, ComponentStates.ACTIVE)}, diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index 778e74d34..b8e8a4ffd 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -6,14 +6,15 @@ import {Migration} from '../../../../../src/core/config/remote/migration.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {RemoteConfigMetadata} from '../../../../../src/core/config/remote/metadata.js'; import {type EmailAddress, type RemoteConfigMetadataStructure} from '../../../../../src/core/config/remote/types.js'; - import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; -export function createMetadata(): { +interface MetadataTestStructure { metadata: RemoteConfigMetadata; migration: Migration; values: RemoteConfigMetadataStructure; -} { +} + +export function createMetadata(): MetadataTestStructure { const lastUpdatedAt: Date = new Date(); const lastUpdateBy: EmailAddress = 'test@test.test'; @@ -25,11 +26,11 @@ export function createMetadata(): { migration: new Migration(lastUpdatedAt, lastUpdateBy, '0.0.0'), lastUpdatedAt, lastUpdateBy, - soloChartVersion: '', - hederaPlatformVersion: '', - hederaMirrorNodeChartVersion: '', - hederaExplorerChartVersion: '', - hederaJsonRpcRelayChartVersion: '', + soloChartVersion: '1.0.0', + hederaPlatformVersion: '1.0.0', + hederaMirrorNodeChartVersion: '1.0.0', + hederaExplorerChartVersion: '1.0.0', + hederaJsonRpcRelayChartVersion: '1.0.0', }; return { @@ -77,26 +78,23 @@ describe('RemoteConfigMetadata', () => { }); it('should successfully create instance using fromObject', () => { - const { - metadata, - values: {namespace, deploymentName, lastUpdatedAt, lastUpdateBy, soloVersion, state}, - } = createMetadata(); + const {metadata, values} = createMetadata(); // @ts-expect-error - TS234: to access private property delete metadata._migration; const newMetadata: RemoteConfigMetadata = RemoteConfigMetadata.fromObject({ - namespace, - deploymentName, - state, - lastUpdatedAt, - lastUpdateBy, - soloVersion, - soloChartVersion: '', - hederaPlatformVersion: '', - hederaMirrorNodeChartVersion: '', - hederaExplorerChartVersion: '', - hederaJsonRpcRelayChartVersion: '', + namespace: values.namespace, + deploymentName: values.deploymentName, + state: values.state, + lastUpdatedAt: values.lastUpdatedAt, + lastUpdateBy: values.lastUpdateBy, + soloVersion: values.soloVersion, + soloChartVersion: values.soloChartVersion, + hederaPlatformVersion: values.hederaPlatformVersion, + hederaMirrorNodeChartVersion: values.hederaMirrorNodeChartVersion, + hederaExplorerChartVersion: values.hederaExplorerChartVersion, + hederaJsonRpcRelayChartVersion: values.hederaJsonRpcRelayChartVersion, }); expect(newMetadata.toObject()).to.deep.equal(metadata.toObject()); From 249e415914522ab8b72faa90a55db86bb259af4a Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Fri, 4 Apr 2025 15:27:30 +0300 Subject: [PATCH 20/70] setting up new component creating for remote config Signed-off-by: Zhan Milenkov --- src/commands/network.ts | 53 +++++++++++++++++-------------- src/commands/node/tasks.ts | 65 ++++++++++++++++---------------------- src/commands/relay.ts | 8 ++--- 3 files changed, 61 insertions(+), 65 deletions(-) diff --git a/src/commands/network.ts b/src/commands/network.ts index 4f40f7849..d82cbd26a 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -37,7 +37,7 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js'; import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js'; import {type ConsensusNode} from '../core/model/consensus-node.js'; -import {type ClusterReference, type ClusterReferences} from '../core/config/remote/types.js'; +import {type ClusterReference, type ClusterReferences, type ComponentName} from '../core/config/remote/types.js'; import {Base64} from 'js-base64'; import {SecretType} from '../integration/kube/resources/secret/secret-type.js'; import {Duration} from '../core/time/duration.js'; @@ -46,6 +46,7 @@ import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; export interface NetworkDeployConfigClass { applicationEnv: string; @@ -256,7 +257,7 @@ export class NetworkCommand extends BaseCommand { const isMinioSecretCreated = await this.k8Factory .getK8(context) .secrets() - .createOrReplace(namespace, constants.MINIO_SECRET_NAME, SecretType.OPAQUE, minioData, undefined); + .createOrReplace(namespace, constants.MINIO_SECRET_NAME, SecretType.OPAQUE, minioData); if (!isMinioSecretCreated) { throw new SoloError(`failed to create new minio secret using context: ${context}`); @@ -298,7 +299,7 @@ export class NetworkCommand extends BaseCommand { const isCloudSecretCreated = await this.k8Factory .getK8(context) .secrets() - .createOrReplace(namespace, constants.UPLOADER_SECRET_NAME, SecretType.OPAQUE, cloudData, undefined); + .createOrReplace(namespace, constants.UPLOADER_SECRET_NAME, SecretType.OPAQUE, cloudData); if (!isCloudSecretCreated) { throw new SoloError( @@ -330,7 +331,7 @@ export class NetworkCommand extends BaseCommand { const k8client = this.k8Factory.getK8(context); const isBackupSecretCreated = await k8client .secrets() - .createOrReplace(namespace, constants.BACKUP_SECRET_NAME, SecretType.OPAQUE, backupData, undefined); + .createOrReplace(namespace, constants.BACKUP_SECRET_NAME, SecretType.OPAQUE, backupData); if (!isBackupSecretCreated) { throw new SoloError(`failed to create secret for backup uploader using context: ${context}`); @@ -1329,12 +1330,15 @@ export class NetworkCommand extends BaseCommand { task: async (context_): Promise => { const {namespace} = context_.config; - await this.remoteConfigManager.modify(async remoteConfig => { + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { for (const consensusNode of context_.config.consensusNodes) { + const nodeAlias: NodeAlias = consensusNode.name; + const clusterReference: ClusterReference = consensusNode.cluster; + remoteConfig.components.editComponent( new ConsensusNodeComponent( - consensusNode.name, - consensusNode.cluster, + nodeAlias, + clusterReference, namespace.name, ComponentStates.ACTIVE, ConsensusNodeStates.REQUESTED, @@ -1342,23 +1346,26 @@ export class NetworkCommand extends BaseCommand { ), ); - remoteConfig.components.addNewComponent( - new EnvoyProxyComponent( - `envoy-proxy-${consensusNode.name}`, - consensusNode.cluster, - namespace.name, - ComponentStates.ACTIVE, - ), - ); + // eslint-disable-next-line unicorn/consistent-function-scoping + const createNewEnvoyProxyComponent: () => EnvoyProxyComponent = (): EnvoyProxyComponent => { + const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); - remoteConfig.components.addNewComponent( - new HaProxyComponent( - `haproxy-${consensusNode.name}`, - consensusNode.cluster, - namespace.name, - ComponentStates.ACTIVE, - ), - ); + const componentName: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); + + return new EnvoyProxyComponent(componentName, clusterReference, namespace.name, ComponentStates.ACTIVE); + }; + + // eslint-disable-next-line unicorn/consistent-function-scoping + const createNewHaProxyComponent: () => HaProxyComponent = (): HaProxyComponent => { + const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); + + const componentName: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); + + return new HaProxyComponent(componentName, clusterReference, namespace.name, ComponentStates.ACTIVE); + }; + + remoteConfig.components.addNewComponent(createNewEnvoyProxyComponent()); + remoteConfig.components.addNewComponent(createNewHaProxyComponent()); } }); }, diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 140c1ebc7..7f37822af 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -78,7 +78,9 @@ import {NetworkNodes} from '../../core/network-nodes.js'; import {container, inject, injectable} from 'tsyringe-neo'; import {type Optional, type SoloListr, type SoloListrTask, type SoloListrTaskWrapper} from '../../types/index.js'; import { - type ClusterReference, ComponentName, + type ClusterReference, + type ComponentName, + type Context, type DeploymentName, type NamespaceNameAsString, } from '../../core/config/remote/types.js'; @@ -119,7 +121,6 @@ import {type NetworkNodeServices} from '../../core/network-node-services.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; import {ComponentStates} from '../../core/config/remote/enumerations/component-states.js'; import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js'; -import {RelayComponent} from '../../core/config/remote/components/relay-component.js'; @injectable() export class NodeCommandTasks { @@ -204,11 +205,10 @@ export class NodeCommandTasks { const zipBytesChunk = new Uint8Array(zipBytes.subarray(start, start + constants.UPGRADE_FILE_CHUNK_SIZE)); let fileTransaction = null; - if (start === 0) { - fileTransaction = new FileUpdateTransaction().setFileId(constants.UPGRADE_FILE_ID).setContents(zipBytesChunk); - } else { - fileTransaction = new FileAppendTransaction().setFileId(constants.UPGRADE_FILE_ID).setContents(zipBytesChunk); - } + fileTransaction = + start === 0 + ? new FileUpdateTransaction().setFileId(constants.UPGRADE_FILE_ID).setContents(zipBytesChunk) + : new FileAppendTransaction().setFileId(constants.UPGRADE_FILE_ID).setContents(zipBytesChunk); const resp = await fileTransaction.execute(nodeClient); const receipt = await resp.getReceipt(nodeClient); this.logger.debug( @@ -281,11 +281,9 @@ export class NodeCommandTasks { for (const nodeAlias of nodeAliases) { const podReference = podReferences[nodeAlias]; const context = helpers.extractContextFromConsensusNodes(nodeAlias, consensusNodes); - if (buildPathMap.has(nodeAlias)) { - localDataLibraryBuildPath = buildPathMap.get(nodeAlias); - } else { - localDataLibraryBuildPath = defaultDataLibraryBuildPath; - } + localDataLibraryBuildPath = buildPathMap.has(nodeAlias) + ? buildPathMap.get(nodeAlias) + : defaultDataLibraryBuildPath; if (!fs.existsSync(localDataLibraryBuildPath)) { throw new SoloError(`local build path does not exist: ${localDataLibraryBuildPath}`); @@ -462,7 +460,7 @@ export class NodeCommandTasks { .execContainer([ 'bash', '-c', - 'curl -s http://localhost:9999/metrics | grep platform_PlatformStatus | grep -v \\#', + String.raw`curl -s http://localhost:9999/metrics | grep platform_PlatformStatus | grep -v \#`, ]); if (!response) { @@ -988,7 +986,7 @@ export class NodeCommandTasks { context_: CheckedNodesContext, task: SoloListrTaskWrapper, nodeAliases: NodeAliases, - maxAttempts: number = undefined, + maxAttempts?: number, ) { context_.config.podRefs = {}; const consensusNodes = context_.config.consensusNodes; @@ -1542,12 +1540,7 @@ export class NodeCommandTasks { this.configManager.getFlag(flags.deployment), this.configManager.getFlag(flags.forcePortForward), ); - await this._addStake( - context_.config.namespace, - context_.newNode.accountId, - context_.config.nodeAlias, - undefined, - ); + await this._addStake(context_.config.namespace, context_.newNode.accountId, context_.config.nodeAlias); }, }; } @@ -2518,10 +2511,10 @@ export class NodeCommandTasks { return { title: 'Add new node to remote config', task: async (context_, task) => { - const nodeAlias = context_.config.nodeAlias; + const nodeAlias: NodeAlias = context_.config.nodeAlias; const namespace: NamespaceNameAsString = context_.config.namespace.name; - const clusterReference = context_.config.clusterRef; - const context = this.localConfig.clusterRefs[clusterReference]; + const clusterReference: ClusterReference = context_.config.clusterRef; + const context: Context = this.localConfig.clusterRefs[clusterReference]; task.title += `: ${nodeAlias}`; @@ -2537,26 +2530,24 @@ export class NodeCommandTasks { ), ); - const newEnvoyProxyIndex: number = this.remoteConfigManager.components.getNewComponentIndex( - ComponentTypes.HaProxy, - ); + const createNewEnvoyProxyComponent: () => EnvoyProxyComponent = (): EnvoyProxyComponent => { + const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); - const newEnvoyProxyName: ComponentName = HaProxyComponent.renderHaProxyName(newEnvoyProxyIndex, nodeAlias); + const componentName: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); + return new EnvoyProxyComponent(componentName, clusterReference, namespace, ComponentStates.ACTIVE); + }; - remoteConfig.components.addNewComponent( - new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace, ComponentStates.ACTIVE), - ); + const createNewHaProxyComponent: () => HaProxyComponent = (): HaProxyComponent => { + const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); - const newHaProxyIndex: number = this.remoteConfigManager.components.getNewComponentIndex( - ComponentTypes.HaProxy, - ); + const componentName: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); - const newHaProxyName: ComponentName = HaProxyComponent.renderHaProxyName(newHaProxyIndex, nodeAlias); + return new HaProxyComponent(componentName, clusterReference, namespace, ComponentStates.ACTIVE); + }; - remoteConfig.components.addNewComponent( - new HaProxyComponent(newHaProxyName, clusterReference, namespace, ComponentStates.ACTIVE), - ); + remoteConfig.components.addNewComponent(createNewEnvoyProxyComponent()); + remoteConfig.components.addNewComponent(createNewHaProxyComponent()); }); context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes(); diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 2b233e23c..4f47a5281 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -548,14 +548,12 @@ export class RelayCommand extends BaseCommand { await this.remoteConfigManager.modify(async remoteConfig => { const {namespace, nodeAliases, clusterRef} = context_.config; - const newComponentIndex: number = this.remoteConfigManager.components.getNewComponentIndex( - ComponentTypes.Relay, - ); + const newRelayIndex: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.Relay); - const componentName: ComponentName = RelayComponent.renderRelayName(newComponentIndex); + const newRelayName: ComponentName = RelayComponent.renderRelayName(newRelayIndex); remoteConfig.components.addNewComponent( - new RelayComponent(componentName, clusterRef, namespace.name, ComponentStates.ACTIVE, nodeAliases), + new RelayComponent(newRelayName, clusterRef, namespace.name, ComponentStates.ACTIVE, nodeAliases), ); }); }, From d4382a1dcf2d4e4f89d6357c3481b33e0ac1010b Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Fri, 4 Apr 2025 16:05:31 +0300 Subject: [PATCH 21/70] transitioning block node command to use new remote config creation strategy Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 35 +++++++++++++---------------------- src/commands/explorer.ts | 18 ++++++++++++------ src/commands/mirror-node.ts | 14 ++++++++------ 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 9b0e3a56b..0498082d1 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -25,6 +25,8 @@ import {type NamespaceName} from '../integration/kube/resources/namespace/namesp import os from 'node:os'; import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -41,7 +43,7 @@ interface BlockNodeDeployConfigClass { context: string; isChartInstalled: boolean; valuesArg: string; - blockNodeName: string; + newBlockNodeName: string; releaseName: string; } @@ -78,7 +80,7 @@ export class BlockNodeCommand extends BaseCommand { valuesArgument += helpers.prepareValuesFiles(config.valuesFile); } - valuesArgument += helpers.populateHelmArguments({nameOverride: config.blockNodeName}); + valuesArgument += helpers.populateHelmArguments({nameOverride: config.newBlockNodeName}); if (config.domainName) { valuesArgument += helpers.populateHelmArguments({ @@ -100,8 +102,8 @@ export class BlockNodeCommand extends BaseCommand { return valuesArgument; } - private getReleaseName(blockNodeId: string): string { - return constants.BLOCK_NODE_RELEASE_NAME + '-' + blockNodeId; + private getReleaseName(blockNodeIndex: number): string { + return constants.BLOCK_NODE_RELEASE_NAME + '-' + blockNodeIndex; } private async deploy(argv: ArgvStruct): Promise { @@ -154,23 +156,12 @@ export class BlockNodeCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodeDeployConfigClass = context_.config; - let newBlockNodeNumber: number = 1; - - if ( - this.remoteConfigManager.components.blockNodes && - Object.values(this.remoteConfigManager.components.blockNodes).length > 0 - ) { - for (const blockNodeComponent of Object.values(this.remoteConfigManager.components.blockNodes)) { - const blockNodeNumber: number = +blockNodeComponent.name.split('-').at(-1); - if (blockNodeNumber >= newBlockNodeNumber) { - newBlockNodeNumber = blockNodeNumber + 1; - } - } - } + const newBlockNodeIndex: number = this.remoteConfigManager.components.getNewComponentIndex( + ComponentTypes.BlockNode, + ); - const releaseName: string = this.getReleaseName(newBlockNodeNumber.toString()); - config.blockNodeName = releaseName; - config.releaseName = releaseName; + config.newBlockNodeName = MirrorNodeComponent.renderMirrorNodeName(newBlockNodeIndex); + config.releaseName = this.getReleaseName(newBlockNodeIndex); }, }, { @@ -272,10 +263,10 @@ export class BlockNodeCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { - const {namespace, clusterRef, blockNodeName} = context_.config; + const {namespace, clusterRef, newBlockNodeName} = context_.config; remoteConfig.components.addNewComponent( - new BlockNodeComponent(blockNodeName, clusterRef, namespace.name, ComponentStates.ACTIVE), + new BlockNodeComponent(newBlockNodeName, clusterRef, namespace.name, ComponentStates.ACTIVE), ); }); }, diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 942cd343c..ca352e833 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -26,10 +26,12 @@ import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; import * as helpers from '../core/helpers.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; +import {type ClusterReference, type ComponentName} from '../core/config/remote/types.js'; +import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; interface ExplorerDeployConfigClass { chartDirectory: string; - clusterRef: string; + clusterRef: ClusterReference; clusterContext: string; enableIngress: boolean; enableHederaExplorerTls: boolean; @@ -590,12 +592,16 @@ export class ExplorerCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { - const { - config: {namespace}, - } = context_; - const cluster = this.remoteConfigManager.currentCluster; // TODO: <--- + const {namespace, clusterRef} = context_.config; + + const index: number = this.remoteConfigManager.components.getNewComponentIndex( + ComponentTypes.MirrorNodeExplorer, + ); + + const componentName: ComponentName = MirrorNodeExplorerComponent.renderMirrorNodeExplorerName(index); + remoteConfig.components.addNewComponent( - new MirrorNodeExplorerComponent('mirrorNodeExplorer', cluster, namespace.name, ComponentStates.ACTIVE), + new MirrorNodeExplorerComponent(componentName, clusterRef, namespace.name, ComponentStates.ACTIVE), ); }); }, diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 4980978a9..2fba52943 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -32,7 +32,7 @@ import chalk from 'chalk'; import {type CommandFlag} from '../types/flag-types.js'; import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js'; import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js'; -import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; +import {type ClusterReference, type ComponentName, type DeploymentName} from '../core/config/remote/types.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; @@ -894,13 +894,15 @@ export class MirrorNodeCommand extends BaseCommand { title: 'Add mirror node to remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - await this.remoteConfigManager.modify(async remoteConfig => { - const { - config: {namespace, clusterRef}, - } = context_; + await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + const {namespace, clusterRef} = context_.config; + + const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNode); + + const componentName: ComponentName = MirrorNodeComponent.renderMirrorNodeName(index); remoteConfig.components.addNewComponent( - new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name, ComponentStates.ACTIVE), + new MirrorNodeComponent(componentName, clusterRef, namespace.name, ComponentStates.ACTIVE), ); }); }, From 169be5c0eba1c861c040087cff3c47c84d91bc3d Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Fri, 4 Apr 2025 18:00:26 +0300 Subject: [PATCH 22/70] created new methods for initiating remote config components, changed codebase to use them, fixed most unit tests, todo: fix all tests, add new logic for editing remote components which should only work for consensus node's node state field Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 19 +++---- src/commands/deployment.ts | 9 +--- src/commands/explorer.ts | 10 +--- src/commands/mirror-node.ts | 9 ++-- src/commands/network.ts | 35 +++--------- src/commands/node/handlers.ts | 1 + src/commands/node/tasks.ts | 53 +++++-------------- src/commands/relay.ts | 6 +-- .../config/remote/components-data-wrapper.ts | 9 ++-- .../remote/components/block-node-component.ts | 24 +++++++-- .../components/consensus-node-component.ts | 28 ++++++++-- .../components/envoy-proxy-component.ts | 25 +++++++-- .../remote/components/ha-proxy-component.ts | 25 +++++++-- .../components/mirror-node-component.ts | 24 +++++++-- .../mirror-node-explorer-component.ts | 24 +++++++-- .../remote/components/relay-component.ts | 31 ++++++++--- .../config/remote/remote-config-manager.ts | 2 +- .../config/remote/remote-config-validator.ts | 4 ++ .../core/remote-config-validator.test.ts | 10 +++- .../remote/components-data-wrapper.test.ts | 13 +++++ .../remote/components/components.test.ts | 6 +++ 21 files changed, 217 insertions(+), 150 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 0498082d1..358d9b7d0 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -24,9 +24,7 @@ import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import os from 'node:os'; import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; -import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -43,7 +41,7 @@ interface BlockNodeDeployConfigClass { context: string; isChartInstalled: boolean; valuesArg: string; - newBlockNodeName: string; + newBlockNodeComponent: BlockNodeComponent; releaseName: string; } @@ -80,7 +78,7 @@ export class BlockNodeCommand extends BaseCommand { valuesArgument += helpers.prepareValuesFiles(config.valuesFile); } - valuesArgument += helpers.populateHelmArguments({nameOverride: config.newBlockNodeName}); + valuesArgument += helpers.populateHelmArguments({nameOverride: config.newBlockNodeComponent.name}); if (config.domainName) { valuesArgument += helpers.populateHelmArguments({ @@ -160,7 +158,12 @@ export class BlockNodeCommand extends BaseCommand { ComponentTypes.BlockNode, ); - config.newBlockNodeName = MirrorNodeComponent.renderMirrorNodeName(newBlockNodeIndex); + config.newBlockNodeComponent = BlockNodeComponent.createNew( + this.remoteConfigManager, + config.clusterRef, + config.namespace, + ); + config.releaseName = this.getReleaseName(newBlockNodeIndex); }, }, @@ -263,11 +266,9 @@ export class BlockNodeCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { - const {namespace, clusterRef, newBlockNodeName} = context_.config; + const config: BlockNodeDeployConfigClass = context_.config; - remoteConfig.components.addNewComponent( - new BlockNodeComponent(newBlockNodeName, clusterRef, namespace.name, ComponentStates.ACTIVE), - ); + remoteConfig.components.addNewComponent(config.newBlockNodeComponent); }); }, }; diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index e505bde40..933c36cc3 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -700,14 +700,7 @@ export class DeploymentCommand extends BaseCommand { //* add the new nodes to components for (const nodeAlias of nodeAliases) { remoteConfig.components.addNewComponent( - new ConsensusNodeComponent( - nodeAlias, - clusterRef, - namespace.name, - ComponentStates.ACTIVE, - ConsensusNodeStates.NON_DEPLOYED, - Templates.nodeIdFromNodeAlias(nodeAlias), - ), + ConsensusNodeComponent.createNew(nodeAlias, clusterRef, namespace, ConsensusNodeStates.NON_DEPLOYED), ); } }); diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index ca352e833..24b17e474 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -573,7 +573,7 @@ export class ExplorerCommand extends BaseCommand { } /** Removes the explorer components from remote config. */ - private disableMirrorNodeExplorerComponents(): SoloListrTask { + private disableMirrorNodeExplorerComponents(): SoloListrTask { // TODO: IMPLEMENT return { title: 'Remove explorer from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), @@ -594,14 +594,8 @@ export class ExplorerCommand extends BaseCommand { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { const {namespace, clusterRef} = context_.config; - const index: number = this.remoteConfigManager.components.getNewComponentIndex( - ComponentTypes.MirrorNodeExplorer, - ); - - const componentName: ComponentName = MirrorNodeExplorerComponent.renderMirrorNodeExplorerName(index); - remoteConfig.components.addNewComponent( - new MirrorNodeExplorerComponent(componentName, clusterRef, namespace.name, ComponentStates.ACTIVE), + MirrorNodeExplorerComponent.createNew(this.remoteConfigManager, clusterRef, namespace), ); }); }, diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 2fba52943..11890032c 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -876,12 +876,13 @@ export class MirrorNodeCommand extends BaseCommand { } /** Removes the mirror node components from remote config. */ - public disableMirrorNodeComponents(): SoloListrTask { // TODO + public disableMirrorNodeComponents(): SoloListrTask { return { title: 'Remove mirror node from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { + // TODO: IMPLEMENT remoteConfig.components.disableComponent('mirrorNode', ComponentTypes.MirrorNode); }); }, @@ -897,12 +898,8 @@ export class MirrorNodeCommand extends BaseCommand { await this.remoteConfigManager.modify(async (remoteConfig): Promise => { const {namespace, clusterRef} = context_.config; - const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNode); - - const componentName: ComponentName = MirrorNodeComponent.renderMirrorNodeName(index); - remoteConfig.components.addNewComponent( - new MirrorNodeComponent(componentName, clusterRef, namespace.name, ComponentStates.ACTIVE), + MirrorNodeComponent.createNew(this.remoteConfigManager, clusterRef, namespace), ); }); }, diff --git a/src/commands/network.ts b/src/commands/network.ts index d82cbd26a..f50d37753 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -1336,36 +1336,15 @@ export class NetworkCommand extends BaseCommand { const clusterReference: ClusterReference = consensusNode.cluster; remoteConfig.components.editComponent( - new ConsensusNodeComponent( - nodeAlias, - clusterReference, - namespace.name, - ComponentStates.ACTIVE, - ConsensusNodeStates.REQUESTED, - consensusNode.nodeId, - ), + ConsensusNodeComponent.createNew(nodeAlias, clusterReference, namespace, ConsensusNodeStates.REQUESTED), ); - // eslint-disable-next-line unicorn/consistent-function-scoping - const createNewEnvoyProxyComponent: () => EnvoyProxyComponent = (): EnvoyProxyComponent => { - const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); - - const componentName: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); - - return new EnvoyProxyComponent(componentName, clusterReference, namespace.name, ComponentStates.ACTIVE); - }; - - // eslint-disable-next-line unicorn/consistent-function-scoping - const createNewHaProxyComponent: () => HaProxyComponent = (): HaProxyComponent => { - const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); - - const componentName: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); - - return new HaProxyComponent(componentName, clusterReference, namespace.name, ComponentStates.ACTIVE); - }; - - remoteConfig.components.addNewComponent(createNewEnvoyProxyComponent()); - remoteConfig.components.addNewComponent(createNewHaProxyComponent()); + remoteConfig.components.addNewComponent( + EnvoyProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ); + remoteConfig.components.addNewComponent( + HaProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ); } }); }, diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 461d640e4..abbbd4818 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -937,6 +937,7 @@ export class NodeCommandHandlers extends CommandHandler { for (const consensusNode of context_.config.consensusNodes) { remoteConfig.components.editComponent( + // TODO: ADD NEW METHOD FOR EDITING NODE STATE new ConsensusNodeComponent( consensusNode.name, consensusNode.cluster, diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 7f37822af..6cc4da530 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -77,13 +77,7 @@ import {ContainerReference} from '../../integration/kube/resources/container/con import {NetworkNodes} from '../../core/network-nodes.js'; import {container, inject, injectable} from 'tsyringe-neo'; import {type Optional, type SoloListr, type SoloListrTask, type SoloListrTaskWrapper} from '../../types/index.js'; -import { - type ClusterReference, - type ComponentName, - type Context, - type DeploymentName, - type NamespaceNameAsString, -} from '../../core/config/remote/types.js'; +import {type ClusterReference, type Context, type DeploymentName} from '../../core/config/remote/types.js'; import {patchInject} from '../../core/dependency-injection/container-helper.js'; import {ConsensusNode} from '../../core/model/consensus-node.js'; import {type K8} from '../../integration/kube/k8.js'; @@ -119,8 +113,7 @@ import {type NodeStartConfigClass} from './config-interfaces/node-start-config-c import {type CheckedNodesConfigClass, type CheckedNodesContext} from './config-interfaces/node-common-config-class.js'; import {type NetworkNodeServices} from '../../core/network-node-services.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentStates} from '../../core/config/remote/enumerations/component-states.js'; -import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js'; +import {Cluster} from '../../core/config/remote/cluster.js'; @injectable() export class NodeCommandTasks { @@ -2512,7 +2505,7 @@ export class NodeCommandTasks { title: 'Add new node to remote config', task: async (context_, task) => { const nodeAlias: NodeAlias = context_.config.nodeAlias; - const namespace: NamespaceNameAsString = context_.config.namespace.name; + const namespace: NamespaceName = context_.config.namespace; const clusterReference: ClusterReference = context_.config.clusterRef; const context: Context = this.localConfig.clusterRefs[clusterReference]; @@ -2520,47 +2513,27 @@ export class NodeCommandTasks { await this.remoteConfigManager.modify(async remoteConfig => { remoteConfig.components.addNewComponent( - new ConsensusNodeComponent( - nodeAlias, - clusterReference, - namespace, - ComponentStates.ACTIVE, - ConsensusNodeStates.STARTED, - Templates.nodeIdFromNodeAlias(nodeAlias), - ), + ConsensusNodeComponent.createNew(nodeAlias, clusterReference, namespace, ConsensusNodeStates.STARTED), + ); + remoteConfig.components.addNewComponent( + EnvoyProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ); + remoteConfig.components.addNewComponent( + HaProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), ); - - const createNewEnvoyProxyComponent: () => EnvoyProxyComponent = (): EnvoyProxyComponent => { - const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); - - const componentName: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); - - return new EnvoyProxyComponent(componentName, clusterReference, namespace, ComponentStates.ACTIVE); - }; - - const createNewHaProxyComponent: () => HaProxyComponent = (): HaProxyComponent => { - const index: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); - - const componentName: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); - - return new HaProxyComponent(componentName, clusterReference, namespace, ComponentStates.ACTIVE); - }; - - remoteConfig.components.addNewComponent(createNewEnvoyProxyComponent()); - remoteConfig.components.addNewComponent(createNewHaProxyComponent()); }); context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes(); // if the consensusNodes does not contain the nodeAlias then add it if (!context_.config.consensusNodes.some((node: ConsensusNode) => node.name === nodeAlias)) { - const cluster = this.remoteConfigManager.clusters[clusterReference]; + const cluster: Cluster = this.remoteConfigManager.clusters[clusterReference]; context_.config.consensusNodes.push( new ConsensusNode( nodeAlias, Templates.nodeIdFromNodeAlias(nodeAlias), - namespace, + namespace.name, clusterReference, context, cluster.dnsBaseDomain, @@ -2568,7 +2541,7 @@ export class NodeCommandTasks { Templates.renderConsensusNodeFullyQualifiedDomainName( nodeAlias, Templates.nodeIdFromNodeAlias(nodeAlias), - namespace, + namespace.name, clusterReference, cluster.dnsBaseDomain, cluster.dnsConsensusNodePattern, diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 4f47a5281..267787bc7 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -548,12 +548,8 @@ export class RelayCommand extends BaseCommand { await this.remoteConfigManager.modify(async remoteConfig => { const {namespace, nodeAliases, clusterRef} = context_.config; - const newRelayIndex: number = this.remoteConfigManager.components.getNewComponentIndex(ComponentTypes.Relay); - - const newRelayName: ComponentName = RelayComponent.renderRelayName(newRelayIndex); - remoteConfig.components.addNewComponent( - new RelayComponent(newRelayName, clusterRef, namespace.name, ComponentStates.ACTIVE, nodeAliases), + RelayComponent.createNew(this.remoteConfigManager, clusterRef, namespace, nodeAliases), ); }); }, diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 27c530adc..49fbf5b96 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -16,15 +16,14 @@ import { type ComponentsDataStructure, type IConsensusNodeComponent, type IRelayComponent, - type NamespaceNameAsString, } from './types.js'; import {type ToObject, type Validate} from '../../../types/index.js'; -import {Templates} from '../../templates.js'; import {type NodeAliases} from '../../../types/aliases.js'; import {type CloneTrait} from '../../../types/traits/clone-trait.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; +import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; /** * Represent the components in the remote config and handles: @@ -273,18 +272,16 @@ export class ComponentsDataWrapper public static initializeWithNodes( nodeAliases: NodeAliases, clusterReference: ClusterReference, - namespace: NamespaceNameAsString, + namespace: NamespaceName, ): ComponentsDataWrapper { const consensusNodeComponents: Record = {}; for (const nodeAlias of nodeAliases) { - consensusNodeComponents[nodeAlias] = new ConsensusNodeComponent( + consensusNodeComponents[nodeAlias] = ConsensusNodeComponent.createNew( nodeAlias, clusterReference, namespace, - ComponentStates.ACTIVE, ConsensusNodeStates.NON_DEPLOYED, - Templates.nodeIdFromNodeAlias(nodeAlias), ); } diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index 74a6631cf..8247e8392 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -1,15 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {ComponentStates} from '../enumerations/component-states.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type RemoteConfigManager} from '../remote-config-manager.js'; export class BlockNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'block-node'; - public constructor( - name: string, + private constructor( + name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, @@ -20,13 +22,25 @@ export class BlockNodeComponent extends BaseComponent { /* -------- Utilities -------- */ + public static createNew( + remoteConfigManager: RemoteConfigManager, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): BlockNodeComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.BlockNode); + + const name: ComponentName = BlockNodeComponent.renderBlockNodeName(index); + + return new BlockNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: Component): BlockNodeComponent { const {name, cluster, namespace, state} = component; return new BlockNodeComponent(name, cluster, namespace, state); } - public static renderBlockNodeName(index: number): string { + private static renderBlockNodeName(index: number): string { return BlockNodeComponent.renderComponentName(BlockNodeComponent.BASE_NAME, index); } } diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index 77b376a5a..372564506 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -11,7 +11,10 @@ import { import {type ToObject} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {ComponentStates} from '../enumerations/component-states.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type NodeAlias, type NodeId} from '../../../../types/aliases.js'; +import {Templates} from '../../../templates.js'; /** * Represents a consensus node component within the system. @@ -31,7 +34,7 @@ export class ConsensusNodeComponent * @param state - the component state * @param nodeState - of the consensus node */ - public constructor( + private constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -46,13 +49,30 @@ export class ConsensusNodeComponent /* -------- Utilities -------- */ + public static createNew( + nodeAlias: NodeAlias, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeState: ConsensusNodeStates.REQUESTED | ConsensusNodeStates.NON_DEPLOYED | ConsensusNodeStates.STARTED, + ): ConsensusNodeComponent { + const nodeId: NodeId = Templates.nodeIdFromNodeAlias(nodeAlias); + return new ConsensusNodeComponent( + nodeAlias, + clusterReference, + namespace.name, + ComponentStates.ACTIVE, + nodeState, + nodeId, + ); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: IConsensusNodeComponent): ConsensusNodeComponent { const {name, cluster, state, namespace, nodeState, nodeId} = component; return new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, nodeId); } - public validate(): void { + public override validate(): void { super.validate(); if (!Object.values(ConsensusNodeStates).includes(this.nodeState)) { @@ -68,7 +88,7 @@ export class ConsensusNodeComponent } } - public toObject(): IConsensusNodeComponent { + public override toObject(): IConsensusNodeComponent { return { ...super.toObject(), nodeState: this.nodeState, diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index 2e5a67e9b..09773ad5a 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -1,16 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; +import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class EnvoyProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}}`; - public constructor( - name: string, + private constructor( + name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, @@ -21,13 +23,26 @@ export class EnvoyProxyComponent extends BaseComponent { /* -------- Utilities -------- */ + public static createNew( + remoteConfigManager: RemoteConfigManager, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeAlias: NodeAlias, + ): EnvoyProxyComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); + + const name: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); + + return new EnvoyProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: Component): EnvoyProxyComponent { const {name, cluster, namespace, state} = component; return new EnvoyProxyComponent(name, cluster, namespace, state); } - public static renderEnvoyProxyName(index: number, nodeAlias: NodeAlias): string { + private static renderEnvoyProxyName(index: number, nodeAlias: NodeAlias): string { return EnvoyProxyComponent.renderComponentName(EnvoyProxyComponent.BASE_NAME(nodeAlias), index); } } diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index bc27e1a66..0f1cea679 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -1,16 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; +import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class HaProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}}`; - public constructor( - name: string, + private constructor( + name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, @@ -21,13 +23,26 @@ export class HaProxyComponent extends BaseComponent { /* -------- Utilities -------- */ + public static createNew( + remoteConfigManager: RemoteConfigManager, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeAlias: NodeAlias, + ): HaProxyComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); + + const name: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); + + return new HaProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: Component): HaProxyComponent { const {name, cluster, namespace, state} = component; return new HaProxyComponent(name, cluster, namespace, state); } - public static renderHaProxyName(index: number, nodeAlias: NodeAlias): string { + private static renderHaProxyName(index: number, nodeAlias: NodeAlias): string { return HaProxyComponent.renderComponentName(HaProxyComponent.BASE_NAME(nodeAlias), index); } } diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index 800c4ed63..3b7ce332a 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -1,15 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {ComponentStates} from '../enumerations/component-states.js'; +import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class MirrorNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node'; - public constructor( - name: string, + private constructor( + name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, @@ -20,13 +22,25 @@ export class MirrorNodeComponent extends BaseComponent { /* -------- Utilities -------- */ + public static createNew( + remoteConfigManager: RemoteConfigManager, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): MirrorNodeComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNode); + + const name: ComponentName = MirrorNodeComponent.renderMirrorNodeName(index); + + return new MirrorNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: Component): MirrorNodeComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeComponent(name, cluster, namespace, state); } - public static renderMirrorNodeName(index: number): string { + private static renderMirrorNodeName(index: number): string { return MirrorNodeComponent.renderComponentName(MirrorNodeComponent.BASE_NAME, index); } } diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 3687cf8d1..7bcf971a3 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -1,15 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {ComponentStates} from '../enumerations/component-states.js'; +import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class MirrorNodeExplorerComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node-explorer'; - public constructor( - name: string, + private constructor( + name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, @@ -20,13 +22,25 @@ export class MirrorNodeExplorerComponent extends BaseComponent { /* -------- Utilities -------- */ + public static createNew( + remoteConfigManager: RemoteConfigManager, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): MirrorNodeExplorerComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNodeExplorer); + + const name: ComponentName = MirrorNodeExplorerComponent.renderMirrorNodeExplorerName(index); + + return new MirrorNodeExplorerComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: Component): MirrorNodeExplorerComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeExplorerComponent(name, cluster, namespace, state); } - public static renderMirrorNodeExplorerName(index: number): string { + private static renderMirrorNodeExplorerName(index: number): string { return MirrorNodeExplorerComponent.renderComponentName(MirrorNodeExplorerComponent.BASE_NAME, index); } } diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index 40375ce65..5edb87f93 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -2,11 +2,13 @@ import {SoloError} from '../../../errors/solo-error.js'; import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type IRelayComponent, type NamespaceNameAsString} from '../types.js'; +import {ComponentTypes} from '../enumerations/component-types.js'; +import {ComponentStates} from '../enumerations/component-states.js'; +import {type ClusterReference, type ComponentName, type IRelayComponent, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; -import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; +import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class RelayComponent extends BaseComponent implements IRelayComponent, ToObject { private static readonly BASE_NAME: string = 'relay'; @@ -18,8 +20,8 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To * @param state - the state of the component * @param consensusNodeAliases - list node aliases */ - public constructor( - name: string, + private constructor( + name: ComponentName, clusterReference: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, @@ -31,13 +33,26 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To /* -------- Utilities -------- */ + public static createNew( + remoteConfigManager: RemoteConfigManager, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeAliases: NodeAliases, + ): RelayComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.Relay); + + const name: ComponentName = RelayComponent.renderRelayName(index); + + return new RelayComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE, nodeAliases); + } + /** Handles creating instance of the class from plain object. */ public static fromObject(component: IRelayComponent): RelayComponent { const {name, cluster, namespace, state, consensusNodeAliases} = component; return new RelayComponent(name, cluster, namespace, state, consensusNodeAliases); } - public validate(): void { + public override validate(): void { super.validate(); for (const alias of this.consensusNodeAliases) { @@ -47,14 +62,14 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To } } - public toObject(): IRelayComponent { + public override toObject(): IRelayComponent { return { consensusNodeAliases: this.consensusNodeAliases, ...super.toObject(), }; } - public static renderRelayName(index: number): string { + private static renderRelayName(index: number): string { return RelayComponent.renderComponentName(RelayComponent.BASE_NAME, index); } } diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index fc4d8d7a4..548662098 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -139,7 +139,7 @@ export class RemoteConfigManager { metadata: new RemoteConfigMetadata(namespace.name, deployment, state, lastUpdatedAt, email, soloVersion), commandHistory: [currentCommand], lastExecutedCommand: currentCommand, - components: ComponentsDataWrapper.initializeWithNodes(nodeAliases, clusterReference, namespace.name), + components: ComponentsDataWrapper.initializeWithNodes(nodeAliases, clusterReference, namespace), flags: await CommonFlagsDataWrapper.initialize(this.configManager, argv), }); diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index fd0d9f42e..236547e9f 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -11,6 +11,7 @@ import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; +import {ComponentStates} from './enumerations/component-states.js'; /** * Static class is used to validate that components in the remote config @@ -66,6 +67,9 @@ export class RemoteConfigValidator { skipCondition?: (component: BaseComponent) => boolean, ): Promise[] { return Object.values(components).map(async (component): Promise => { + if (component.state === ComponentStates.DELETED) { + return; + } if (skipCondition?.(component)) { return; } diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index 0c4a78e9f..2719da110 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -59,8 +59,8 @@ describe('RemoteConfigValidator', () => { const nodeState = ConsensusNodeStates.STARTED; const nodeAlias = 'node1' as NodeAlias; - const haProxyName = Templates.renderHaProxyName(nodeAlias); - const envoyProxyName = Templates.renderEnvoyProxyName(nodeAlias); + const haProxyName = 'haproxy-' + nodeAlias; + const envoyProxyName = 'envoy-proxy-' + nodeAlias; const relayName = 'relay'; const mirrorNodeName = 'mirror-node'; const mirrorNodeExplorerName = 'mirror-node-explorer'; @@ -69,11 +69,16 @@ describe('RemoteConfigValidator', () => { // @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private const components: ComponentsDataWrapper = new ComponentsDataWrapper( + // @ts-expect-error - to access private constructor {[relayName]: new RelayComponent(relayName, cluster, namespace.name, ComponentStates.ACTIVE, consensusNodeAliases)}, + // @ts-expect-error - to access private constructor {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, + // @ts-expect-error - to access private constructor {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name, ComponentStates.ACTIVE)}, + // @ts-expect-error - to access private constructor {[envoyProxyName]: new EnvoyProxyComponent(envoyProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, { + // @ts-expect-error - to access private constructor [nodeAlias]: new ConsensusNodeComponent( nodeAlias, cluster, @@ -84,6 +89,7 @@ describe('RemoteConfigValidator', () => { ), }, { + // @ts-expect-error - to access private constructor [mirrorNodeExplorerName]: new MirrorNodeExplorerComponent( mirrorNodeExplorerName, cluster, diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index ec101f7a2..b238cb82d 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -53,33 +53,43 @@ export function createComponentsDataWrapper(): { const state: ComponentStates = ComponentStates.ACTIVE; const relays: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new RelayComponent(name, cluster, namespace, state, consensusNodeAliases), }; const haProxies: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new HaProxyComponent(name, cluster, namespace, state), + // @ts-expect-error - to access private constructor ['serviceName2']: new HaProxyComponent('name2', 'cluster2', namespace, state), }; const mirrorNodes: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new MirrorNodeComponent(name, cluster, namespace, state), }; const envoyProxies: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new EnvoyProxyComponent(name, cluster, namespace, state), + // @ts-expect-error - to access private constructor ['serviceName2']: new EnvoyProxyComponent('name2', 'cluster2', namespace, state), }; const consensusNodes: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), + // @ts-expect-error - to access private constructor ['serviceName2']: new ConsensusNodeComponent('node2', 'cluster2', namespace, state, nodeState, 1), }; const mirrorNodeExplorers: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new MirrorNodeExplorerComponent(name, cluster, namespace, state), }; const blockNodes: Record = { + // @ts-expect-error - to access private constructor [serviceName]: new BlockNodeComponent(name, cluster, namespace, state), }; @@ -157,6 +167,7 @@ describe('ComponentsDataWrapper', () => { cluster: 'cluster', namespace: 'new-namespace', }; + // @ts-expect-error - to access private constructor const newComponent: EnvoyProxyComponent = new EnvoyProxyComponent(name, cluster, namespace, state); componentsDataWrapper.addNewComponent(newComponent); @@ -188,6 +199,7 @@ describe('ComponentsDataWrapper', () => { const newCluster: ClusterReference = 'newCluster'; + // @ts-expect-error - to access private constructor const newReplayComponent: RelayComponent = new RelayComponent(relayComponent.name, newCluster, namespace, state); componentsDataWrapper.editComponent(newReplayComponent); @@ -201,6 +213,7 @@ describe('ComponentsDataWrapper', () => { values: {cluster, namespace, state}, } = createComponentsDataWrapper(); const notFoundServiceName: string = 'not_found'; + // @ts-expect-error - to access private constructor const relay: RelayComponent = new RelayComponent(notFoundServiceName, cluster, namespace, state); expect(() => componentsDataWrapper.editComponent(relay)).to.throw( diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 17ce5657c..e0339c350 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -47,10 +47,12 @@ describe('BlockNodeComponent', () => testBaseComponentData(BlockNodeComponent)); describe('RelayComponent', () => { it('should successfully create ', () => { + // @ts-expect-error - to access private constructor new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); }); it('should be an instance of BaseComponent', () => { + // @ts-expect-error - to access private constructor const component: RelayComponent = new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); expect(component).to.be.instanceOf(BaseComponent); }); @@ -64,6 +66,7 @@ describe('RelayComponent', () => { consensusNodeAliases: ['node1'], }; + // @ts-expect-error - to access private constructor const component: RelayComponent = new RelayComponent( values.name, values.cluster, @@ -78,10 +81,12 @@ describe('RelayComponent', () => { describe('ConsensusNodeComponent', () => { it('should successfully create ', () => { + // @ts-expect-error - to access private constructor new ConsensusNodeComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0); }); it('should be an instance of BaseComponent', () => { + // @ts-expect-error - to access private constructor const component: ConsensusNodeComponent = new ConsensusNodeComponent( 'valid', 'valid', @@ -104,6 +109,7 @@ describe('ConsensusNodeComponent', () => { nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; + // @ts-expect-error - to access private constructor const component: ConsensusNodeComponent = new ConsensusNodeComponent( values.name, values.cluster, From 3b01f3a1f84bec689453e88846503323a973cfd1 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Sun, 6 Apr 2025 23:47:41 +0300 Subject: [PATCH 23/70] added new validation helper method Signed-off-by: Zhan Milenkov --- src/core/config-manager.ts | 5 +++-- src/core/config/remote/components-data-wrapper.ts | 3 ++- src/core/config/remote/components/base-component.ts | 5 +++-- .../config/remote/components/consensus-node-component.ts | 3 ++- src/core/config/remote/metadata.ts | 3 ++- src/core/util/validation-helpers.ts | 8 ++++++++ 6 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 src/core/util/validation-helpers.ts diff --git a/src/core/config-manager.ts b/src/core/config-manager.ts index 282ee61d2..2c3dd6b07 100644 --- a/src/core/config-manager.ts +++ b/src/core/config-manager.ts @@ -8,13 +8,14 @@ import {Flags, Flags as flags} from '../commands/flags.js'; import type * as yargs from 'yargs'; import {type CommandFlag} from '../types/flag-types.js'; import {patchInject} from './dependency-injection/container-helper.js'; -import * as constants from '../core/constants.js'; +import {StorageType} from './constants.js'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {InjectTokens} from './dependency-injection/inject-tokens.js'; import {type ArgvStruct, type AnyListrContext, type AnyObject, type AnyYargs} from '../types/aliases.js'; import {type Optional, type SoloListrTaskWrapper} from '../types/index.js'; import {PathEx} from '../business/utils/path-ex.js'; import {getSoloVersion} from '../../version.js'; +import {isValidEnum} from './util/validation-helpers.js'; /** * ConfigManager cache command flag values so that user doesn't need to enter the same values repeatedly. @@ -115,7 +116,7 @@ export class ConfigManager { case 'StorageType': { // @ts-expect-error: TS2475: const enums can only be used in property or index access expressions - if (Object.values(constants.StorageType).includes(`${value}`)) { + if (isValidEnum(`${value}`, StorageType)) { this.config.flags[flag.name] = value; } else { throw new SoloError(`Invalid storage type value '${value}'`); diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 49fbf5b96..cc5cff3d2 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -24,6 +24,7 @@ import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; +import {isValidEnum} from '../../util/validation-helpers.js'; /** * Represent the components in the remote config and handles: @@ -98,7 +99,7 @@ export class ComponentsDataWrapper throw new SoloError(`Service name is required ${serviceName}`); } - if (!Object.values(ComponentTypes).includes(type)) { + if (!isValidEnum(type, ComponentTypes)) { throw new SoloError(`Invalid component type ${type}`); } diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index b557b5d19..126327f32 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -5,6 +5,7 @@ import {type ClusterReference, type Component, type ComponentName, type Namespac import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; +import {isValidEnum} from '../../../util/validation-helpers.js'; /** * Represents the base structure and common functionality for all components within the system. @@ -54,11 +55,11 @@ export class BaseComponent implements Component, Validate, ToObject { ); } - if (!Object.values(ComponentTypes).includes(this.type)) { + if (!isValidEnum(this.type, ComponentTypes)) { throw new SoloError(`Invalid component type: ${this.type}`); } - if (!Object.values(ComponentStates).includes(this.state)) { + if (!isValidEnum(this.state, ComponentStates)) { throw new SoloError(`Invalid component state: ${this.state}`); } } diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index 372564506..aaa39cf6a 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -15,6 +15,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type NodeAlias, type NodeId} from '../../../../types/aliases.js'; import {Templates} from '../../../templates.js'; +import {isValidEnum} from '../../../util/validation-helpers.js'; /** * Represents a consensus node component within the system. @@ -75,7 +76,7 @@ export class ConsensusNodeComponent public override validate(): void { super.validate(); - if (!Object.values(ConsensusNodeStates).includes(this.nodeState)) { + if (!isValidEnum(this.nodeState, ConsensusNodeStates)) { throw new SoloError(`Invalid consensus node state: ${this.nodeState}`); } diff --git a/src/core/config/remote/metadata.ts b/src/core/config/remote/metadata.ts index 836f55801..a649181a2 100644 --- a/src/core/config/remote/metadata.ts +++ b/src/core/config/remote/metadata.ts @@ -12,6 +12,7 @@ import { import {type Optional, type ToObject, type Validate} from '../../../types/index.js'; import {DeploymentStates} from './enumerations/deployment-states.js'; +import {isValidEnum} from '../../util/validation-helpers.js'; /** * Represent the remote config metadata object and handles: @@ -111,7 +112,7 @@ export class RemoteConfigMetadata throw new SoloError(`Invalid soloVersion: ${this.soloVersion}`); } - if (!Object.values(DeploymentStates).includes(this.state)) { + if (!isValidEnum(this.state, DeploymentStates)) { throw new SoloError(`Invalid cluster state: ${this.state}`); } diff --git a/src/core/util/validation-helpers.ts b/src/core/util/validation-helpers.ts new file mode 100644 index 000000000..ffaf26f6d --- /dev/null +++ b/src/core/util/validation-helpers.ts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 + +export function isValidEnum>( + value: unknown, + enumeration: E, +): value is E[keyof E] { + return Object.values(enumeration).includes(value as E[keyof E]); +} From 8d904580528a6334cf7f1bc20326bd744d63a7a3 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 00:56:09 +0300 Subject: [PATCH 24/70] set @typescript-eslint/explicit-function-return-type allowTypedFunctionExpressions to avoid having to have dublicated function return value difinitions Signed-off-by: Zhan Milenkov --- eslint.config.mjs | 2 +- src/commands/block-node.ts | 2 +- src/commands/explorer.ts | 4 ++-- src/commands/mirror-node.ts | 2 +- src/commands/network.ts | 2 +- src/commands/node/handlers.ts | 2 +- src/commands/relay.ts | 2 +- src/core/config/remote/components-data-wrapper.ts | 10 +++++----- src/core/config/remote/remote-config-manager.ts | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 04a96ce52..ca747b8fb 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -137,7 +137,7 @@ export default [ 'warn', { allowExpressions: false, - allowTypedFunctionExpressions: false, + allowTypedFunctionExpressions: true, allowHigherOrderFunctions: false, }, ], diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 358d9b7d0..63b16aef7 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -265,7 +265,7 @@ export class BlockNodeCommand extends BaseCommand { title: 'Add block node component in remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { const config: BlockNodeDeployConfigClass = context_.config; remoteConfig.components.addNewComponent(config.newBlockNodeComponent); diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 24b17e474..710789cc9 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -578,7 +578,7 @@ export class ExplorerCommand extends BaseCommand { title: 'Remove explorer from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { remoteConfig.components.disableComponent('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer); }); }, @@ -591,7 +591,7 @@ export class ExplorerCommand extends BaseCommand { title: 'Add explorer to remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { const {namespace, clusterRef} = context_.config; remoteConfig.components.addNewComponent( diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 11890032c..862d5137f 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -895,7 +895,7 @@ export class MirrorNodeCommand extends BaseCommand { title: 'Add mirror node to remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { const {namespace, clusterRef} = context_.config; remoteConfig.components.addNewComponent( diff --git a/src/commands/network.ts b/src/commands/network.ts index f50d37753..104dac42a 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -1330,7 +1330,7 @@ export class NetworkCommand extends BaseCommand { task: async (context_): Promise => { const {namespace} = context_.config; - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { for (const consensusNode of context_.config.consensusNodes) { const nodeAlias: NodeAlias = consensusNode.name; const clusterReference: ClusterReference = consensusNode.cluster; diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index abbbd4818..dc3e3bba5 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -907,7 +907,7 @@ export class NodeCommandHandlers extends CommandHandler { title: 'Remove node and proxies from remote config', task: async (): Promise => { // TODO - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { remoteConfig.components.disableComponent('Consensus node name', ComponentTypes.ConsensusNode); remoteConfig.components.disableComponent('Envoy proxy name', ComponentTypes.EnvoyProxy); remoteConfig.components.disableComponent('HaProxy name', ComponentTypes.HaProxy); diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 267787bc7..5937c2321 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -562,7 +562,7 @@ export class RelayCommand extends BaseCommand { title: 'Remove relay component from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { - await this.remoteConfigManager.modify(async (remoteConfig): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { remoteConfig.components.disableComponent('relay', ComponentTypes.Relay); }); }, diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index cc5cff3d2..034452763 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -61,7 +61,7 @@ export class ComponentsDataWrapper throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); } - const addComponentCallback: (components: Record) => void = (components): void => { + const addComponentCallback: (components: Record) => void = components => { if (this.checkComponentExists(components, component)) { throw new SoloError('Component exists', undefined, component.toObject()); } @@ -83,7 +83,7 @@ export class ComponentsDataWrapper throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); } - const editComponentCallback: (components: Record) => void = (components): void => { + const editComponentCallback: (components: Record) => void = components => { if (!components[serviceName]) { throw new SoloError(`Component doesn't exist, name: ${serviceName}`, undefined, {component}); } @@ -103,7 +103,7 @@ export class ComponentsDataWrapper throw new SoloError(`Invalid component type ${type}`); } - const disableComponentCallback: (components: Record) => void = (components): void => { + const disableComponentCallback: (components: Record) => void = components => { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`); } @@ -118,7 +118,7 @@ export class ComponentsDataWrapper public getComponent(type: ComponentTypes, serviceName: ComponentName): T { let component: T; - const getComponentCallback: (components: Record) => void = (components): void => { + const getComponentCallback: (components: Record) => void = components => { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to read`); } @@ -300,7 +300,7 @@ export class ComponentsDataWrapper public getNewComponentIndex(componentType: ComponentTypes): number { let newComponentIndex: number = 1; - const callback: (components: Record) => void = (components): void => { + const callback: (components: Record) => void = components => { for (const componentName of Object.keys(components)) { const componentIndex: number = BaseComponent.parseComponentName(componentName); if (newComponentIndex <= componentIndex) { diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index 548662098..b07a0c6e2 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -328,7 +328,7 @@ export class RemoteConfigManager { /** Empties the component data inside the remote config */ public async deleteComponents(): Promise { - await this.modify(async (remoteConfig): Promise => { + await this.modify(async remoteConfig => { remoteConfig.components = ComponentsDataWrapper.initializeEmpty(); }); } From 07e92b9f18eb9d46e68a77e0bbb1377dca9a8c7d Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 09:43:58 +0300 Subject: [PATCH 25/70] remove method editComponentState and add new method changeNodeState to remote config Signed-off-by: Zhan Milenkov --- src/commands/network.ts | 4 +-- src/commands/node/handlers.ts | 22 +++---------- .../config/remote/components-data-wrapper.ts | 22 ++++--------- .../components/consensus-node-component.ts | 13 +++++++- .../remote/components-data-wrapper.test.ts | 31 +++++-------------- 5 files changed, 30 insertions(+), 62 deletions(-) diff --git a/src/commands/network.ts b/src/commands/network.ts index 104dac42a..1465d397a 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -1335,9 +1335,7 @@ export class NetworkCommand extends BaseCommand { const nodeAlias: NodeAlias = consensusNode.name; const clusterReference: ClusterReference = consensusNode.cluster; - remoteConfig.components.editComponent( - ConsensusNodeComponent.createNew(nodeAlias, clusterReference, namespace, ConsensusNodeStates.REQUESTED), - ); + remoteConfig.components.changeNodeState(nodeAlias, ConsensusNodeStates.REQUESTED); remoteConfig.components.addNewComponent( EnvoyProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index dc3e3bba5..0a46a9ced 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -919,34 +919,20 @@ export class NodeCommandHandlers extends CommandHandler { /** * Changes the state from all consensus nodes components in remote config. * - * @param nodeStates - to which to change the consensus node component + * @param nodeState - to which to change the consensus node component */ - public changeAllNodeStates(nodeStates: ConsensusNodeStates): SoloListrTask { + public changeAllNodeStates(nodeState: ConsensusNodeStates): SoloListrTask { interface Context { config: {namespace: NamespaceName; consensusNodes: ConsensusNode[]}; } return { - title: `Change node state to ${nodeStates} in remote config`, + title: `Change node state to ${nodeState} in remote config`, skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_: Context): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const { - config: {namespace}, - } = context_; - for (const consensusNode of context_.config.consensusNodes) { - remoteConfig.components.editComponent( - // TODO: ADD NEW METHOD FOR EDITING NODE STATE - new ConsensusNodeComponent( - consensusNode.name, - consensusNode.cluster, - namespace.name, - ComponentStates.ACTIVE, - nodeStates, - consensusNode.nodeId, - ), - ); + remoteConfig.components.changeNodeState(consensusNode.name, nodeState); } }); }, diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 034452763..d9d0a1596 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -71,26 +71,16 @@ export class ComponentsDataWrapper this.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); } - /** Used to edit an existing component from their respective group. */ - public editComponent(component: BaseComponent): void { - const serviceName: string = component.name; - - if (!serviceName || typeof serviceName !== 'string') { - throw new SoloError(`Service name is required ${serviceName}`); + public changeNodeState(serviceName: ComponentName, nodeState: ConsensusNodeStates): void { + if (!this.consensusNodes[serviceName]) { + throw new SoloError(`Consensus node ${serviceName} doesn't exist`); } - if (!(component instanceof BaseComponent)) { - throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); + if (!isValidEnum(nodeState, ConsensusNodeStates)) { + throw new SoloError(`Invalid node state ${nodeState}`); } - const editComponentCallback: (components: Record) => void = components => { - if (!components[serviceName]) { - throw new SoloError(`Component doesn't exist, name: ${serviceName}`, undefined, {component}); - } - components[serviceName] = component; - }; - - this.applyCallbackToComponentGroup(component.type, serviceName, editComponentCallback); + this.consensusNodes[serviceName].changeNodeState(nodeState); } /** Used to remove specific component from their respective group. */ diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index aaa39cf6a..3065c7f9c 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -27,6 +27,8 @@ export class ConsensusNodeComponent extends BaseComponent implements IConsensusNodeComponent, ToObject { + private _nodeState: ConsensusNodeStates; + /** * @param name - the name to distinguish components. * @param nodeId - node id of the consensus node @@ -40,11 +42,20 @@ export class ConsensusNodeComponent cluster: ClusterReference, namespace: NamespaceNameAsString, state: ComponentStates, - public readonly nodeState: ConsensusNodeStates, + nodeState: ConsensusNodeStates, public readonly nodeId: number, ) { super(ComponentTypes.ConsensusNode, name, cluster, namespace, state); + this._nodeState = nodeState; + this.validate(); + } + + public get nodeState(): ConsensusNodeStates { + return this._nodeState; + } + public changeNodeState(nodeState: ConsensusNodeStates): void { + this._nodeState = nodeState; this.validate(); } diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index b238cb82d..e0037b17c 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -60,8 +60,6 @@ export function createComponentsDataWrapper(): { const haProxies: Record = { // @ts-expect-error - to access private constructor [serviceName]: new HaProxyComponent(name, cluster, namespace, state), - // @ts-expect-error - to access private constructor - ['serviceName2']: new HaProxyComponent('name2', 'cluster2', namespace, state), }; const mirrorNodes: Record = { @@ -72,15 +70,11 @@ export function createComponentsDataWrapper(): { const envoyProxies: Record = { // @ts-expect-error - to access private constructor [serviceName]: new EnvoyProxyComponent(name, cluster, namespace, state), - // @ts-expect-error - to access private constructor - ['serviceName2']: new EnvoyProxyComponent('name2', 'cluster2', namespace, state), }; const consensusNodes: Record = { // @ts-expect-error - to access private constructor [serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), - // @ts-expect-error - to access private constructor - ['serviceName2']: new ConsensusNodeComponent('node2', 'cluster2', namespace, state, nodeState, 1), }; const mirrorNodeExplorers: Record = { @@ -183,42 +177,31 @@ describe('ComponentsDataWrapper', () => { state, }); - expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(3); + expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(2); }); - it('should be able to edit component with the .editComponent()', () => { + it('should be able to change node state with the .changeNodeState(()', () => { const { wrapper: {componentsDataWrapper}, - components: {relays}, - values: {namespace, state}, serviceName, } = createComponentsDataWrapper(); - const relayComponent: RelayComponent = relays[serviceName]; - componentsDataWrapper.editComponent(relayComponent); + const newNodeState: ConsensusNodeStates = ConsensusNodeStates.STOPPED; - const newCluster: ClusterReference = 'newCluster'; + componentsDataWrapper.changeNodeState(serviceName, newNodeState); - // @ts-expect-error - to access private constructor - const newReplayComponent: RelayComponent = new RelayComponent(relayComponent.name, newCluster, namespace, state); - - componentsDataWrapper.editComponent(newReplayComponent); - - expect(componentsDataWrapper.toObject()[ComponentTypes.Relay][relayComponent.name].cluster).to.equal(newCluster); + expect(componentsDataWrapper.consensusNodes[serviceName].nodeState).to.equal(newNodeState); }); it("should not be able to edit component with the .editComponent() if it doesn't exist ", () => { const { wrapper: {componentsDataWrapper}, - values: {cluster, namespace, state}, } = createComponentsDataWrapper(); const notFoundServiceName: string = 'not_found'; - // @ts-expect-error - to access private constructor - const relay: RelayComponent = new RelayComponent(notFoundServiceName, cluster, namespace, state); - expect(() => componentsDataWrapper.editComponent(relay)).to.throw( + expect(() => componentsDataWrapper.changeNodeState(notFoundServiceName, ConsensusNodeStates.NON_DEPLOYED)).to.throw( SoloError, - `Component doesn't exist, name: ${notFoundServiceName}`, + `Consensus node ${notFoundServiceName} doesn't exist`, ); }); From 0e15c8fa42c9561ba1b09bc02a852107a1d97327 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 09:49:23 +0300 Subject: [PATCH 26/70] change remote config components data wrapper serviceName to componentName Signed-off-by: Zhan Milenkov --- .../config/remote/components-data-wrapper.ts | 86 +++++++++---------- .../remote/components-data-wrapper.test.ts | 61 +++++++------ 2 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index d9d0a1596..55527e324 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -51,10 +51,10 @@ export class ComponentsDataWrapper /** Used to add new component to their respective group. */ public addNewComponent(component: BaseComponent): void { - const serviceName: string = component.name; + const componentName: string = component.name; - if (!serviceName || typeof serviceName !== 'string') { - throw new SoloError(`Service name is required ${serviceName}`); + if (!componentName || typeof componentName !== 'string') { + throw new SoloError(`Component name is required ${componentName}`); } if (!(component instanceof BaseComponent)) { @@ -65,28 +65,28 @@ export class ComponentsDataWrapper if (this.checkComponentExists(components, component)) { throw new SoloError('Component exists', undefined, component.toObject()); } - components[serviceName] = component; + components[componentName] = component; }; - this.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); + this.applyCallbackToComponentGroup(component.type, componentName, addComponentCallback); } - public changeNodeState(serviceName: ComponentName, nodeState: ConsensusNodeStates): void { - if (!this.consensusNodes[serviceName]) { - throw new SoloError(`Consensus node ${serviceName} doesn't exist`); + public changeNodeState(componentName: ComponentName, nodeState: ConsensusNodeStates): void { + if (!this.consensusNodes[componentName]) { + throw new SoloError(`Consensus node ${componentName} doesn't exist`); } if (!isValidEnum(nodeState, ConsensusNodeStates)) { throw new SoloError(`Invalid node state ${nodeState}`); } - this.consensusNodes[serviceName].changeNodeState(nodeState); + this.consensusNodes[componentName].changeNodeState(nodeState); } /** Used to remove specific component from their respective group. */ - public disableComponent(serviceName: ComponentName, type: ComponentTypes): void { - if (!serviceName || typeof serviceName !== 'string') { - throw new SoloError(`Service name is required ${serviceName}`); + public disableComponent(componentName: ComponentName, type: ComponentTypes): void { + if (!componentName || typeof componentName !== 'string') { + throw new SoloError(`Component name is required ${componentName}`); } if (!isValidEnum(type, ComponentTypes)) { @@ -94,28 +94,28 @@ export class ComponentsDataWrapper } const disableComponentCallback: (components: Record) => void = components => { - if (!components[serviceName]) { - throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`); + if (!components[componentName]) { + throw new SoloError(`Component ${componentName} of type ${type} not found while attempting to remove`); } - components[serviceName].state = ComponentStates.DELETED; + components[componentName].state = ComponentStates.DELETED; }; - this.applyCallbackToComponentGroup(type, serviceName, disableComponentCallback); + this.applyCallbackToComponentGroup(type, componentName, disableComponentCallback); } /* -------- Utilities -------- */ - public getComponent(type: ComponentTypes, serviceName: ComponentName): T { + public getComponent(type: ComponentTypes, componentName: ComponentName): T { let component: T; const getComponentCallback: (components: Record) => void = components => { - if (!components[serviceName]) { - throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to read`); + if (!components[componentName]) { + throw new SoloError(`Component ${componentName} of type ${type} not found while attempting to read`); } - component = components[serviceName] as T; + component = components[componentName] as T; }; - this.applyCallbackToComponentGroup(type, serviceName, getComponentCallback); + this.applyCallbackToComponentGroup(type, componentName, getComponentCallback); return component; } @@ -126,7 +126,7 @@ export class ComponentsDataWrapper */ private applyCallbackToComponentGroup( componentType: ComponentTypes, - serviceName: ComponentName, + componentName: ComponentName, callback: (components: Record) => void, ): void { switch (componentType) { @@ -166,7 +166,7 @@ export class ComponentsDataWrapper } default: { - throw new SoloError(`Unknown component type ${componentType}, service name: ${serviceName}`); + throw new SoloError(`Unknown component type ${componentType}, component name: ${componentName}`); } } @@ -190,50 +190,50 @@ export class ComponentsDataWrapper for (const [componentType, subComponents] of Object.entries(components)) { switch (componentType) { case ComponentTypes.Relay: { - for (const [serviceName, component] of Object.entries(subComponents)) { - relays[serviceName] = RelayComponent.fromObject(component as IRelayComponent); + for (const [componentName, component] of Object.entries(subComponents)) { + relays[componentName] = RelayComponent.fromObject(component as IRelayComponent); } break; } case ComponentTypes.HaProxy: { - for (const [serviceName, component] of Object.entries(subComponents)) { - haProxies[serviceName] = HaProxyComponent.fromObject(component); + for (const [componentName, component] of Object.entries(subComponents)) { + haProxies[componentName] = HaProxyComponent.fromObject(component); } break; } case ComponentTypes.MirrorNode: { - for (const [serviceName, component] of Object.entries(subComponents)) { - mirrorNodes[serviceName] = MirrorNodeComponent.fromObject(component); + for (const [componentName, component] of Object.entries(subComponents)) { + mirrorNodes[componentName] = MirrorNodeComponent.fromObject(component); } break; } case ComponentTypes.EnvoyProxy: { - for (const [serviceName, component] of Object.entries(subComponents)) { - envoyProxies[serviceName] = EnvoyProxyComponent.fromObject(component); + for (const [componentName, component] of Object.entries(subComponents)) { + envoyProxies[componentName] = EnvoyProxyComponent.fromObject(component); } break; } case ComponentTypes.ConsensusNode: { - for (const [serviceName, component] of Object.entries(subComponents)) { - consensusNodes[serviceName] = ConsensusNodeComponent.fromObject(component as IConsensusNodeComponent); + for (const [componentName, component] of Object.entries(subComponents)) { + consensusNodes[componentName] = ConsensusNodeComponent.fromObject(component as IConsensusNodeComponent); } break; } case ComponentTypes.MirrorNodeExplorer: { - for (const [serviceName, component] of Object.entries(subComponents)) { - mirrorNodeExplorers[serviceName] = MirrorNodeExplorerComponent.fromObject(component); + for (const [componentName, component] of Object.entries(subComponents)) { + mirrorNodeExplorers[componentName] = MirrorNodeExplorerComponent.fromObject(component); } break; } case ComponentTypes.BlockNode: { - for (const [serviceName, component] of Object.entries(subComponents)) { - blockNodes[serviceName] = BlockNodeComponent.fromObject(component); + for (const [componentName, component] of Object.entries(subComponents)) { + blockNodes[componentName] = BlockNodeComponent.fromObject(component); } break; } @@ -306,14 +306,14 @@ export class ComponentsDataWrapper /** Validates that the component group mapping has only components from the expected instance */ private validateComponentTypes(components: Record, expectedInstance: any): void { - for (const [serviceName, component] of Object.entries(components)) { - if (!serviceName || typeof serviceName !== 'string') { - throw new SoloError(`Invalid component service name ${{[serviceName]: component?.constructor?.name}}`); + for (const [componentName, component] of Object.entries(components)) { + if (!componentName || typeof componentName !== 'string') { + throw new SoloError(`Invalid component name ${{[componentName]: component?.constructor?.name}}`); } if (!(component instanceof expectedInstance)) { throw new SoloError( - `Invalid component type, service name: ${serviceName}, ` + + `Invalid component type, component name: ${componentName}, ` + `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`, undefined, {component}, @@ -337,8 +337,8 @@ export class ComponentsDataWrapper ): Record { const transformedComponents: Record = {}; - for (const [serviceName, component] of Object.entries(components)) { - transformedComponents[serviceName] = component.toObject() as Component; + for (const [componentName, component] of Object.entries(components)) { + transformedComponents[componentName] = component.toObject() as Component; } return transformedComponents; diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index e0037b17c..050385ae2 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -41,10 +41,10 @@ export function createComponentsDataWrapper(): { blockNodes: Record; }; wrapper: {componentsDataWrapper: ComponentsDataWrapper}; - serviceName: string; + componentName: string; } { const name: string = 'name'; - const serviceName: string = name; + const componentName: string = name; const cluster: ClusterReference = 'cluster'; const namespace: NamespaceNameAsString = 'namespace'; @@ -54,37 +54,37 @@ export function createComponentsDataWrapper(): { const relays: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new RelayComponent(name, cluster, namespace, state, consensusNodeAliases), + [componentName]: new RelayComponent(name, cluster, namespace, state, consensusNodeAliases), }; const haProxies: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new HaProxyComponent(name, cluster, namespace, state), + [componentName]: new HaProxyComponent(name, cluster, namespace, state), }; const mirrorNodes: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new MirrorNodeComponent(name, cluster, namespace, state), + [componentName]: new MirrorNodeComponent(name, cluster, namespace, state), }; const envoyProxies: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new EnvoyProxyComponent(name, cluster, namespace, state), + [componentName]: new EnvoyProxyComponent(name, cluster, namespace, state), }; const consensusNodes: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), + [componentName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), }; const mirrorNodeExplorers: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new MirrorNodeExplorerComponent(name, cluster, namespace, state), + [componentName]: new MirrorNodeExplorerComponent(name, cluster, namespace, state), }; const blockNodes: Record = { // @ts-expect-error - to access private constructor - [serviceName]: new BlockNodeComponent(name, cluster, namespace, state), + [componentName]: new BlockNodeComponent(name, cluster, namespace, state), }; // @ts-expect-error - TS267: to access private constructor @@ -102,7 +102,7 @@ export function createComponentsDataWrapper(): { values: {name, cluster, namespace, nodeState, consensusNodeAliases, state}, components: {consensusNodes, haProxies, envoyProxies, mirrorNodes, mirrorNodeExplorers, relays, blockNodes}, wrapper: {componentsDataWrapper}, - serviceName, + componentName, }; } @@ -111,7 +111,7 @@ describe('ComponentsDataWrapper', () => { it('should not be able to create a instance if wrong data is passed to constructor', () => { // @ts-expect-error - TS267: to access private constructor - expect((): ComponentsDataWrapper => new ComponentsDataWrapper({serviceName: {}})).to.throw( + expect((): ComponentsDataWrapper => new ComponentsDataWrapper({componentName: {}})).to.throw( SoloError, 'Invalid component type', ); @@ -141,10 +141,10 @@ describe('ComponentsDataWrapper', () => { const { wrapper: {componentsDataWrapper}, components: {consensusNodes}, - serviceName, + componentName, } = createComponentsDataWrapper(); - const existingComponent: ConsensusNodeComponent = consensusNodes[serviceName]; + const existingComponent: ConsensusNodeComponent = consensusNodes[componentName]; expect(() => componentsDataWrapper.addNewComponent(existingComponent)).to.throw(SoloError, 'Component exists'); }); @@ -155,9 +155,9 @@ describe('ComponentsDataWrapper', () => { values: {state}, } = createComponentsDataWrapper(); - const newServiceName: string = 'envoy'; + const newComponentName: string = 'envoy'; const {name, cluster, namespace} = { - name: newServiceName, + name: newComponentName, cluster: 'cluster', namespace: 'new-namespace', }; @@ -168,9 +168,9 @@ describe('ComponentsDataWrapper', () => { const componentDataWrapperObject: ComponentsDataStructure = componentsDataWrapper.toObject(); - expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newServiceName); + expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newComponentName); - expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy][newServiceName]).to.deep.equal({ + expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy][newComponentName]).to.deep.equal({ name, cluster, namespace, @@ -183,38 +183,37 @@ describe('ComponentsDataWrapper', () => { it('should be able to change node state with the .changeNodeState(()', () => { const { wrapper: {componentsDataWrapper}, - serviceName, + componentName, } = createComponentsDataWrapper(); const newNodeState: ConsensusNodeStates = ConsensusNodeStates.STOPPED; - componentsDataWrapper.changeNodeState(serviceName, newNodeState); + componentsDataWrapper.changeNodeState(componentName, newNodeState); - expect(componentsDataWrapper.consensusNodes[serviceName].nodeState).to.equal(newNodeState); + expect(componentsDataWrapper.consensusNodes[componentName].nodeState).to.equal(newNodeState); }); it("should not be able to edit component with the .editComponent() if it doesn't exist ", () => { const { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const notFoundServiceName: string = 'not_found'; + const notFoundComponentName: string = 'not_found'; - expect(() => componentsDataWrapper.changeNodeState(notFoundServiceName, ConsensusNodeStates.NON_DEPLOYED)).to.throw( - SoloError, - `Consensus node ${notFoundServiceName} doesn't exist`, - ); + expect(() => + componentsDataWrapper.changeNodeState(notFoundComponentName, ConsensusNodeStates.NON_DEPLOYED), + ).to.throw(SoloError, `Consensus node ${notFoundComponentName} doesn't exist`); }); it('should be able to disable component with the .disableComponent()', () => { const { wrapper: {componentsDataWrapper}, components: {relays}, - serviceName, + componentName, } = createComponentsDataWrapper(); - componentsDataWrapper.disableComponent(serviceName, ComponentTypes.Relay); + componentsDataWrapper.disableComponent(componentName, ComponentTypes.Relay); - expect(relays[serviceName].state).to.equal(ComponentStates.DELETED); + expect(relays[componentName].state).to.equal(ComponentStates.DELETED); }); it("should not be able to disable component with the .disableComponent() if it doesn't exist ", () => { @@ -222,11 +221,11 @@ describe('ComponentsDataWrapper', () => { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const notFoundServiceName: string = 'not_found'; + const notFoundComponentName: string = 'not_found'; - expect(() => componentsDataWrapper.disableComponent(notFoundServiceName, ComponentTypes.Relay)).to.throw( + expect(() => componentsDataWrapper.disableComponent(notFoundComponentName, ComponentTypes.Relay)).to.throw( SoloError, - `Component ${notFoundServiceName} of type ${ComponentTypes.Relay} not found while attempting to remove`, + `Component ${notFoundComponentName} of type ${ComponentTypes.Relay} not found while attempting to remove`, ); }); }); From 7a46443635460637ed448c750b484115b2a8751b Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 10:32:20 +0300 Subject: [PATCH 27/70] improve unit tests Signed-off-by: Zhan Milenkov --- .../remote/components/components.test.ts | 86 +++++++++++-------- .../remote/remote-config-data-wrapper.test.ts | 8 +- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index e0339c350..0c25d7f5a 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -16,19 +16,33 @@ import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enum import {ComponentStates} from '../../../../../../src/core/config/remote/enumerations/component-states.js'; import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; import { + type ClusterReference, type Component, + type ComponentName, type IConsensusNodeComponent, type IRelayComponent, } from '../../../../../../src/core/config/remote/types.js'; +import {NamespaceName} from '../../../../../../src/integration/kube/resources/namespace/namespace-name.js'; + +const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; + +const componentName: ComponentName = 'componentName'; +const clusterReference: ClusterReference = 'cluster-reference'; +const namespace: NamespaceName = NamespaceName.of('valid'); function testBaseComponentData(classComponent: any): void { it('should be an instance of BaseComponent', () => { - const component: any = new classComponent('service-name', 'cluster-reference', 'namespace', ComponentStates.ACTIVE); + const component: any = new classComponent(componentName, clusterReference, namespace.name, ComponentStates.ACTIVE); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { - const data: Component = {name: 'name', cluster: 'cluster', namespace: 'namespace', state: ComponentStates.ACTIVE}; + const data: Component = { + name: componentName, + cluster: clusterReference, + namespace: namespace.name, + state: ComponentStates.ACTIVE, + }; const component: any = new classComponent(data.name, data.cluster, data.namespace, data.state); expect(component.toObject()).to.deep.equal(data); @@ -47,31 +61,37 @@ describe('BlockNodeComponent', () => testBaseComponentData(BlockNodeComponent)); describe('RelayComponent', () => { it('should successfully create ', () => { - // @ts-expect-error - to access private constructor - new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); + RelayComponent.createNew(remoteConfigManagerMock, clusterReference, namespace, []); }); it('should be an instance of BaseComponent', () => { - // @ts-expect-error - to access private constructor - const component: RelayComponent = new RelayComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE); + const component: RelayComponent = RelayComponent.createNew( + remoteConfigManagerMock, + clusterReference, + namespace, + [], + ); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { + // @ts-expect-error: to access private property + const name: ComponentName = RelayComponent.renderRelayName( + remoteConfigManagerMock.components.getNewComponentIndex(), + ); + const values: IRelayComponent = { - name: 'name', - cluster: 'cluster', - namespace: 'namespace', + name, + cluster: clusterReference, + namespace: namespace.name, state: ComponentStates.ACTIVE, consensusNodeAliases: ['node1'], }; - // @ts-expect-error - to access private constructor - const component: RelayComponent = new RelayComponent( - values.name, + const component: RelayComponent = RelayComponent.createNew( + remoteConfigManagerMock, values.cluster, - values.namespace, - values.state, + namespace, values.consensusNodeAliases, ); @@ -80,21 +100,21 @@ describe('RelayComponent', () => { }); describe('ConsensusNodeComponent', () => { + const nodeAlias: NodeAlias = 'node1'; + const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; + it('should successfully create ', () => { - // @ts-expect-error - to access private constructor - new ConsensusNodeComponent('valid', 'valid', 'valid', ComponentStates.ACTIVE, ConsensusNodeStates.STARTED, 0); + ConsensusNodeComponent.createNew(nodeAlias, 'valid', namespace, nodeState); }); it('should be an instance of BaseComponent', () => { - // @ts-expect-error - to access private constructor - const component: ConsensusNodeComponent = new ConsensusNodeComponent( - 'valid', - 'valid', - 'valid', - ComponentStates.ACTIVE, - ConsensusNodeStates.STARTED, - 0, + const component: ConsensusNodeComponent = ConsensusNodeComponent.createNew( + nodeAlias, + clusterReference, + namespace, + nodeState, ); + expect(component).to.be.instanceOf(BaseComponent); }); @@ -102,22 +122,20 @@ describe('ConsensusNodeComponent', () => { const nodeAlias: NodeAlias = 'node1'; const values: IConsensusNodeComponent = { name: nodeAlias, - cluster: 'cluster', - namespace: 'namespace', + cluster: clusterReference, + namespace: namespace.name, state: ComponentStates.ACTIVE, - nodeState: ConsensusNodeStates.STARTED, + nodeState, nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; - // @ts-expect-error - to access private constructor - const component: ConsensusNodeComponent = new ConsensusNodeComponent( - values.name, + const component: ConsensusNodeComponent = ConsensusNodeComponent.createNew( + values.name as NodeAlias, values.cluster, - values.namespace, - values.state, - values.nodeState, - values.nodeId, + namespace, + values.nodeState as ConsensusNodeStates.STARTED, ); + expect(component.toObject()).to.deep.equal(values); }); }); diff --git a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts index 638f22069..8947757b2 100644 --- a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts +++ b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts @@ -13,10 +13,10 @@ import {type RemoteConfigDataStructure} from '../../../../../src/core/config/rem import {type RemoteConfigData} from '../../../../../src/core/config/remote/remote-config-data.js'; const configManagerMock: any = { - update: (..._arguments: any): true => true, - getFlag: (..._arguments: any): true => true, - hasFlag: (..._arguments: any): true => true, - setFlag: (..._arguments: any): true => true, + update: (...arguments_: any) => true, + getFlag: (...arguments_: any) => true, + hasFlag: (...arguments_: any) => true, + setFlag: (...arguments_: any) => true, }; async function createRemoteConfigDataWrapper(): Promise<{ From 1e706053742fa0894cd6624642a526dd9b96738f Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 11:12:11 +0300 Subject: [PATCH 28/70] updated logic for disabling components Signed-off-by: Zhan Milenkov --- src/commands/explorer.ts | 35 ++++++++++++------- src/commands/mirror-node.ts | 18 +++++++--- src/commands/node/handlers.ts | 19 ---------- src/commands/relay.ts | 17 ++++++--- .../config/remote/components-data-wrapper.ts | 31 ++++++++++++---- 5 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 710789cc9..6a211b75e 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -7,6 +7,7 @@ import {SoloError} from '../core/errors/solo-error.js'; import {MissingArgumentError} from '../core/errors/missing-argument-error.js'; import {UserBreak} from '../core/errors/user-break.js'; import * as constants from '../core/constants.js'; +import {HEDERA_EXPLORER_CHART_URL, INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; @@ -14,6 +15,7 @@ import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; +import * as helpers from '../core/helpers.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; @@ -21,13 +23,9 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {type ClusterChecks} from '../core/cluster-checks.js'; import {container} from 'tsyringe-neo'; import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; -import {HEDERA_EXPLORER_CHART_URL, INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; -import * as helpers from '../core/helpers.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; -import {type ClusterReference, type ComponentName} from '../core/config/remote/types.js'; -import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; +import {type ClusterReference, type Context} from '../core/config/remote/types.js'; interface ExplorerDeployConfigClass { chartDirectory: string; @@ -59,6 +57,7 @@ interface ExplorerDeployContext { interface ExplorerDestroyContext { config: { clusterContext: string; + clusterReference: ClusterReference; namespace: NamespaceName; isChartInstalled: boolean; }; @@ -439,14 +438,16 @@ export class ExplorerCommand extends BaseCommand { self.configManager.update(argv); const namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); - const clusterReference = this.configManager.getFlag(flags.clusterRef) as string; - const clusterContext = clusterReference - ? this.localConfig.clusterRefs[clusterReference] - : this.k8Factory.default().contexts().readCurrent(); + const clusterReference: ClusterReference = this.configManager.hasFlag(flags.clusterRef) + ? this.configManager.getFlag(flags.clusterRef) + : this.remoteConfigManager.currentCluster; + + const clusterContext: Context = this.localConfig.clusterRefs[clusterReference]; context_.config = { namespace, clusterContext, + clusterReference, isChartInstalled: await this.chartManager.isChartInstalled( namespace, constants.HEDERA_EXPLORER_RELEASE_NAME, @@ -573,13 +574,23 @@ export class ExplorerCommand extends BaseCommand { } /** Removes the explorer components from remote config. */ - private disableMirrorNodeExplorerComponents(): SoloListrTask { // TODO: IMPLEMENT + private disableMirrorNodeExplorerComponents(): SoloListrTask { return { title: 'Remove explorer from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (): Promise => { + task: async (context_): Promise => { + const clusterReference: ClusterReference = context_.config.clusterReference; + await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.disableComponent('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer); + const explorerComponents: MirrorNodeExplorerComponent[] = + remoteConfig.components.getComponentsByClusterReference( + ComponentTypes.MirrorNodeExplorer, + clusterReference, + ); + + for (const explorerComponent of explorerComponents) { + remoteConfig.components.disableComponent(explorerComponent.name, ComponentTypes.MirrorNodeExplorer); + } }); }, }; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 862d5137f..59accf716 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -32,11 +32,10 @@ import chalk from 'chalk'; import {type CommandFlag} from '../types/flag-types.js'; import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js'; import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js'; -import {type ClusterReference, type ComponentName, type DeploymentName} from '../core/config/remote/types.js'; +import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface MirrorNodeDeployConfigClass { chartDirectory: string; @@ -880,10 +879,19 @@ export class MirrorNodeCommand extends BaseCommand { return { title: 'Remove mirror node from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (): Promise => { + task: async (context_): Promise => { + const clusterReference: ClusterReference = context_.config.clusterRef; + await this.remoteConfigManager.modify(async remoteConfig => { - // TODO: IMPLEMENT - remoteConfig.components.disableComponent('mirrorNode', ComponentTypes.MirrorNode); + const mirrorNodeComponents: MirrorNodeComponent[] = + remoteConfig.components.getComponentsByClusterReference( + ComponentTypes.MirrorNode, + clusterReference, + ); + + for (const mirrorNodeComponent of mirrorNodeComponents) { + remoteConfig.components.disableComponent(mirrorNodeComponent.name, ComponentTypes.MirrorNode); + } }); }, }; diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 0a46a9ced..37ef3e80c 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -29,7 +29,6 @@ import {type NodeUpdateContext} from './config-interfaces/node-update-context.js import {type NodeUpgradeContext} from './config-interfaces/node-upgrade-context.js'; import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentStates} from '../../core/config/remote/enumerations/component-states.js'; @injectable() export class NodeCommandHandlers extends CommandHandler { @@ -898,24 +897,6 @@ export class NodeCommandHandlers extends CommandHandler { return true; } - // TODO MOVE TO TASKS - - /** Removes the consensus node, envoy and haproxy components from remote config. */ - public removeNodeAndProxies(): SoloListrTask { - return { - skip: (): boolean => !this.remoteConfigManager.isLoaded(), - title: 'Remove node and proxies from remote config', - task: async (): Promise => { - // TODO - await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.disableComponent('Consensus node name', ComponentTypes.ConsensusNode); - remoteConfig.components.disableComponent('Envoy proxy name', ComponentTypes.EnvoyProxy); - remoteConfig.components.disableComponent('HaProxy name', ComponentTypes.HaProxy); - }); - }, - }; - } - /** * Changes the state from all consensus nodes components in remote config. * diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 5937c2321..08bc4633f 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -17,11 +17,10 @@ import {ListrLock} from '../core/lock/listr-lock.js'; import {RelayComponent} from '../core/config/remote/components/relay-component.js'; import * as Base64 from 'js-base64'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; -import {type ClusterReference, ComponentName, type DeploymentName} from '../core/config/remote/types.js'; +import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface RelayDestroyConfigClass { chartDirectory: string; @@ -561,9 +560,19 @@ export class RelayCommand extends BaseCommand { return { title: 'Remove relay component from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (): Promise => { + task: async (context_): Promise => { + const clusterReference: ClusterReference = context_.config.clusterRef; + await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.disableComponent('relay', ComponentTypes.Relay); + const relayComponents: RelayComponent[] = + remoteConfig.components.getComponentsByClusterReference( + ComponentTypes.Relay, + clusterReference, + ); + + for (const relayComponent of relayComponents) { + remoteConfig.components.disableComponent(relayComponent.name, ComponentTypes.Relay); + } }); }, }; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 55527e324..f7462bb2d 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -68,7 +68,7 @@ export class ComponentsDataWrapper components[componentName] = component; }; - this.applyCallbackToComponentGroup(component.type, componentName, addComponentCallback); + this.applyCallbackToComponentGroup(component.type, addComponentCallback, componentName); } public changeNodeState(componentName: ComponentName, nodeState: ConsensusNodeStates): void { @@ -100,7 +100,7 @@ export class ComponentsDataWrapper components[componentName].state = ComponentStates.DELETED; }; - this.applyCallbackToComponentGroup(type, componentName, disableComponentCallback); + this.applyCallbackToComponentGroup(type, disableComponentCallback, componentName); } /* -------- Utilities -------- */ @@ -115,19 +115,36 @@ export class ComponentsDataWrapper component = components[componentName] as T; }; - this.applyCallbackToComponentGroup(type, componentName, getComponentCallback); + this.applyCallbackToComponentGroup(type, getComponentCallback, componentName); return component; } + public getComponentsByClusterReference( + type: ComponentTypes, + clusterReference: ClusterReference, + ): T[] { + let components: T[]; + + const getComponentsByClusterReferenceCallback: ( + components: Record, + ) => void = components => { + Object.values(components).filter(component => component.cluster === clusterReference); + }; + + this.applyCallbackToComponentGroup(type, getComponentsByClusterReferenceCallback); + + return components; + } + /** * Method used to map the type to the specific component group * and pass it to a callback to apply modifications */ private applyCallbackToComponentGroup( componentType: ComponentTypes, - componentName: ComponentName, callback: (components: Record) => void, + componentName?: ComponentName, ): void { switch (componentType) { case ComponentTypes.Relay: { @@ -290,7 +307,9 @@ export class ComponentsDataWrapper public getNewComponentIndex(componentType: ComponentTypes): number { let newComponentIndex: number = 1; - const callback: (components: Record) => void = components => { + const calculateNewComponentIndexCallback: ( + components: Record, + ) => void = components => { for (const componentName of Object.keys(components)) { const componentIndex: number = BaseComponent.parseComponentName(componentName); if (newComponentIndex <= componentIndex) { @@ -299,7 +318,7 @@ export class ComponentsDataWrapper } }; - this.applyCallbackToComponentGroup(componentType, '', callback); + this.applyCallbackToComponentGroup(componentType, calculateNewComponentIndexCallback); return newComponentIndex; } From 75ff454a5def5e53bd8e6d5c81f781719b98390e Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 13:34:04 +0300 Subject: [PATCH 29/70] cleanup after implementing new remote config components modification and disabling logic Signed-off-by: Zhan Milenkov --- src/commands/base.ts | 2 +- src/commands/deployment.ts | 1 - src/commands/flags.ts | 32 +++++----- src/commands/network.ts | 5 +- .../remote/components/relay-component.ts | 6 +- .../remote/remote-config-data-wrapper.ts | 4 +- .../config/remote/remote-config-manager.ts | 63 +++++++++---------- src/core/enumerations.ts | 4 +- src/core/lock/listr-lock.ts | 6 +- src/core/templates.ts | 15 ++--- src/index.ts | 2 +- src/types/index.ts | 2 +- 12 files changed, 61 insertions(+), 81 deletions(-) diff --git a/src/commands/base.ts b/src/commands/base.ts index b47a88fb8..f1c129786 100644 --- a/src/commands/base.ts +++ b/src/commands/base.ts @@ -51,7 +51,7 @@ export abstract class BaseCommand extends ShellRunner { public readonly localConfig: LocalConfig; protected readonly remoteConfigManager: RemoteConfigManager; - public constructor(options: Options) { + constructor(options: Options) { if (!options || !options.helm) { throw new Error('An instance of core/Helm is required'); } diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 933c36cc3..90643daf3 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -21,7 +21,6 @@ import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; import {DeploymentStates} from '../core/config/remote/enumerations/deployment-states.js'; -import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; interface DeploymentAddClusterConfig { quiet: boolean; diff --git a/src/commands/flags.ts b/src/commands/flags.ts index 17445984a..e2b4e4f04 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -21,7 +21,7 @@ import chalk from 'chalk'; import {PathEx} from '../business/utils/path-ex.js'; export class Flags { - public static KEY_COMMON: string = '_COMMON_'; + public static KEY_COMMON = '_COMMON_'; private static async prompt( type: 'toggle' | 'input' | 'number', @@ -36,7 +36,7 @@ export class Flags { // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { try { - let needsPrompt: boolean = type === 'toggle' ? input === undefined || typeof input !== 'boolean' : !input; + let needsPrompt = type === 'toggle' ? input === undefined || typeof input !== 'boolean' : !input; needsPrompt = type === 'number' ? typeof input !== 'number' : needsPrompt; if (needsPrompt) { @@ -46,7 +46,7 @@ export class Flags { throw new SoloError('Cannot prompt for input in non-interactive mode'); } - const promptOptions: {default: any; message: string} = {default: defaultValue, message: promptMessage}; + const promptOptions = {default: defaultValue, message: promptMessage}; switch (type) { case 'input': { @@ -2343,7 +2343,7 @@ export class Flags { type: 'number', }, prompt: async function (task: SoloListrTaskWrapper, input: number): Promise { - const promptForInput: () => Promise = (): Promise => + const promptForInput = (): Promise => Flags.prompt( 'number', task, @@ -2539,19 +2539,17 @@ export class Flags { ]; /** Resets the definition.disablePrompt for all flags */ - private static resetDisabledPrompts(): void { - for (const flag of Flags.allFlags) { - if (flag.definition.disablePrompt) { - delete flag.definition.disablePrompt; + private static resetDisabledPrompts() { + for (const f of Flags.allFlags) { + if (f.definition.disablePrompt) { + delete f.definition.disablePrompt; } } } - public static readonly allFlagsMap: Map = new Map( - Flags.allFlags.map((flag): [string, CommandFlag] => [flag.name, flag]), - ); + public static readonly allFlagsMap = new Map(Flags.allFlags.map(f => [f.name, f])); - public static readonly nodeConfigFileFlags: Map = new Map( + public static readonly nodeConfigFileFlags = new Map( [ Flags.apiPermissionProperties, Flags.applicationEnv, @@ -2559,12 +2557,10 @@ export class Flags { Flags.bootstrapProperties, Flags.log4j2Xml, Flags.settingTxt, - ].map((flag): [string, CommandFlag] => [flag.name, flag]), + ].map(f => [f.name, f]), ); - public static readonly integerFlags: Map = new Map( - [Flags.replicaCount].map((flag): [string, CommandFlag] => [flag.name, flag]), - ); + public static readonly integerFlags = new Map([Flags.replicaCount].map(f => [f.name, f])); public static readonly DEFAULT_FLAGS: CommandFlags = { required: [], @@ -2588,12 +2584,12 @@ export class Flags { } // remove flags that use the default value - const flag: CommandFlag = Flags.allFlags.find((flag): boolean => flag.name === name); + const flag = Flags.allFlags.find(flag => flag.name === name); if (!flag || (flag.definition.defaultValue && flag.definition.defaultValue === value)) { continue; } - const flagName: string = flag.name; + const flagName = flag.name; // if the flag is boolean based, render it without value if (value === true) { diff --git a/src/commands/network.ts b/src/commands/network.ts index 1465d397a..73877a61c 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -28,7 +28,6 @@ import {type ProfileManager} from '../core/profile-manager.js'; import {type CertificateManager} from '../core/certificate-manager.js'; import {type AnyYargs, type IP, type NodeAlias, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; import {HaProxyComponent} from '../core/config/remote/components/ha-proxy-component.js'; import {v4 as uuidv4} from 'uuid'; @@ -37,7 +36,7 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js'; import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js'; import {type ConsensusNode} from '../core/model/consensus-node.js'; -import {type ClusterReference, type ClusterReferences, type ComponentName} from '../core/config/remote/types.js'; +import {type ClusterReference, type ClusterReferences} from '../core/config/remote/types.js'; import {Base64} from 'js-base64'; import {SecretType} from '../integration/kube/resources/secret/secret-type.js'; import {Duration} from '../core/time/duration.js'; @@ -45,8 +44,6 @@ import {type PodReference} from '../integration/kube/resources/pod/pod-reference import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentStates} from '../core/config/remote/enumerations/component-states.js'; -import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; export interface NetworkDeployConfigClass { applicationEnv: string; diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index 5edb87f93..cfd8c6649 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -55,9 +55,9 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To public override validate(): void { super.validate(); - for (const alias of this.consensusNodeAliases) { - if (!alias || typeof alias !== 'string') { - throw new SoloError(`Invalid consensus node alias: ${alias}, aliases ${this.consensusNodeAliases}`); + for (const nodeAlias of this.consensusNodeAliases) { + if (!nodeAlias || typeof nodeAlias !== 'string') { + throw new SoloError(`Invalid consensus node alias: ${nodeAlias}, aliases ${this.consensusNodeAliases}`); } } } diff --git a/src/core/config/remote/remote-config-data-wrapper.ts b/src/core/config/remote/remote-config-data-wrapper.ts index d10817347..f7c3cc784 100644 --- a/src/core/config/remote/remote-config-data-wrapper.ts +++ b/src/core/config/remote/remote-config-data-wrapper.ts @@ -96,7 +96,7 @@ export class RemoteConfigDataWrapper implements Validate, ToObject typeof c !== 'string')) { + if (!Array.isArray(this.commandHistory) || this.commandHistory.some(c => typeof c !== 'string')) { throw new SoloError(`Invalid remote config command history: ${this.commandHistory}`); } diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index b07a0c6e2..3791ff93f 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -14,7 +14,7 @@ import { type ClusterReferences, type Context, type DeploymentName, - type EmailAddress, + type NamespaceNameAsString, type Version, } from './types.js'; import {type SoloLogger} from '../../logging/solo-logger.js'; @@ -34,7 +34,6 @@ import {Templates} from '../../templates.js'; import {promptTheUserForDeployment, resolveNamespaceFromDeployment} from '../../resolvers.js'; import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; import {getSoloVersion} from '../../../../version.js'; -import {DeploymentStructure} from '../local/local-config-data.js'; import {DeploymentStates} from './enumerations/deployment-states.js'; /** @@ -129,10 +128,10 @@ export class RemoteConfigManager { ), }; - const lastUpdatedAt: Date = new Date(); - const email: EmailAddress = this.localConfig.userEmailAddress; - const soloVersion: string = getSoloVersion(); - const currentCommand: string = argv._.join(' '); + const lastUpdatedAt = new Date(); + const email = this.localConfig.userEmailAddress; + const soloVersion = getSoloVersion(); + const currentCommand = argv._.join(' '); this.remoteConfig = new RemoteConfigDataWrapper({ clusters, @@ -169,7 +168,7 @@ export class RemoteConfigManager { return; } try { - const configMap: ConfigMap = await this.getConfigMap(namespace, context); + const configMap = await this.getConfigMap(namespace, context); this.remoteConfig = RemoteConfigDataWrapper.fromConfigmap(this.configManager, configMap); } catch (error) { @@ -182,8 +181,7 @@ export class RemoteConfigManager { * @returns RemoteConfigDataWrapper */ public async get(context?: Context): Promise { - const namespace: NamespaceName = - this.configManager.getFlag(flags.namespace) ?? (await this.getNamespace()); + const namespace = this.configManager.getFlag(flags.namespace) ?? (await this.getNamespace()); await this.load(namespace, context); try { @@ -210,8 +208,8 @@ export class RemoteConfigManager { public static compare(remoteConfig1: RemoteConfigDataWrapper, remoteConfig2: RemoteConfigDataWrapper): boolean { // Compare clusters - const clusters1: string[] = Object.keys(remoteConfig1.clusters); - const clusters2: string[] = Object.keys(remoteConfig2.clusters); + const clusters1 = Object.keys(remoteConfig1.clusters); + const clusters2 = Object.keys(remoteConfig2.clusters); if (clusters1.length !== clusters2.length) { return false; } @@ -258,8 +256,8 @@ export class RemoteConfigManager { skipConsensusNodesValidation, ); - const currentCommand: string = argv._?.join(' '); - const commandArguments: string = flags.stringifyArgv(argv); + const currentCommand = argv._?.join(' '); + const commandArguments = flags.stringifyArgv(argv); this.remoteConfig!.addCommandToHistory( `Executed by ${this.localConfig.userEmailAddress}: ${currentCommand} ${commandArguments}`.trim(), @@ -276,7 +274,7 @@ export class RemoteConfigManager { const command: string = argv._[0]; const subcommand: string = argv._[1]; - const isCommandUsingSoloChartVersionFlag: boolean = + const isCommandUsingSoloChartVersionFlag = (command === 'network' && subcommand === 'deploy') || (command === 'network' && subcommand === 'refresh') || (command === 'node' && subcommand === 'update') || @@ -292,7 +290,7 @@ export class RemoteConfigManager { this.remoteConfig.metadata.soloChartVersion = flags.soloChartVersion.definition.defaultValue as Version; } - const isCommandUsingReleaseTagVersionFlag: boolean = + const isCommandUsingReleaseTagVersionFlag = (command === 'node' && subcommand !== 'keys' && subcommand !== 'logs' && subcommand !== 'states') || (command === 'network' && subcommand === 'deploy'); @@ -354,7 +352,7 @@ export class RemoteConfigManager { } try { - const configMap: ConfigMap = await this.k8Factory + const configMap = await this.k8Factory .getK8(context) .configMaps() .read(namespace, constants.SOLO_REMOTE_CONFIGMAP_NAME); @@ -376,10 +374,10 @@ export class RemoteConfigManager { * Creates a new ConfigMap entry in the Kubernetes cluster with the remote configuration data. */ public async createConfigMap(context?: Context): Promise { - const namespace: NamespaceName = await this.getNamespace(); - const name: string = constants.SOLO_REMOTE_CONFIGMAP_NAME; - const labels: Record = constants.SOLO_REMOTE_CONFIGMAP_LABELS; - const data: {'remote-config-data': string} = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; + const namespace = await this.getNamespace(); + const name = constants.SOLO_REMOTE_CONFIGMAP_NAME; + const labels = constants.SOLO_REMOTE_CONFIGMAP_LABELS; + const data = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; await this.k8Factory.getK8(context).configMaps().create(namespace, name, labels, data); } @@ -388,12 +386,12 @@ export class RemoteConfigManager { * Replaces an existing ConfigMap in the Kubernetes cluster with the current remote configuration data. */ private async replaceConfigMap(): Promise { - const namespace: NamespaceName = await this.getNamespace(); - const name: string = constants.SOLO_REMOTE_CONFIGMAP_NAME; - const labels: Record = constants.SOLO_REMOTE_CONFIGMAP_LABELS; - const data: {'remote-config-data': string} = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; + const namespace = await this.getNamespace(); + const name = constants.SOLO_REMOTE_CONFIGMAP_NAME; + const labels = constants.SOLO_REMOTE_CONFIGMAP_LABELS; + const data = {'remote-config-data': yaml.stringify(this.remoteConfig.toObject())}; - const deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment); + const deploymentName = this.configManager.getFlag(flags.deployment); if (!deploymentName) { throw new SoloError('Failed to get deployment'); @@ -410,10 +408,7 @@ export class RemoteConfigManager { ); await Promise.all( - contexts.map( - (context): Promise => - this.k8Factory.getK8(context).configMaps().replace(namespace, name, labels, data), - ), + contexts.map(context => this.k8Factory.getK8(context).configMaps().replace(namespace, name, labels, data)), ); } @@ -423,8 +418,8 @@ export class RemoteConfigManager { } // TODO: Current quick fix for commands where namespace is not passed - let deploymentName: string = this.configManager.getFlag(flags.deployment); - let currentDeployment: DeploymentStructure = this.localConfig.deployments[deploymentName]; + let deploymentName = this.configManager.getFlag(flags.deployment); + let currentDeployment = this.localConfig.deployments[deploymentName]; if (!deploymentName) { deploymentName = await promptTheUserForDeployment(this.configManager); @@ -443,7 +438,7 @@ export class RemoteConfigManager { throw new SoloError(`Selected deployment name is not set in local config - ${deploymentName}`); } - const namespace: string = currentDeployment.namespace; + const namespace: NamespaceNameAsString = currentDeployment.namespace; this.logger.warn(`Namespace not found in flags, setting it to: ${namespace}`); this.configManager.setFlag(flags.namespace, namespace); @@ -530,7 +525,7 @@ export class RemoteConfigManager { * @returns an object of cluster references. */ public getClusterRefs(): ClusterReferences { - const nodes: ConsensusNode[] = this.getConsensusNodes(); + const nodes = this.getConsensusNodes(); const accumulator: ClusterReferences = {}; for (const node of nodes) { @@ -541,7 +536,7 @@ export class RemoteConfigManager { } private getContextForFirstCluster(): string { - const deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment); + const deploymentName = this.configManager.getFlag(flags.deployment); const clusterReference: ClusterReference = this.localConfig.deployments[deploymentName].clusters[0]; diff --git a/src/core/enumerations.ts b/src/core/enumerations.ts index 7e22db657..0d38273ce 100644 --- a/src/core/enumerations.ts +++ b/src/core/enumerations.ts @@ -14,7 +14,7 @@ export enum NodeStatusCodes { CATASTROPHIC_FAILURE = 11, } -export const NodeStatusEnums: Record = { +export const NodeStatusEnums = { 0: 'NO_VALUE', 1: 'STARTING_UP', 2: 'ACTIVE', @@ -26,7 +26,7 @@ export const NodeStatusEnums: Record = { 9: 'CHECKING', 10: 'RECONNECT_COMPLETE', 11: 'CATASTROPHIC_FAILURE', -}; +} as const; /** * - GRPC - Represents HAProxy Proxy diff --git a/src/core/lock/listr-lock.ts b/src/core/lock/listr-lock.ts index 9a28f23f6..290a15e26 100644 --- a/src/core/lock/listr-lock.ts +++ b/src/core/lock/listr-lock.ts @@ -5,8 +5,6 @@ import {type Lock} from './lock.js'; import {LockAcquisitionError} from './lock-acquisition-error.js'; import {type SoloListrTaskWrapper} from '../../types/index.js'; -import {type AnyListrContext} from '../../types/aliases.js'; -import {type Listr} from 'listr2'; /** * A utility class for managing lock acquisition tasks in Listr2 based workflows. @@ -35,12 +33,12 @@ export class ListrLock { * @param task - the parent task to which the lock acquisition task will be added. * @returns a new Listr2 task for acquiring a lock with retry logic. */ - public static newAcquireLockTask(lock: Lock, task: SoloListrTaskWrapper): Listr { + public static newAcquireLockTask(lock: Lock, task: SoloListrTaskWrapper) { return task.newListr( [ { title: ListrLock.ACQUIRE_LOCK_TASK_TITLE, - task: async (_, task): Promise => { + task: async (_, task) => { await ListrLock.acquireWithRetry(lock, task); }, }, diff --git a/src/core/templates.ts b/src/core/templates.ts index 186ae37a4..ea8003582 100644 --- a/src/core/templates.ts +++ b/src/core/templates.ts @@ -65,7 +65,7 @@ export class Templates { return `${nodeAlias}-admin`; } - public static renderNodeFriendlyName(prefix: string, nodeAlias: NodeAlias, suffix: string = ''): string { + public static renderNodeFriendlyName(prefix: string, nodeAlias: NodeAlias, suffix = ''): string { const parts = [prefix, nodeAlias]; if (suffix) { parts.push(suffix); @@ -228,10 +228,7 @@ export class Templates { * * @returns the appropriate secret labels */ - public static renderGrpcTlsCertificatesSecretLabelObject( - nodeAlias: NodeAlias, - type: GrpcProxyTlsEnums, - ): Record { + static renderGrpcTlsCertificatesSecretLabelObject(nodeAlias: NodeAlias, type: GrpcProxyTlsEnums) { switch (type) { //? HAProxy Proxy case GrpcProxyTlsEnums.GRPC: { @@ -254,7 +251,6 @@ export class Templates { if (!nodeAlias || typeof nodeAlias !== 'string') { throw new SoloError(`Can't parse node alias: ${data}`); } - if (!ip || typeof ip !== 'string') { throw new SoloError(`Can't parse ip: ${data}`); } @@ -274,7 +270,6 @@ export class Templates { if (!nodeAlias || typeof nodeAlias !== 'string') { throw new SoloError(`Can't parse node alias: ${data}`); } - if (!domainName || typeof domainName !== 'string') { throw new SoloError(`Can't parse domain name: ${data}`); } @@ -299,15 +294,15 @@ export class Templates { * @param dnsBaseDomain - the base domain of the cluster * @param dnsConsensusNodePattern - the pattern to use for the consensus node */ - public static renderConsensusNodeFullyQualifiedDomainName( + static renderConsensusNodeFullyQualifiedDomainName( nodeAlias: string, nodeId: number, namespace: NamespaceNameAsString, cluster: ClusterReference, dnsBaseDomain: string, dnsConsensusNodePattern: string, - ): string { - const searchReplace: Record = { + ) { + const searchReplace = { '{nodeAlias}': nodeAlias, '{nodeId}': nodeId.toString(), '{namespace}': namespace, diff --git a/src/index.ts b/src/index.ts index 509e16d5d..85b687ae1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,7 +45,7 @@ export async function main(argv: string[], context?: {logger: SoloLogger}) { throw new SoloError('Error initializing container'); } - const logger: SoloLogger = container.resolve(InjectTokens.SoloLogger); + const logger = container.resolve(InjectTokens.SoloLogger); if (context) { // save the logger so that solo.ts can use it to properly flush the logs and exit diff --git a/src/types/index.ts b/src/types/index.ts index a373a7c41..cab58cf71 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -6,7 +6,7 @@ import type * as WebSocket from 'ws'; import type crypto from 'node:crypto'; import {type ListrTask, type ListrTaskWrapper} from 'listr2'; import {type PublicKey} from '@hashgraph/sdk'; -import {type AnyYargs, ArgvStruct, type JsonString} from './aliases.js'; +import {type AnyYargs, type ArgvStruct, type JsonString} from './aliases.js'; import {type Listr} from 'listr2'; // NOTE: DO NOT add any Solo imports in this file to avoid circular dependencies From 8271f61ae53d2a78dcf2117297f6009523456e46 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 14:04:23 +0300 Subject: [PATCH 30/70] added unit tests for all new ComponentsDataWrapper Signed-off-by: Zhan Milenkov --- .../config/remote/components-data-wrapper.ts | 10 ++-- .../remote/components/base-component.ts | 4 +- .../remote/components-data-wrapper.test.ts | 48 ++++++++++++++++++- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index f7462bb2d..1f6a416a3 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -124,17 +124,15 @@ export class ComponentsDataWrapper type: ComponentTypes, clusterReference: ClusterReference, ): T[] { - let components: T[]; + let filteredComponents: T[] = []; - const getComponentsByClusterReferenceCallback: ( - components: Record, - ) => void = components => { - Object.values(components).filter(component => component.cluster === clusterReference); + const getComponentsByClusterReferenceCallback: (components: Record) => void = components => { + filteredComponents = Object.values(components).filter(component => component.cluster === clusterReference); }; this.applyCallbackToComponentGroup(type, getComponentsByClusterReferenceCallback); - return components; + return filteredComponents; } /** diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 126327f32..5b35cf483 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -80,7 +80,7 @@ export class BaseComponent implements Component, Validate, ToObject { * @param index - total number of components from this kind * @returns a unique name to be used for creating components */ - protected static renderComponentName(baseName: string, index: number): string { + protected static renderComponentName(baseName: string, index: number): ComponentName { return `${baseName}-${index}`; } @@ -90,7 +90,7 @@ export class BaseComponent implements Component, Validate, ToObject { * @param name - full component name (e.g., "mirror-node-node1-42") * @returns the numeric index (e.g., 42) */ - public static parseComponentName(name: string): number { + public static parseComponentName(name: ComponentName): number { const parts: string[] = name.split('-'); const lastPart: string = parts.at(-1); const componentIndex: number = Number.parseInt(lastPart, 10); diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 050385ae2..72ca16cbb 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -16,7 +16,7 @@ import {ConsensusNodeStates} from '../../../../../src/core/config/remote/enumera import {ComponentStates} from '../../../../../src/core/config/remote/enumerations/component-states.js'; import {type NodeAliases} from '../../../../../src/types/aliases.js'; import { - type ClusterReference, + type ClusterReference, ComponentName, type ComponentsDataStructure, type NamespaceNameAsString, } from '../../../../../src/core/config/remote/types.js'; @@ -228,4 +228,50 @@ describe('ComponentsDataWrapper', () => { `Component ${notFoundComponentName} of type ${ComponentTypes.Relay} not found while attempting to remove`, ); }); + + it('should be able to get components with .getComponent()', () => { + const { + wrapper: {componentsDataWrapper}, + componentName, + components: {blockNodes}, + } = createComponentsDataWrapper(); + + const blockNodeComponent: BlockNodeComponent = componentsDataWrapper.getComponent( + ComponentTypes.BlockNode, + componentName, + ); + + expect(blockNodes[componentName].toObject()).to.deep.equal(blockNodeComponent.toObject()); + }); + + it("should fail if trying to get component that doesn't exist with .getComponent()", () => { + const { + wrapper: {componentsDataWrapper}, + } = createComponentsDataWrapper(); + + const notFoundComponentName: ComponentName = 'not_found'; + const type: ComponentTypes = ComponentTypes.BlockNode; + + expect(() => componentsDataWrapper.getComponent(type, notFoundComponentName)).to.throw( + `Component ${notFoundComponentName} of type ${type} not found while attempting to read`, + ); + }); + + it('should be able to get components with .applyCallbackToComponentGroup()', () => { + const { + wrapper: {componentsDataWrapper}, + components: {blockNodes}, + values: {cluster}, + } = createComponentsDataWrapper(); + + const blockNodeComponents: BlockNodeComponent[] = + componentsDataWrapper.getComponentsByClusterReference(ComponentTypes.BlockNode, cluster); + + for (const blockNodeComponent of blockNodeComponents) { + expect(blockNodeComponent.toObject()).to.deep.equal(blockNodes[blockNodeComponent.name].toObject()); + expect(blockNodeComponent.cluster).to.equal(cluster); + } + + expect(Object.keys(blockNodes).length).to.equal(blockNodeComponents.length); + }); }); From ca5ce4e3b56f3a37b867f3ce538c34b2baec49ff Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 14:30:36 +0300 Subject: [PATCH 31/70] fixed component name rendering logic and added unit tests for it Signed-off-by: Zhan Milenkov --- .../components/envoy-proxy-component.ts | 2 +- .../remote/components/ha-proxy-component.ts | 2 +- .../remote/components/component-names.test.ts | 70 +++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/unit/core/config/remote/components/component-names.test.ts diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index 09773ad5a..c5128a1c0 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -9,7 +9,7 @@ import {type RemoteConfigManager} from '../remote-config-manager.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class EnvoyProxyComponent extends BaseComponent { - private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}}`; + private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}`; private constructor( name: ComponentName, diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 0f1cea679..119ba246b 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -9,7 +9,7 @@ import {type RemoteConfigManager} from '../remote-config-manager.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; export class HaProxyComponent extends BaseComponent { - private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}}`; + private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}`; private constructor( name: ComponentName, diff --git a/test/unit/core/config/remote/components/component-names.test.ts b/test/unit/core/config/remote/components/component-names.test.ts new file mode 100644 index 000000000..09d27e52e --- /dev/null +++ b/test/unit/core/config/remote/components/component-names.test.ts @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {expect} from 'chai'; +import {describe, it} from 'mocha'; +import {MirrorNodeComponent} from '../../../../../../src/core/config/remote/components/mirror-node-component.js'; +import {type ComponentName} from '../../../../../../src/core/config/remote/types.js'; +import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; +import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/components/envoy-proxy-component.js'; +import {HaProxyComponent} from '../../../../../../src/core/config/remote/components/ha-proxy-component.js'; +import {MirrorNodeExplorerComponent} from '../../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; +import {RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; +import {NodeAlias, type NodeAliases} from '../../../../../../src/types/aliases.js'; +import {Templates} from '../../../../../../src/core/templates.js'; + +describe('', () => { + const maxTestIndex = 10; + const nodeAliases: NodeAliases = Templates.renderNodeAliasesFromCount(maxTestIndex, 0); + + it('should create a valid component name for MirrorNodeComponent', () => { + for (let index: number = 0; index < maxTestIndex; index++) { + // @ts-expect-error - to access private method + const componentName: ComponentName = MirrorNodeComponent.renderMirrorNodeName(index); + expect(componentName).to.equal(`mirror-node-${index}`); + } + }); + + it('should create a valid component name for BlockNodeComponent', () => { + for (let index: number = 0; index < maxTestIndex; index++) { + // @ts-expect-error - to access private method + const componentName: ComponentName = BlockNodeComponent.renderBlockNodeName(index); + expect(componentName).to.equal(`block-node-${index}`); + } + }); + + it('should create a valid component name for EnvoyProxyComponent', () => { + for (let index: number = 0; index < maxTestIndex; index++) { + const nodeAlias: NodeAlias = nodeAliases[index]; + + // @ts-expect-error - to access private method + const componentName: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); + expect(componentName).to.equal(`envoy-proxy-${nodeAlias}-${index}`); + } + }); + + it('should create a valid component name for HaProxyComponent', () => { + for (let index: number = 0; index < maxTestIndex; index++) { + const nodeAlias: NodeAlias = nodeAliases[index]; + + // @ts-expect-error - to access private method + const componentName: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); + expect(componentName).to.equal(`haproxy-${nodeAlias}-${index}`); + } + }); + + it('should create a valid component name for MirrorNodeExplorerComponent', () => { + for (let index: number = 0; index < maxTestIndex; index++) { + // @ts-expect-error - to access private method + const componentName: ComponentName = MirrorNodeExplorerComponent.renderMirrorNodeExplorerName(index); + expect(componentName).to.equal(`mirror-node-explorer-${index}`); + } + }); + + it('should create a valid component name for RelayComponent', () => { + for (let index: number = 0; index < maxTestIndex; index++) { + // @ts-expect-error - to access private method + const componentName: ComponentName = RelayComponent.renderRelayName(index); + expect(componentName).to.equal(`relay-${index}`); + } + }); +}); From 81732475f76d39f3f495bf9b62b77c1e9d80b7b3 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 14:32:03 +0300 Subject: [PATCH 32/70] lint-fix Signed-off-by: Zhan Milenkov --- src/commands/node/handlers.ts | 2 +- test/unit/core/config/remote/components-data-wrapper.test.ts | 3 ++- .../core/config/remote/components/component-names.test.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 37ef3e80c..a326a2209 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -11,7 +11,7 @@ import {type Lock} from '../../core/lock/lock.js'; import {type NodeCommandTasks} from './tasks.js'; import {NodeSubcommandType} from '../../core/enumerations.js'; import {NodeHelper} from './helper.js'; -import {AnyListrContext, type ArgvStruct, type NodeAlias, type NodeAliases} from '../../types/aliases.js'; +import {type ArgvStruct, type NodeAlias, type NodeAliases} from '../../types/aliases.js'; import {ConsensusNodeComponent} from '../../core/config/remote/components/consensus-node-component.js'; import {type Listr} from 'listr2'; import chalk from 'chalk'; diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 72ca16cbb..aaaca24a4 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -16,7 +16,8 @@ import {ConsensusNodeStates} from '../../../../../src/core/config/remote/enumera import {ComponentStates} from '../../../../../src/core/config/remote/enumerations/component-states.js'; import {type NodeAliases} from '../../../../../src/types/aliases.js'; import { - type ClusterReference, ComponentName, + type ClusterReference, + type ComponentName, type ComponentsDataStructure, type NamespaceNameAsString, } from '../../../../../src/core/config/remote/types.js'; diff --git a/test/unit/core/config/remote/components/component-names.test.ts b/test/unit/core/config/remote/components/component-names.test.ts index 09d27e52e..2e7f43690 100644 --- a/test/unit/core/config/remote/components/component-names.test.ts +++ b/test/unit/core/config/remote/components/component-names.test.ts @@ -9,11 +9,11 @@ import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/comp import {HaProxyComponent} from '../../../../../../src/core/config/remote/components/ha-proxy-component.js'; import {MirrorNodeExplorerComponent} from '../../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; import {RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; -import {NodeAlias, type NodeAliases} from '../../../../../../src/types/aliases.js'; +import {type NodeAlias, type NodeAliases} from '../../../../../../src/types/aliases.js'; import {Templates} from '../../../../../../src/core/templates.js'; describe('', () => { - const maxTestIndex = 10; + const maxTestIndex: number = 10; const nodeAliases: NodeAliases = Templates.renderNodeAliasesFromCount(maxTestIndex, 0); it('should create a valid component name for MirrorNodeComponent', () => { From 25c34fde8c3f10f0e2c229e7dba45fcf149d6228 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 15:18:27 +0300 Subject: [PATCH 33/70] creating and moving new interfaces into their own files to remove circular dep Signed-off-by: Zhan Milenkov --- .../remote/api/components-data-wrapper-api.ts | 33 ++++++ .../remote/api/remote-config-manager-api.ts | 112 ++++++++++++++++++ .../config/remote/components-data-wrapper.ts | 18 +-- .../remote/components/base-component.ts | 7 +- .../remote/components/block-node-component.ts | 9 +- .../components/consensus-node-component.ts | 8 +- .../components/envoy-proxy-component.ts | 9 +- .../remote/components/ha-proxy-component.ts | 9 +- .../interface/base-component-structure.ts | 11 ++ .../consensus-node-component-structure.ts | 9 ++ .../interface/relay-component-structure.ts | 8 ++ .../components/mirror-node-component.ts | 9 +- .../mirror-node-explorer-component.ts | 9 +- .../remote/components/relay-component.ts | 13 +- .../config/remote/remote-config-manager.ts | 56 +-------- src/core/config/remote/types.ts | 22 +--- .../remote/components/components.test.ts | 18 ++- 17 files changed, 239 insertions(+), 121 deletions(-) create mode 100644 src/core/config/remote/api/components-data-wrapper-api.ts create mode 100644 src/core/config/remote/api/remote-config-manager-api.ts create mode 100644 src/core/config/remote/components/interface/base-component-structure.ts create mode 100644 src/core/config/remote/components/interface/consensus-node-component-structure.ts create mode 100644 src/core/config/remote/components/interface/relay-component-structure.ts diff --git a/src/core/config/remote/api/components-data-wrapper-api.ts b/src/core/config/remote/api/components-data-wrapper-api.ts new file mode 100644 index 000000000..c34ffaf13 --- /dev/null +++ b/src/core/config/remote/api/components-data-wrapper-api.ts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type ToObject, type Validate} from '../../../../types/index.js'; +import {type ClusterReference, type ComponentName, type ComponentsDataStructure} from '../types.js'; +import {type CloneTrait} from '../../../../types/traits/clone-trait.js'; +import {type BaseComponent} from '../components/base-component.js'; +import {type ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; +import {type ComponentTypes} from '../enumerations/component-types.js'; + +export interface ComponentsDataWrapperApi + extends Validate, + ToObject, + CloneTrait { + /** Used to add new component to their respective group. */ + addNewComponent(component: BaseComponent): void; + + changeNodeState(componentName: ComponentName, nodeState: ConsensusNodeStates): void; + + /** Used to remove specific component from their respective group. */ + disableComponent(componentName: ComponentName, type: ComponentTypes): void; + + getComponent(type: ComponentTypes, componentName: ComponentName): T; + + getComponentsByClusterReference( + type: ComponentTypes, + clusterReference: ClusterReference, + ): T[]; + + /** + * Checks all existing components of specified type and gives you a new unique index + */ + getNewComponentIndex(componentType: ComponentTypes): number; +} diff --git a/src/core/config/remote/api/remote-config-manager-api.ts b/src/core/config/remote/api/remote-config-manager-api.ts new file mode 100644 index 000000000..5b54aa568 --- /dev/null +++ b/src/core/config/remote/api/remote-config-manager-api.ts @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type RemoteConfigDataWrapper} from '../remote-config-data-wrapper.js'; +import {type ClusterReference, type ClusterReferences, type Context, type DeploymentName} from '../types.js'; +import {type ComponentsDataWrapper} from '../components-data-wrapper.js'; +import {type Cluster} from '../cluster.js'; +import {type AnyObject, type ArgvStruct, type NodeAliases} from '../../../../types/aliases.js'; +import {type DeploymentStates} from '../enumerations/deployment-states.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type ConsensusNode} from '../../../model/consensus-node.js'; +import {type ConfigMap} from '../../../../integration/kube/resources/config-map/config-map.js'; + +export interface RemoteConfigManagerApi { + /** @returns the default cluster from kubectl */ + get currentCluster(): ClusterReference; + + /** @returns the components data wrapper cloned */ + get components(): ComponentsDataWrapper; + + /** + * @returns the remote configuration data's clusters cloned + */ + get clusters(): Record; + + /** + * Modifies the loaded remote configuration data using a provided callback function. + * The callback operates on the configuration data, which is then saved to the cluster. + * + * @param callback - an async function that modifies the remote configuration data. + * @throws if the configuration is not loaded before modification, will throw a SoloError {@link SoloError} + */ + modify(callback: (remoteConfig: RemoteConfigDataWrapper) => Promise): Promise; + + /** + * Creates a new remote configuration in the Kubernetes cluster. + * Gathers data from the local configuration and constructs a new ConfigMap + * entry in the cluster with initial command history and metadata. + */ + create( + argv: ArgvStruct, + state: DeploymentStates, + nodeAliases: NodeAliases, + namespace: NamespaceName, + deployment: DeploymentName, + clusterReference: ClusterReference, + context: Context, + dnsBaseDomain: string, + dnsConsensusNodePattern: string, + ): Promise; + + /** + * Loads the remote configuration, performs a validation and returns it + * @returns RemoteConfigDataWrapper + */ + get(context?: Context): Promise; + + /** Unload the remote config from the remote config manager. */ + unload(): void; + + /** + * Performs the loading of the remote configuration. + * Checks if the configuration is already loaded, otherwise loads and adds the command to history. + * + * @param argv - arguments containing command input for historical reference. + * @param validate - whether to validate the remote configuration. + * @param [skipConsensusNodesValidation] - whether or not to validate the consensusNodes + */ + loadAndValidate( + argv: {_: string[]} & AnyObject, + validate: boolean, + skipConsensusNodesValidation: boolean, + ): Promise; + + /** Empties the component data inside the remote config */ + deleteComponents(): Promise; + + /** @returns if the remote config is already loaded */ + isLoaded(): boolean; + + /** + * Retrieves the ConfigMap containing the remote configuration from the Kubernetes cluster. + * + * @param namespace - The namespace to search for the ConfigMap. + * @param context - The context to use for the Kubernetes client. + * @returns the remote configuration data. + * @throws if the ConfigMap could not be read and the error is not a 404 status, will throw a SoloError {@link SoloError} + */ + getConfigMap(namespace?: NamespaceName, context?: Context): Promise; + + /** + * Creates a new ConfigMap entry in the Kubernetes cluster with the remote configuration data. + */ + createConfigMap(context?: Context): Promise; + + /** + * Get the consensus nodes from the remoteConfigManager and use the localConfig to get the context + * @returns an array of ConsensusNode objects + */ + getConsensusNodes(): ConsensusNode[]; + + /** + * Gets a list of distinct contexts from the consensus nodes. + * @returns an array of context strings. + */ + getContexts(): Context[]; + + /** + * Gets a list of distinct cluster references from the consensus nodes. + * @returns an object of cluster references. + */ + getClusterRefs(): ClusterReferences; +} diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 1f6a416a3..de1d9a886 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -11,11 +11,10 @@ import {ConsensusNodeComponent} from './components/consensus-node-component.js'; import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-component.js'; import { type ClusterReference, - type Component, type ComponentName, type ComponentsDataStructure, - type IConsensusNodeComponent, - type IRelayComponent, + + } from './types.js'; import {type ToObject, type Validate} from '../../../types/index.js'; import {type NodeAliases} from '../../../types/aliases.js'; @@ -25,6 +24,9 @@ import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {isValidEnum} from '../../util/validation-helpers.js'; +import {BaseComponentStructure} from './components/interface/base-component-structure.js'; +import {RelayComponentStructure} from './components/interface/relay-component-structure.js'; +import {ConsensusNodeComponentStructure} from './components/interface/consensus-node-component-structure.js'; /** * Represent the components in the remote config and handles: @@ -206,7 +208,7 @@ export class ComponentsDataWrapper switch (componentType) { case ComponentTypes.Relay: { for (const [componentName, component] of Object.entries(subComponents)) { - relays[componentName] = RelayComponent.fromObject(component as IRelayComponent); + relays[componentName] = RelayComponent.fromObject(component as RelayComponentStructure); } break; } @@ -234,7 +236,7 @@ export class ComponentsDataWrapper case ComponentTypes.ConsensusNode: { for (const [componentName, component] of Object.entries(subComponents)) { - consensusNodes[componentName] = ConsensusNodeComponent.fromObject(component as IConsensusNodeComponent); + consensusNodes[componentName] = ConsensusNodeComponent.fromObject(component as ConsensusNodeComponentStructure); } break; } @@ -351,11 +353,11 @@ export class ComponentsDataWrapper private transformComponentGroupToObject( components: Record, - ): Record { - const transformedComponents: Record = {}; + ): Record { + const transformedComponents: Record = {}; for (const [componentName, component] of Object.entries(components)) { - transformedComponents[componentName] = component.toObject() as Component; + transformedComponents[componentName] = component.toObject() as BaseComponentStructure; } return transformedComponents; diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 5b35cf483..ce84665d2 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -1,17 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 import {SoloError} from '../../../errors/solo-error.js'; -import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; +import {BaseComponentStructure} from './interface/base-component-structure.js'; /** * Represents the base structure and common functionality for all components within the system. * This class provides validation, comparison, and serialization functionality for components. */ -export class BaseComponent implements Component, Validate, ToObject { +export class BaseComponent implements BaseComponentStructure, Validate, ToObject { /** * @param type - type for identifying. * @param name - the name to distinguish components. @@ -64,7 +65,7 @@ export class BaseComponent implements Component, Validate, ToObject { } } - public toObject(): Component { + public toObject(): BaseComponentStructure { return { name: this.name, cluster: this.cluster, diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index 8247e8392..3509a1ec3 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {BaseComponentStructure} from './interface/base-component-structure.js'; export class BlockNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'block-node'; @@ -23,7 +24,7 @@ export class BlockNodeComponent extends BaseComponent { /* -------- Utilities -------- */ public static createNew( - remoteConfigManager: RemoteConfigManager, + remoteConfigManager: RemoteConfigManagerApi, clusterReference: ClusterReference, namespace: NamespaceName, ): BlockNodeComponent { @@ -35,7 +36,7 @@ export class BlockNodeComponent extends BaseComponent { } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: Component): BlockNodeComponent { + public static fromObject(component: BaseComponentStructure): BlockNodeComponent { const {name, cluster, namespace, state} = component; return new BlockNodeComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index 3065c7f9c..7a3072c7e 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -5,7 +5,6 @@ import {SoloError} from '../../../errors/solo-error.js'; import { type ClusterReference, type ComponentName, - type IConsensusNodeComponent, type NamespaceNameAsString, } from '../types.js'; import {type ToObject} from '../../../../types/index.js'; @@ -16,6 +15,7 @@ import {type NamespaceName} from '../../../../integration/kube/resources/namespa import {type NodeAlias, type NodeId} from '../../../../types/aliases.js'; import {Templates} from '../../../templates.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; +import {ConsensusNodeComponentStructure} from './interface/consensus-node-component-structure.js'; /** * Represents a consensus node component within the system. @@ -25,7 +25,7 @@ import {isValidEnum} from '../../../util/validation-helpers.js'; */ export class ConsensusNodeComponent extends BaseComponent - implements IConsensusNodeComponent, ToObject + implements ConsensusNodeComponentStructure, ToObject { private _nodeState: ConsensusNodeStates; @@ -79,7 +79,7 @@ export class ConsensusNodeComponent } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: IConsensusNodeComponent): ConsensusNodeComponent { + public static fromObject(component: ConsensusNodeComponentStructure): ConsensusNodeComponent { const {name, cluster, state, namespace, nodeState, nodeId} = component; return new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, nodeId); } @@ -100,7 +100,7 @@ export class ConsensusNodeComponent } } - public override toObject(): IConsensusNodeComponent { + public override toObject(): ConsensusNodeComponentStructure { return { ...super.toObject(), nodeState: this.nodeState, diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index c5128a1c0..56733a306 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -1,12 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; -import {type RemoteConfigManager} from '../remote-config-manager.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {BaseComponentStructure} from './interface/base-component-structure.js'; export class EnvoyProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}`; @@ -24,7 +25,7 @@ export class EnvoyProxyComponent extends BaseComponent { /* -------- Utilities -------- */ public static createNew( - remoteConfigManager: RemoteConfigManager, + remoteConfigManager: RemoteConfigManagerApi, clusterReference: ClusterReference, namespace: NamespaceName, nodeAlias: NodeAlias, @@ -37,7 +38,7 @@ export class EnvoyProxyComponent extends BaseComponent { } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: Component): EnvoyProxyComponent { + public static fromObject(component: BaseComponentStructure): EnvoyProxyComponent { const {name, cluster, namespace, state} = component; return new EnvoyProxyComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 119ba246b..d70a6ae52 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -1,12 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; -import {type RemoteConfigManager} from '../remote-config-manager.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {BaseComponentStructure} from './interface/base-component-structure.js'; export class HaProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}`; @@ -24,7 +25,7 @@ export class HaProxyComponent extends BaseComponent { /* -------- Utilities -------- */ public static createNew( - remoteConfigManager: RemoteConfigManager, + remoteConfigManager: RemoteConfigManagerApi, clusterReference: ClusterReference, namespace: NamespaceName, nodeAlias: NodeAlias, @@ -37,7 +38,7 @@ export class HaProxyComponent extends BaseComponent { } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: Component): HaProxyComponent { + public static fromObject(component: BaseComponentStructure): HaProxyComponent { const {name, cluster, namespace, state} = component; return new HaProxyComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/interface/base-component-structure.ts b/src/core/config/remote/components/interface/base-component-structure.ts new file mode 100644 index 000000000..cc0d186be --- /dev/null +++ b/src/core/config/remote/components/interface/base-component-structure.ts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type ComponentStates} from '../../enumerations/component-states.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../types.js'; + +export interface BaseComponentStructure { + name: ComponentName; + cluster: ClusterReference; + namespace: NamespaceNameAsString; + state: ComponentStates; +} \ No newline at end of file diff --git a/src/core/config/remote/components/interface/consensus-node-component-structure.ts b/src/core/config/remote/components/interface/consensus-node-component-structure.ts new file mode 100644 index 000000000..eba65740a --- /dev/null +++ b/src/core/config/remote/components/interface/consensus-node-component-structure.ts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type BaseComponentStructure} from './base-component-structure.js'; +import {type ConsensusNodeStates} from '../../enumerations/consensus-node-states.js'; + +export interface ConsensusNodeComponentStructure extends BaseComponentStructure { + nodeId: number; + nodeState: ConsensusNodeStates; +} diff --git a/src/core/config/remote/components/interface/relay-component-structure.ts b/src/core/config/remote/components/interface/relay-component-structure.ts new file mode 100644 index 000000000..e476dc9e4 --- /dev/null +++ b/src/core/config/remote/components/interface/relay-component-structure.ts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type BaseComponentStructure} from './base-component-structure.js'; +import {type NodeAliases} from '../../../../../types/aliases.js'; + +export interface RelayComponentStructure extends BaseComponentStructure { + consensusNodeAliases: NodeAliases; +} diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index 3b7ce332a..9d016656d 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -3,9 +3,10 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; -import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {BaseComponentStructure} from './interface/base-component-structure.js'; export class MirrorNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node'; @@ -23,7 +24,7 @@ export class MirrorNodeComponent extends BaseComponent { /* -------- Utilities -------- */ public static createNew( - remoteConfigManager: RemoteConfigManager, + remoteConfigManager: RemoteConfigManagerApi, clusterReference: ClusterReference, namespace: NamespaceName, ): MirrorNodeComponent { @@ -35,7 +36,7 @@ export class MirrorNodeComponent extends BaseComponent { } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: Component): MirrorNodeComponent { + public static fromObject(component: BaseComponentStructure): MirrorNodeComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 7bcf971a3..25e269c2a 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -3,9 +3,10 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; -import {type ClusterReference, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type RemoteConfigManager} from '../remote-config-manager.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {BaseComponentStructure} from './interface/base-component-structure.js'; export class MirrorNodeExplorerComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node-explorer'; @@ -23,7 +24,7 @@ export class MirrorNodeExplorerComponent extends BaseComponent { /* -------- Utilities -------- */ public static createNew( - remoteConfigManager: RemoteConfigManager, + remoteConfigManager: RemoteConfigManagerApi, clusterReference: ClusterReference, namespace: NamespaceName, ): MirrorNodeExplorerComponent { @@ -35,7 +36,7 @@ export class MirrorNodeExplorerComponent extends BaseComponent { } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: Component): MirrorNodeExplorerComponent { + public static fromObject(component: BaseComponentStructure): MirrorNodeExplorerComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeExplorerComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index cfd8c6649..a758035a3 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -4,13 +4,14 @@ import {SoloError} from '../../../errors/solo-error.js'; import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; -import {type ClusterReference, type ComponentName, type IRelayComponent, type NamespaceNameAsString} from '../types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; -import {type RemoteConfigManager} from '../remote-config-manager.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {RelayComponentStructure} from './interface/relay-component-structure.js'; -export class RelayComponent extends BaseComponent implements IRelayComponent, ToObject { +export class RelayComponent extends BaseComponent implements RelayComponentStructure, ToObject { private static readonly BASE_NAME: string = 'relay'; /** @@ -34,7 +35,7 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To /* -------- Utilities -------- */ public static createNew( - remoteConfigManager: RemoteConfigManager, + remoteConfigManager: RemoteConfigManagerApi, clusterReference: ClusterReference, namespace: NamespaceName, nodeAliases: NodeAliases, @@ -47,7 +48,7 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To } /** Handles creating instance of the class from plain object. */ - public static fromObject(component: IRelayComponent): RelayComponent { + public static fromObject(component: RelayComponentStructure): RelayComponent { const {name, cluster, namespace, state, consensusNodeAliases} = component; return new RelayComponent(name, cluster, namespace, state, consensusNodeAliases); } @@ -62,7 +63,7 @@ export class RelayComponent extends BaseComponent implements IRelayComponent, To } } - public override toObject(): IRelayComponent { + public override toObject(): RelayComponentStructure { return { consensusNodeAliases: this.consensusNodeAliases, ...super.toObject(), diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index 3791ff93f..63ea4043c 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -35,13 +35,14 @@ import {promptTheUserForDeployment, resolveNamespaceFromDeployment} from '../../ import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; import {getSoloVersion} from '../../../../version.js'; import {DeploymentStates} from './enumerations/deployment-states.js'; +import {type RemoteConfigManagerApi} from './api/remote-config-manager-api.js'; /** * Uses Kubernetes ConfigMaps to manage the remote configuration data by creating, loading, modifying, * and saving the configuration data to and from a Kubernetes cluster. */ @injectable() -export class RemoteConfigManager { +export class RemoteConfigManager implements RemoteConfigManagerApi { /** Stores the loaded remote configuration data. */ private remoteConfig: Optional; @@ -69,27 +70,16 @@ export class RemoteConfigManager { return this.k8Factory.default().clusters().readCurrent(); } - /** @returns the components data wrapper cloned */ public get components(): ComponentsDataWrapper { return this.remoteConfig.components.clone(); } - /** - * @returns the remote configuration data's clusters cloned - */ public get clusters(): Record { return structuredClone(this.remoteConfig.clusters); } /* ---------- Readers and Modifiers ---------- */ - /** - * Modifies the loaded remote configuration data using a provided callback function. - * The callback operates on the configuration data, which is then saved to the cluster. - * - * @param callback - an async function that modifies the remote configuration data. - * @throws if the configuration is not loaded before modification, will throw a SoloError {@link SoloError} - */ public async modify(callback: (remoteConfig: RemoteConfigDataWrapper) => Promise): Promise { if (!this.remoteConfig) { throw new SoloError('Attempting to modify remote config without loading it first'); @@ -102,11 +92,6 @@ export class RemoteConfigManager { await this.save(); } - /** - * Creates a new remote configuration in the Kubernetes cluster. - * Gathers data from the local configuration and constructs a new ConfigMap - * entry in the cluster with initial command history and metadata. - */ public async create( argv: ArgvStruct, state: DeploymentStates, @@ -176,10 +161,6 @@ export class RemoteConfigManager { } } - /** - * Loads the remote configuration, performs a validation and returns it - * @returns RemoteConfigDataWrapper - */ public async get(context?: Context): Promise { const namespace = this.configManager.getFlag(flags.namespace) ?? (await this.getNamespace()); @@ -201,7 +182,6 @@ export class RemoteConfigManager { return this.remoteConfig; } - /** Unload the remote config from the remote config manager. */ public unload(): void { this.remoteConfig = undefined; } @@ -225,14 +205,6 @@ export class RemoteConfigManager { /* ---------- Listr Task Builders ---------- */ - /** - * Performs the loading of the remote configuration. - * Checks if the configuration is already loaded, otherwise loads and adds the command to history. - * - * @param argv - arguments containing command input for historical reference. - * @param validate - whether to validate the remote configuration. - * @param [skipConsensusNodesValidation] - whether or not to validate the consensusNodes - */ public async loadAndValidate( argv: {_: string[]} & AnyObject, validate: boolean = true, @@ -324,7 +296,6 @@ export class RemoteConfigManager { /* ---------- Utilities ---------- */ - /** Empties the component data inside the remote config */ public async deleteComponents(): Promise { await this.modify(async remoteConfig => { remoteConfig.components = ComponentsDataWrapper.initializeEmpty(); @@ -335,14 +306,6 @@ export class RemoteConfigManager { return !!this.remoteConfig; } - /** - * Retrieves the ConfigMap containing the remote configuration from the Kubernetes cluster. - * - * @param namespace - The namespace to search for the ConfigMap. - * @param context - The context to use for the Kubernetes client. - * @returns the remote configuration data. - * @throws if the ConfigMap could not be read and the error is not a 404 status, will throw a SoloError {@link SoloError} - */ public async getConfigMap(namespace?: NamespaceName, context?: Context): Promise { if (!namespace) { namespace = await this.getNamespace(); @@ -370,9 +333,6 @@ export class RemoteConfigManager { } } - /** - * Creates a new ConfigMap entry in the Kubernetes cluster with the remote configuration data. - */ public async createConfigMap(context?: Context): Promise { const namespace = await this.getNamespace(); const name = constants.SOLO_REMOTE_CONFIGMAP_NAME; @@ -470,10 +430,6 @@ export class RemoteConfigManager { //* Common Commands - /** - * Get the consensus nodes from the remoteConfigManager and use the localConfig to get the context - * @returns an array of ConsensusNode objects - */ public getConsensusNodes(): ConsensusNode[] { if (!this.isLoaded()) { throw new SoloError('Remote configuration is not loaded, and was expected to be loaded'); @@ -512,18 +468,10 @@ export class RemoteConfigManager { return consensusNodes; } - /** - * Gets a list of distinct contexts from the consensus nodes. - * @returns an array of context strings. - */ public getContexts(): Context[] { return [...new Set(this.getConsensusNodes().map((node): Context => node.context))]; } - /** - * Gets a list of distinct cluster references from the consensus nodes. - * @returns an object of cluster references. - */ public getClusterRefs(): ClusterReferences { const nodes = this.getConsensusNodes(); const accumulator: ClusterReferences = {}; diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index fbb0702dc..46e9fcdcd 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -1,10 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 -import {type NodeAliases} from '../../../types/aliases.js'; import {type ComponentTypes} from './enumerations/component-types.js'; -import {type ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {type DeploymentStates} from './enumerations/deployment-states.js'; -import {type ComponentStates} from './enumerations/component-states.js'; +import {type BaseComponentStructure} from './components/interface/base-component-structure.js'; export type EmailAddress = `${string}@${string}.${string}`; export type Version = string; @@ -23,22 +21,6 @@ export interface IMigration { fromVersion: Version; } -export interface Component { - name: ComponentName; - cluster: ClusterReference; - namespace: NamespaceNameAsString; - state: ComponentStates; -} - -export interface IRelayComponent extends Component { - consensusNodeAliases: NodeAliases; -} - -export interface IConsensusNodeComponent extends Component { - nodeId: number; - nodeState: ConsensusNodeStates; -} - export interface ICluster { name: string; namespace: string; @@ -47,7 +29,7 @@ export interface ICluster { dnsConsensusNodePattern: string; } -export type ComponentsDataStructure = Record>; +export type ComponentsDataStructure = Record>; export type RemoteConfigCommonFlagsStruct = { releaseTag?: string; diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 0c25d7f5a..bf1c12710 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -17,12 +17,18 @@ import {ComponentStates} from '../../../../../../src/core/config/remote/enumerat import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; import { type ClusterReference, - type Component, type ComponentName, - type IConsensusNodeComponent, - type IRelayComponent, + + } from '../../../../../../src/core/config/remote/types.js'; import {NamespaceName} from '../../../../../../src/integration/kube/resources/namespace/namespace-name.js'; +import {BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interface/base-component-structure.js'; +import { + RelayComponentStructure +} from '../../../../../../src/core/config/remote/components/interface/relay-component-structure.js'; +import { + ConsensusNodeComponentStructure +} from '../../../../../../src/core/config/remote/components/interface/consensus-node-component-structure.js'; const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; @@ -37,7 +43,7 @@ function testBaseComponentData(classComponent: any): void { }); it('calling toObject() should return a valid data', () => { - const data: Component = { + const data: BaseComponentStructure = { name: componentName, cluster: clusterReference, namespace: namespace.name, @@ -80,7 +86,7 @@ describe('RelayComponent', () => { remoteConfigManagerMock.components.getNewComponentIndex(), ); - const values: IRelayComponent = { + const values: RelayComponentStructure = { name, cluster: clusterReference, namespace: namespace.name, @@ -120,7 +126,7 @@ describe('ConsensusNodeComponent', () => { it('calling toObject() should return a valid data', () => { const nodeAlias: NodeAlias = 'node1'; - const values: IConsensusNodeComponent = { + const values: ConsensusNodeComponentStructure = { name: nodeAlias, cluster: clusterReference, namespace: namespace.name, From fa2ecfe63dd4c2355ad6b63fc2453f0abb81adf8 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 15:34:29 +0300 Subject: [PATCH 34/70] lint-fix after moving interfaces into their own files Signed-off-by: Zhan Milenkov --- .../remote/api/components-data-wrapper-api.ts | 2 +- .../config/remote/components-data-wrapper.ts | 25 +++++++------------ .../remote/components/base-component.ts | 2 +- .../remote/components/block-node-component.ts | 2 +- .../components/consensus-node-component.ts | 8 ++---- .../components/envoy-proxy-component.ts | 2 +- .../remote/components/ha-proxy-component.ts | 2 +- .../interface/base-component-structure.ts | 2 +- .../components/mirror-node-component.ts | 2 +- .../mirror-node-explorer-component.ts | 2 +- .../remote/components/relay-component.ts | 7 ++++-- .../remote/components/components.test.ts | 17 +++---------- 12 files changed, 28 insertions(+), 45 deletions(-) diff --git a/src/core/config/remote/api/components-data-wrapper-api.ts b/src/core/config/remote/api/components-data-wrapper-api.ts index c34ffaf13..bf86fbb78 100644 --- a/src/core/config/remote/api/components-data-wrapper-api.ts +++ b/src/core/config/remote/api/components-data-wrapper-api.ts @@ -10,7 +10,7 @@ import {type ComponentTypes} from '../enumerations/component-types.js'; export interface ComponentsDataWrapperApi extends Validate, ToObject, - CloneTrait { + CloneTrait { /** Used to add new component to their respective group. */ addNewComponent(component: BaseComponent): void; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index de1d9a886..3c6c396e4 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -9,24 +9,17 @@ import {MirrorNodeComponent} from './components/mirror-node-component.js'; import {EnvoyProxyComponent} from './components/envoy-proxy-component.js'; import {ConsensusNodeComponent} from './components/consensus-node-component.js'; import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-component.js'; -import { - type ClusterReference, - type ComponentName, - type ComponentsDataStructure, - - -} from './types.js'; -import {type ToObject, type Validate} from '../../../types/index.js'; +import {type ClusterReference, type ComponentName, type ComponentsDataStructure} from './types.js'; import {type NodeAliases} from '../../../types/aliases.js'; -import {type CloneTrait} from '../../../types/traits/clone-trait.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {isValidEnum} from '../../util/validation-helpers.js'; -import {BaseComponentStructure} from './components/interface/base-component-structure.js'; -import {RelayComponentStructure} from './components/interface/relay-component-structure.js'; -import {ConsensusNodeComponentStructure} from './components/interface/consensus-node-component-structure.js'; +import {type BaseComponentStructure} from './components/interface/base-component-structure.js'; +import {type RelayComponentStructure} from './components/interface/relay-component-structure.js'; +import {type ConsensusNodeComponentStructure} from './components/interface/consensus-node-component-structure.js'; +import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; /** * Represent the components in the remote config and handles: @@ -34,9 +27,7 @@ import {ConsensusNodeComponentStructure} from './components/interface/consensus- * - Validation. * - Conversion FROM and TO plain object. */ -export class ComponentsDataWrapper - implements Validate, ToObject, CloneTrait -{ +export class ComponentsDataWrapper implements ComponentsDataWrapperApi { private constructor( public readonly relays: Record = {}, public readonly haProxies: Record = {}, @@ -236,7 +227,9 @@ export class ComponentsDataWrapper case ComponentTypes.ConsensusNode: { for (const [componentName, component] of Object.entries(subComponents)) { - consensusNodes[componentName] = ConsensusNodeComponent.fromObject(component as ConsensusNodeComponentStructure); + consensusNodes[componentName] = ConsensusNodeComponent.fromObject( + component as ConsensusNodeComponentStructure, + ); } break; } diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index ce84665d2..115dae0ef 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -6,7 +6,7 @@ import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; -import {BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interface/base-component-structure.js'; /** * Represents the base structure and common functionality for all components within the system. diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index 3509a1ec3..c65734dde 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -6,7 +6,7 @@ import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interface/base-component-structure.js'; export class BlockNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'block-node'; diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index 7a3072c7e..aec329dba 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -2,11 +2,7 @@ import {BaseComponent} from './base-component.js'; import {SoloError} from '../../../errors/solo-error.js'; -import { - type ClusterReference, - type ComponentName, - type NamespaceNameAsString, -} from '../types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; @@ -15,7 +11,7 @@ import {type NamespaceName} from '../../../../integration/kube/resources/namespa import {type NodeAlias, type NodeId} from '../../../../types/aliases.js'; import {Templates} from '../../../templates.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; -import {ConsensusNodeComponentStructure} from './interface/consensus-node-component-structure.js'; +import {type ConsensusNodeComponentStructure} from './interface/consensus-node-component-structure.js'; /** * Represents a consensus node component within the system. diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index 56733a306..80964ffad 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -7,7 +7,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interface/base-component-structure.js'; export class EnvoyProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}`; diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index d70a6ae52..36b4de1c2 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -7,7 +7,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interface/base-component-structure.js'; export class HaProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}`; diff --git a/src/core/config/remote/components/interface/base-component-structure.ts b/src/core/config/remote/components/interface/base-component-structure.ts index cc0d186be..3904bf917 100644 --- a/src/core/config/remote/components/interface/base-component-structure.ts +++ b/src/core/config/remote/components/interface/base-component-structure.ts @@ -8,4 +8,4 @@ export interface BaseComponentStructure { cluster: ClusterReference; namespace: NamespaceNameAsString; state: ComponentStates; -} \ No newline at end of file +} diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index 9d016656d..adc1c6cf2 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -6,7 +6,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interface/base-component-structure.js'; export class MirrorNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node'; diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 25e269c2a..47e9d6225 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -6,7 +6,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interface/base-component-structure.js'; export class MirrorNodeExplorerComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node-explorer'; diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index a758035a3..484eaee52 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -9,9 +9,12 @@ import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {RelayComponentStructure} from './interface/relay-component-structure.js'; +import {type RelayComponentStructure} from './interface/relay-component-structure.js'; -export class RelayComponent extends BaseComponent implements RelayComponentStructure, ToObject { +export class RelayComponent + extends BaseComponent + implements RelayComponentStructure, ToObject +{ private static readonly BASE_NAME: string = 'relay'; /** diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index bf1c12710..7366e478e 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -15,20 +15,11 @@ import {Templates} from '../../../../../../src/core/templates.js'; import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; import {ComponentStates} from '../../../../../../src/core/config/remote/enumerations/component-states.js'; import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; -import { - type ClusterReference, - type ComponentName, - - -} from '../../../../../../src/core/config/remote/types.js'; +import {type ClusterReference, type ComponentName} from '../../../../../../src/core/config/remote/types.js'; import {NamespaceName} from '../../../../../../src/integration/kube/resources/namespace/namespace-name.js'; -import {BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interface/base-component-structure.js'; -import { - RelayComponentStructure -} from '../../../../../../src/core/config/remote/components/interface/relay-component-structure.js'; -import { - ConsensusNodeComponentStructure -} from '../../../../../../src/core/config/remote/components/interface/consensus-node-component-structure.js'; +import {type BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interface/base-component-structure.js'; +import {type RelayComponentStructure} from '../../../../../../src/core/config/remote/components/interface/relay-component-structure.js'; +import {type ConsensusNodeComponentStructure} from '../../../../../../src/core/config/remote/components/interface/consensus-node-component-structure.js'; const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; From 9de8fb1daa3645d4fef1f968ba0bf4e782a035d3 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 15:40:09 +0300 Subject: [PATCH 35/70] renamed folder Signed-off-by: Zhan Milenkov --- src/core/config/remote/components-data-wrapper.ts | 6 +++--- src/core/config/remote/components/base-component.ts | 2 +- src/core/config/remote/components/block-node-component.ts | 2 +- .../config/remote/components/consensus-node-component.ts | 2 +- src/core/config/remote/components/envoy-proxy-component.ts | 2 +- src/core/config/remote/components/ha-proxy-component.ts | 2 +- .../{interface => interfaces}/base-component-structure.ts | 0 .../consensus-node-component-structure.ts | 0 .../{interface => interfaces}/relay-component-structure.ts | 0 src/core/config/remote/components/mirror-node-component.ts | 2 +- .../remote/components/mirror-node-explorer-component.ts | 2 +- src/core/config/remote/components/relay-component.ts | 2 +- src/core/config/remote/types.ts | 2 +- test/unit/core/config/remote/components/components.test.ts | 6 +++--- 14 files changed, 15 insertions(+), 15 deletions(-) rename src/core/config/remote/components/{interface => interfaces}/base-component-structure.ts (100%) rename src/core/config/remote/components/{interface => interfaces}/consensus-node-component-structure.ts (100%) rename src/core/config/remote/components/{interface => interfaces}/relay-component-structure.ts (100%) diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 3c6c396e4..2ba56f659 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -16,9 +16,9 @@ import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {isValidEnum} from '../../util/validation-helpers.js'; -import {type BaseComponentStructure} from './components/interface/base-component-structure.js'; -import {type RelayComponentStructure} from './components/interface/relay-component-structure.js'; -import {type ConsensusNodeComponentStructure} from './components/interface/consensus-node-component-structure.js'; +import {type BaseComponentStructure} from './components/interfaces/base-component-structure.js'; +import {type RelayComponentStructure} from './components/interfaces/relay-component-structure.js'; +import {type ConsensusNodeComponentStructure} from './components/interfaces/consensus-node-component-structure.js'; import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; /** diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 115dae0ef..6c66fe79c 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -6,7 +6,7 @@ import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; -import {type BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; /** * Represents the base structure and common functionality for all components within the system. diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index c65734dde..9b217e689 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -6,7 +6,7 @@ import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {type BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class BlockNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'block-node'; diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index aec329dba..2f45d1414 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -11,7 +11,7 @@ import {type NamespaceName} from '../../../../integration/kube/resources/namespa import {type NodeAlias, type NodeId} from '../../../../types/aliases.js'; import {Templates} from '../../../templates.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; -import {type ConsensusNodeComponentStructure} from './interface/consensus-node-component-structure.js'; +import {type ConsensusNodeComponentStructure} from './interfaces/consensus-node-component-structure.js'; /** * Represents a consensus node component within the system. diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index 80964ffad..ea59d726f 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -7,7 +7,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {type BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class EnvoyProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}`; diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 36b4de1c2..91e7e387d 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -7,7 +7,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type NodeAlias} from '../../../../types/aliases.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {type BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class HaProxyComponent extends BaseComponent { private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}`; diff --git a/src/core/config/remote/components/interface/base-component-structure.ts b/src/core/config/remote/components/interfaces/base-component-structure.ts similarity index 100% rename from src/core/config/remote/components/interface/base-component-structure.ts rename to src/core/config/remote/components/interfaces/base-component-structure.ts diff --git a/src/core/config/remote/components/interface/consensus-node-component-structure.ts b/src/core/config/remote/components/interfaces/consensus-node-component-structure.ts similarity index 100% rename from src/core/config/remote/components/interface/consensus-node-component-structure.ts rename to src/core/config/remote/components/interfaces/consensus-node-component-structure.ts diff --git a/src/core/config/remote/components/interface/relay-component-structure.ts b/src/core/config/remote/components/interfaces/relay-component-structure.ts similarity index 100% rename from src/core/config/remote/components/interface/relay-component-structure.ts rename to src/core/config/remote/components/interfaces/relay-component-structure.ts diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index adc1c6cf2..0aa518434 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -6,7 +6,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {type BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class MirrorNodeComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node'; diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 47e9d6225..023f11ec1 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -6,7 +6,7 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {type BaseComponentStructure} from './interface/base-component-structure.js'; +import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class MirrorNodeExplorerComponent extends BaseComponent { private static readonly BASE_NAME: string = 'mirror-node-explorer'; diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index 484eaee52..f4ee95afc 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -9,7 +9,7 @@ import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; -import {type RelayComponentStructure} from './interface/relay-component-structure.js'; +import {type RelayComponentStructure} from './interfaces/relay-component-structure.js'; export class RelayComponent extends BaseComponent diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index 46e9fcdcd..d2290018a 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -2,7 +2,7 @@ import {type ComponentTypes} from './enumerations/component-types.js'; import {type DeploymentStates} from './enumerations/deployment-states.js'; -import {type BaseComponentStructure} from './components/interface/base-component-structure.js'; +import {type BaseComponentStructure} from './components/interfaces/base-component-structure.js'; export type EmailAddress = `${string}@${string}.${string}`; export type Version = string; diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 7366e478e..015ce3249 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -17,9 +17,9 @@ import {ComponentStates} from '../../../../../../src/core/config/remote/enumerat import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; import {type ClusterReference, type ComponentName} from '../../../../../../src/core/config/remote/types.js'; import {NamespaceName} from '../../../../../../src/integration/kube/resources/namespace/namespace-name.js'; -import {type BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interface/base-component-structure.js'; -import {type RelayComponentStructure} from '../../../../../../src/core/config/remote/components/interface/relay-component-structure.js'; -import {type ConsensusNodeComponentStructure} from '../../../../../../src/core/config/remote/components/interface/consensus-node-component-structure.js'; +import {type BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/base-component-structure.js'; +import {type RelayComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/relay-component-structure.js'; +import {type ConsensusNodeComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/consensus-node-component-structure.js'; const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; From 65813b21d5239053c3c078eacd6ea68d53a7df6c Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 15:45:26 +0300 Subject: [PATCH 36/70] renamed ICluster to ClusterStructure and moved it into it's own file Signed-off-by: Zhan Milenkov --- src/core/config/remote/cluster.ts | 11 ++++++----- .../config/remote/interfaces/cluster-structure.ts | 11 +++++++++++ src/core/config/remote/types.ts | 11 ++--------- test/unit/core/config/remote/cluster.test.ts | 11 ++++++----- 4 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 src/core/config/remote/interfaces/cluster-structure.ts diff --git a/src/core/config/remote/cluster.ts b/src/core/config/remote/cluster.ts index 9149d82e8..8f0fc1a44 100644 --- a/src/core/config/remote/cluster.ts +++ b/src/core/config/remote/cluster.ts @@ -1,10 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 import {type ToObject} from '../../../types/index.js'; -import {type ClusterReference, type DeploymentName, type ICluster, type NamespaceNameAsString} from './types.js'; +import {type ClusterReference, type DeploymentName, type NamespaceNameAsString} from './types.js'; import {SoloError} from '../../errors/solo-error.js'; +import {type ClusterStructure} from './interfaces/cluster-structure.js'; -export class Cluster implements ICluster, ToObject { +export class Cluster implements ClusterStructure, ToObject { public constructor( public readonly name: string, public readonly namespace: NamespaceNameAsString, @@ -37,7 +38,7 @@ export class Cluster implements ICluster, ToObject { } } - public toObject(): ICluster { + public toObject(): ClusterStructure { return { name: this.name, namespace: this.namespace, @@ -47,7 +48,7 @@ export class Cluster implements ICluster, ToObject { }; } - public static fromObject(cluster: ICluster) { + public static fromObject(cluster: ClusterStructure) { return new Cluster( cluster.name, cluster.namespace, @@ -66,7 +67,7 @@ export class Cluster implements ICluster, ToObject { const clusters: Record = {}; for (const [reference, cluster] of Object.entries(object)) { - clusters[reference] = Cluster.fromObject(cluster as ICluster); + clusters[reference] = Cluster.fromObject(cluster as ClusterStructure); } return clusters; diff --git a/src/core/config/remote/interfaces/cluster-structure.ts b/src/core/config/remote/interfaces/cluster-structure.ts new file mode 100644 index 000000000..61f7c9cd8 --- /dev/null +++ b/src/core/config/remote/interfaces/cluster-structure.ts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type DeploymentName} from '../types.js'; + +export interface ClusterStructure { + name: string; + namespace: string; + deployment: DeploymentName; + dnsBaseDomain: string; + dnsConsensusNodePattern: string; +} diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index d2290018a..0c95bbcfb 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -3,6 +3,7 @@ import {type ComponentTypes} from './enumerations/component-types.js'; import {type DeploymentStates} from './enumerations/deployment-states.js'; import {type BaseComponentStructure} from './components/interfaces/base-component-structure.js'; +import {type ClusterStructure} from './interfaces/cluster-structure.js'; export type EmailAddress = `${string}@${string}.${string}`; export type Version = string; @@ -21,14 +22,6 @@ export interface IMigration { fromVersion: Version; } -export interface ICluster { - name: string; - namespace: string; - deployment: DeploymentName; - dnsBaseDomain: string; - dnsConsensusNodePattern: string; -} - export type ComponentsDataStructure = Record>; export type RemoteConfigCommonFlagsStruct = { @@ -44,7 +37,7 @@ export type RemoteConfigCommonFlagsStruct = { export interface RemoteConfigDataStructure { metadata: RemoteConfigMetadataStructure; version: Version; - clusters: Record; + clusters: Record; components: ComponentsDataStructure; commandHistory: string[]; lastExecutedCommand: string; diff --git a/test/unit/core/config/remote/cluster.test.ts b/test/unit/core/config/remote/cluster.test.ts index afcd46a9c..91b1db16f 100644 --- a/test/unit/core/config/remote/cluster.test.ts +++ b/test/unit/core/config/remote/cluster.test.ts @@ -3,11 +3,12 @@ import {it} from 'mocha'; import {expect} from 'chai'; import {Cluster} from '../../../../../src/core/config/remote/cluster.js'; -import {type ClusterReference, type ICluster} from '../../../../../src/core/config/remote/types.js'; +import {type ClusterReference} from '../../../../../src/core/config/remote/types.js'; +import {type ClusterStructure} from '../../../../../src/core/config/remote/interfaces/cluster-structure.js'; describe('Cluster', () => { it('should convert to an object', () => { - const clusterData: ICluster = { + const clusterData: ClusterStructure = { name: 'name', namespace: 'namespace', deployment: 'deployment', @@ -23,7 +24,7 @@ describe('Cluster', () => { clusterData.dnsConsensusNodePattern, ); - const clusterObject: ICluster = cluster.toObject(); + const clusterObject: ClusterStructure = cluster.toObject(); expect(clusterObject.name).to.equal(clusterData.name); expect(clusterObject.namespace).to.equal(clusterData.namespace); expect(clusterObject.deployment).to.equal(clusterData.deployment); @@ -32,7 +33,7 @@ describe('Cluster', () => { }); it('should convert clusters map to an object', () => { - const clusterData1: ICluster = { + const clusterData1: ClusterStructure = { name: 'name1', namespace: 'namespace1', deployment: 'deployment1', @@ -40,7 +41,7 @@ describe('Cluster', () => { dnsConsensusNodePattern: 'network1.svc', }; - const clusterData2: ICluster = { + const clusterData2: ClusterStructure = { name: 'name2', namespace: 'namespace2', deployment: 'deployment2', From 79059d77ccd9585ce9048b9ef0b379954da5a4ce Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 15:55:20 +0300 Subject: [PATCH 37/70] renamed and moved to their own dedicated file remote config interfaces to remove circular dependency Signed-off-by: Zhan Milenkov --- .../remote/api/components-data-wrapper-api.ts | 3 +- .../remote/common-flags-data-wrapper.ts | 2 +- .../config/remote/components-data-wrapper.ts | 3 +- .../interfaces/components-data-structure.ts | 7 +++ .../remote/interfaces/migration-structure.ts | 9 ++++ .../remote-config-common-flags-struct.ts | 11 +++++ .../remote-config-data-structure.ts | 17 +++++++ .../remote-config-metadata-structure.ts | 20 ++++++++ src/core/config/remote/metadata.ts | 9 +--- src/core/config/remote/migration.ts | 7 +-- .../remote/remote-config-data-wrapper.ts | 3 +- src/core/config/remote/types.ts | 48 ------------------- .../remote/components-data-wrapper.test.ts | 2 +- test/unit/core/config/remote/metadata.test.ts | 3 +- .../unit/core/config/remote/migration.test.ts | 5 +- .../remote/remote-config-data-wrapper.test.ts | 2 +- 16 files changed, 84 insertions(+), 67 deletions(-) create mode 100644 src/core/config/remote/interfaces/components-data-structure.ts create mode 100644 src/core/config/remote/interfaces/migration-structure.ts create mode 100644 src/core/config/remote/interfaces/remote-config-common-flags-struct.ts create mode 100644 src/core/config/remote/interfaces/remote-config-data-structure.ts create mode 100644 src/core/config/remote/interfaces/remote-config-metadata-structure.ts diff --git a/src/core/config/remote/api/components-data-wrapper-api.ts b/src/core/config/remote/api/components-data-wrapper-api.ts index bf86fbb78..6678311db 100644 --- a/src/core/config/remote/api/components-data-wrapper-api.ts +++ b/src/core/config/remote/api/components-data-wrapper-api.ts @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 import {type ToObject, type Validate} from '../../../../types/index.js'; -import {type ClusterReference, type ComponentName, type ComponentsDataStructure} from '../types.js'; +import {type ClusterReference, type ComponentName} from '../types.js'; import {type CloneTrait} from '../../../../types/traits/clone-trait.js'; import {type BaseComponent} from '../components/base-component.js'; import {type ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; import {type ComponentTypes} from '../enumerations/component-types.js'; +import {type ComponentsDataStructure} from '../interfaces/components-data-structure.js'; export interface ComponentsDataWrapperApi extends Validate, diff --git a/src/core/config/remote/common-flags-data-wrapper.ts b/src/core/config/remote/common-flags-data-wrapper.ts index 66bb0c16d..d80f02b6c 100644 --- a/src/core/config/remote/common-flags-data-wrapper.ts +++ b/src/core/config/remote/common-flags-data-wrapper.ts @@ -2,11 +2,11 @@ import {Flags as flags} from '../../../commands/flags.js'; import {type ToObject} from '../../../types/index.js'; -import {type RemoteConfigCommonFlagsStruct} from './types.js'; import {type ConfigManager} from '../../config-manager.js'; import {type CommandFlag} from '../../../types/flag-types.js'; import {type AnyObject} from '../../../types/aliases.js'; import {select as selectPrompt} from '@inquirer/prompts'; +import {type RemoteConfigCommonFlagsStruct} from './interfaces/remote-config-common-flags-struct.js'; export class CommonFlagsDataWrapper implements ToObject { private static readonly COMMON_FLAGS: CommandFlag[] = [ diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 2ba56f659..a5c548fec 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -9,7 +9,7 @@ import {MirrorNodeComponent} from './components/mirror-node-component.js'; import {EnvoyProxyComponent} from './components/envoy-proxy-component.js'; import {ConsensusNodeComponent} from './components/consensus-node-component.js'; import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-component.js'; -import {type ClusterReference, type ComponentName, type ComponentsDataStructure} from './types.js'; +import {type ClusterReference, type ComponentName} from './types.js'; import {type NodeAliases} from '../../../types/aliases.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; @@ -20,6 +20,7 @@ import {type BaseComponentStructure} from './components/interfaces/base-componen import {type RelayComponentStructure} from './components/interfaces/relay-component-structure.js'; import {type ConsensusNodeComponentStructure} from './components/interfaces/consensus-node-component-structure.js'; import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; +import {type ComponentsDataStructure} from './interfaces/components-data-structure.js'; /** * Represent the components in the remote config and handles: diff --git a/src/core/config/remote/interfaces/components-data-structure.ts b/src/core/config/remote/interfaces/components-data-structure.ts new file mode 100644 index 000000000..9aa674a35 --- /dev/null +++ b/src/core/config/remote/interfaces/components-data-structure.ts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type ComponentTypes} from '../enumerations/component-types.js'; +import {type BaseComponentStructure} from '../components/interfaces/base-component-structure.js'; +import {type ComponentName} from '../types.js'; + +export type ComponentsDataStructure = Record>; diff --git a/src/core/config/remote/interfaces/migration-structure.ts b/src/core/config/remote/interfaces/migration-structure.ts new file mode 100644 index 000000000..2f215a7da --- /dev/null +++ b/src/core/config/remote/interfaces/migration-structure.ts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type EmailAddress, type Version} from '../types.js'; + +export interface MigrationStructure { + migratedAt: Date; + migratedBy: EmailAddress; + fromVersion: Version; +} diff --git a/src/core/config/remote/interfaces/remote-config-common-flags-struct.ts b/src/core/config/remote/interfaces/remote-config-common-flags-struct.ts new file mode 100644 index 000000000..a8474472d --- /dev/null +++ b/src/core/config/remote/interfaces/remote-config-common-flags-struct.ts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +export type RemoteConfigCommonFlagsStruct = { + releaseTag?: string; + chartDirectory?: string; + relayReleaseTag?: string; + soloChartVersion?: string; + mirrorNodeVersion?: string; + nodeAliasesUnparsed?: string; + hederaExplorerVersion?: string; +}; diff --git a/src/core/config/remote/interfaces/remote-config-data-structure.ts b/src/core/config/remote/interfaces/remote-config-data-structure.ts new file mode 100644 index 000000000..a525b05db --- /dev/null +++ b/src/core/config/remote/interfaces/remote-config-data-structure.ts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type ClusterStructure} from './cluster-structure.js'; +import {type RemoteConfigCommonFlagsStruct} from './remote-config-common-flags-struct.js'; +import {type ClusterReference, type Version} from '../types.js'; +import {type RemoteConfigMetadataStructure} from './remote-config-metadata-structure.js'; +import {type ComponentsDataStructure} from './components-data-structure.js'; + +export interface RemoteConfigDataStructure { + metadata: RemoteConfigMetadataStructure; + version: Version; + clusters: Record; + components: ComponentsDataStructure; + commandHistory: string[]; + lastExecutedCommand: string; + flags: RemoteConfigCommonFlagsStruct; +} diff --git a/src/core/config/remote/interfaces/remote-config-metadata-structure.ts b/src/core/config/remote/interfaces/remote-config-metadata-structure.ts new file mode 100644 index 000000000..dd5c8618e --- /dev/null +++ b/src/core/config/remote/interfaces/remote-config-metadata-structure.ts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type DeploymentStates} from '../enumerations/deployment-states.js'; +import {type MigrationStructure} from './migration-structure.js'; +import {type DeploymentName, type EmailAddress, type NamespaceNameAsString, type Version} from '../types.js'; + +export interface RemoteConfigMetadataStructure { + namespace: NamespaceNameAsString; + state: DeploymentStates; + deploymentName: DeploymentName; + lastUpdatedAt: Date; + lastUpdateBy: EmailAddress; + soloVersion: Version; + soloChartVersion: Version; + hederaPlatformVersion: Version; + hederaMirrorNodeChartVersion: Version; + hederaExplorerChartVersion: Version; + hederaJsonRpcRelayChartVersion: Version; + migration?: MigrationStructure; +} diff --git a/src/core/config/remote/metadata.ts b/src/core/config/remote/metadata.ts index a649181a2..6eccd2739 100644 --- a/src/core/config/remote/metadata.ts +++ b/src/core/config/remote/metadata.ts @@ -2,17 +2,12 @@ import {Migration} from './migration.js'; import {SoloError} from '../../errors/solo-error.js'; -import { - type DeploymentName, - type EmailAddress, - type NamespaceNameAsString, - type RemoteConfigMetadataStructure, - type Version, -} from './types.js'; +import {type DeploymentName, type EmailAddress, type NamespaceNameAsString, type Version} from './types.js'; import {type Optional, type ToObject, type Validate} from '../../../types/index.js'; import {DeploymentStates} from './enumerations/deployment-states.js'; import {isValidEnum} from '../../util/validation-helpers.js'; +import {type RemoteConfigMetadataStructure} from './interfaces/remote-config-metadata-structure.js'; /** * Represent the remote config metadata object and handles: diff --git a/src/core/config/remote/migration.ts b/src/core/config/remote/migration.ts index 723d51010..312546b41 100644 --- a/src/core/config/remote/migration.ts +++ b/src/core/config/remote/migration.ts @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 import {SoloError} from '../../errors/solo-error.js'; -import {type EmailAddress, type IMigration, type Version} from './types.js'; +import {type EmailAddress, type Version} from './types.js'; +import {type MigrationStructure} from './interfaces/migration-structure.js'; -export class Migration implements IMigration { +export class Migration implements MigrationStructure { private readonly _migratedAt: Date; private readonly _migratedBy: EmailAddress; private readonly _fromVersion: Version; @@ -43,7 +44,7 @@ export class Migration implements IMigration { } } - public toObject(): IMigration { + public toObject(): MigrationStructure { return { migratedAt: this.migratedAt, migratedBy: this.migratedBy, diff --git a/src/core/config/remote/remote-config-data-wrapper.ts b/src/core/config/remote/remote-config-data-wrapper.ts index f7c3cc784..9bfa54c83 100644 --- a/src/core/config/remote/remote-config-data-wrapper.ts +++ b/src/core/config/remote/remote-config-data-wrapper.ts @@ -6,12 +6,13 @@ import {RemoteConfigMetadata} from './metadata.js'; import {ComponentsDataWrapper} from './components-data-wrapper.js'; import * as constants from '../../constants.js'; import {CommonFlagsDataWrapper} from './common-flags-data-wrapper.js'; -import {type ClusterReference, type RemoteConfigDataStructure, type Version} from './types.js'; +import {type ClusterReference, type Version} from './types.js'; import {type ToObject, type Validate} from '../../../types/index.js'; import {type ConfigManager} from '../../config-manager.js'; import {type RemoteConfigData} from './remote-config-data.js'; import {Cluster} from './cluster.js'; import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; +import {type RemoteConfigDataStructure} from './interfaces/remote-config-data-structure.js'; export class RemoteConfigDataWrapper implements Validate, ToObject { private readonly _version: Version = '1.0.0'; diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index 0c95bbcfb..e7a3fdb9e 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -1,10 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -import {type ComponentTypes} from './enumerations/component-types.js'; -import {type DeploymentStates} from './enumerations/deployment-states.js'; -import {type BaseComponentStructure} from './components/interfaces/base-component-structure.js'; -import {type ClusterStructure} from './interfaces/cluster-structure.js'; - export type EmailAddress = `${string}@${string}.${string}`; export type Version = string; /// TODO - see if we can use NamespaceName and use some annotations and overrides to covert to strings @@ -15,46 +10,3 @@ export type ComponentName = string; export type ClusterReference = string; export type ClusterReferences = Record; - -export interface IMigration { - migratedAt: Date; - migratedBy: EmailAddress; - fromVersion: Version; -} - -export type ComponentsDataStructure = Record>; - -export type RemoteConfigCommonFlagsStruct = { - releaseTag?: string; - chartDirectory?: string; - relayReleaseTag?: string; - soloChartVersion?: string; - mirrorNodeVersion?: string; - nodeAliasesUnparsed?: string; - hederaExplorerVersion?: string; -}; - -export interface RemoteConfigDataStructure { - metadata: RemoteConfigMetadataStructure; - version: Version; - clusters: Record; - components: ComponentsDataStructure; - commandHistory: string[]; - lastExecutedCommand: string; - flags: RemoteConfigCommonFlagsStruct; -} - -export interface RemoteConfigMetadataStructure { - namespace: NamespaceNameAsString; - state: DeploymentStates; - deploymentName: DeploymentName; - lastUpdatedAt: Date; - lastUpdateBy: EmailAddress; - soloVersion: Version; - soloChartVersion: Version; - hederaPlatformVersion: Version; - hederaMirrorNodeChartVersion: Version; - hederaExplorerChartVersion: Version; - hederaJsonRpcRelayChartVersion: Version; - migration?: IMigration; -} diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index aaaca24a4..e7bd16e28 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -18,10 +18,10 @@ import {type NodeAliases} from '../../../../../src/types/aliases.js'; import { type ClusterReference, type ComponentName, - type ComponentsDataStructure, type NamespaceNameAsString, } from '../../../../../src/core/config/remote/types.js'; import {BlockNodeComponent} from '../../../../../src/core/config/remote/components/block-node-component.js'; +import {type ComponentsDataStructure} from '../../../../../src/core/config/remote/interfaces/components-data-structure.js'; export function createComponentsDataWrapper(): { values: { diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index b8e8a4ffd..e9718b175 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -5,8 +5,9 @@ import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {RemoteConfigMetadata} from '../../../../../src/core/config/remote/metadata.js'; -import {type EmailAddress, type RemoteConfigMetadataStructure} from '../../../../../src/core/config/remote/types.js'; +import {type EmailAddress} from '../../../../../src/core/config/remote/types.js'; import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; +import {type RemoteConfigMetadataStructure} from '../../../../../src/core/config/remote/interfaces/remote-config-metadata-structure.js'; interface MetadataTestStructure { metadata: RemoteConfigMetadata; diff --git a/test/unit/core/config/remote/migration.test.ts b/test/unit/core/config/remote/migration.test.ts index 3f96bb261..701a5aaf1 100644 --- a/test/unit/core/config/remote/migration.test.ts +++ b/test/unit/core/config/remote/migration.test.ts @@ -3,10 +3,11 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; -import {type IMigration} from '../../../../../src/core/config/remote/types.js'; + +import {type MigrationStructure} from '../../../../../src/core/config/remote/interfaces/migration-structure.js'; describe('Migration', () => { - const values: IMigration = {migratedAt: new Date(), migratedBy: 'test@test.test', fromVersion: '1.0.0'}; + const values: MigrationStructure = {migratedAt: new Date(), migratedBy: 'test@test.test', fromVersion: '1.0.0'}; let migration: Migration; beforeEach(() => { diff --git a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts index 8947757b2..c9362b67a 100644 --- a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts +++ b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts @@ -9,8 +9,8 @@ import {createMetadata} from './metadata.test.js'; import {createComponentsDataWrapper} from './components-data-wrapper.test.js'; import * as constants from '../../../../../src/core/constants.js'; import {CommonFlagsDataWrapper} from '../../../../../src/core/config/remote/common-flags-data-wrapper.js'; -import {type RemoteConfigDataStructure} from '../../../../../src/core/config/remote/types.js'; import {type RemoteConfigData} from '../../../../../src/core/config/remote/remote-config-data.js'; +import {type RemoteConfigDataStructure} from '../../../../../src/core/config/remote/interfaces/remote-config-data-structure.js'; const configManagerMock: any = { update: (...arguments_: any) => true, From 1dcd2485c290ae9e8f101333c4aaedd1a86ff8d0 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 16:35:46 +0300 Subject: [PATCH 38/70] rename remote config structures to struct Signed-off-by: Zhan Milenkov --- .../remote/api/components-data-wrapper-api.ts | 4 ++-- src/core/config/remote/cluster.ts | 10 +++++----- .../config/remote/components-data-wrapper.ts | 8 ++++---- .../{cluster-structure.ts => cluster-struct.ts} | 2 +- ...a-structure.ts => components-data-struct.ts} | 2 +- ...gration-structure.ts => migration-struct.ts} | 2 +- .../interfaces/remote-config-data-struct.ts | 17 +++++++++++++++++ .../interfaces/remote-config-data-structure.ts | 17 ----------------- ...ture.ts => remote-config-metadata-struct.ts} | 6 +++--- src/core/config/remote/metadata.ts | 12 ++++++------ src/core/config/remote/migration.ts | 6 +++--- .../config/remote/remote-config-data-wrapper.ts | 6 +++--- test/unit/core/config/remote/cluster.test.ts | 10 +++++----- .../remote/components-data-wrapper.test.ts | 6 +++--- test/unit/core/config/remote/metadata.test.ts | 6 +++--- test/unit/core/config/remote/migration.test.ts | 4 ++-- .../remote/remote-config-data-wrapper.test.ts | 4 ++-- 17 files changed, 61 insertions(+), 61 deletions(-) rename src/core/config/remote/interfaces/{cluster-structure.ts => cluster-struct.ts} (85%) rename src/core/config/remote/interfaces/{components-data-structure.ts => components-data-struct.ts} (70%) rename src/core/config/remote/interfaces/{migration-structure.ts => migration-struct.ts} (82%) create mode 100644 src/core/config/remote/interfaces/remote-config-data-struct.ts delete mode 100644 src/core/config/remote/interfaces/remote-config-data-structure.ts rename src/core/config/remote/interfaces/{remote-config-metadata-structure.ts => remote-config-metadata-struct.ts} (79%) diff --git a/src/core/config/remote/api/components-data-wrapper-api.ts b/src/core/config/remote/api/components-data-wrapper-api.ts index 6678311db..0d19f8329 100644 --- a/src/core/config/remote/api/components-data-wrapper-api.ts +++ b/src/core/config/remote/api/components-data-wrapper-api.ts @@ -6,11 +6,11 @@ import {type CloneTrait} from '../../../../types/traits/clone-trait.js'; import {type BaseComponent} from '../components/base-component.js'; import {type ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; import {type ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentsDataStructure} from '../interfaces/components-data-structure.js'; +import {type ComponentsDataStruct} from '../interfaces/components-data-struct.js'; export interface ComponentsDataWrapperApi extends Validate, - ToObject, + ToObject, CloneTrait { /** Used to add new component to their respective group. */ addNewComponent(component: BaseComponent): void; diff --git a/src/core/config/remote/cluster.ts b/src/core/config/remote/cluster.ts index 8f0fc1a44..f44807a22 100644 --- a/src/core/config/remote/cluster.ts +++ b/src/core/config/remote/cluster.ts @@ -3,9 +3,9 @@ import {type ToObject} from '../../../types/index.js'; import {type ClusterReference, type DeploymentName, type NamespaceNameAsString} from './types.js'; import {SoloError} from '../../errors/solo-error.js'; -import {type ClusterStructure} from './interfaces/cluster-structure.js'; +import {type ClusterStruct} from './interfaces/cluster-struct.js'; -export class Cluster implements ClusterStructure, ToObject { +export class Cluster implements ClusterStruct, ToObject { public constructor( public readonly name: string, public readonly namespace: NamespaceNameAsString, @@ -38,7 +38,7 @@ export class Cluster implements ClusterStructure, ToObject { } } - public toObject(): ClusterStructure { + public toObject(): ClusterStruct { return { name: this.name, namespace: this.namespace, @@ -48,7 +48,7 @@ export class Cluster implements ClusterStructure, ToObject { }; } - public static fromObject(cluster: ClusterStructure) { + public static fromObject(cluster: ClusterStruct) { return new Cluster( cluster.name, cluster.namespace, @@ -67,7 +67,7 @@ export class Cluster implements ClusterStructure, ToObject { const clusters: Record = {}; for (const [reference, cluster] of Object.entries(object)) { - clusters[reference] = Cluster.fromObject(cluster as ClusterStructure); + clusters[reference] = Cluster.fromObject(cluster as ClusterStruct); } return clusters; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index a5c548fec..2e7ff98e9 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -20,7 +20,7 @@ import {type BaseComponentStructure} from './components/interfaces/base-componen import {type RelayComponentStructure} from './components/interfaces/relay-component-structure.js'; import {type ConsensusNodeComponentStructure} from './components/interfaces/consensus-node-component-structure.js'; import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; -import {type ComponentsDataStructure} from './interfaces/components-data-structure.js'; +import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; /** * Represent the components in the remote config and handles: @@ -187,7 +187,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { * * @param components - component groups distinguished by their type. */ - public static fromObject(components: ComponentsDataStructure): ComponentsDataWrapper { + public static fromObject(components: ComponentsDataStruct): ComponentsDataWrapper { const relays: Record = {}; const haProxies: Record = {}; const mirrorNodes: Record = {}; @@ -357,7 +357,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { return transformedComponents; } - public toObject(): ComponentsDataStructure { + public toObject(): ComponentsDataStruct { return { [ComponentTypes.Relay]: this.transformComponentGroupToObject(this.relays), [ComponentTypes.HaProxy]: this.transformComponentGroupToObject(this.haProxies), @@ -370,7 +370,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { } public clone(): ComponentsDataWrapper { - const data: ComponentsDataStructure = this.toObject(); + const data: ComponentsDataStruct = this.toObject(); return ComponentsDataWrapper.fromObject(data); } diff --git a/src/core/config/remote/interfaces/cluster-structure.ts b/src/core/config/remote/interfaces/cluster-struct.ts similarity index 85% rename from src/core/config/remote/interfaces/cluster-structure.ts rename to src/core/config/remote/interfaces/cluster-struct.ts index 61f7c9cd8..acd426410 100644 --- a/src/core/config/remote/interfaces/cluster-structure.ts +++ b/src/core/config/remote/interfaces/cluster-struct.ts @@ -2,7 +2,7 @@ import {type DeploymentName} from '../types.js'; -export interface ClusterStructure { +export interface ClusterStruct { name: string; namespace: string; deployment: DeploymentName; diff --git a/src/core/config/remote/interfaces/components-data-structure.ts b/src/core/config/remote/interfaces/components-data-struct.ts similarity index 70% rename from src/core/config/remote/interfaces/components-data-structure.ts rename to src/core/config/remote/interfaces/components-data-struct.ts index 9aa674a35..3d3563e7d 100644 --- a/src/core/config/remote/interfaces/components-data-structure.ts +++ b/src/core/config/remote/interfaces/components-data-struct.ts @@ -4,4 +4,4 @@ import {type ComponentTypes} from '../enumerations/component-types.js'; import {type BaseComponentStructure} from '../components/interfaces/base-component-structure.js'; import {type ComponentName} from '../types.js'; -export type ComponentsDataStructure = Record>; +export type ComponentsDataStruct = Record>; diff --git a/src/core/config/remote/interfaces/migration-structure.ts b/src/core/config/remote/interfaces/migration-struct.ts similarity index 82% rename from src/core/config/remote/interfaces/migration-structure.ts rename to src/core/config/remote/interfaces/migration-struct.ts index 2f215a7da..7e103cf61 100644 --- a/src/core/config/remote/interfaces/migration-structure.ts +++ b/src/core/config/remote/interfaces/migration-struct.ts @@ -2,7 +2,7 @@ import {type EmailAddress, type Version} from '../types.js'; -export interface MigrationStructure { +export interface MigrationStruct { migratedAt: Date; migratedBy: EmailAddress; fromVersion: Version; diff --git a/src/core/config/remote/interfaces/remote-config-data-struct.ts b/src/core/config/remote/interfaces/remote-config-data-struct.ts new file mode 100644 index 000000000..bad58010d --- /dev/null +++ b/src/core/config/remote/interfaces/remote-config-data-struct.ts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type ClusterStruct} from './cluster-struct.js'; +import {type RemoteConfigCommonFlagsStruct} from './remote-config-common-flags-struct.js'; +import {type ClusterReference, type Version} from '../types.js'; +import {type RemoteConfigMetadataStruct} from './remote-config-metadata-struct.js'; +import {type ComponentsDataStruct} from './components-data-struct.js'; + +export interface RemoteConfigDataStruct { + metadata: RemoteConfigMetadataStruct; + version: Version; + clusters: Record; + components: ComponentsDataStruct; + commandHistory: string[]; + lastExecutedCommand: string; + flags: RemoteConfigCommonFlagsStruct; +} diff --git a/src/core/config/remote/interfaces/remote-config-data-structure.ts b/src/core/config/remote/interfaces/remote-config-data-structure.ts deleted file mode 100644 index a525b05db..000000000 --- a/src/core/config/remote/interfaces/remote-config-data-structure.ts +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {type ClusterStructure} from './cluster-structure.js'; -import {type RemoteConfigCommonFlagsStruct} from './remote-config-common-flags-struct.js'; -import {type ClusterReference, type Version} from '../types.js'; -import {type RemoteConfigMetadataStructure} from './remote-config-metadata-structure.js'; -import {type ComponentsDataStructure} from './components-data-structure.js'; - -export interface RemoteConfigDataStructure { - metadata: RemoteConfigMetadataStructure; - version: Version; - clusters: Record; - components: ComponentsDataStructure; - commandHistory: string[]; - lastExecutedCommand: string; - flags: RemoteConfigCommonFlagsStruct; -} diff --git a/src/core/config/remote/interfaces/remote-config-metadata-structure.ts b/src/core/config/remote/interfaces/remote-config-metadata-struct.ts similarity index 79% rename from src/core/config/remote/interfaces/remote-config-metadata-structure.ts rename to src/core/config/remote/interfaces/remote-config-metadata-struct.ts index dd5c8618e..8a613c8a9 100644 --- a/src/core/config/remote/interfaces/remote-config-metadata-structure.ts +++ b/src/core/config/remote/interfaces/remote-config-metadata-struct.ts @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 import {type DeploymentStates} from '../enumerations/deployment-states.js'; -import {type MigrationStructure} from './migration-structure.js'; +import {type MigrationStruct} from './migration-struct.js'; import {type DeploymentName, type EmailAddress, type NamespaceNameAsString, type Version} from '../types.js'; -export interface RemoteConfigMetadataStructure { +export interface RemoteConfigMetadataStruct { namespace: NamespaceNameAsString; state: DeploymentStates; deploymentName: DeploymentName; @@ -16,5 +16,5 @@ export interface RemoteConfigMetadataStructure { hederaMirrorNodeChartVersion: Version; hederaExplorerChartVersion: Version; hederaJsonRpcRelayChartVersion: Version; - migration?: MigrationStructure; + migration?: MigrationStruct; } diff --git a/src/core/config/remote/metadata.ts b/src/core/config/remote/metadata.ts index 6eccd2739..d6ec20fcb 100644 --- a/src/core/config/remote/metadata.ts +++ b/src/core/config/remote/metadata.ts @@ -7,7 +7,7 @@ import {type Optional, type ToObject, type Validate} from '../../../types/index. import {DeploymentStates} from './enumerations/deployment-states.js'; import {isValidEnum} from '../../util/validation-helpers.js'; -import {type RemoteConfigMetadataStructure} from './interfaces/remote-config-metadata-structure.js'; +import {type RemoteConfigMetadataStruct} from './interfaces/remote-config-metadata-struct.js'; /** * Represent the remote config metadata object and handles: @@ -17,7 +17,7 @@ import {type RemoteConfigMetadataStructure} from './interfaces/remote-config-met * - Converting from and to plain object */ export class RemoteConfigMetadata - implements RemoteConfigMetadataStructure, Validate, ToObject + implements RemoteConfigMetadataStruct, Validate, ToObject { private _migration?: Migration; @@ -56,7 +56,7 @@ export class RemoteConfigMetadata /* -------- Utilities -------- */ /** Handles conversion from a plain object to instance */ - public static fromObject(metadata: RemoteConfigMetadataStructure): RemoteConfigMetadata { + public static fromObject(metadata: RemoteConfigMetadataStruct): RemoteConfigMetadata { let migration: Optional = undefined; if (metadata.migration) { @@ -116,8 +116,8 @@ export class RemoteConfigMetadata } } - public toObject(): RemoteConfigMetadataStructure { - const data: RemoteConfigMetadataStructure = { + public toObject(): RemoteConfigMetadataStruct { + const data: RemoteConfigMetadataStruct = { namespace: this.namespace, deploymentName: this.deploymentName, state: this.state, @@ -129,7 +129,7 @@ export class RemoteConfigMetadata hederaExplorerChartVersion: this.hederaExplorerChartVersion, hederaJsonRpcRelayChartVersion: this.hederaJsonRpcRelayChartVersion, soloVersion: this.soloVersion, - } as RemoteConfigMetadataStructure; + } as RemoteConfigMetadataStruct; if (this.migration) { data.migration = this.migration.toObject(); diff --git a/src/core/config/remote/migration.ts b/src/core/config/remote/migration.ts index 312546b41..f277c02c2 100644 --- a/src/core/config/remote/migration.ts +++ b/src/core/config/remote/migration.ts @@ -2,9 +2,9 @@ import {SoloError} from '../../errors/solo-error.js'; import {type EmailAddress, type Version} from './types.js'; -import {type MigrationStructure} from './interfaces/migration-structure.js'; +import {type MigrationStruct} from './interfaces/migration-struct.js'; -export class Migration implements MigrationStructure { +export class Migration implements MigrationStruct { private readonly _migratedAt: Date; private readonly _migratedBy: EmailAddress; private readonly _fromVersion: Version; @@ -44,7 +44,7 @@ export class Migration implements MigrationStructure { } } - public toObject(): MigrationStructure { + public toObject(): MigrationStruct { return { migratedAt: this.migratedAt, migratedBy: this.migratedBy, diff --git a/src/core/config/remote/remote-config-data-wrapper.ts b/src/core/config/remote/remote-config-data-wrapper.ts index 9bfa54c83..5d808b709 100644 --- a/src/core/config/remote/remote-config-data-wrapper.ts +++ b/src/core/config/remote/remote-config-data-wrapper.ts @@ -12,9 +12,9 @@ import {type ConfigManager} from '../../config-manager.js'; import {type RemoteConfigData} from './remote-config-data.js'; import {Cluster} from './cluster.js'; import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; -import {type RemoteConfigDataStructure} from './interfaces/remote-config-data-structure.js'; +import {type RemoteConfigDataStruct} from './interfaces/remote-config-data-struct.js'; -export class RemoteConfigDataWrapper implements Validate, ToObject { +export class RemoteConfigDataWrapper implements Validate, ToObject { private readonly _version: Version = '1.0.0'; private _metadata: RemoteConfigMetadata; private readonly _clusters: Record; @@ -154,7 +154,7 @@ export class RemoteConfigDataWrapper implements Validate, ToObject { it('should convert to an object', () => { - const clusterData: ClusterStructure = { + const clusterData: ClusterStruct = { name: 'name', namespace: 'namespace', deployment: 'deployment', @@ -24,7 +24,7 @@ describe('Cluster', () => { clusterData.dnsConsensusNodePattern, ); - const clusterObject: ClusterStructure = cluster.toObject(); + const clusterObject: ClusterStruct = cluster.toObject(); expect(clusterObject.name).to.equal(clusterData.name); expect(clusterObject.namespace).to.equal(clusterData.namespace); expect(clusterObject.deployment).to.equal(clusterData.deployment); @@ -33,7 +33,7 @@ describe('Cluster', () => { }); it('should convert clusters map to an object', () => { - const clusterData1: ClusterStructure = { + const clusterData1: ClusterStruct = { name: 'name1', namespace: 'namespace1', deployment: 'deployment1', @@ -41,7 +41,7 @@ describe('Cluster', () => { dnsConsensusNodePattern: 'network1.svc', }; - const clusterData2: ClusterStructure = { + const clusterData2: ClusterStruct = { name: 'name2', namespace: 'namespace2', deployment: 'deployment2', diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index e7bd16e28..1af4ae778 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -21,7 +21,7 @@ import { type NamespaceNameAsString, } from '../../../../../src/core/config/remote/types.js'; import {BlockNodeComponent} from '../../../../../src/core/config/remote/components/block-node-component.js'; -import {type ComponentsDataStructure} from '../../../../../src/core/config/remote/interfaces/components-data-structure.js'; +import {type ComponentsDataStruct} from '../../../../../src/core/config/remote/interfaces/components-data-struct.js'; export function createComponentsDataWrapper(): { values: { @@ -127,7 +127,7 @@ describe('ComponentsDataWrapper', () => { componentsDataWrapper.toObject(), ); - const componentsDataWrapperObject: ComponentsDataStructure = componentsDataWrapper.toObject(); + const componentsDataWrapperObject: ComponentsDataStruct = componentsDataWrapper.toObject(); expect(componentsDataWrapperObject).to.deep.equal(newComponentsDataWrapper.toObject()); @@ -167,7 +167,7 @@ describe('ComponentsDataWrapper', () => { componentsDataWrapper.addNewComponent(newComponent); - const componentDataWrapperObject: ComponentsDataStructure = componentsDataWrapper.toObject(); + const componentDataWrapperObject: ComponentsDataStruct = componentsDataWrapper.toObject(); expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newComponentName); diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index e9718b175..93a5691e3 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -7,19 +7,19 @@ import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {RemoteConfigMetadata} from '../../../../../src/core/config/remote/metadata.js'; import {type EmailAddress} from '../../../../../src/core/config/remote/types.js'; import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; -import {type RemoteConfigMetadataStructure} from '../../../../../src/core/config/remote/interfaces/remote-config-metadata-structure.js'; +import {type RemoteConfigMetadataStruct} from '../../../../../src/core/config/remote/interfaces/remote-config-metadata-struct.js'; interface MetadataTestStructure { metadata: RemoteConfigMetadata; migration: Migration; - values: RemoteConfigMetadataStructure; + values: RemoteConfigMetadataStruct; } export function createMetadata(): MetadataTestStructure { const lastUpdatedAt: Date = new Date(); const lastUpdateBy: EmailAddress = 'test@test.test'; - const values: RemoteConfigMetadataStructure = { + const values: RemoteConfigMetadataStruct = { namespace: 'namespace', deploymentName: 'kind-namespace', state: DeploymentStates.PRE_GENESIS, diff --git a/test/unit/core/config/remote/migration.test.ts b/test/unit/core/config/remote/migration.test.ts index 701a5aaf1..1e19ec036 100644 --- a/test/unit/core/config/remote/migration.test.ts +++ b/test/unit/core/config/remote/migration.test.ts @@ -4,10 +4,10 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; -import {type MigrationStructure} from '../../../../../src/core/config/remote/interfaces/migration-structure.js'; +import {type MigrationStruct} from '../../../../../src/core/config/remote/interfaces/migration-struct.js'; describe('Migration', () => { - const values: MigrationStructure = {migratedAt: new Date(), migratedBy: 'test@test.test', fromVersion: '1.0.0'}; + const values: MigrationStruct = {migratedAt: new Date(), migratedBy: 'test@test.test', fromVersion: '1.0.0'}; let migration: Migration; beforeEach(() => { diff --git a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts index c9362b67a..d5694b43e 100644 --- a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts +++ b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts @@ -10,7 +10,7 @@ import {createComponentsDataWrapper} from './components-data-wrapper.test.js'; import * as constants from '../../../../../src/core/constants.js'; import {CommonFlagsDataWrapper} from '../../../../../src/core/config/remote/common-flags-data-wrapper.js'; import {type RemoteConfigData} from '../../../../../src/core/config/remote/remote-config-data.js'; -import {type RemoteConfigDataStructure} from '../../../../../src/core/config/remote/interfaces/remote-config-data-structure.js'; +import {type RemoteConfigDataStruct} from '../../../../../src/core/config/remote/interfaces/remote-config-data-struct.js'; const configManagerMock: any = { update: (...arguments_: any) => true, @@ -65,7 +65,7 @@ describe('RemoteConfigDataWrapper', async () => { it('should successfully be able to parse yaml and create instance with fromConfigmap()', async () => { const {dataWrapper} = await createRemoteConfigDataWrapper(); - const dataWrapperObject: RemoteConfigDataStructure = dataWrapper.toObject(); + const dataWrapperObject: RemoteConfigDataStruct = dataWrapper.toObject(); const yamlData: string = yaml.stringify({ metadata: dataWrapperObject.metadata, From bd285cff71c202a7824eb9a3ae81a1d992807965 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 16:49:33 +0300 Subject: [PATCH 39/70] moved all remote config name templating logic to new static class ComponentNameTemplates Signed-off-by: Zhan Milenkov --- .../config/remote/components-data-wrapper.ts | 3 +- .../remote/components/base-component.ts | 29 -------- .../remote/components/block-node-component.ts | 9 +-- .../components/component-name-templates.ts | 67 +++++++++++++++++++ .../components/envoy-proxy-component.ts | 11 +-- .../remote/components/ha-proxy-component.ts | 9 +-- .../components/mirror-node-component.ts | 9 +-- .../mirror-node-explorer-component.ts | 9 +-- .../remote/components/relay-component.ts | 9 +-- .../remote/components/component-names.test.ts | 27 +++----- 10 files changed, 90 insertions(+), 92 deletions(-) create mode 100644 src/core/config/remote/components/component-name-templates.ts diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 2e7ff98e9..1d586e2c1 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -21,6 +21,7 @@ import {type RelayComponentStructure} from './components/interfaces/relay-compon import {type ConsensusNodeComponentStructure} from './components/interfaces/consensus-node-component-structure.js'; import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; +import {ComponentNameTemplates} from './components/component-name-templates.js'; /** * Represent the components in the remote config and handles: @@ -305,7 +306,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { components: Record, ) => void = components => { for (const componentName of Object.keys(components)) { - const componentIndex: number = BaseComponent.parseComponentName(componentName); + const componentIndex: number = ComponentNameTemplates.parseComponentName(componentName); if (newComponentIndex <= componentIndex) { newComponentIndex = componentIndex + 1; } diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 6c66fe79c..65864f859 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -73,33 +73,4 @@ export class BaseComponent implements BaseComponentStructure, Validate, ToObject state: this.state, }; } - - /** - * Used for rendering component name with additional data. - * - * @param baseName - unique name for the component ( ex. mirror-node ) - * @param index - total number of components from this kind - * @returns a unique name to be used for creating components - */ - protected static renderComponentName(baseName: string, index: number): ComponentName { - return `${baseName}-${index}`; - } - - /** - * Extracts the index from a component name by splitting on '-' and taking the last segment. - * - * @param name - full component name (e.g., "mirror-node-node1-42") - * @returns the numeric index (e.g., 42) - */ - public static parseComponentName(name: ComponentName): number { - const parts: string[] = name.split('-'); - const lastPart: string = parts.at(-1); - const componentIndex: number = Number.parseInt(lastPart, 10); - - if (Number.isNaN(componentIndex)) { - throw new SoloError(`Invalid component index in component name: ${name}`); - } - - return componentIndex; - } } diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index 9b217e689..db80e2e01 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -7,10 +7,9 @@ import {ComponentStates} from '../enumerations/component-states.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; export class BlockNodeComponent extends BaseComponent { - private static readonly BASE_NAME: string = 'block-node'; - private constructor( name: ComponentName, cluster: ClusterReference, @@ -30,7 +29,7 @@ export class BlockNodeComponent extends BaseComponent { ): BlockNodeComponent { const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.BlockNode); - const name: ComponentName = BlockNodeComponent.renderBlockNodeName(index); + const name: ComponentName = ComponentNameTemplates.renderBlockNodeName(index); return new BlockNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); } @@ -40,8 +39,4 @@ export class BlockNodeComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new BlockNodeComponent(name, cluster, namespace, state); } - - private static renderBlockNodeName(index: number): string { - return BlockNodeComponent.renderComponentName(BlockNodeComponent.BASE_NAME, index); - } } diff --git a/src/core/config/remote/components/component-name-templates.ts b/src/core/config/remote/components/component-name-templates.ts new file mode 100644 index 000000000..d6ba59648 --- /dev/null +++ b/src/core/config/remote/components/component-name-templates.ts @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {SoloError} from '../../../errors/solo-error.js'; +import {type ComponentName} from '../types.js'; +import {type NodeAlias} from '../../../../types/aliases.js'; + +export class ComponentNameTemplates { + private static BLOCK_NODE_BASE_NAME: string = 'block-node'; + private static RELAY_BASE_NAME: string = 'relay'; + private static EXPLORER_BASE_NAME: string = 'mirror-node-explorer'; + private static MIRROR_NODE_BASE_NAME: string = 'mirror-node'; + private static HA_PROXY_BASE_NAME: (nodeAlias: NodeAlias) => string = nodeAlias => `haproxy-${nodeAlias}`; + private static ENVOY_PROXY_BASE_NAME: (nodeAlias: NodeAlias) => string = nodeAlias => `envoy-proxy-${nodeAlias}`; + + public static renderBlockNodeName(index: number): ComponentName { + return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.BLOCK_NODE_BASE_NAME, index); + } + + public static renderRelayName(index: number): ComponentName { + return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.RELAY_BASE_NAME, index); + } + + public static renderMirrorNodeExplorerName(index: number): ComponentName { + return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.EXPLORER_BASE_NAME, index); + } + + public static renderMirrorNodeName(index: number): ComponentName { + return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.MIRROR_NODE_BASE_NAME, index); + } + + public static renderHaProxyName(index: number, nodeAlias: NodeAlias): ComponentName { + return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.HA_PROXY_BASE_NAME(nodeAlias), index); + } + + public static renderEnvoyProxyName(index: number, nodeAlias: NodeAlias): ComponentName { + return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.ENVOY_PROXY_BASE_NAME(nodeAlias), index); + } + + /** + * Used for rendering component name with additional data. + * + * @param baseName - unique name for the component ( ex. mirror-node ) + * @param index - total number of components from this kind + * @returns a unique name to be used for creating components + */ + private static renderComponentName(baseName: string, index: number): ComponentName { + return `${baseName}-${index}`; + } + + /** + * Extracts the index from a component name by splitting on '-' and taking the last segment. + * + * @param name - full component name (e.g., "mirror-node-node1-42") + * @returns the numeric index (e.g., 42) + */ + public static parseComponentName(name: ComponentName): number { + const parts: string[] = name.split('-'); + const lastPart: string = parts.at(-1); + const componentIndex: number = Number.parseInt(lastPart, 10); + + if (Number.isNaN(componentIndex)) { + throw new SoloError(`Invalid component index in component name: ${name}`); + } + + return componentIndex; + } +} diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index ea59d726f..e35370abf 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -1,17 +1,16 @@ // SPDX-License-Identifier: Apache-2.0 import {BaseComponent} from './base-component.js'; -import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NodeAlias} from '../../../../types/aliases.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class EnvoyProxyComponent extends BaseComponent { - private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `envoy-proxy-${nodeAlias}`; - private constructor( name: ComponentName, cluster: ClusterReference, @@ -32,7 +31,7 @@ export class EnvoyProxyComponent extends BaseComponent { ): EnvoyProxyComponent { const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); - const name: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); + const name: ComponentName = ComponentNameTemplates.renderEnvoyProxyName(index, nodeAlias); return new EnvoyProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); } @@ -42,8 +41,4 @@ export class EnvoyProxyComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new EnvoyProxyComponent(name, cluster, namespace, state); } - - private static renderEnvoyProxyName(index: number, nodeAlias: NodeAlias): string { - return EnvoyProxyComponent.renderComponentName(EnvoyProxyComponent.BASE_NAME(nodeAlias), index); - } } diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 91e7e387d..8e87e81f8 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -8,10 +8,9 @@ import {type NodeAlias} from '../../../../types/aliases.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; export class HaProxyComponent extends BaseComponent { - private static BASE_NAME: (nodeAlias: NodeAlias) => string = (nodeAlias): string => `haproxy-${nodeAlias}`; - private constructor( name: ComponentName, cluster: ClusterReference, @@ -32,7 +31,7 @@ export class HaProxyComponent extends BaseComponent { ): HaProxyComponent { const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); - const name: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); + const name: ComponentName = ComponentNameTemplates.renderHaProxyName(index, nodeAlias); return new HaProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); } @@ -42,8 +41,4 @@ export class HaProxyComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new HaProxyComponent(name, cluster, namespace, state); } - - private static renderHaProxyName(index: number, nodeAlias: NodeAlias): string { - return HaProxyComponent.renderComponentName(HaProxyComponent.BASE_NAME(nodeAlias), index); - } } diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index 0aa518434..ec9131fe5 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -7,10 +7,9 @@ import {type ClusterReference, type ComponentName, type NamespaceNameAsString} f import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; export class MirrorNodeComponent extends BaseComponent { - private static readonly BASE_NAME: string = 'mirror-node'; - private constructor( name: ComponentName, cluster: ClusterReference, @@ -30,7 +29,7 @@ export class MirrorNodeComponent extends BaseComponent { ): MirrorNodeComponent { const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNode); - const name: ComponentName = MirrorNodeComponent.renderMirrorNodeName(index); + const name: ComponentName = ComponentNameTemplates.renderMirrorNodeName(index); return new MirrorNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); } @@ -40,8 +39,4 @@ export class MirrorNodeComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeComponent(name, cluster, namespace, state); } - - private static renderMirrorNodeName(index: number): string { - return MirrorNodeComponent.renderComponentName(MirrorNodeComponent.BASE_NAME, index); - } } diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 023f11ec1..3a4f0c637 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -7,10 +7,9 @@ import {type ClusterReference, type ComponentName, type NamespaceNameAsString} f import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; export class MirrorNodeExplorerComponent extends BaseComponent { - private static readonly BASE_NAME: string = 'mirror-node-explorer'; - private constructor( name: ComponentName, cluster: ClusterReference, @@ -30,7 +29,7 @@ export class MirrorNodeExplorerComponent extends BaseComponent { ): MirrorNodeExplorerComponent { const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNodeExplorer); - const name: ComponentName = MirrorNodeExplorerComponent.renderMirrorNodeExplorerName(index); + const name: ComponentName = ComponentNameTemplates.renderMirrorNodeExplorerName(index); return new MirrorNodeExplorerComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); } @@ -40,8 +39,4 @@ export class MirrorNodeExplorerComponent extends BaseComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeExplorerComponent(name, cluster, namespace, state); } - - private static renderMirrorNodeExplorerName(index: number): string { - return MirrorNodeExplorerComponent.renderComponentName(MirrorNodeExplorerComponent.BASE_NAME, index); - } } diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index f4ee95afc..d287e6a3c 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -10,13 +10,12 @@ import {type ToObject} from '../../../../types/index.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type RelayComponentStructure} from './interfaces/relay-component-structure.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; export class RelayComponent extends BaseComponent implements RelayComponentStructure, ToObject { - private static readonly BASE_NAME: string = 'relay'; - /** * @param name - to distinguish components. * @param clusterReference - in which the component is deployed. @@ -45,7 +44,7 @@ export class RelayComponent ): RelayComponent { const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.Relay); - const name: ComponentName = RelayComponent.renderRelayName(index); + const name: ComponentName = ComponentNameTemplates.renderRelayName(index); return new RelayComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE, nodeAliases); } @@ -72,8 +71,4 @@ export class RelayComponent ...super.toObject(), }; } - - private static renderRelayName(index: number): string { - return RelayComponent.renderComponentName(RelayComponent.BASE_NAME, index); - } } diff --git a/test/unit/core/config/remote/components/component-names.test.ts b/test/unit/core/config/remote/components/component-names.test.ts index 2e7f43690..b260122cc 100644 --- a/test/unit/core/config/remote/components/component-names.test.ts +++ b/test/unit/core/config/remote/components/component-names.test.ts @@ -2,32 +2,25 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; -import {MirrorNodeComponent} from '../../../../../../src/core/config/remote/components/mirror-node-component.js'; import {type ComponentName} from '../../../../../../src/core/config/remote/types.js'; -import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; -import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/components/envoy-proxy-component.js'; -import {HaProxyComponent} from '../../../../../../src/core/config/remote/components/ha-proxy-component.js'; -import {MirrorNodeExplorerComponent} from '../../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; -import {RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; import {type NodeAlias, type NodeAliases} from '../../../../../../src/types/aliases.js'; import {Templates} from '../../../../../../src/core/templates.js'; +import {ComponentNameTemplates} from '../../../../../../src/core/config/remote/components/component-name-templates.js'; -describe('', () => { +describe('ComponentNameTemplates', () => { const maxTestIndex: number = 10; const nodeAliases: NodeAliases = Templates.renderNodeAliasesFromCount(maxTestIndex, 0); it('should create a valid component name for MirrorNodeComponent', () => { for (let index: number = 0; index < maxTestIndex; index++) { - // @ts-expect-error - to access private method - const componentName: ComponentName = MirrorNodeComponent.renderMirrorNodeName(index); + const componentName: ComponentName = ComponentNameTemplates.renderMirrorNodeName(index); expect(componentName).to.equal(`mirror-node-${index}`); } }); it('should create a valid component name for BlockNodeComponent', () => { for (let index: number = 0; index < maxTestIndex; index++) { - // @ts-expect-error - to access private method - const componentName: ComponentName = BlockNodeComponent.renderBlockNodeName(index); + const componentName: ComponentName = ComponentNameTemplates.renderBlockNodeName(index); expect(componentName).to.equal(`block-node-${index}`); } }); @@ -36,8 +29,7 @@ describe('', () => { for (let index: number = 0; index < maxTestIndex; index++) { const nodeAlias: NodeAlias = nodeAliases[index]; - // @ts-expect-error - to access private method - const componentName: ComponentName = EnvoyProxyComponent.renderEnvoyProxyName(index, nodeAlias); + const componentName: ComponentName = ComponentNameTemplates.renderEnvoyProxyName(index, nodeAlias); expect(componentName).to.equal(`envoy-proxy-${nodeAlias}-${index}`); } }); @@ -46,24 +38,21 @@ describe('', () => { for (let index: number = 0; index < maxTestIndex; index++) { const nodeAlias: NodeAlias = nodeAliases[index]; - // @ts-expect-error - to access private method - const componentName: ComponentName = HaProxyComponent.renderHaProxyName(index, nodeAlias); + const componentName: ComponentName = ComponentNameTemplates.renderHaProxyName(index, nodeAlias); expect(componentName).to.equal(`haproxy-${nodeAlias}-${index}`); } }); it('should create a valid component name for MirrorNodeExplorerComponent', () => { for (let index: number = 0; index < maxTestIndex; index++) { - // @ts-expect-error - to access private method - const componentName: ComponentName = MirrorNodeExplorerComponent.renderMirrorNodeExplorerName(index); + const componentName: ComponentName = ComponentNameTemplates.renderMirrorNodeExplorerName(index); expect(componentName).to.equal(`mirror-node-explorer-${index}`); } }); it('should create a valid component name for RelayComponent', () => { for (let index: number = 0; index < maxTestIndex; index++) { - // @ts-expect-error - to access private method - const componentName: ComponentName = RelayComponent.renderRelayName(index); + const componentName: ComponentName = ComponentNameTemplates.renderRelayName(index); expect(componentName).to.equal(`relay-${index}`); } }); From a50f43a8a2e00fc2d0617b16ac80822288ebc480 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 17:00:37 +0300 Subject: [PATCH 40/70] moved all remote config component creation logic into a seperate ComponentFactory class Signed-off-by: Zhan Milenkov --- .../remote/components/block-node-component.ts | 19 +-- .../remote/components/component-factory.ts | 112 ++++++++++++++++++ .../components/consensus-node-component.ts | 28 +---- .../components/envoy-proxy-component.ts | 21 +--- .../remote/components/ha-proxy-component.ts | 21 +--- .../components/mirror-node-component.ts | 19 +-- .../mirror-node-explorer-component.ts | 19 +-- .../remote/components/relay-component.ts | 20 +--- 8 files changed, 128 insertions(+), 131 deletions(-) create mode 100644 src/core/config/remote/components/component-factory.ts diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index db80e2e01..ddbe13f33 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -3,14 +3,11 @@ import {BaseComponent} from './base-component.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; -import {ComponentNameTemplates} from './component-name-templates.js'; export class BlockNodeComponent extends BaseComponent { - private constructor( + public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -22,18 +19,6 @@ export class BlockNodeComponent extends BaseComponent { /* -------- Utilities -------- */ - public static createNew( - remoteConfigManager: RemoteConfigManagerApi, - clusterReference: ClusterReference, - namespace: NamespaceName, - ): BlockNodeComponent { - const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.BlockNode); - - const name: ComponentName = ComponentNameTemplates.renderBlockNodeName(index); - - return new BlockNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStructure): BlockNodeComponent { const {name, cluster, namespace, state} = component; diff --git a/src/core/config/remote/components/component-factory.ts b/src/core/config/remote/components/component-factory.ts new file mode 100644 index 000000000..ba973811f --- /dev/null +++ b/src/core/config/remote/components/component-factory.ts @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {ComponentTypes} from '../enumerations/component-types.js'; +import {ComponentNameTemplates} from './component-name-templates.js'; +import {ComponentStates} from '../enumerations/component-states.js'; +import {RelayComponent} from './relay-component.js'; +import {MirrorNodeExplorerComponent} from './mirror-node-explorer-component.js'; +import {MirrorNodeComponent} from './mirror-node-component.js'; +import {HaProxyComponent} from './ha-proxy-component.js'; +import {EnvoyProxyComponent} from './envoy-proxy-component.js'; +import {Templates} from '../../../templates.js'; +import {ConsensusNodeComponent} from './consensus-node-component.js'; +import {BlockNodeComponent} from './block-node-component.js'; +import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {type ClusterReference, type ComponentName} from '../types.js'; +import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; +import {type NodeAlias, type NodeAliases, type NodeId} from '../../../../types/aliases.js'; +import {type ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; + +export class ComponentFactory { + public static createNewRelayComponent( + remoteConfigManager: RemoteConfigManagerApi, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeAliases: NodeAliases, + ): RelayComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.Relay); + + const name: ComponentName = ComponentNameTemplates.renderRelayName(index); + + return new RelayComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE, nodeAliases); + } + + public static createNewExplorerComponent( + remoteConfigManager: RemoteConfigManagerApi, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): MirrorNodeExplorerComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNodeExplorer); + + const name: ComponentName = ComponentNameTemplates.renderMirrorNodeExplorerName(index); + + return new MirrorNodeExplorerComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + + public static createNewMirrorNodeComponent( + remoteConfigManager: RemoteConfigManagerApi, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): MirrorNodeComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNode); + + const name: ComponentName = ComponentNameTemplates.renderMirrorNodeName(index); + + return new MirrorNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + + public static createNewHaProxyComponent( + remoteConfigManager: RemoteConfigManagerApi, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeAlias: NodeAlias, + ): HaProxyComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); + + const name: ComponentName = ComponentNameTemplates.renderHaProxyName(index, nodeAlias); + + return new HaProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + + public static createNewEnvoyProxyComponent( + remoteConfigManager: RemoteConfigManagerApi, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeAlias: NodeAlias, + ): EnvoyProxyComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); + + const name: ComponentName = ComponentNameTemplates.renderEnvoyProxyName(index, nodeAlias); + + return new EnvoyProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } + + public static createNewConsensusNodeComponent( + nodeAlias: NodeAlias, + clusterReference: ClusterReference, + namespace: NamespaceName, + nodeState: ConsensusNodeStates.REQUESTED | ConsensusNodeStates.NON_DEPLOYED | ConsensusNodeStates.STARTED, + ): ConsensusNodeComponent { + const nodeId: NodeId = Templates.nodeIdFromNodeAlias(nodeAlias); + return new ConsensusNodeComponent( + nodeAlias, + clusterReference, + namespace.name, + ComponentStates.ACTIVE, + nodeState, + nodeId, + ); + } + + public static createNewBlockNodeComponent( + remoteConfigManager: RemoteConfigManagerApi, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): BlockNodeComponent { + const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.BlockNode); + + const name: ComponentName = ComponentNameTemplates.renderBlockNodeName(index); + + return new BlockNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); + } +} diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index 2f45d1414..ca82ec5f4 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -2,15 +2,12 @@ import {BaseComponent} from './base-component.js'; import {SoloError} from '../../../errors/solo-error.js'; -import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type ToObject} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; -import {ComponentStates} from '../enumerations/component-states.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type NodeAlias, type NodeId} from '../../../../types/aliases.js'; -import {Templates} from '../../../templates.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type ToObject} from '../../../../types/index.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type ConsensusNodeComponentStructure} from './interfaces/consensus-node-component-structure.js'; /** @@ -33,7 +30,7 @@ export class ConsensusNodeComponent * @param state - the component state * @param nodeState - of the consensus node */ - private constructor( + public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -57,23 +54,6 @@ export class ConsensusNodeComponent /* -------- Utilities -------- */ - public static createNew( - nodeAlias: NodeAlias, - clusterReference: ClusterReference, - namespace: NamespaceName, - nodeState: ConsensusNodeStates.REQUESTED | ConsensusNodeStates.NON_DEPLOYED | ConsensusNodeStates.STARTED, - ): ConsensusNodeComponent { - const nodeId: NodeId = Templates.nodeIdFromNodeAlias(nodeAlias); - return new ConsensusNodeComponent( - nodeAlias, - clusterReference, - namespace.name, - ComponentStates.ACTIVE, - nodeState, - nodeId, - ); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: ConsensusNodeComponentStructure): ConsensusNodeComponent { const {name, cluster, state, namespace, nodeState, nodeId} = component; diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index e35370abf..1b990e617 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -2,16 +2,12 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; -import {ComponentNameTemplates} from './component-name-templates.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type NodeAlias} from '../../../../types/aliases.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; export class EnvoyProxyComponent extends BaseComponent { - private constructor( + public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -23,19 +19,6 @@ export class EnvoyProxyComponent extends BaseComponent { /* -------- Utilities -------- */ - public static createNew( - remoteConfigManager: RemoteConfigManagerApi, - clusterReference: ClusterReference, - namespace: NamespaceName, - nodeAlias: NodeAlias, - ): EnvoyProxyComponent { - const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.EnvoyProxy); - - const name: ComponentName = ComponentNameTemplates.renderEnvoyProxyName(index, nodeAlias); - - return new EnvoyProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStructure): EnvoyProxyComponent { const {name, cluster, namespace, state} = component; diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 8e87e81f8..49e892196 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -3,15 +3,11 @@ import {BaseComponent} from './base-component.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; -import {type NodeAlias} from '../../../../types/aliases.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; -import {ComponentNameTemplates} from './component-name-templates.js'; export class HaProxyComponent extends BaseComponent { - private constructor( + public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -23,19 +19,6 @@ export class HaProxyComponent extends BaseComponent { /* -------- Utilities -------- */ - public static createNew( - remoteConfigManager: RemoteConfigManagerApi, - clusterReference: ClusterReference, - namespace: NamespaceName, - nodeAlias: NodeAlias, - ): HaProxyComponent { - const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.HaProxy); - - const name: ComponentName = ComponentNameTemplates.renderHaProxyName(index, nodeAlias); - - return new HaProxyComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStructure): HaProxyComponent { const {name, cluster, namespace, state} = component; diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index ec9131fe5..c835b2c04 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -2,15 +2,12 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; -import {ComponentNameTemplates} from './component-name-templates.js'; export class MirrorNodeComponent extends BaseComponent { - private constructor( + public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -22,18 +19,6 @@ export class MirrorNodeComponent extends BaseComponent { /* -------- Utilities -------- */ - public static createNew( - remoteConfigManager: RemoteConfigManagerApi, - clusterReference: ClusterReference, - namespace: NamespaceName, - ): MirrorNodeComponent { - const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNode); - - const name: ComponentName = ComponentNameTemplates.renderMirrorNodeName(index); - - return new MirrorNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStructure): MirrorNodeComponent { const {name, cluster, namespace, state} = component; diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 3a4f0c637..c8aa9703c 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -2,15 +2,12 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; -import {ComponentNameTemplates} from './component-name-templates.js'; export class MirrorNodeExplorerComponent extends BaseComponent { - private constructor( + public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, @@ -22,18 +19,6 @@ export class MirrorNodeExplorerComponent extends BaseComponent { /* -------- Utilities -------- */ - public static createNew( - remoteConfigManager: RemoteConfigManagerApi, - clusterReference: ClusterReference, - namespace: NamespaceName, - ): MirrorNodeExplorerComponent { - const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.MirrorNodeExplorer); - - const name: ComponentName = ComponentNameTemplates.renderMirrorNodeExplorerName(index); - - return new MirrorNodeExplorerComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStructure): MirrorNodeExplorerComponent { const {name, cluster, namespace, state} = component; diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index d287e6a3c..501b9e02b 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -3,14 +3,11 @@ import {SoloError} from '../../../errors/solo-error.js'; import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; +import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type RelayComponentStructure} from './interfaces/relay-component-structure.js'; -import {ComponentNameTemplates} from './component-name-templates.js'; export class RelayComponent extends BaseComponent @@ -23,7 +20,7 @@ export class RelayComponent * @param state - the state of the component * @param consensusNodeAliases - list node aliases */ - private constructor( + public constructor( name: ComponentName, clusterReference: ClusterReference, namespace: NamespaceNameAsString, @@ -36,19 +33,6 @@ export class RelayComponent /* -------- Utilities -------- */ - public static createNew( - remoteConfigManager: RemoteConfigManagerApi, - clusterReference: ClusterReference, - namespace: NamespaceName, - nodeAliases: NodeAliases, - ): RelayComponent { - const index: number = remoteConfigManager.components.getNewComponentIndex(ComponentTypes.Relay); - - const name: ComponentName = ComponentNameTemplates.renderRelayName(index); - - return new RelayComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE, nodeAliases); - } - /** Handles creating instance of the class from plain object. */ public static fromObject(component: RelayComponentStructure): RelayComponent { const {name, cluster, namespace, state, consensusNodeAliases} = component; From d962c718081262e0175be72811718b20b1175ec2 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 17:02:49 +0300 Subject: [PATCH 41/70] switched all places to use the new ComponentFactory Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 3 ++- src/commands/deployment.ts | 3 ++- src/commands/explorer.ts | 3 ++- src/commands/mirror-node.ts | 3 ++- src/commands/network.ts | 5 +++-- src/commands/node/tasks.ts | 7 ++++--- src/commands/relay.ts | 3 ++- src/core/config/remote/components-data-wrapper.ts | 3 ++- .../config/remote/components/components.test.ts | 13 +++++++------ 9 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 63b16aef7..af6aadd23 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -25,6 +25,7 @@ import {type NamespaceName} from '../integration/kube/resources/namespace/namesp import os from 'node:os'; import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -158,7 +159,7 @@ export class BlockNodeCommand extends BaseCommand { ComponentTypes.BlockNode, ); - config.newBlockNodeComponent = BlockNodeComponent.createNew( + config.newBlockNodeComponent = ComponentFactory.createNewBlockNodeComponent( this.remoteConfigManager, config.clusterRef, config.namespace, diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 90643daf3..524b00814 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -21,6 +21,7 @@ import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; import {DeploymentStates} from '../core/config/remote/enumerations/deployment-states.js'; +import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface DeploymentAddClusterConfig { quiet: boolean; @@ -699,7 +700,7 @@ export class DeploymentCommand extends BaseCommand { //* add the new nodes to components for (const nodeAlias of nodeAliases) { remoteConfig.components.addNewComponent( - ConsensusNodeComponent.createNew(nodeAlias, clusterRef, namespace, ConsensusNodeStates.NON_DEPLOYED), + ComponentFactory.createNewConsensusNodeComponent(nodeAlias, clusterRef, namespace, ConsensusNodeStates.NON_DEPLOYED), ); } }); diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 6a211b75e..5d4fe15fe 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -26,6 +26,7 @@ import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; import {type ClusterReference, type Context} from '../core/config/remote/types.js'; +import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface ExplorerDeployConfigClass { chartDirectory: string; @@ -606,7 +607,7 @@ export class ExplorerCommand extends BaseCommand { const {namespace, clusterRef} = context_.config; remoteConfig.components.addNewComponent( - MirrorNodeExplorerComponent.createNew(this.remoteConfigManager, clusterRef, namespace), + ComponentFactory.createNewExplorerComponent(this.remoteConfigManager, clusterRef, namespace), ); }); }, diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 59accf716..52bd4f6a0 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -36,6 +36,7 @@ import {type ClusterReference, type DeploymentName} from '../core/config/remote/ import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface MirrorNodeDeployConfigClass { chartDirectory: string; @@ -907,7 +908,7 @@ export class MirrorNodeCommand extends BaseCommand { const {namespace, clusterRef} = context_.config; remoteConfig.components.addNewComponent( - MirrorNodeComponent.createNew(this.remoteConfigManager, clusterRef, namespace), + ComponentFactory.createNewMirrorNodeComponent(this.remoteConfigManager, clusterRef, namespace), ); }); }, diff --git a/src/commands/network.ts b/src/commands/network.ts index 73877a61c..34179406d 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -44,6 +44,7 @@ import {type PodReference} from '../integration/kube/resources/pod/pod-reference import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; export interface NetworkDeployConfigClass { applicationEnv: string; @@ -1335,10 +1336,10 @@ export class NetworkCommand extends BaseCommand { remoteConfig.components.changeNodeState(nodeAlias, ConsensusNodeStates.REQUESTED); remoteConfig.components.addNewComponent( - EnvoyProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewEnvoyProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), ); remoteConfig.components.addNewComponent( - HaProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewHaProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), ); } }); diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 6cc4da530..2754b8789 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -114,6 +114,7 @@ import {type CheckedNodesConfigClass, type CheckedNodesContext} from './config-i import {type NetworkNodeServices} from '../../core/network-node-services.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; import {Cluster} from '../../core/config/remote/cluster.js'; +import {ComponentFactory} from '../../core/config/remote/components/component-factory.js'; @injectable() export class NodeCommandTasks { @@ -2513,13 +2514,13 @@ export class NodeCommandTasks { await this.remoteConfigManager.modify(async remoteConfig => { remoteConfig.components.addNewComponent( - ConsensusNodeComponent.createNew(nodeAlias, clusterReference, namespace, ConsensusNodeStates.STARTED), + ComponentFactory.createNewConsensusNodeComponent(nodeAlias, clusterReference, namespace, ConsensusNodeStates.STARTED), ); remoteConfig.components.addNewComponent( - EnvoyProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewEnvoyProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), ); remoteConfig.components.addNewComponent( - HaProxyComponent.createNew(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewHaProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), ); }); diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 08bc4633f..e892bf0f6 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -21,6 +21,7 @@ import {type ClusterReference, type DeploymentName} from '../core/config/remote/ import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface RelayDestroyConfigClass { chartDirectory: string; @@ -548,7 +549,7 @@ export class RelayCommand extends BaseCommand { const {namespace, nodeAliases, clusterRef} = context_.config; remoteConfig.components.addNewComponent( - RelayComponent.createNew(this.remoteConfigManager, clusterRef, namespace, nodeAliases), + ComponentFactory.createNewRelayComponent(this.remoteConfigManager, clusterRef, namespace, nodeAliases), ); }); }, diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 1d586e2c1..509bc35fc 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -22,6 +22,7 @@ import {type ConsensusNodeComponentStructure} from './components/interfaces/cons import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; import {ComponentNameTemplates} from './components/component-name-templates.js'; +import {ComponentFactory} from './components/component-factory.js'; /** * Represent the components in the remote config and handles: @@ -280,7 +281,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { const consensusNodeComponents: Record = {}; for (const nodeAlias of nodeAliases) { - consensusNodeComponents[nodeAlias] = ConsensusNodeComponent.createNew( + consensusNodeComponents[nodeAlias] = ComponentFactory.createNewConsensusNodeComponent( nodeAlias, clusterReference, namespace, diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 015ce3249..ad24d5d76 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -20,6 +20,7 @@ import {NamespaceName} from '../../../../../../src/integration/kube/resources/na import {type BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/base-component-structure.js'; import {type RelayComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/relay-component-structure.js'; import {type ConsensusNodeComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/consensus-node-component-structure.js'; +import {ComponentFactory} from '../../../../../../src/core/config/remote/components/component-factory.js'; const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; @@ -58,11 +59,11 @@ describe('BlockNodeComponent', () => testBaseComponentData(BlockNodeComponent)); describe('RelayComponent', () => { it('should successfully create ', () => { - RelayComponent.createNew(remoteConfigManagerMock, clusterReference, namespace, []); + ComponentFactory.createNewRelayComponent(remoteConfigManagerMock, clusterReference, namespace, []); }); it('should be an instance of BaseComponent', () => { - const component: RelayComponent = RelayComponent.createNew( + const component: RelayComponent = ComponentFactory.createNewRelayComponent( remoteConfigManagerMock, clusterReference, namespace, @@ -85,7 +86,7 @@ describe('RelayComponent', () => { consensusNodeAliases: ['node1'], }; - const component: RelayComponent = RelayComponent.createNew( + const component: RelayComponent = ComponentFactory.createNewRelayComponent( remoteConfigManagerMock, values.cluster, namespace, @@ -101,11 +102,11 @@ describe('ConsensusNodeComponent', () => { const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; it('should successfully create ', () => { - ConsensusNodeComponent.createNew(nodeAlias, 'valid', namespace, nodeState); + ComponentFactory.createNewConsensusNodeComponent(nodeAlias, 'valid', namespace, nodeState); }); it('should be an instance of BaseComponent', () => { - const component: ConsensusNodeComponent = ConsensusNodeComponent.createNew( + const component: ConsensusNodeComponent = ComponentFactory.createNewConsensusNodeComponent( nodeAlias, clusterReference, namespace, @@ -126,7 +127,7 @@ describe('ConsensusNodeComponent', () => { nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; - const component: ConsensusNodeComponent = ConsensusNodeComponent.createNew( + const component: ConsensusNodeComponent = ComponentFactory.createNewConsensusNodeComponent( values.name as NodeAlias, values.cluster, namespace, From c6b43dbbd61addae55f7039d2fd8c8f23838d626 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 17:04:07 +0300 Subject: [PATCH 42/70] lint-fix Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 2 +- src/commands/deployment.ts | 8 +++++-- src/commands/explorer.ts | 2 +- src/commands/mirror-node.ts | 2 +- src/commands/network.ts | 16 +++++++++---- src/commands/node/tasks.ts | 24 ++++++++++++++----- src/commands/relay.ts | 2 +- .../remote/components/components.test.ts | 2 +- 8 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index af6aadd23..dfc05f5dc 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -23,7 +23,7 @@ import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import os from 'node:os'; -import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; +import {type BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 524b00814..8339a1577 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -16,7 +16,6 @@ import {container} from 'tsyringe-neo'; import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {Templates} from '../core/templates.js'; -import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; @@ -700,7 +699,12 @@ export class DeploymentCommand extends BaseCommand { //* add the new nodes to components for (const nodeAlias of nodeAliases) { remoteConfig.components.addNewComponent( - ComponentFactory.createNewConsensusNodeComponent(nodeAlias, clusterRef, namespace, ConsensusNodeStates.NON_DEPLOYED), + ComponentFactory.createNewConsensusNodeComponent( + nodeAlias, + clusterRef, + namespace, + ConsensusNodeStates.NON_DEPLOYED, + ), ); } }); diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 5d4fe15fe..6c26201c4 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -14,7 +14,7 @@ import {Flags as flags} from './flags.js'; import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; +import {type MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; import * as helpers from '../core/helpers.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 52bd4f6a0..2d7f3ab1d 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -19,7 +19,7 @@ import {showVersionBanner} from '../core/helpers.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {type PodName} from '../integration/kube/resources/pod/pod-name.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; +import {type MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; import * as fs from 'node:fs'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import * as Base64 from 'js-base64'; diff --git a/src/commands/network.ts b/src/commands/network.ts index 34179406d..7a276ecf2 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -28,8 +28,6 @@ import {type ProfileManager} from '../core/profile-manager.js'; import {type CertificateManager} from '../core/certificate-manager.js'; import {type AnyYargs, type IP, type NodeAlias, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; -import {HaProxyComponent} from '../core/config/remote/components/ha-proxy-component.js'; import {v4 as uuidv4} from 'uuid'; import {type CommandDefinition, type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; @@ -1336,10 +1334,20 @@ export class NetworkCommand extends BaseCommand { remoteConfig.components.changeNodeState(nodeAlias, ConsensusNodeStates.REQUESTED); remoteConfig.components.addNewComponent( - ComponentFactory.createNewEnvoyProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewEnvoyProxyComponent( + this.remoteConfigManager, + clusterReference, + namespace, + nodeAlias, + ), ); remoteConfig.components.addNewComponent( - ComponentFactory.createNewHaProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewHaProxyComponent( + this.remoteConfigManager, + clusterReference, + namespace, + nodeAlias, + ), ); } }); diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 2754b8789..a2a423138 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -86,9 +86,6 @@ import {InjectTokens} from '../../core/dependency-injection/inject-tokens.js'; import {type RemoteConfigManager} from '../../core/config/remote/remote-config-manager.js'; import {type LocalConfig} from '../../core/config/local/local-config.js'; import {BaseCommand} from '../base.js'; -import {ConsensusNodeComponent} from '../../core/config/remote/components/consensus-node-component.js'; -import {EnvoyProxyComponent} from '../../core/config/remote/components/envoy-proxy-component.js'; -import {HaProxyComponent} from '../../core/config/remote/components/ha-proxy-component.js'; import {HEDERA_PLATFORM_VERSION} from '../../../version.js'; import {ShellRunner} from '../../core/shell-runner.js'; import {PathEx} from '../../business/utils/path-ex.js'; @@ -2514,13 +2511,28 @@ export class NodeCommandTasks { await this.remoteConfigManager.modify(async remoteConfig => { remoteConfig.components.addNewComponent( - ComponentFactory.createNewConsensusNodeComponent(nodeAlias, clusterReference, namespace, ConsensusNodeStates.STARTED), + ComponentFactory.createNewConsensusNodeComponent( + nodeAlias, + clusterReference, + namespace, + ConsensusNodeStates.STARTED, + ), ); remoteConfig.components.addNewComponent( - ComponentFactory.createNewEnvoyProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewEnvoyProxyComponent( + this.remoteConfigManager, + clusterReference, + namespace, + nodeAlias, + ), ); remoteConfig.components.addNewComponent( - ComponentFactory.createNewHaProxyComponent(this.remoteConfigManager, clusterReference, namespace, nodeAlias), + ComponentFactory.createNewHaProxyComponent( + this.remoteConfigManager, + clusterReference, + namespace, + nodeAlias, + ), ); }); diff --git a/src/commands/relay.ts b/src/commands/relay.ts index e892bf0f6..1cd2d626f 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -14,7 +14,7 @@ import {Flags as flags} from './flags.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {RelayComponent} from '../core/config/remote/components/relay-component.js'; +import {type RelayComponent} from '../core/config/remote/components/relay-component.js'; import * as Base64 from 'js-base64'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index ad24d5d76..634df26c8 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -5,7 +5,7 @@ import {describe, it} from 'mocha'; import {RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; import {BaseComponent} from '../../../../../../src/core/config/remote/components/base-component.js'; -import {ConsensusNodeComponent} from '../../../../../../src/core/config/remote/components/consensus-node-component.js'; +import {type ConsensusNodeComponent} from '../../../../../../src/core/config/remote/components/consensus-node-component.js'; import {HaProxyComponent} from '../../../../../../src/core/config/remote/components/ha-proxy-component.js'; import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/components/envoy-proxy-component.js'; import {MirrorNodeComponent} from '../../../../../../src/core/config/remote/components/mirror-node-component.js'; From d94c755e51dacd552302ffd69415f541998296eb Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 17:11:58 +0300 Subject: [PATCH 43/70] fixed circular dep Signed-off-by: Zhan Milenkov --- .../config/remote/components-data-wrapper.ts | 18 +--------------- .../remote/components/component-factory.ts | 21 ++++++++++++++++++- .../config/remote/remote-config-manager.ts | 8 ++++++- .../core/remote-config-validator.test.ts | 6 ------ .../remote/components-data-wrapper.test.ts | 8 ------- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 509bc35fc..339154478 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -10,11 +10,9 @@ import {EnvoyProxyComponent} from './components/envoy-proxy-component.js'; import {ConsensusNodeComponent} from './components/consensus-node-component.js'; import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-component.js'; import {type ClusterReference, type ComponentName} from './types.js'; -import {type NodeAliases} from '../../../types/aliases.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; -import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {isValidEnum} from '../../util/validation-helpers.js'; import {type BaseComponentStructure} from './components/interfaces/base-component-structure.js'; import {type RelayComponentStructure} from './components/interfaces/relay-component-structure.js'; @@ -22,7 +20,6 @@ import {type ConsensusNodeComponentStructure} from './components/interfaces/cons import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; import {ComponentNameTemplates} from './components/component-name-templates.js'; -import {ComponentFactory} from './components/component-factory.js'; /** * Represent the components in the remote config and handles: @@ -274,21 +271,8 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { } public static initializeWithNodes( - nodeAliases: NodeAliases, - clusterReference: ClusterReference, - namespace: NamespaceName, + consensusNodeComponents: Record, ): ComponentsDataWrapper { - const consensusNodeComponents: Record = {}; - - for (const nodeAlias of nodeAliases) { - consensusNodeComponents[nodeAlias] = ComponentFactory.createNewConsensusNodeComponent( - nodeAlias, - clusterReference, - namespace, - ConsensusNodeStates.NON_DEPLOYED, - ); - } - return new ComponentsDataWrapper(undefined, undefined, undefined, undefined, consensusNodeComponents); } diff --git a/src/core/config/remote/components/component-factory.ts b/src/core/config/remote/components/component-factory.ts index ba973811f..cf21d20d6 100644 --- a/src/core/config/remote/components/component-factory.ts +++ b/src/core/config/remote/components/component-factory.ts @@ -11,11 +11,11 @@ import {EnvoyProxyComponent} from './envoy-proxy-component.js'; import {Templates} from '../../../templates.js'; import {ConsensusNodeComponent} from './consensus-node-component.js'; import {BlockNodeComponent} from './block-node-component.js'; +import {ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; import {type RemoteConfigManagerApi} from '../api/remote-config-manager-api.js'; import {type ClusterReference, type ComponentName} from '../types.js'; import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; import {type NodeAlias, type NodeAliases, type NodeId} from '../../../../types/aliases.js'; -import {type ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; export class ComponentFactory { public static createNewRelayComponent( @@ -109,4 +109,23 @@ export class ComponentFactory { return new BlockNodeComponent(name, clusterReference, namespace.name, ComponentStates.ACTIVE); } + + public static createConsensusNodeComponentsFromNodeAliases( + nodeAliases: NodeAliases, + clusterReference: ClusterReference, + namespace: NamespaceName, + ): Record { + const consensusNodeComponents: Record = {}; + + for (const nodeAlias of nodeAliases) { + consensusNodeComponents[nodeAlias] = ComponentFactory.createNewConsensusNodeComponent( + nodeAlias, + clusterReference, + namespace, + ConsensusNodeStates.NON_DEPLOYED, + ); + } + + return consensusNodeComponents; + } } diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index 63ea4043c..f0d39511a 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -12,6 +12,7 @@ import {type K8Factory} from '../../../integration/kube/k8-factory.js'; import { type ClusterReference, type ClusterReferences, + type ComponentName, type Context, type DeploymentName, type NamespaceNameAsString, @@ -36,6 +37,8 @@ import {type ConfigMap} from '../../../integration/kube/resources/config-map/con import {getSoloVersion} from '../../../../version.js'; import {DeploymentStates} from './enumerations/deployment-states.js'; import {type RemoteConfigManagerApi} from './api/remote-config-manager-api.js'; +import {ComponentFactory} from './components/component-factory.js'; +import {ConsensusNodeComponent} from './components/consensus-node-component.js'; /** * Uses Kubernetes ConfigMaps to manage the remote configuration data by creating, loading, modifying, @@ -118,12 +121,15 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { const soloVersion = getSoloVersion(); const currentCommand = argv._.join(' '); + const consensusNodeComponents: Record = + ComponentFactory.createConsensusNodeComponentsFromNodeAliases(nodeAliases, clusterReference, namespace); + this.remoteConfig = new RemoteConfigDataWrapper({ clusters, metadata: new RemoteConfigMetadata(namespace.name, deployment, state, lastUpdatedAt, email, soloVersion), commandHistory: [currentCommand], lastExecutedCommand: currentCommand, - components: ComponentsDataWrapper.initializeWithNodes(nodeAliases, clusterReference, namespace), + components: ComponentsDataWrapper.initializeWithNodes(consensusNodeComponents), flags: await CommonFlagsDataWrapper.initialize(this.configManager, argv), }); diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index 2719da110..fff8a1afa 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -69,16 +69,11 @@ describe('RemoteConfigValidator', () => { // @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private const components: ComponentsDataWrapper = new ComponentsDataWrapper( - // @ts-expect-error - to access private constructor {[relayName]: new RelayComponent(relayName, cluster, namespace.name, ComponentStates.ACTIVE, consensusNodeAliases)}, - // @ts-expect-error - to access private constructor {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, - // @ts-expect-error - to access private constructor {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name, ComponentStates.ACTIVE)}, - // @ts-expect-error - to access private constructor {[envoyProxyName]: new EnvoyProxyComponent(envoyProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, { - // @ts-expect-error - to access private constructor [nodeAlias]: new ConsensusNodeComponent( nodeAlias, cluster, @@ -89,7 +84,6 @@ describe('RemoteConfigValidator', () => { ), }, { - // @ts-expect-error - to access private constructor [mirrorNodeExplorerName]: new MirrorNodeExplorerComponent( mirrorNodeExplorerName, cluster, diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 1af4ae778..802d9014b 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -54,37 +54,30 @@ export function createComponentsDataWrapper(): { const state: ComponentStates = ComponentStates.ACTIVE; const relays: Record = { - // @ts-expect-error - to access private constructor [componentName]: new RelayComponent(name, cluster, namespace, state, consensusNodeAliases), }; const haProxies: Record = { - // @ts-expect-error - to access private constructor [componentName]: new HaProxyComponent(name, cluster, namespace, state), }; const mirrorNodes: Record = { - // @ts-expect-error - to access private constructor [componentName]: new MirrorNodeComponent(name, cluster, namespace, state), }; const envoyProxies: Record = { - // @ts-expect-error - to access private constructor [componentName]: new EnvoyProxyComponent(name, cluster, namespace, state), }; const consensusNodes: Record = { - // @ts-expect-error - to access private constructor [componentName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), }; const mirrorNodeExplorers: Record = { - // @ts-expect-error - to access private constructor [componentName]: new MirrorNodeExplorerComponent(name, cluster, namespace, state), }; const blockNodes: Record = { - // @ts-expect-error - to access private constructor [componentName]: new BlockNodeComponent(name, cluster, namespace, state), }; @@ -162,7 +155,6 @@ describe('ComponentsDataWrapper', () => { cluster: 'cluster', namespace: 'new-namespace', }; - // @ts-expect-error - to access private constructor const newComponent: EnvoyProxyComponent = new EnvoyProxyComponent(name, cluster, namespace, state); componentsDataWrapper.addNewComponent(newComponent); From f1714882d137448c3177b3d69e60fa18c42f004e Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 17:17:53 +0300 Subject: [PATCH 44/70] fix failing tests Signed-off-by: Zhan Milenkov --- test/e2e/integration/core/remote-config-validator.test.ts | 2 ++ test/unit/core/config/remote/components/components.test.ts | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index fff8a1afa..b5279e84a 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -55,6 +55,8 @@ describe('RemoteConfigValidator', () => { await k8Factory.default().namespaces().delete(namespace); }); + // TODO TODO TODO: FIX TESTS + const cluster = 'cluster'; const nodeState = ConsensusNodeStates.STARTED; diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 634df26c8..0bd655fbf 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -3,7 +3,7 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; -import {RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; +import {type RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; import {BaseComponent} from '../../../../../../src/core/config/remote/components/base-component.js'; import {type ConsensusNodeComponent} from '../../../../../../src/core/config/remote/components/consensus-node-component.js'; import {HaProxyComponent} from '../../../../../../src/core/config/remote/components/ha-proxy-component.js'; @@ -21,6 +21,7 @@ import {type BaseComponentStructure} from '../../../../../../src/core/config/rem import {type RelayComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/relay-component-structure.js'; import {type ConsensusNodeComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/consensus-node-component-structure.js'; import {ComponentFactory} from '../../../../../../src/core/config/remote/components/component-factory.js'; +import {ComponentNameTemplates} from '../../../../../../src/core/config/remote/components/component-name-templates.js'; const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; @@ -73,8 +74,7 @@ describe('RelayComponent', () => { }); it('calling toObject() should return a valid data', () => { - // @ts-expect-error: to access private property - const name: ComponentName = RelayComponent.renderRelayName( + const name: ComponentName = ComponentNameTemplates.renderRelayName( remoteConfigManagerMock.components.getNewComponentIndex(), ); From 209e305a2c2b8562a52894e192dfe09f226bb1b5 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 23:35:24 +0300 Subject: [PATCH 45/70] refactored RemoteConfigValidator Signed-off-by: Zhan Milenkov --- .../config/remote/remote-config-validator.ts | 109 ++++++++++++++---- 1 file changed, 84 insertions(+), 25 deletions(-) diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 236547e9f..899c76f43 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -2,6 +2,8 @@ import * as constants from '../../constants.js'; import {SoloError} from '../../errors/solo-error.js'; +import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; +import {ComponentStates} from './enumerations/component-states.js'; import {type K8Factory} from '../../../integration/kube/k8-factory.js'; import {type ComponentsDataWrapper} from './components-data-wrapper.js'; import {type BaseComponent} from './components/base-component.js'; @@ -10,34 +12,83 @@ import {type LocalConfig} from '../local/local-config.js'; import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; -import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; -import {ComponentStates} from './enumerations/component-states.js'; /** * Static class is used to validate that components in the remote config * are present in the kubernetes cluster, and throw errors if there is mismatch. */ export class RemoteConfigValidator { + private static getRelayLabels(): string[] { + return [constants.SOLO_RELAY_LABEL]; + } + + private static getHaProxyLabels(component: BaseComponent): string[] { + return [`app=${component.name}`]; + } + + private static getMirrorNodeLabels(): string[] { + return constants.SOLO_HEDERA_MIRROR_IMPORTER; + } + + private static getEnvoyProxyLabels(component: BaseComponent): string[] { + return [`app=${component.name}`]; + } + + private static getMirrorNodeExplorerLabels(): string[] { + return [constants.SOLO_HEDERA_EXPLORER_LABEL]; + } + + private static getBlockNodeLabels(component: BaseComponent): string[] { + return [`app.kubernetes.io/instance=${component.name}`]; + } + + private static getConsensusNodeLabels(component: BaseComponent): string[] { + return [`app=network-${component.name}`]; + } + + private static consensusNodeSkipConditionCallback(nodeComponent: ConsensusNodeComponent): boolean { + return ( + nodeComponent.nodeState === ConsensusNodeStates.REQUESTED || + nodeComponent.nodeState === ConsensusNodeStates.NON_DEPLOYED + ); + } + private static componentValidations: Record< string, { - label: string[] | ((c: BaseComponent) => string[]); + getLabelsCallback: (component: BaseComponent) => string[]; type: string; - skipCondition?: (c: BaseComponent) => boolean; + skipCondition?: (component: BaseComponent) => boolean; } > = { - relays: {label: [constants.SOLO_RELAY_LABEL], type: 'Relay'}, - haProxies: {label: (c: BaseComponent): string[] => [`app=${c.name}`], type: 'HaProxy'}, - mirrorNodes: {label: constants.SOLO_HEDERA_MIRROR_IMPORTER, type: 'Block nodes'}, - envoyProxies: {label: (c: BaseComponent): string[] => [`app=${c.name}`], type: 'Envoy proxy'}, - mirrorNodeExplorers: {label: [constants.SOLO_HEDERA_EXPLORER_LABEL], type: 'Mirror node explorer'}, - blockNodes: {label: (c: BaseComponent): string[] => [`app.kubernetes.io/instance=${c.name}`], type: 'Block nodes'}, + relays: { + type: 'Relay', + getLabelsCallback: RemoteConfigValidator.getRelayLabels, + }, + haProxies: { + type: 'HaProxy', + getLabelsCallback: RemoteConfigValidator.getHaProxyLabels, + }, + mirrorNodes: { + type: 'Block nodes', + getLabelsCallback: RemoteConfigValidator.getMirrorNodeLabels, + }, + envoyProxies: { + type: 'Envoy proxy', + getLabelsCallback: RemoteConfigValidator.getEnvoyProxyLabels, + }, + mirrorNodeExplorers: { + type: 'Mirror node explorer', + getLabelsCallback: RemoteConfigValidator.getMirrorNodeExplorerLabels, + }, + blockNodes: { + type: 'Block nodes', + getLabelsCallback: RemoteConfigValidator.getBlockNodeLabels, + }, consensusNodes: { - label: (c: BaseComponent): string[] => [`app=network-${c.name}`], type: 'Consensus node', - skipCondition(c: ConsensusNodeComponent): boolean { - return c.nodeState === ConsensusNodeStates.REQUESTED || c.nodeState === ConsensusNodeStates.NON_DEPLOYED; - }, + getLabelsCallback: RemoteConfigValidator.getConsensusNodeLabels, + skipCondition: RemoteConfigValidator.consensusNodeSkipConditionCallback, }, }; @@ -48,13 +99,21 @@ export class RemoteConfigValidator { localConfig: LocalConfig, skipConsensusNodes: boolean, ): Promise { - const validations: Promise[] = Object.entries(this.componentValidations) - .filter(([key]): boolean => key !== 'consensusNodes' || !skipConsensusNodes) - .flatMap(([key, {label, type, skipCondition}]): Promise[] => - this.validateComponentList(namespace, components[key], k8Factory, localConfig, label, type, skipCondition), + const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidations) + .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) + .flatMap(([key, {getLabelsCallback, type, skipCondition}]): Promise[] => + RemoteConfigValidator.validateComponentList( + namespace, + components[key], + k8Factory, + localConfig, + getLabelsCallback, + type, + skipCondition, + ), ); - await Promise.all(validations); + await Promise.all(validationPromises); } private static validateComponentList( @@ -62,7 +121,7 @@ export class RemoteConfigValidator { components: Record, k8Factory: K8Factory, localConfig: LocalConfig, - label: string[] | ((c: BaseComponent) => string[]), + getLabelsCallback: (component: BaseComponent) => string[], type: string, skipCondition?: (component: BaseComponent) => boolean, ): Promise[] { @@ -75,9 +134,9 @@ export class RemoteConfigValidator { } const context: Context = localConfig.clusterRefs[component.cluster]; - const labels: string[] = typeof label === 'function' ? label(component) : label; + const labels: string[] = getLabelsCallback(component); - await this.validateComponent(namespace, component, k8Factory, context, labels, type); + await RemoteConfigValidator.validateComponent(namespace, component, k8Factory, context, labels, type); }); } @@ -96,7 +155,7 @@ export class RemoteConfigValidator { throw new Error('Pod not found'); // to return the generic error message } } catch (error) { - RemoteConfigValidator.throwValidationError(errorType, component, error); + throw RemoteConfigValidator.buildValidationError(errorType, component, error); } } @@ -107,8 +166,8 @@ export class RemoteConfigValidator { * @param component - component which is not found in the cluster * @param error - original error for the kube client */ - private static throwValidationError(type: string, component: BaseComponent, error: Error | unknown): never { - throw new SoloError( + private static buildValidationError(type: string, component: BaseComponent, error: Error | unknown): SoloError { + return new SoloError( `${type} in remote config with name ${component.name} ` + `was not found in namespace: ${component.namespace}, cluster: ${component.cluster}`, error, From 639a3d40ba2977792dae7f7f5a19a2fb0a31f26a Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Mon, 7 Apr 2025 23:43:40 +0300 Subject: [PATCH 46/70] refactoring RemoteConfigValidator Signed-off-by: Zhan Milenkov --- .../config/remote/remote-config-validator.ts | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 899c76f43..5f3672ed7 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -4,14 +4,14 @@ import * as constants from '../../constants.js'; import {SoloError} from '../../errors/solo-error.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; -import {type K8Factory} from '../../../integration/kube/k8-factory.js'; +import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; import {type ComponentsDataWrapper} from './components-data-wrapper.js'; import {type BaseComponent} from './components/base-component.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {type LocalConfig} from '../local/local-config.js'; import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; -import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; +import {type K8Factory} from '../../../integration/kube/k8-factory.js'; /** * Static class is used to validate that components in the remote config @@ -102,7 +102,7 @@ export class RemoteConfigValidator { const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidations) .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) .flatMap(([key, {getLabelsCallback, type, skipCondition}]): Promise[] => - RemoteConfigValidator.validateComponentList( + RemoteConfigValidator.validateComponentGroup( namespace, components[key], k8Factory, @@ -116,7 +116,7 @@ export class RemoteConfigValidator { await Promise.all(validationPromises); } - private static validateComponentList( + private static validateComponentGroup( namespace: NamespaceName, components: Record, k8Factory: K8Factory, @@ -136,27 +136,16 @@ export class RemoteConfigValidator { const context: Context = localConfig.clusterRefs[component.cluster]; const labels: string[] = getLabelsCallback(component); - await RemoteConfigValidator.validateComponent(namespace, component, k8Factory, context, labels, type); - }); - } - - private static async validateComponent( - namespace: NamespaceName, - component: BaseComponent, - k8Factory: K8Factory, - context: Context, - labels: string[], - errorType: string, - ): Promise { - try { - const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - if (pods.length === 0) { - throw new Error('Pod not found'); // to return the generic error message + if (pods.length === 0) { + throw new Error('Pod not found'); // to return the generic error message + } + } catch (error) { + throw RemoteConfigValidator.buildValidationError(type, component, error); } - } catch (error) { - throw RemoteConfigValidator.buildValidationError(errorType, component, error); - } + }); } /** From dd007e364f7e7cfd98ff8def388f2aa4736f6b9f Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 00:04:57 +0300 Subject: [PATCH 47/70] renamed files with Strcuture postfix to Struct Signed-off-by: Zhan Milenkov --- .../config/remote/components-data-wrapper.ts | 16 ++++++++-------- .../config/remote/components/base-component.ts | 6 +++--- .../remote/components/block-node-component.ts | 4 ++-- .../components/consensus-node-component.ts | 8 ++++---- .../remote/components/envoy-proxy-component.ts | 4 ++-- .../remote/components/ha-proxy-component.ts | 4 ++-- ...ent-structure.ts => base-component-struct.ts} | 2 +- ...ure.ts => consensus-node-component-struct.ts} | 4 ++-- .../interfaces/relay-component-struct.ts | 8 ++++++++ .../interfaces/relay-component-structure.ts | 8 -------- .../remote/components/mirror-node-component.ts | 4 ++-- .../components/mirror-node-explorer-component.ts | 4 ++-- .../config/remote/components/relay-component.ts | 11 ++++------- .../remote/interfaces/components-data-struct.ts | 4 ++-- .../config/remote/remote-config-validator.ts | 4 ++-- .../config/remote/components/components.test.ts | 12 ++++++------ 16 files changed, 50 insertions(+), 53 deletions(-) rename src/core/config/remote/components/interfaces/{base-component-structure.ts => base-component-struct.ts} (88%) rename src/core/config/remote/components/interfaces/{consensus-node-component-structure.ts => consensus-node-component-struct.ts} (53%) create mode 100644 src/core/config/remote/components/interfaces/relay-component-struct.ts delete mode 100644 src/core/config/remote/components/interfaces/relay-component-structure.ts diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 339154478..126c2eb6d 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -14,9 +14,9 @@ import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {ComponentStates} from './enumerations/component-states.js'; import {isValidEnum} from '../../util/validation-helpers.js'; -import {type BaseComponentStructure} from './components/interfaces/base-component-structure.js'; -import {type RelayComponentStructure} from './components/interfaces/relay-component-structure.js'; -import {type ConsensusNodeComponentStructure} from './components/interfaces/consensus-node-component-structure.js'; +import {type BaseComponentStruct} from './components/interfaces/base-component-struct.js'; +import {type RelayComponentStruct} from './components/interfaces/relay-component-struct.js'; +import {type ConsensusNodeComponentStruct} from './components/interfaces/consensus-node-component-struct.js'; import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; import {ComponentNameTemplates} from './components/component-name-templates.js'; @@ -199,7 +199,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { switch (componentType) { case ComponentTypes.Relay: { for (const [componentName, component] of Object.entries(subComponents)) { - relays[componentName] = RelayComponent.fromObject(component as RelayComponentStructure); + relays[componentName] = RelayComponent.fromObject(component as RelayComponentStruct); } break; } @@ -228,7 +228,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { case ComponentTypes.ConsensusNode: { for (const [componentName, component] of Object.entries(subComponents)) { consensusNodes[componentName] = ConsensusNodeComponent.fromObject( - component as ConsensusNodeComponentStructure, + component as ConsensusNodeComponentStruct, ); } break; @@ -333,11 +333,11 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { private transformComponentGroupToObject( components: Record, - ): Record { - const transformedComponents: Record = {}; + ): Record { + const transformedComponents: Record = {}; for (const [componentName, component] of Object.entries(components)) { - transformedComponents[componentName] = component.toObject() as BaseComponentStructure; + transformedComponents[componentName] = component.toObject() as BaseComponentStruct; } return transformedComponents; diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 65864f859..10fceb025 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -6,13 +6,13 @@ import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {ComponentStates} from '../enumerations/component-states.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; -import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; /** * Represents the base structure and common functionality for all components within the system. * This class provides validation, comparison, and serialization functionality for components. */ -export class BaseComponent implements BaseComponentStructure, Validate, ToObject { +export class BaseComponent implements BaseComponentStruct, Validate, ToObject { /** * @param type - type for identifying. * @param name - the name to distinguish components. @@ -65,7 +65,7 @@ export class BaseComponent implements BaseComponentStructure, Validate, ToObject } } - public toObject(): BaseComponentStructure { + public toObject(): BaseComponentStruct { return { name: this.name, cluster: this.cluster, diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index ddbe13f33..4682d66eb 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -4,7 +4,7 @@ import {BaseComponent} from './base-component.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; -import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class BlockNodeComponent extends BaseComponent { public constructor( @@ -20,7 +20,7 @@ export class BlockNodeComponent extends BaseComponent { /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: BaseComponentStructure): BlockNodeComponent { + public static fromObject(component: BaseComponentStruct): BlockNodeComponent { const {name, cluster, namespace, state} = component; return new BlockNodeComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index ca82ec5f4..1e9569038 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -8,7 +8,7 @@ import {isValidEnum} from '../../../util/validation-helpers.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject} from '../../../../types/index.js'; import {type ComponentStates} from '../enumerations/component-states.js'; -import {type ConsensusNodeComponentStructure} from './interfaces/consensus-node-component-structure.js'; +import {type ConsensusNodeComponentStruct} from './interfaces/consensus-node-component-struct.js'; /** * Represents a consensus node component within the system. @@ -18,7 +18,7 @@ import {type ConsensusNodeComponentStructure} from './interfaces/consensus-node- */ export class ConsensusNodeComponent extends BaseComponent - implements ConsensusNodeComponentStructure, ToObject + implements ConsensusNodeComponentStruct, ToObject { private _nodeState: ConsensusNodeStates; @@ -55,7 +55,7 @@ export class ConsensusNodeComponent /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: ConsensusNodeComponentStructure): ConsensusNodeComponent { + public static fromObject(component: ConsensusNodeComponentStruct): ConsensusNodeComponent { const {name, cluster, state, namespace, nodeState, nodeId} = component; return new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, nodeId); } @@ -76,7 +76,7 @@ export class ConsensusNodeComponent } } - public override toObject(): ConsensusNodeComponentStructure { + public override toObject(): ConsensusNodeComponentStruct { return { ...super.toObject(), nodeState: this.nodeState, diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index 1b990e617..97e247b77 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -4,7 +4,7 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class EnvoyProxyComponent extends BaseComponent { public constructor( @@ -20,7 +20,7 @@ export class EnvoyProxyComponent extends BaseComponent { /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: BaseComponentStructure): EnvoyProxyComponent { + public static fromObject(component: BaseComponentStruct): EnvoyProxyComponent { const {name, cluster, namespace, state} = component; return new EnvoyProxyComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 49e892196..4eb5710f2 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -4,7 +4,7 @@ import {BaseComponent} from './base-component.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; -import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class HaProxyComponent extends BaseComponent { public constructor( @@ -20,7 +20,7 @@ export class HaProxyComponent extends BaseComponent { /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: BaseComponentStructure): HaProxyComponent { + public static fromObject(component: BaseComponentStruct): HaProxyComponent { const {name, cluster, namespace, state} = component; return new HaProxyComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/interfaces/base-component-structure.ts b/src/core/config/remote/components/interfaces/base-component-struct.ts similarity index 88% rename from src/core/config/remote/components/interfaces/base-component-structure.ts rename to src/core/config/remote/components/interfaces/base-component-struct.ts index 3904bf917..9e7ca059a 100644 --- a/src/core/config/remote/components/interfaces/base-component-structure.ts +++ b/src/core/config/remote/components/interfaces/base-component-struct.ts @@ -3,7 +3,7 @@ import {type ComponentStates} from '../../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../types.js'; -export interface BaseComponentStructure { +export interface BaseComponentStruct { name: ComponentName; cluster: ClusterReference; namespace: NamespaceNameAsString; diff --git a/src/core/config/remote/components/interfaces/consensus-node-component-structure.ts b/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts similarity index 53% rename from src/core/config/remote/components/interfaces/consensus-node-component-structure.ts rename to src/core/config/remote/components/interfaces/consensus-node-component-struct.ts index eba65740a..85aabc1bf 100644 --- a/src/core/config/remote/components/interfaces/consensus-node-component-structure.ts +++ b/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -import {type BaseComponentStructure} from './base-component-structure.js'; +import {type BaseComponentStruct} from './base-component-struct.js'; import {type ConsensusNodeStates} from '../../enumerations/consensus-node-states.js'; -export interface ConsensusNodeComponentStructure extends BaseComponentStructure { +export interface ConsensusNodeComponentStruct extends BaseComponentStruct { nodeId: number; nodeState: ConsensusNodeStates; } diff --git a/src/core/config/remote/components/interfaces/relay-component-struct.ts b/src/core/config/remote/components/interfaces/relay-component-struct.ts new file mode 100644 index 000000000..358a14a54 --- /dev/null +++ b/src/core/config/remote/components/interfaces/relay-component-struct.ts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type BaseComponentStruct} from './base-component-struct.js'; +import {type NodeAliases} from '../../../../../types/aliases.js'; + +export interface RelayComponentStruct extends BaseComponentStruct { + consensusNodeAliases: NodeAliases; +} diff --git a/src/core/config/remote/components/interfaces/relay-component-structure.ts b/src/core/config/remote/components/interfaces/relay-component-structure.ts deleted file mode 100644 index e476dc9e4..000000000 --- a/src/core/config/remote/components/interfaces/relay-component-structure.ts +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {type BaseComponentStructure} from './base-component-structure.js'; -import {type NodeAliases} from '../../../../../types/aliases.js'; - -export interface RelayComponentStructure extends BaseComponentStructure { - consensusNodeAliases: NodeAliases; -} diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index c835b2c04..ed9360b7d 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -4,7 +4,7 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class MirrorNodeComponent extends BaseComponent { public constructor( @@ -20,7 +20,7 @@ export class MirrorNodeComponent extends BaseComponent { /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: BaseComponentStructure): MirrorNodeComponent { + public static fromObject(component: BaseComponentStruct): MirrorNodeComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index c8aa9703c..412562d2d 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -4,7 +4,7 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; -import {type BaseComponentStructure} from './interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class MirrorNodeExplorerComponent extends BaseComponent { public constructor( @@ -20,7 +20,7 @@ export class MirrorNodeExplorerComponent extends BaseComponent { /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: BaseComponentStructure): MirrorNodeExplorerComponent { + public static fromObject(component: BaseComponentStruct): MirrorNodeExplorerComponent { const {name, cluster, namespace, state} = component; return new MirrorNodeExplorerComponent(name, cluster, namespace, state); } diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index 501b9e02b..e0df62a79 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -7,12 +7,9 @@ import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; -import {type RelayComponentStructure} from './interfaces/relay-component-structure.js'; +import {type RelayComponentStruct} from './interfaces/relay-component-struct.js'; -export class RelayComponent - extends BaseComponent - implements RelayComponentStructure, ToObject -{ +export class RelayComponent extends BaseComponent implements RelayComponentStruct, ToObject { /** * @param name - to distinguish components. * @param clusterReference - in which the component is deployed. @@ -34,7 +31,7 @@ export class RelayComponent /* -------- Utilities -------- */ /** Handles creating instance of the class from plain object. */ - public static fromObject(component: RelayComponentStructure): RelayComponent { + public static fromObject(component: RelayComponentStruct): RelayComponent { const {name, cluster, namespace, state, consensusNodeAliases} = component; return new RelayComponent(name, cluster, namespace, state, consensusNodeAliases); } @@ -49,7 +46,7 @@ export class RelayComponent } } - public override toObject(): RelayComponentStructure { + public override toObject(): RelayComponentStruct { return { consensusNodeAliases: this.consensusNodeAliases, ...super.toObject(), diff --git a/src/core/config/remote/interfaces/components-data-struct.ts b/src/core/config/remote/interfaces/components-data-struct.ts index 3d3563e7d..aacd463dd 100644 --- a/src/core/config/remote/interfaces/components-data-struct.ts +++ b/src/core/config/remote/interfaces/components-data-struct.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 import {type ComponentTypes} from '../enumerations/component-types.js'; -import {type BaseComponentStructure} from '../components/interfaces/base-component-structure.js'; +import {type BaseComponentStruct} from '../components/interfaces/base-component-struct.js'; import {type ComponentName} from '../types.js'; -export type ComponentsDataStruct = Record>; +export type ComponentsDataStruct = Record>; diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 5f3672ed7..cc5abfba7 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -53,7 +53,7 @@ export class RemoteConfigValidator { ); } - private static componentValidations: Record< + private static componentValidationsMapping: Record< string, { getLabelsCallback: (component: BaseComponent) => string[]; @@ -99,7 +99,7 @@ export class RemoteConfigValidator { localConfig: LocalConfig, skipConsensusNodes: boolean, ): Promise { - const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidations) + const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidationsMapping) .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) .flatMap(([key, {getLabelsCallback, type, skipCondition}]): Promise[] => RemoteConfigValidator.validateComponentGroup( diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 0bd655fbf..57e2604f3 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -17,9 +17,9 @@ import {ComponentStates} from '../../../../../../src/core/config/remote/enumerat import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; import {type ClusterReference, type ComponentName} from '../../../../../../src/core/config/remote/types.js'; import {NamespaceName} from '../../../../../../src/integration/kube/resources/namespace/namespace-name.js'; -import {type BaseComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/base-component-structure.js'; -import {type RelayComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/relay-component-structure.js'; -import {type ConsensusNodeComponentStructure} from '../../../../../../src/core/config/remote/components/interfaces/consensus-node-component-structure.js'; +import {type BaseComponentStruct} from '../../../../../../src/core/config/remote/components/interfaces/base-component-struct.js'; +import {type RelayComponentStruct} from '../../../../../../src/core/config/remote/components/interfaces/relay-component-struct.js'; +import {type ConsensusNodeComponentStruct} from '../../../../../../src/core/config/remote/components/interfaces/consensus-node-component-struct.js'; import {ComponentFactory} from '../../../../../../src/core/config/remote/components/component-factory.js'; import {ComponentNameTemplates} from '../../../../../../src/core/config/remote/components/component-name-templates.js'; @@ -36,7 +36,7 @@ function testBaseComponentData(classComponent: any): void { }); it('calling toObject() should return a valid data', () => { - const data: BaseComponentStructure = { + const data: BaseComponentStruct = { name: componentName, cluster: clusterReference, namespace: namespace.name, @@ -78,7 +78,7 @@ describe('RelayComponent', () => { remoteConfigManagerMock.components.getNewComponentIndex(), ); - const values: RelayComponentStructure = { + const values: RelayComponentStruct = { name, cluster: clusterReference, namespace: namespace.name, @@ -118,7 +118,7 @@ describe('ConsensusNodeComponent', () => { it('calling toObject() should return a valid data', () => { const nodeAlias: NodeAlias = 'node1'; - const values: ConsensusNodeComponentStructure = { + const values: ConsensusNodeComponentStruct = { name: nodeAlias, cluster: clusterReference, namespace: namespace.name, From 3bc7bff0643097e8d366d70bd69fa6c764cb0c4d Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 00:10:58 +0300 Subject: [PATCH 48/70] removed listr-config-tasks.ts Signed-off-by: Zhan Milenkov --- src/commands/explorer.ts | 15 ++++++++--- src/core/config/remote/listr-config-tasks.ts | 28 -------------------- 2 files changed, 12 insertions(+), 31 deletions(-) delete mode 100644 src/core/config/remote/listr-config-tasks.ts diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 6c26201c4..d88e37f28 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -12,7 +12,7 @@ import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js'; -import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; +import {AnyListrContext, type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {type MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; import * as helpers from '../core/helpers.js'; @@ -237,7 +237,7 @@ export class ExplorerCommand extends BaseCommand { return ListrLock.newAcquireLockTask(lease, task); }, }, - ListrRemoteConfig.loadRemoteConfig(this.remoteConfigManager, argv), + this.loadRemoteConfigTask(argv), { title: 'Install cert manager', task: async context_ => { @@ -463,7 +463,7 @@ export class ExplorerCommand extends BaseCommand { return ListrLock.newAcquireLockTask(lease, task); }, }, - ListrRemoteConfig.loadRemoteConfig(this.remoteConfigManager, argv), + this.loadRemoteConfigTask(argv), { title: 'Destroy explorer', task: async context_ => { @@ -574,6 +574,15 @@ export class ExplorerCommand extends BaseCommand { }; } + private loadRemoteConfigTask(argv: ArgvStruct): SoloListrTask { + return { + title: 'Load remote config', + task: async (): Promise => { + await this.remoteConfigManager.loadAndValidate(argv); + }, + }; + } + /** Removes the explorer components from remote config. */ private disableMirrorNodeExplorerComponents(): SoloListrTask { return { diff --git a/src/core/config/remote/listr-config-tasks.ts b/src/core/config/remote/listr-config-tasks.ts deleted file mode 100644 index 5c1ccb551..000000000 --- a/src/core/config/remote/listr-config-tasks.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {type SoloListrTask} from '../../../types/index.js'; -import {type AnyObject} from '../../../types/aliases.js'; -import {type RemoteConfigManager} from './remote-config-manager.js'; - -/** - * Static class that handles all tasks related to remote config used by other commands. - */ -export class ListrRemoteConfig { - /** - * Loads the remote config from the config class and performs component validation. - * - * @param remoteConfigManager - * @param argv - used to update the last executed command and command history - */ - public static loadRemoteConfig( - remoteConfigManager: RemoteConfigManager, - argv: {_: string[]} & AnyObject, - ): SoloListrTask { - return { - title: 'Load remote config', - task: async (): Promise => { - await remoteConfigManager.loadAndValidate(argv); - }, - }; - } -} From 4d12250a5b31fee8addda08070e272c9bbddef2a Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 00:12:20 +0300 Subject: [PATCH 49/70] lint-fix Signed-off-by: Zhan Milenkov --- src/commands/explorer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index d88e37f28..99906f667 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -11,8 +11,7 @@ import {HEDERA_EXPLORER_CHART_URL, INGRESS_CONTROLLER_NAME} from '../core/consta import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; -import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js'; -import {AnyListrContext, type AnyYargs, type ArgvStruct} from '../types/aliases.js'; +import {type AnyListrContext, type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {type MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; import * as helpers from '../core/helpers.js'; From b325bc6e1602d3cd91d2a0f2067b142668eb20ea Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 13:48:53 +0300 Subject: [PATCH 50/70] improve RemoteConfigValidator and add intergration tests after validation strategy rafoctor Signed-off-by: Zhan Milenkov --- .../config/remote/remote-config-validator.ts | 45 ++- .../core/remote-config-validator.test.ts | 374 ++++++++++-------- 2 files changed, 227 insertions(+), 192 deletions(-) diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index cc5abfba7..b5769f3a3 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -57,36 +57,36 @@ export class RemoteConfigValidator { string, { getLabelsCallback: (component: BaseComponent) => string[]; - type: string; + displayName: string; skipCondition?: (component: BaseComponent) => boolean; } > = { relays: { - type: 'Relay', + displayName: 'Relay', getLabelsCallback: RemoteConfigValidator.getRelayLabels, }, haProxies: { - type: 'HaProxy', + displayName: 'HaProxy', getLabelsCallback: RemoteConfigValidator.getHaProxyLabels, }, mirrorNodes: { - type: 'Block nodes', + displayName: 'Mirror node', getLabelsCallback: RemoteConfigValidator.getMirrorNodeLabels, }, envoyProxies: { - type: 'Envoy proxy', + displayName: 'Envoy proxy', getLabelsCallback: RemoteConfigValidator.getEnvoyProxyLabels, }, mirrorNodeExplorers: { - type: 'Mirror node explorer', + displayName: 'Mirror node explorer', getLabelsCallback: RemoteConfigValidator.getMirrorNodeExplorerLabels, }, blockNodes: { - type: 'Block nodes', + displayName: 'Block node', getLabelsCallback: RemoteConfigValidator.getBlockNodeLabels, }, consensusNodes: { - type: 'Consensus node', + displayName: 'Consensus node', getLabelsCallback: RemoteConfigValidator.getConsensusNodeLabels, skipCondition: RemoteConfigValidator.consensusNodeSkipConditionCallback, }, @@ -101,14 +101,14 @@ export class RemoteConfigValidator { ): Promise { const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidationsMapping) .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) - .flatMap(([key, {getLabelsCallback, type, skipCondition}]): Promise[] => + .flatMap(([key, {getLabelsCallback, displayName, skipCondition}]): Promise[] => RemoteConfigValidator.validateComponentGroup( namespace, components[key], k8Factory, localConfig, getLabelsCallback, - type, + displayName, skipCondition, ), ); @@ -122,7 +122,7 @@ export class RemoteConfigValidator { k8Factory: K8Factory, localConfig: LocalConfig, getLabelsCallback: (component: BaseComponent) => string[], - type: string, + displayName: string, skipCondition?: (component: BaseComponent) => boolean, ): Promise[] { return Object.values(components).map(async (component): Promise => { @@ -143,7 +143,7 @@ export class RemoteConfigValidator { throw new Error('Pod not found'); // to return the generic error message } } catch (error) { - throw RemoteConfigValidator.buildValidationError(type, component, error); + throw RemoteConfigValidator.buildValidationError(displayName, component, error); } }); } @@ -151,16 +151,27 @@ export class RemoteConfigValidator { /** * Generic handler that throws errors. * - * @param type - name to display in error message + * @param displayName - name to display in error message * @param component - component which is not found in the cluster * @param error - original error for the kube client */ - private static buildValidationError(type: string, component: BaseComponent, error: Error | unknown): SoloError { + private static buildValidationError( + displayName: string, + component: BaseComponent, + error: Error | unknown, + ): SoloError { return new SoloError( - `${type} in remote config with name ${component.name} ` + - `was not found in namespace: ${component.namespace}, cluster: ${component.cluster}`, + RemoteConfigValidator.buildValidationErrorMessage(displayName, component), error, - {component: component.toObject()}, + component.toObject(), + ); + } + + public static buildValidationErrorMessage(displayName: string, component: BaseComponent): string { + return ( + `${displayName} in remote config with name ${component.name} was not found in ` + + `namespace: ${component.namespace}, ` + + `cluster: ${component.cluster}` ); } } diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index b5279e84a..b0ca8a9d6 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -1,23 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 -import {describe, it} from 'mocha'; +import {beforeEach, describe, it} from 'mocha'; import {expect} from 'chai'; - -import * as constants from '../../../../src/core/constants.js'; -import {type ConfigManager} from '../../../../src/core/config-manager.js'; -import {Templates} from '../../../../src/core/templates.js'; -import {Flags as flags} from '../../../../src/commands/flags.js'; import {RemoteConfigValidator} from '../../../../src/core/config/remote/remote-config-validator.js'; import {ComponentsDataWrapper} from '../../../../src/core/config/remote/components-data-wrapper.js'; import {SoloError} from '../../../../src/core/errors/solo-error.js'; -import {RelayComponent} from '../../../../src/core/config/remote/components/relay-component.js'; -import {HaProxyComponent} from '../../../../src/core/config/remote/components/ha-proxy-component.js'; -import {MirrorNodeComponent} from '../../../../src/core/config/remote/components/mirror-node-component.js'; -import {ConsensusNodeComponent} from '../../../../src/core/config/remote/components/consensus-node-component.js'; -import {MirrorNodeExplorerComponent} from '../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; -import {EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy-proxy-component.js'; - -import {type ArgvStruct, type NodeAlias, type NodeAliases} from '../../../../src/types/aliases.js'; +import {type RelayComponent} from '../../../../src/core/config/remote/components/relay-component.js'; +import {type HaProxyComponent} from '../../../../src/core/config/remote/components/ha-proxy-component.js'; +import {type MirrorNodeComponent} from '../../../../src/core/config/remote/components/mirror-node-component.js'; +import {type ConsensusNodeComponent} from '../../../../src/core/config/remote/components/consensus-node-component.js'; +import {type MirrorNodeExplorerComponent} from '../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; +import {type EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy-proxy-component.js'; + +import {type NodeAlias, NodeAliases} from '../../../../src/types/aliases.js'; import {container} from 'tsyringe-neo'; import {NamespaceName} from '../../../../src/integration/kube/resources/namespace/namespace-name.js'; import {PodReference} from '../../../../src/integration/kube/resources/pod/pod-reference.js'; @@ -30,19 +25,100 @@ import {getTestCacheDirectory} from '../../../test-utility.js'; import {Duration} from '../../../../src/core/time/duration.js'; import {LocalConfigDataWrapper} from '../../../../src/core/config/local/local-config-data-wrapper.js'; import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentStates} from '../../../../src/core/config/remote/enumerations/component-states.js'; +import {type ClusterReference, ComponentName} from '../../../../src/core/config/remote/types.js'; +import {ComponentFactory} from '../../../../src/core/config/remote/components/component-factory.js'; +import {type BlockNodeComponent} from '../../../../src/core/config/remote/components/block-node-component.js'; +import {BaseComponent} from '../../../../src/core/config/remote/components/base-component.js'; +import {ComponentTypes} from '../../../../src/core/config/remote/enumerations/component-types.js'; + +interface ComponentsRecord { + explorer: MirrorNodeExplorerComponent; + blockNode: BlockNodeComponent; + mirrorNode: MirrorNodeComponent; + relay: RelayComponent; + consensusNode: ConsensusNodeComponent; + haProxy: HaProxyComponent; + envoyProxy: EnvoyProxyComponent; +} + +interface LabelRecord { + explorer: string[]; + blockNode: string[]; + mirrorNode: string[]; + relay: string[]; + consensusNode: string[]; + haProxy: string[]; + envoyProxy: string[]; +} + +interface ComponentsData { + namespace: NamespaceName; + components: ComponentsRecord; + labelRecord: LabelRecord; + componentsDataWrapper: ComponentsDataWrapper; +} + +function prepareComponentsData(namespace: NamespaceName): ComponentsData { + const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; + + const clusterReference: ClusterReference = 'cluster'; + const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; + const nodeAlias: NodeAlias = 'node1'; + + const components: ComponentsRecord = { + explorer: ComponentFactory.createNewExplorerComponent(remoteConfigManagerMock, clusterReference, namespace), + blockNode: ComponentFactory.createNewBlockNodeComponent(remoteConfigManagerMock, clusterReference, namespace), + mirrorNode: ComponentFactory.createNewMirrorNodeComponent(remoteConfigManagerMock, clusterReference, namespace), + relay: ComponentFactory.createNewRelayComponent(remoteConfigManagerMock, clusterReference, namespace, [nodeAlias]), + consensusNode: ComponentFactory.createNewConsensusNodeComponent(nodeAlias, clusterReference, namespace, nodeState), + haProxy: ComponentFactory.createNewHaProxyComponent( + remoteConfigManagerMock, + clusterReference, + namespace, + nodeAlias, + ), + envoyProxy: ComponentFactory.createNewEnvoyProxyComponent( + remoteConfigManagerMock, + clusterReference, + namespace, + nodeAlias, + ), + }; + + const labelRecord: LabelRecord = { + // @ts-expect-error - to access private property + relay: RemoteConfigValidator.getRelayLabels(), + // @ts-expect-error - to access private property + haProxy: RemoteConfigValidator.getHaProxyLabels(components.haProxy), + // @ts-expect-error - to access private property + mirrorNode: RemoteConfigValidator.getMirrorNodeLabels(), + // @ts-expect-error - to access private property + envoyProxy: RemoteConfigValidator.getEnvoyProxyLabels(components.envoyProxy), + // @ts-expect-error - to access private property + explorer: RemoteConfigValidator.getMirrorNodeExplorerLabels(), + // @ts-expect-error - to access private property + blockNode: RemoteConfigValidator.getBlockNodeLabels(components.blockNode), + // @ts-expect-error - to access private property + consensusNode: RemoteConfigValidator.getConsensusNodeLabels(components.consensusNode), + }; + + const componentsDataWrapper: ComponentsDataWrapper = ComponentsDataWrapper.initializeEmpty(); + + return {namespace, components, labelRecord, componentsDataWrapper}; +} describe('RemoteConfigValidator', () => { - const namespace = NamespaceName.of('remote-config-validator'); + const namespace: NamespaceName = NamespaceName.of('remote-config-validator'); - let configManager: ConfigManager; let k8Factory: K8Factory; let localConfig: LocalConfig; - const filePath = `${getTestCacheDirectory('LocalConfig')}/localConfig.yaml`; + const filePath: string = `${getTestCacheDirectory('LocalConfig')}/localConfig.yaml`; + + let components: ComponentsRecord; + let labelRecord: LabelRecord; + let componentsDataWrapper: ComponentsDataWrapper; before(async () => { - configManager = container.resolve(InjectTokens.ConfigManager); - configManager.update({[flags.namespace.name]: namespace} as ArgvStruct); k8Factory = container.resolve(InjectTokens.K8Factory); localConfig = new LocalConfig(filePath); // @ts-expect-error - TS2341: to mock @@ -50,189 +126,137 @@ describe('RemoteConfigValidator', () => { await k8Factory.default().namespaces().create(namespace); }); + beforeEach(() => { + const testData: ComponentsData = prepareComponentsData(namespace); + components = testData.components; + labelRecord = testData.labelRecord; + componentsDataWrapper = testData.componentsDataWrapper; + }); + after(async function () { this.timeout(Duration.ofMinutes(5).toMillis()); await k8Factory.default().namespaces().delete(namespace); }); - // TODO TODO TODO: FIX TESTS - - const cluster = 'cluster'; - const nodeState = ConsensusNodeStates.STARTED; - - const nodeAlias = 'node1' as NodeAlias; - const haProxyName = 'haproxy-' + nodeAlias; - const envoyProxyName = 'envoy-proxy-' + nodeAlias; - const relayName = 'relay'; - const mirrorNodeName = 'mirror-node'; - const mirrorNodeExplorerName = 'mirror-node-explorer'; - - const consensusNodeAliases: NodeAliases = [nodeAlias]; - - // @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private - const components: ComponentsDataWrapper = new ComponentsDataWrapper( - {[relayName]: new RelayComponent(relayName, cluster, namespace.name, ComponentStates.ACTIVE, consensusNodeAliases)}, - {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, - {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name, ComponentStates.ACTIVE)}, - {[envoyProxyName]: new EnvoyProxyComponent(envoyProxyName, cluster, namespace.name, ComponentStates.ACTIVE)}, - { - [nodeAlias]: new ConsensusNodeComponent( - nodeAlias, - cluster, - namespace.name, - ComponentStates.ACTIVE, - nodeState, - Templates.nodeIdFromNodeAlias(nodeAlias), - ), - }, - { - [mirrorNodeExplorerName]: new MirrorNodeExplorerComponent( - mirrorNodeExplorerName, - cluster, - namespace.name, - ComponentStates.ACTIVE, - ), - }, - ); - - async function createPod(name: string, labels: Record) { - try { - await k8Factory - .default() - .pods() - .create( - PodReference.of(namespace, PodName.of(name)), - labels, - ContainerName.of(name), - 'alpine:latest', - ['/bin/sh', '-c', 'apk update && apk upgrade && apk add --update bash && sleep 7200'], - ['bash', '-c', 'exit 0'], - ); - } catch (error) { - console.error(error); - throw new Error('Error creating pod'); + async function createPod(name: string, labelsRaw: string[]): Promise { + const labels: Record = {}; + + for (const rawLabel of labelsRaw) { + const [key, value] = rawLabel.split('='); + labels[key] = value; } + + await k8Factory + .default() + .pods() + .create( + PodReference.of(namespace, PodName.of(name)), + labels, + ContainerName.of(name), + 'alpine:latest', + ['/bin/sh', '-c', 'apk update && apk upgrade && apk add --update bash && sleep 7200'], + ['bash', '-c', 'exit 0'], + ); } - describe('Relays validation', () => { - it('should fail if component is not present', async () => { - try { - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig)); - throw new Error(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); - } - }); + const testCasesForIndividualComponents: Array<{ + componentKey: keyof ComponentsRecord; + displayName: string; + }> = [ + {componentKey: 'relay', displayName: 'Relay'}, + {componentKey: 'haProxy', displayName: 'HaProxy'}, + {componentKey: 'mirrorNode', displayName: 'Mirror node'}, + {componentKey: 'envoyProxy', displayName: 'Envoy proxy'}, + {componentKey: 'consensusNode', displayName: 'Consensus node'}, + {componentKey: 'explorer', displayName: 'Mirror node explorer'}, + {componentKey: 'blockNode', displayName: 'Block node'}, + ]; - it('should succeed if component is present', async () => { - const [key, value] = constants.SOLO_RELAY_LABEL.split('='); - await createPod(relayName, {[key]: value}); + for (const {componentKey, displayName} of testCasesForIndividualComponents) { + describe(`${displayName} validation`, () => { + it('should fail if component is not present', async () => { + const component: BaseComponent = components[componentKey]; - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig)); - }); - }); + componentsDataWrapper.addNewComponent(component); - describe('HaProxies validation', () => { - it('should fail if component is not present', async () => { - try { - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig)); - throw new Error(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); - } - }); + try { + await RemoteConfigValidator.validateComponents( + namespace, + componentsDataWrapper, + k8Factory, + localConfig, + false, + ); + expect.fail(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); + expect(error.message).to.equal(RemoteConfigValidator.buildValidationErrorMessage(displayName, component)); + } + }); - it('should succeed if component is present', async () => { - await createPod(haProxyName, {app: haProxyName}); + it('should succeed if component is present', async () => { + const component: BaseComponent = components[componentKey]; - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig)); - }); - }); + await createPod(component.name, labelRecord[componentKey]); - describe('Mirror Node Components validation', () => { - it('should fail if component is not present', async () => { - try { - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig)); - throw new Error(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); - } + await RemoteConfigValidator.validateComponents(namespace, componentsDataWrapper, k8Factory, localConfig, false); + }); }); + } - it('should succeed if component is present', async () => { - const [key1, value1] = constants.SOLO_HEDERA_MIRROR_IMPORTER[0].split('='); - const [key2, value2] = constants.SOLO_HEDERA_MIRROR_IMPORTER[1].split('='); - await createPod(mirrorNodeName, {[key1]: value1, [key2]: value2}); + describe('Additional test cases', () => { + it('Should not validate disabled components', async () => { + const component: BlockNodeComponent = components.blockNode; - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig)); - }); - }); + componentsDataWrapper.addNewComponent(component); + componentsDataWrapper.disableComponent(component.name, ComponentTypes.BlockNode); - describe('Envoy Proxies validation', () => { - it('should fail if component is not present', async () => { - try { - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig)); - throw new Error(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); - } + await RemoteConfigValidator.validateComponents(namespace, componentsDataWrapper, k8Factory, localConfig, false); }); - it('should succeed if component is present', async () => { - await createPod(envoyProxyName, {app: envoyProxyName}); + it('Should not validate consensus nodes if skipConsensusNodes is enabled', async () => { + const skipConsensusNodes: boolean = true; - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig)); - }); - }); + const nodeAliases: NodeAliases = ['node1', 'node2', 'node3']; + + const consensusNodeComponents: Record = + ComponentFactory.createConsensusNodeComponentsFromNodeAliases(nodeAliases, 'cluster-ref', namespace); - describe('Consensus Nodes validation', () => { - it('should fail if component is not present', async () => { - try { - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig)); - throw new Error(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); + const componentsDataWrapper: ComponentsDataWrapper = + ComponentsDataWrapper.initializeWithNodes(consensusNodeComponents); + + for (const nodeAlias of nodeAliases) { + // Make sure the status is STARTED + componentsDataWrapper.changeNodeState(nodeAlias, ConsensusNodeStates.STARTED); } + + await RemoteConfigValidator.validateComponents( + namespace, + componentsDataWrapper, + k8Factory, + localConfig, + skipConsensusNodes, + ); }); - it('should succeed if component is present', async () => { - await createPod(nodeAlias, {app: `network-${nodeAlias}`}); + const nodeStates: ConsensusNodeStates[] = [ConsensusNodeStates.REQUESTED, ConsensusNodeStates.NON_DEPLOYED]; - // @ts-expect-error - TS2341: Property is private - await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig)); - }); - }); + for (const nodeState of nodeStates) { + it(`Should not validate consensus nodes if status is ${nodeState} `, async () => { + const nodeAliases: NodeAliases = ['node1', 'node2', 'node3']; - describe('Mirror Node Explorers validation', () => { - it('should fail if component is not present', async () => { - try { - await Promise.all( - // @ts-expect-error - TS2341: Property is private - RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), - ); - throw new Error(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); - } - }); + const consensusNodeComponents: Record = + ComponentFactory.createConsensusNodeComponentsFromNodeAliases(nodeAliases, 'cluster-ref', namespace); - it('should succeed if component is present', async () => { - const [key, value] = constants.SOLO_HEDERA_EXPLORER_LABEL.split('='); - await createPod(mirrorNodeExplorerName, {[key]: value}); + const componentsDataWrapper: ComponentsDataWrapper = + ComponentsDataWrapper.initializeWithNodes(consensusNodeComponents); - await Promise.all( - // @ts-expect-error - TS2341: Property is private - RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), - ); - }); + for (const nodeAlias of nodeAliases) { + componentsDataWrapper.changeNodeState(nodeAlias, nodeState); + } + + await RemoteConfigValidator.validateComponents(namespace, componentsDataWrapper, k8Factory, localConfig, false); + }); + } }); }); From bbaa5649d6e60bf9f3a413ae195bcd5e6644ed18 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 13:50:05 +0300 Subject: [PATCH 51/70] lint-fix Signed-off-by: Zhan Milenkov --- test/e2e/integration/core/remote-config-validator.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index b0ca8a9d6..486d11da4 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -12,7 +12,7 @@ import {type ConsensusNodeComponent} from '../../../../src/core/config/remote/co import {type MirrorNodeExplorerComponent} from '../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; import {type EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy-proxy-component.js'; -import {type NodeAlias, NodeAliases} from '../../../../src/types/aliases.js'; +import {type NodeAlias, type NodeAliases} from '../../../../src/types/aliases.js'; import {container} from 'tsyringe-neo'; import {NamespaceName} from '../../../../src/integration/kube/resources/namespace/namespace-name.js'; import {PodReference} from '../../../../src/integration/kube/resources/pod/pod-reference.js'; @@ -25,10 +25,10 @@ import {getTestCacheDirectory} from '../../../test-utility.js'; import {Duration} from '../../../../src/core/time/duration.js'; import {LocalConfigDataWrapper} from '../../../../src/core/config/local/local-config-data-wrapper.js'; import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations/consensus-node-states.js'; -import {type ClusterReference, ComponentName} from '../../../../src/core/config/remote/types.js'; +import {type ClusterReference, type ComponentName} from '../../../../src/core/config/remote/types.js'; import {ComponentFactory} from '../../../../src/core/config/remote/components/component-factory.js'; import {type BlockNodeComponent} from '../../../../src/core/config/remote/components/block-node-component.js'; -import {BaseComponent} from '../../../../src/core/config/remote/components/base-component.js'; +import {type BaseComponent} from '../../../../src/core/config/remote/components/base-component.js'; import {ComponentTypes} from '../../../../src/core/config/remote/enumerations/component-types.js'; interface ComponentsRecord { From 69054359d767f9a382f2d2180a6ee0e07c4bacb0 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 14:08:13 +0300 Subject: [PATCH 52/70] update label creation logic Signed-off-by: Zhan Milenkov --- src/core/config/remote/remote-config-validator.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index b5769f3a3..9a2dda06c 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -23,7 +23,8 @@ export class RemoteConfigValidator { } private static getHaProxyLabels(component: BaseComponent): string[] { - return [`app=${component.name}`]; + const name: string = component.name.split('-').slice(0, -1).join('-'); + return [`app=${name}`]; } private static getMirrorNodeLabels(): string[] { @@ -31,7 +32,8 @@ export class RemoteConfigValidator { } private static getEnvoyProxyLabels(component: BaseComponent): string[] { - return [`app=${component.name}`]; + const name: string = component.name.split('-').slice(0, -1).join('-'); + return [`app=${name}`]; } private static getMirrorNodeExplorerLabels(): string[] { From 8780bd2c61fadec31800a9816239e0ae44c34ce7 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 14:50:13 +0300 Subject: [PATCH 53/70] add e2e tests and add them to the workflow Signed-off-by: Zhan Milenkov --- .github/workflows/flow-build-application.yaml | 3 + .../workflows/flow-pull-request-checks.yaml | 3 + .github/workflows/zxc-code-analysis.yaml | 17 +++++ .github/workflows/zxc-env-vars.yaml | 8 +++ Taskfile.tests.yml | 10 ++- test/e2e/commands/block-node.test.ts | 63 +++++++++++++++++++ 6 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 test/e2e/commands/block-node.test.ts diff --git a/.github/workflows/flow-build-application.yaml b/.github/workflows/flow-build-application.yaml index 607ea1db8..778b5fefa 100644 --- a/.github/workflows/flow-build-application.yaml +++ b/.github/workflows/flow-build-application.yaml @@ -83,6 +83,7 @@ jobs: - { name: "Node PEM Kill", test-script: "test-${{ needs.env-vars.outputs.e2e-node-pem-kill-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-pem-kill-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-pem-kill-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-pem-kill-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Standard", test-script: "test-${{ needs.env-vars.outputs.e2e-standard-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-standard-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-standard-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-standard-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Relay", test-script: "test-${{ needs.env-vars.outputs.e2e-relay-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-relay-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-relay-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-relay-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } + - { name: "Block Node", test-script: "test-${{ needs.env-vars.outputs.e2e-block-node-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-block-node-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-block-node-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-block-node-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Node Update", test-script: "test-${{ needs.env-vars.outputs.e2e-node-update-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-update-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-update-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-update-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Node Upgrade", test-script: "test-${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-upgrade-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Node PEM Stop", test-script: "test-${{ needs.env-vars.outputs.e2e-node-pem-stop-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-pem-stop-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-pem-stop-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-pem-stop-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } @@ -131,6 +132,7 @@ jobs: e2e-node-upgrade-test-subdir: ${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }} e2e-node-upgrade-separate-commands-test-subdir: ${{ needs.env-vars.outputs.e2e-node-upgrade-separate-commands-test-subdir }} e2e-relay-test-subdir: ${{ needs.env-vars.outputs.e2e-relay-test-subdir }} + e2e-block-node-test-subdir: ${{ needs.env-vars.outputs.e2e-block-node-test-subdir }} e2e-integration-coverage-report: ${{ needs.env-vars.outputs.e2e-integration-coverage-report }} e2e-dual-cluster-full-coverage-report: ${{ needs.env-vars.outputs.e2e-dual-cluster-full-coverage-report }} e2e-standard-coverage-report: ${{ needs.env-vars.outputs.e2e-standard-coverage-report }} @@ -148,6 +150,7 @@ jobs: e2e-node-upgrade-coverage-report: ${{ needs.env-vars.outputs.e2e-node-upgrade-coverage-report }} e2e-node-upgrade-separate-commands-coverage-report: ${{ needs.env-vars.outputs.e2e-node-upgrade-separate-commands-coverage-report }} e2e-relay-coverage-report: ${{ needs.env-vars.outputs.e2e-relay-coverage-report }} + e2e-block-node-coverage-report: ${{ needs.env-vars.outputs.e2e-block-node-coverage-report }} secrets: snyk-token: ${{ secrets.SNYK_TOKEN }} codecov-token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/flow-pull-request-checks.yaml b/.github/workflows/flow-pull-request-checks.yaml index f170b1269..fca946a95 100644 --- a/.github/workflows/flow-pull-request-checks.yaml +++ b/.github/workflows/flow-pull-request-checks.yaml @@ -82,6 +82,7 @@ jobs: - { name: "Node PEM Kill", test-script: "test-${{ needs.env-vars.outputs.e2e-node-pem-kill-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-pem-kill-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-pem-kill-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-pem-kill-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Standard", test-script: "test-${{ needs.env-vars.outputs.e2e-standard-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-standard-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-standard-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-standard-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Relay", test-script: "test-${{ needs.env-vars.outputs.e2e-relay-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-relay-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-relay-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-relay-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } + - { name: "Block Node", test-script: "test-${{ needs.env-vars.outputs.e2e-block-node-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-block-node-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-block-node-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-block-node-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Node Update", test-script: "test-${{ needs.env-vars.outputs.e2e-node-update-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-update-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-update-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-update-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Node Upgrade", test-script: "test-${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-upgrade-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } - { name: "Node PEM Stop", test-script: "test-${{ needs.env-vars.outputs.e2e-node-pem-stop-test-subdir }}", coverage-subdirectory: "${{ needs.env-vars.outputs.e2e-node-pem-stop-test-subdir }}", coverage-report-name: "${{ needs.env-vars.outputs.e2e-node-pem-stop-coverage-report }}", cluster-name: "${{ needs.env-vars.outputs.e2e-node-pem-stop-test-subdir }}-${{ github.run_id }}-${{ github.run_attempt }}" } @@ -127,6 +128,7 @@ jobs: e2e-node-upgrade-test-subdir: ${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }} e2e-node-upgrade-separate-commands-test-subdir: ${{ needs.env-vars.outputs.e2e-node-upgrade-separate-commands-test-subdir }} e2e-relay-test-subdir: ${{ needs.env-vars.outputs.e2e-relay-test-subdir }} + e2e-block-node-test-subdir: ${{ needs.env-vars.outputs.e2e-block-node-test-subdir }} e2e-integration-coverage-report: ${{ needs.env-vars.outputs.e2e-integration-coverage-report }} e2e-standard-coverage-report: ${{ needs.env-vars.outputs.e2e-standard-coverage-report }} e2e-node-pem-stop-coverage-report: ${{ needs.env-vars.outputs.e2e-node-pem-stop-coverage-report }} @@ -143,6 +145,7 @@ jobs: e2e-node-upgrade-coverage-report: ${{ needs.env-vars.outputs.e2e-node-upgrade-coverage-report }} e2e-node-upgrade-separate-commands-coverage-report: ${{ needs.env-vars.outputs.e2e-node-upgrade-separate-commands-coverage-report }} e2e-relay-coverage-report: ${{ needs.env-vars.outputs.e2e-relay-coverage-report }} + e2e-block-node-coverage-report: ${{ needs.env-vars.outputs.e2e-block-node-coverage-report }} secrets: codecov-token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/zxc-code-analysis.yaml b/.github/workflows/zxc-code-analysis.yaml index 73e11bcdd..6a8507e55 100644 --- a/.github/workflows/zxc-code-analysis.yaml +++ b/.github/workflows/zxc-code-analysis.yaml @@ -140,6 +140,11 @@ on: type: string required: false default: "e2e-relay" + e2e-block-node-test-subdir: + description: "E2E Block Node Test Subdirectory:" + type: string + required: false + default: "e2e-block-node" e2e-integration-coverage-report: description: "E2E Integration Coverage Report:" type: string @@ -225,6 +230,11 @@ on: type: string required: false default: "E2E Relay Tests Coverage Report" + e2e-block-node-coverage-report: + description: "E2E Block Node Coverage Report:" + type: string + required: false + default: "E2E Block Node Tests Coverage Report" secrets: snyk-token: description: "The Snyk access token is used by Snyk to analyze the code for vulnerabilities " @@ -401,6 +411,13 @@ jobs: name: ${{ inputs.e2e-relay-coverage-report }} path: 'coverage/${{ inputs.e2e-relay-test-subdir }}' + - name: Download E2E Block Node Coverage Report + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + if: ${{ (inputs.enable-codecov-analysis || inputs.enable-codacy-coverage) && inputs.enable-e2e-coverage-report && !cancelled() && !failure() }} + with: + name: ${{ inputs.e2e-block-node-coverage-report }} + path: 'coverage/${{ inputs.e2e-block-node-test-subdir }}' + - name: Download E2E Test Report uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 if: ${{ (inputs.enable-codecov-analysis || inputs.enable-codacy-coverage) && inputs.enable-e2e-coverage-report && !cancelled() && !failure() }} diff --git a/.github/workflows/zxc-env-vars.yaml b/.github/workflows/zxc-env-vars.yaml index 7215b8c92..a6d930cd5 100644 --- a/.github/workflows/zxc-env-vars.yaml +++ b/.github/workflows/zxc-env-vars.yaml @@ -77,6 +77,9 @@ on: e2e-relay-test-subdir: description: "E2E Relay Test Subdirectory" value: ${{ jobs.env-vars.outputs.e2e_relay_test_subdir }} + e2e-block-node-test-subdir: + description: "E2E Block Node Test Subdirectory" + value: ${{ jobs.env-vars.outputs.e2e_block_node_test_subdir }} e2e-integration-coverage-report: description: "E2E Integration Tests Coverage Report" value: ${{ jobs.env-vars.outputs.e2e_integration_coverage_report }} @@ -128,6 +131,9 @@ on: e2e-relay-coverage-report: description: "E2E Relay Tests Coverage Report" value: ${{ jobs.env-vars.outputs.e2e_relay_coverage_report }} + e2e-block-node-coverage-report: + description: "E2E Block Node Tests Coverage Report" + value: ${{ jobs.env-vars.outputs.e2e_block_node_coverage_report }} defaults: run: @@ -158,6 +164,7 @@ jobs: e2e_node_upgrade_test_subdir: e2e-node-upgrade e2e_node_upgrade_separate_commands_test_subdir: e2e-node-upgrade-separate e2e_relay_test_subdir: e2e-relay + e2e_block_node_test_subdir: e2e-block-node e2e_integration_coverage_report: "E2E Integration Tests Coverage Report" e2e_dual_cluster_full_coverage_report: "E2E Dual Cluster Full Tests Coverage Report" e2e_standard_coverage_report: "E2E Standard Tests Coverage Report" @@ -176,6 +183,7 @@ jobs: e2e_node_upgrade_coverage_report: "E2E Node Upgrade Tests Coverage Report" e2e_node_upgrade_separate_commands_coverage_report: "E2E Node Upgrade - Separate commands Tests Coverage Report" e2e_relay_coverage_report: "E2E Relay Tests Coverage Report" + e2e_block_node_coverage_report: "E2E Block Node Tests Coverage Report" steps: - name: Harden Runner uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1 diff --git a/Taskfile.tests.yml b/Taskfile.tests.yml index 13dd82bbf..1f7861c39 100644 --- a/Taskfile.tests.yml +++ b/Taskfile.tests.yml @@ -77,6 +77,7 @@ tasks: --ignore 'test/e2e/commands/node*.ts' --ignore 'test/e2e/commands/separate-node*.ts' --ignore 'test/e2e/commands/relay*.ts' + --ignore 'test/e2e/commands/block-node.test.ts' --ignore 'test/e2e/commands/dual-*.ts' --ignore 'test/e2e/commands/separate-node-add.test.ts' --ignore 'test/e2e/commands/separate-node-delete.test.ts' @@ -177,11 +178,18 @@ tasks: test-e2e-relay: cmds: - - "{{ .test_prefix }}=\"Mocha E2E Relay Tests\" + - "{{ .test_prefix }}=\"Mocha E2E Block Node Tests\" {{ .reporter_prefix }}='coverage/e2e-relay' {{ .mocha_bin }} 'test/e2e/commands/relay.test.ts' {{ .reporter_options_prefix }}-e2e-relay.xml" + test-e2e-block-node: + cmds: + - "{{ .test_prefix }}=\"Mocha E2E Relay Tests\" + {{ .reporter_prefix }}='coverage/e2e-block-node' + {{ .mocha_bin }} 'test/e2e/commands/block-node.test.ts' + {{ .reporter_options_prefix }}-e2e-block-node.xml" + test-e2e-dual-cluster-full: cmds: - "{{ .test_prefix }}=\"Mocha E2E Dual Cluster Full Test\" diff --git a/test/e2e/commands/block-node.test.ts b/test/e2e/commands/block-node.test.ts new file mode 100644 index 000000000..c033407c6 --- /dev/null +++ b/test/e2e/commands/block-node.test.ts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {after, afterEach, describe} from 'mocha'; +import {expect} from 'chai'; + +import {Flags as flags} from '../../../src/commands/flags.js'; +import {endToEndTestSuite, getTestCluster, HEDERA_PLATFORM_VERSION_TAG} from '../../test-utility.js'; +import * as version from '../../../version.js'; +import {sleep} from '../../../src/core/helpers.js'; +import {Duration} from '../../../src/core/time/duration.js'; +import {NamespaceName} from '../../../src/integration/kube/resources/namespace/namespace-name.js'; +import {type NetworkNodes} from '../../../src/core/network-nodes.js'; +import {container} from 'tsyringe-neo'; +import {InjectTokens} from '../../../src/core/dependency-injection/inject-tokens.js'; +import {Argv} from '../../helpers/argv-wrapper.js'; +import {BlockNodeCommand} from '../../../src/commands/block-node.js'; + +const testName: string = 'block-node-cmd-e2e'; +const namespace: NamespaceName = NamespaceName.of(testName); +const argv: Argv = Argv.getDefaultArgv(namespace); +argv.setArg(flags.namespace, namespace.name); +argv.setArg(flags.releaseTag, HEDERA_PLATFORM_VERSION_TAG); +argv.setArg(flags.nodeAliasesUnparsed, 'node1'); +argv.setArg(flags.generateGossipKeys, true); +argv.setArg(flags.generateTlsKeys, true); +argv.setArg(flags.clusterRef, getTestCluster()); +argv.setArg(flags.soloChartVersion, version.SOLO_CHART_VERSION); +argv.setArg(flags.force, true); + +endToEndTestSuite(testName, argv, {startNodes: false}, bootstrapResp => { + const { + opts: {k8Factory, logger, commandInvoker}, + } = bootstrapResp; + + describe('BlockNodeCommand', async () => { + const blockNodeCommand: BlockNodeCommand = new BlockNodeCommand(bootstrapResp.opts); + + after(async function () { + this.timeout(Duration.ofMinutes(5).toMillis()); + await container.resolve(InjectTokens.NetworkNodes).getLogs(namespace); + await k8Factory.default().namespaces().delete(namespace); + }); + + afterEach(async () => await sleep(Duration.ofMillis(5))); + + it('Should succeed with deploy command', async function () { + this.timeout(Duration.ofMinutes(5).toMillis()); + + try { + await commandInvoker.invoke({ + argv: argv, + command: BlockNodeCommand.COMMAND_NAME, + subcommand: 'deploy', + // @ts-expect-error to access private property + callback: async argv => blockNodeCommand.deploy(argv), + }); + } catch (error) { + logger.showUserError(error); + expect.fail(); + } + }); + }); +}); From 8e5412a13710f63d6ae8674b0f0ed0aaa56bbe29 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 15:32:17 +0300 Subject: [PATCH 54/70] fix missing options from workflow Signed-off-by: Zhan Milenkov --- .github/workflows/flow-pull-request-checks.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/flow-pull-request-checks.yaml b/.github/workflows/flow-pull-request-checks.yaml index fca946a95..8acab4385 100644 --- a/.github/workflows/flow-pull-request-checks.yaml +++ b/.github/workflows/flow-pull-request-checks.yaml @@ -178,6 +178,7 @@ jobs: e2e-node-upgrade-test-subdir: ${{ needs.env-vars.outputs.e2e-node-upgrade-test-subdir }} e2e-node-upgrade-separate-commands-test-subdir: ${{ needs.env-vars.outputs.e2e-node-upgrade-separate-commands-test-subdir }} e2e-relay-test-subdir: ${{ needs.env-vars.outputs.e2e-relay-test-subdir }} + e2e-block-node-test-subdir: ${{ needs.env-vars.outputs.e2e-block-node-test-subdir }} e2e-integration-coverage-report: ${{ needs.env-vars.outputs.e2e-integration-coverage-report }} e2e-dual-cluster-full-coverage-report: ${{ needs.env-vars.outputs.e2e-dual-cluster-full-coverage-report }} e2e-standard-coverage-report: ${{ needs.env-vars.outputs.e2e-standard-coverage-report }} @@ -195,5 +196,6 @@ jobs: e2e-node-upgrade-coverage-report: ${{ needs.env-vars.outputs.e2e-node-upgrade-coverage-report }} e2e-node-upgrade-separate-commands-coverage-report: ${{ needs.env-vars.outputs.e2e-node-upgrade-separate-commands-coverage-report }} e2e-relay-coverage-report: ${{ needs.env-vars.outputs.e2e-relay-coverage-report }} + e2e-block-node-coverage-report: ${{ needs.env-vars.outputs.e2e-block-node-coverage-report }} secrets: codacy-project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} From fd5e95200519dd1707b8ce5862e32b6f08a34098 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 15:32:41 +0300 Subject: [PATCH 55/70] fix taskfile task for relay Signed-off-by: Zhan Milenkov --- Taskfile.tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.tests.yml b/Taskfile.tests.yml index 1f7861c39..12e031775 100644 --- a/Taskfile.tests.yml +++ b/Taskfile.tests.yml @@ -178,7 +178,7 @@ tasks: test-e2e-relay: cmds: - - "{{ .test_prefix }}=\"Mocha E2E Block Node Tests\" + - "{{ .test_prefix }}=\"Mocha E2E Relay Tests\" {{ .reporter_prefix }}='coverage/e2e-relay' {{ .mocha_bin }} 'test/e2e/commands/relay.test.ts' {{ .reporter_options_prefix }}-e2e-relay.xml" From c04e2fbe41bdd09ee09ef51d44a892e7df4e7f96 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 8 Apr 2025 15:33:07 +0300 Subject: [PATCH 56/70] fix task test-e2e-block-node description Signed-off-by: Zhan Milenkov --- Taskfile.tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.tests.yml b/Taskfile.tests.yml index 12e031775..754775944 100644 --- a/Taskfile.tests.yml +++ b/Taskfile.tests.yml @@ -185,7 +185,7 @@ tasks: test-e2e-block-node: cmds: - - "{{ .test_prefix }}=\"Mocha E2E Relay Tests\" + - "{{ .test_prefix }}=\"Mocha E2E Block Nodes Tests\" {{ .reporter_prefix }}='coverage/e2e-block-node' {{ .mocha_bin }} 'test/e2e/commands/block-node.test.ts' {{ .reporter_options_prefix }}-e2e-block-node.xml" From b8e54db46ed61c950b9fbbc3b0fa4a27498025b5 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 9 Apr 2025 15:29:38 +0300 Subject: [PATCH 57/70] improve logic for detecting apple m4 series chip, and add additional task that determines block node's health by checking it's health endpoint Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 113 ++++++++++++++++++++++++++++++++++--- src/core/constants.ts | 8 ++- src/core/helpers.ts | 13 +++++ 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index dfc05f5dc..6e07a8ed3 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -3,7 +3,7 @@ import {Listr} from 'listr2'; import {SoloError} from '../core/errors/solo-error.js'; import * as helpers from '../core/helpers.js'; -import {showVersionBanner} from '../core/helpers.js'; +import {showVersionBanner, sleep} from '../core/helpers.js'; import * as constants from '../core/constants.js'; import {BaseCommand} from './base.js'; import {Flags as flags} from './flags.js'; @@ -17,15 +17,18 @@ import { } from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; -import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; +import {type CommandDefinition, type Optional, type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js'; import * as versions from '../../version.js'; import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; -import os from 'node:os'; import {type BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; +import {ContainerReference} from '../integration/kube/resources/container/container-reference.js'; +import {Duration} from '../core/time/duration.js'; +import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; +import chalk from 'chalk'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -91,8 +94,16 @@ export class BlockNodeCommand extends BaseCommand { } // Fix for M4 chips (ARM64) - const arch: string = os.arch(); - if (arch === 'arm64' || arch === 'aarch64') { + + const chipType: string[] = await helpers.getAppleSiliconChipset(this.logger); + let isAppleM4SeriesChip: boolean = false; + for (const chip of chipType) { + if (chip.includes('M4')) { + isAppleM4SeriesChip = true; + } + } + + if (isAppleM4SeriesChip) { valuesArgument += helpers.populateHelmArguments({ 'blockNode.config.JAVA_OPTS': '"-Xms8G -Xmx8G -XX:UseSVE=0"', }); @@ -207,7 +218,7 @@ export class BlockNodeCommand extends BaseCommand { }, }, { - title: 'Check block node is running', + title: 'Check block node pod is running', task: async (context_): Promise => { const config: BlockNodeDeployConfigClass = context_.config; @@ -223,7 +234,7 @@ export class BlockNodeCommand extends BaseCommand { }, }, { - title: 'Check block node is ready', + title: 'Check block node pod is ready', task: async (context_): Promise => { const config: BlockNodeDeployConfigClass = context_.config; try { @@ -241,7 +252,8 @@ export class BlockNodeCommand extends BaseCommand { } }, }, - this.addBlockNodeComponent(), + this.checkBlockNodeReadiness(), + // this.addBlockNodeComponent(), ], { concurrent: false, @@ -261,7 +273,7 @@ export class BlockNodeCommand extends BaseCommand { } /** Adds the block node component to remote config. */ - public addBlockNodeComponent(): SoloListrTask { + private addBlockNodeComponent(): SoloListrTask { return { title: 'Add block node component in remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), @@ -275,6 +287,89 @@ export class BlockNodeCommand extends BaseCommand { }; } + private displayHealthcheckData( + task: SoloListrTaskWrapper, + ): (attempt: number, maxAttempt: number, color?: 'yellow' | 'green' | 'red', additionalData?: string) => void { + const baseTitle: string = task.title; + + return function ( + attempt: number, + maxAttempt: number, + color: 'yellow' | 'green' | 'red' = 'yellow', + additionalData: string = '', + ): void { + task.title = `${baseTitle} - ${chalk[color](`[${attempt}/${maxAttempt}]`)} ${chalk[color](additionalData)}`; + }; + } + + private checkBlockNodeReadiness(): SoloListrTask { + return { + title: 'Check block node readiness', + task: async (context_, task): Promise => { + const config: BlockNodeDeployConfigClass = context_.config; + + const displayHealthcheckCallback: ( + attempt: number, + maxAttempt: number, + color?: 'yellow' | 'green' | 'red', + additionalData?: string, + ) => void = this.displayHealthcheckData(task); + + const blockNodePodReference: PodReference = await this.k8Factory + .getK8(config.context) + .pods() + .list(config.namespace, [`app.kubernetes.io/instance=${config.releaseName}`]) + .then(pods => pods[0].podReference); + + const containerReference: ContainerReference = ContainerReference.of( + blockNodePodReference, + constants.BLOCK_NODE_CONTAINER_NAME, + ); + + const maxAttempts: number = constants.BLOCK_NODE_ACTIVE_MAX_ATTEMPTS; + let attempt: number = 1; + let success: boolean = false; + + displayHealthcheckCallback(attempt, maxAttempts); + + while (attempt < maxAttempts) { + try { + const response: string = await helpers.withTimeout( + this.k8Factory + .getK8(config.context) + .containers() + .readByRef(containerReference) + .execContainer(['bash', '-c', 'curl -s http://localhost:8080/healthz/readyz']), + Duration.ofMillis(constants.BLOCK_NODE_ACTIVE_TIMEOUT), + 'Healthcheck timed out', + ); + + if (response !== 'OK') { + throw new SoloError('Bad response status'); + } + + success = true; + break; + } catch (error) { + // Guard + console.error(error); + } + + attempt++; + await sleep(Duration.ofSeconds(constants.BLOCK_NODE_ACTIVE_DELAY)); + displayHealthcheckCallback(attempt, maxAttempts); + } + + if (!success) { + displayHealthcheckCallback(attempt, maxAttempts, 'red', 'max attempts reached'); + throw new SoloError('Max attempts reached'); + } + + displayHealthcheckCallback(attempt, maxAttempts, 'green', 'success'); + }, + }; + } + public getCommandDefinition(): CommandDefinition { const self: this = this; return { diff --git a/src/core/constants.ts b/src/core/constants.ts index 8cbcf22ed..4a97b8c6e 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -83,6 +83,7 @@ export const INGRESS_CONTROLLER_NAME = 'haproxy-ingress.github.io/controller'; export const BLOCK_NODE_CHART_URL = process.env.BLOCK_NODE_CHART_URL ?? 'oci://ghcr.io/hiero-ledger/hiero-block-node'; export const BLOCK_NODE_CHART = 'block-node-helm-chart'; export const BLOCK_NODE_RELEASE_NAME = 'block-node'; +export const BLOCK_NODE_CONTAINER_NAME: ContainerName = ContainerName.of('block-node-helm-chart'); export const CERT_MANAGER_NAME_SPACE = 'cert-manager'; export const SOLO_HEDERA_MIRROR_IMPORTER = [ @@ -222,8 +223,11 @@ export const RELAY_PODS_RUNNING_MAX_ATTEMPTS = +process.env.RELAY_PODS_RUNNING_M export const RELAY_PODS_RUNNING_DELAY = +process.env.RELAY_PODS_RUNNING_DELAY || 1000; export const RELAY_PODS_READY_MAX_ATTEMPTS = +process.env.RELAY_PODS_READY_MAX_ATTEMPTS || 100; export const RELAY_PODS_READY_DELAY = +process.env.RELAY_PODS_READY_DELAY || 1000; -export const BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS = +process.env.BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS || 900; -export const BLOCK_NODE_PODS_RUNNING_DELAY = +process.env.BLOCK_NODE_PODS_RUNNING_DELAY || 1000; +export const BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS: number = +process.env.BLOCK_NODE_PODS_RUNNING_MAX_ATTEMPTS || 900; +export const BLOCK_NODE_PODS_RUNNING_DELAY: number = +process.env.BLOCK_NODE_PODS_RUNNING_DELAY || 1000; +export const BLOCK_NODE_ACTIVE_MAX_ATTEMPTS: number = +process.env.NETWORK_NODE_ACTIVE_MAX_ATTEMPTS || 100; +export const BLOCK_NODE_ACTIVE_DELAY: number = +process.env.NETWORK_NODE_ACTIVE_DELAY || 1000; +export const BLOCK_NODE_ACTIVE_TIMEOUT: number = +process.env.NETWORK_NODE_ACTIVE_TIMEOUT || 1000; export const GRPC_PORT = +process.env.GRPC_PORT || 50_211; export const LOCAL_BUILD_COPY_RETRY = +process.env.LOCAL_BUILD_COPY_RETRY || 3; diff --git a/src/core/helpers.ts b/src/core/helpers.ts index c0265be31..6452d8a64 100644 --- a/src/core/helpers.ts +++ b/src/core/helpers.ts @@ -567,3 +567,16 @@ export async function getAppleSiliconChipset(logger: SoloLogger) { return ['unknown']; } } + +export async function withTimeout( + promise: Promise, + duration: Duration, + errorMessage: string = 'Timeout', +): Promise { + return Promise.race([promise, throwAfter(duration, errorMessage)]); +} + +async function throwAfter(duration: Duration, message: string = 'Timeout'): Promise { + await sleep(duration); + throw new SoloError(message); +} From 47eb9332c901a4f9e3eb2851a685a6f639d3bf6c Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 9 Apr 2025 15:30:05 +0300 Subject: [PATCH 58/70] uncomment important code Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 6e07a8ed3..ecfcb5bdc 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -253,7 +253,7 @@ export class BlockNodeCommand extends BaseCommand { }, }, this.checkBlockNodeReadiness(), - // this.addBlockNodeComponent(), + this.addBlockNodeComponent(), ], { concurrent: false, From edb0bb03a1bdb9d3a130e7c3e262ee0b0fbf4cf9 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 9 Apr 2025 15:44:00 +0300 Subject: [PATCH 59/70] switch remote component index counting to start from 0 instead of 1 Signed-off-by: Zhan Milenkov --- src/core/config/remote/components-data-wrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 126c2eb6d..abf3aebed 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -285,7 +285,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { * Checks all existing components of specified type and gives you a new unique index */ public getNewComponentIndex(componentType: ComponentTypes): number { - let newComponentIndex: number = 1; + let newComponentIndex: number = 0; const calculateNewComponentIndexCallback: ( components: Record, From 78b0365950c638aa38a083baf5a9b0b6a4cbe894 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 9 Apr 2025 15:55:37 +0300 Subject: [PATCH 60/70] added 3 level nested command, and extended e2e test argv builder to support it Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 47 ++++++++++++++++------------ test/e2e/commands/block-node.test.ts | 2 +- test/helpers/argv-wrapper.ts | 6 +++- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index ecfcb5bdc..746fe4ec1 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -54,7 +54,7 @@ interface BlockNodeDeployContext { } export class BlockNodeCommand extends BaseCommand { - public static readonly COMMAND_NAME: string = 'block-node'; + public static readonly COMMAND_NAME: string = 'block'; private static readonly DEPLOY_CONFIGS_NAME: string = 'deployConfigs'; @@ -374,32 +374,39 @@ export class BlockNodeCommand extends BaseCommand { const self: this = this; return { command: BlockNodeCommand.COMMAND_NAME, - desc: 'Manage block nodes in solo network', + desc: 'Manage block related components in solo network', builder: (yargs: AnyYargs): any => { return yargs .command({ - command: 'deploy', - desc: 'Deploy block node', - builder: (y: AnyYargs): void => { - flags.setRequiredCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.required); - flags.setOptionalCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.optional); - }, - handler: async (argv: ArgvStruct): Promise => { - self.logger.info("==== Running 'block node deploy' ===", {argv}); - self.logger.info(argv); - - await self.deploy(argv).then((r): void => { - self.logger.info('==== Finished running `block node deploy`===='); - if (!r) { - throw new SoloError('Error deploying block node, expected return value to be true'); - } - }); + command: 'node', + desc: 'Manage block nodes in solo network', + builder: (yargs: AnyYargs): void => { + return yargs + .command({ + command: 'add', + desc: 'Add block node', + builder: (y: AnyYargs): void => { + flags.setRequiredCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.required); + flags.setOptionalCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.optional); + }, + handler: async (argv: ArgvStruct): Promise => { + self.logger.info("==== Running 'block node deploy' ===", {argv}); + self.logger.info(argv); + + await self.deploy(argv).then((r): void => { + self.logger.info('==== Finished running `block node deploy`===='); + if (!r) { + throw new SoloError('Error deploying block node, expected return value to be true'); + } + }); + }, + }) + .demandCommand(1, 'Select a block node command'); }, }) - .demandCommand(1, 'Select a block node command'); + .demandCommand(1, 'Select a block command'); }, }; } - public async close(): Promise {} // no-op } diff --git a/test/e2e/commands/block-node.test.ts b/test/e2e/commands/block-node.test.ts index c033407c6..844ea2d2c 100644 --- a/test/e2e/commands/block-node.test.ts +++ b/test/e2e/commands/block-node.test.ts @@ -50,7 +50,7 @@ endToEndTestSuite(testName, argv, {startNodes: false}, bootstrapResp => { await commandInvoker.invoke({ argv: argv, command: BlockNodeCommand.COMMAND_NAME, - subcommand: 'deploy', + subcommand: 'node deploy', // @ts-expect-error to access private property callback: async argv => blockNodeCommand.deploy(argv), }); diff --git a/test/helpers/argv-wrapper.ts b/test/helpers/argv-wrapper.ts index 41aa2654b..aa7d08db7 100644 --- a/test/helpers/argv-wrapper.ts +++ b/test/helpers/argv-wrapper.ts @@ -42,7 +42,11 @@ export class Argv implements CloneTrait { const _: string[] = [this.command]; if (this.subcommand) { - _.push(this.subcommand); + if (this.subcommand.includes(' ')) { + _.push(...this.subcommand.split(' ')); + } else { + _.push(this.subcommand); + } } rawArguments._ = _; From fe13d3a8f8d266b92f9f1bf40b47f99046c65029 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 9 Apr 2025 17:21:54 +0300 Subject: [PATCH 61/70] added new classes for building 3 leveled command paths Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 54 +++------ .../command-path-builders/command-builder.ts | 106 ++++++++++++++++++ src/types/index.ts | 2 +- 3 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 src/core/command-path-builders/command-builder.ts diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 746fe4ec1..6a96fff81 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -29,6 +29,7 @@ import {ContainerReference} from '../integration/kube/resources/container/contai import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; import chalk from 'chalk'; +import {CommandBuilder, CommandGroup, Subcommand} from '../core/command-path-builders/command-builder.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -116,7 +117,7 @@ export class BlockNodeCommand extends BaseCommand { return constants.BLOCK_NODE_RELEASE_NAME + '-' + blockNodeIndex; } - private async deploy(argv: ArgvStruct): Promise { + private async add(argv: ArgvStruct): Promise { const lease: Lock = await this.leaseManager.create(); const tasks: Listr = new Listr( @@ -371,42 +372,21 @@ export class BlockNodeCommand extends BaseCommand { } public getCommandDefinition(): CommandDefinition { - const self: this = this; - return { - command: BlockNodeCommand.COMMAND_NAME, - desc: 'Manage block related components in solo network', - builder: (yargs: AnyYargs): any => { - return yargs - .command({ - command: 'node', - desc: 'Manage block nodes in solo network', - builder: (yargs: AnyYargs): void => { - return yargs - .command({ - command: 'add', - desc: 'Add block node', - builder: (y: AnyYargs): void => { - flags.setRequiredCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.required); - flags.setOptionalCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.optional); - }, - handler: async (argv: ArgvStruct): Promise => { - self.logger.info("==== Running 'block node deploy' ===", {argv}); - self.logger.info(argv); - - await self.deploy(argv).then((r): void => { - self.logger.info('==== Finished running `block node deploy`===='); - if (!r) { - throw new SoloError('Error deploying block node, expected return value to be true'); - } - }); - }, - }) - .demandCommand(1, 'Select a block node command'); - }, - }) - .demandCommand(1, 'Select a block command'); - }, - }; + return new CommandBuilder( + BlockNodeCommand.COMMAND_NAME, + 'Manage block related components in solo network', + this.logger, + ) + .addCommandGroup( + new CommandGroup('node', 'Manage block nodes in solo network').addSubcommand( + new Subcommand('add', 'Add block node', this, this.add, (y: AnyYargs): void => { + flags.setRequiredCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.required); + flags.setOptionalCommandFlags(y, ...BlockNodeCommand.DEPLOY_FLAGS_LIST.optional); + }), + ), + ) + .build(); } + public async close(): Promise {} // no-op } diff --git a/src/core/command-path-builders/command-builder.ts b/src/core/command-path-builders/command-builder.ts new file mode 100644 index 000000000..f62b7204f --- /dev/null +++ b/src/core/command-path-builders/command-builder.ts @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {SoloError} from '../errors/solo-error.js'; +import {type AnyYargs, type ArgvStruct} from '../../types/aliases.js'; +import {type SoloLogger} from '../logging/solo-logger.js'; +import {type BaseCommand} from '../../commands/base.js'; +import {type CommandDefinition} from '../../types/index.js'; + +export class Subcommand { + public constructor( + public readonly name: string, + public readonly description: string, + public readonly commandHandlerClass: BaseCommand, + public readonly commandHandler: (argv: ArgvStruct) => Promise, + public readonly builder?: (yargs: AnyYargs) => void, + ) {} +} + +export class CommandGroup { + public readonly subcommands: Subcommand[] = []; + + public constructor( + public readonly name: string, + public readonly description: string, + ) {} + + public addSubcommand(subcommand: Subcommand): CommandGroup { + this.subcommands.push(subcommand); + return this; + } +} + +export class CommandBuilder { + private readonly commandGroups: CommandGroup[] = []; + + public constructor( + private readonly name: string, + private readonly description: string, + private readonly logger: SoloLogger, + ) {} + + public addCommandGroup(commandGroup: CommandGroup): CommandBuilder { + this.commandGroups.push(commandGroup); + return this; + } + + public build(): CommandDefinition { + const commandGroups: CommandGroup[] = this.commandGroups; + const logger: SoloLogger = this.logger; + + const commandName: string = this.name; + const commandDescription: string = this.description; + const demandCommand: string = `select a ${commandName} command`; + + return { + command: commandName, + desc: commandDescription, + builder: (yargs: AnyYargs): AnyYargs => { + for (const commandGroup of commandGroups) { + yargs.command({ + command: commandGroup.name, + desc: commandGroup.description, + builder: (yargs: AnyYargs): AnyYargs => { + for (const subcommand of commandGroup.subcommands) { + const handlerDefinition: CommandDefinition = { + command: subcommand.name, + desc: subcommand.description, + handler: async argv => { + const commandPath: string = `${commandName} ${commandGroup.name} ${subcommand.name}`; + + logger.info(`==== Running '${commandPath}' ===`); + + const handlerCallback: (argv: ArgvStruct) => Promise = subcommand.commandHandler.bind( + subcommand.commandHandlerClass, + ); + + const response: boolean = await handlerCallback(argv); + + logger.info(`==== Finished running '${commandPath}'====`); + + if (!response) { + throw new SoloError(`Error running ${commandPath}, expected return value to be true`); + } + }, + }; + + if (subcommand.builder) { + handlerDefinition.builder = subcommand.builder; + } + + yargs.command(handlerDefinition); + } + + yargs.demandCommand(1, `Select a ${commandName} ${commandGroup.name} command`); + return yargs; + }, + }); + } + + yargs.demandCommand(1, demandCommand); + + return yargs; + }, + }; + } +} diff --git a/src/types/index.ts b/src/types/index.ts index cab58cf71..b74e2949c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -125,6 +125,6 @@ export interface GossipEndpoint { export interface CommandDefinition { command: string; desc: string; - builder: (yargs: AnyYargs) => any; + builder?: (yargs: AnyYargs) => any; handler?: (argv: ArgvStruct) => Promise; } From 079c7dd2a4d143af31120944ba2ed0debf59d407 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Wed, 9 Apr 2025 17:22:31 +0300 Subject: [PATCH 62/70] fix block node e2e test Signed-off-by: Zhan Milenkov --- test/e2e/commands/block-node.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/commands/block-node.test.ts b/test/e2e/commands/block-node.test.ts index 844ea2d2c..dc1af24f5 100644 --- a/test/e2e/commands/block-node.test.ts +++ b/test/e2e/commands/block-node.test.ts @@ -43,16 +43,16 @@ endToEndTestSuite(testName, argv, {startNodes: false}, bootstrapResp => { afterEach(async () => await sleep(Duration.ofMillis(5))); - it('Should succeed with deploy command', async function () { + it('Should succeed with add command', async function () { this.timeout(Duration.ofMinutes(5).toMillis()); try { await commandInvoker.invoke({ argv: argv, command: BlockNodeCommand.COMMAND_NAME, - subcommand: 'node deploy', + subcommand: 'node add', // @ts-expect-error to access private property - callback: async argv => blockNodeCommand.deploy(argv), + callback: async argv => blockNodeCommand.add(argv), }); } catch (error) { logger.showUserError(error); From 68246e5e3c797e401c0713e22a462858497170f2 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Tue, 15 Apr 2025 11:19:34 +0300 Subject: [PATCH 63/70] update SOLO_CHART_VERSION Signed-off-by: Zhan Milenkov --- version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.ts b/version.ts index 7a3682be9..093f9ca23 100644 --- a/version.ts +++ b/version.ts @@ -11,7 +11,7 @@ import fs from 'node:fs'; */ export const HELM_VERSION = 'v3.14.2'; -export const SOLO_CHART_VERSION = '0.50.0'; +export const SOLO_CHART_VERSION = '0.51.0'; export const HEDERA_PLATFORM_VERSION = 'v0.59.5'; export const MIRROR_NODE_VERSION = 'v0.126.0'; export const HEDERA_EXPLORER_VERSION = '24.12.1'; From d93882615df6e9ddaf5951429a79e23f400604dd Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 24 Apr 2025 09:51:33 +0300 Subject: [PATCH 64/70] fixes after merge Signed-off-by: Zhan Milenkov --- src/commands/explorer.ts | 1 - src/commands/mirror-node.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index ce479cc18..d93a48f34 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -7,7 +7,6 @@ import {SoloError} from '../core/errors/solo-error.js'; import {MissingArgumentError} from '../core/errors/missing-argument-error.js'; import {UserBreak} from '../core/errors/user-break.js'; import * as constants from '../core/constants.js'; -import {HEDERA_EXPLORER_CHART_URL, INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 6b9ab9236..6fe49cafa 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -8,14 +8,12 @@ import {MissingArgumentError} from '../core/errors/missing-argument-error.js'; import {SoloError} from '../core/errors/solo-error.js'; import {UserBreak} from '../core/errors/user-break.js'; import * as constants from '../core/constants.js'; -import {INGRESS_CONTROLLER_NAME} from '../core/constants.js'; import {type AccountManager} from '../core/account-manager.js'; import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import * as helpers from '../core/helpers.js'; -import {showVersionBanner} from '../core/helpers.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {type PodName} from '../integration/kube/resources/pod/pod-name.js'; import {ListrLock} from '../core/lock/listr-lock.js'; From 8511154a0399a450b42ee913fba61802149f3e72 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 24 Apr 2025 11:10:33 +0300 Subject: [PATCH 65/70] removing rmeote config Signed-off-by: Zhan Milenkov --- src/commands/deployment.ts | 10 +- src/commands/explorer.ts | 49 +-- src/commands/mirror-node.ts | 29 +- src/commands/network.ts | 42 +- src/commands/node/handlers.ts | 39 +- src/commands/node/tasks.ts | 50 ++- src/commands/relay.ts | 29 +- .../remote/api/components-data-wrapper-api.ts | 34 -- .../remote/api/remote-config-manager-api.ts | 112 ------ src/core/config/remote/cluster.ts | 3 - .../config/remote/components-data-wrapper.ts | 165 ++++---- .../remote/components/base-component.ts | 7 - .../remote/components/block-node-component.ts | 8 +- .../remote/components/component-factory.ts | 131 ------- .../components/component-name-templates.ts | 67 ---- .../components/consensus-node-component.ts | 30 +- .../components/envoy-proxy-component.ts | 14 +- .../remote/components/ha-proxy-component.ts | 14 +- .../interfaces/base-component-struct.ts | 2 - .../consensus-node-component-struct.ts | 2 +- .../components/mirror-node-component.ts | 14 +- .../mirror-node-explorer-component.ts | 14 +- .../remote/components/relay-component.ts | 9 +- .../remote/enumerations/component-states.ts | 9 - .../config/remote/remote-config-manager.ts | 64 ++- .../config/remote/remote-config-validator.ts | 269 +++++++------ src/core/templates.ts | 20 +- .../core/remote-config-validator.test.ts | 365 ++++++++---------- test/unit/core/config/remote/cluster.test.ts | 127 +++--- .../remote/components-data-wrapper.test.ts | 229 ++++------- .../remote/components/component-names.test.ts | 59 --- .../remote/components/components.test.ts | 281 ++++++++++---- test/unit/core/config/remote/metadata.test.ts | 219 +++++++---- .../unit/core/config/remote/migration.test.ts | 51 ++- .../remote/remote-config-data-wrapper.test.ts | 68 +++- 35 files changed, 1165 insertions(+), 1470 deletions(-) delete mode 100644 src/core/config/remote/api/components-data-wrapper-api.ts delete mode 100644 src/core/config/remote/api/remote-config-manager-api.ts delete mode 100644 src/core/config/remote/components/component-factory.ts delete mode 100644 src/core/config/remote/components/component-name-templates.ts delete mode 100644 src/core/config/remote/enumerations/component-states.ts delete mode 100644 test/unit/core/config/remote/components/component-names.test.ts diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 8339a1577..660dd2645 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -14,13 +14,12 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {type ClusterChecks} from '../core/cluster-checks.js'; import {container} from 'tsyringe-neo'; import {InjectTokens} from '../core/dependency-injection/inject-tokens.js'; -import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; +import {type ArgvStruct, type AnyYargs, type NodeAliases} from '../types/aliases.js'; import {Templates} from '../core/templates.js'; import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; import {DeploymentStates} from '../core/config/remote/enumerations/deployment-states.js'; -import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface DeploymentAddClusterConfig { quiet: boolean; @@ -698,12 +697,13 @@ export class DeploymentCommand extends BaseCommand { //* add the new nodes to components for (const nodeAlias of nodeAliases) { - remoteConfig.components.addNewComponent( - ComponentFactory.createNewConsensusNodeComponent( + remoteConfig.components.add( + new ConsensusNodeComponent( nodeAlias, clusterRef, - namespace, + namespace.name, ConsensusNodeStates.NON_DEPLOYED, + Templates.nodeIdFromNodeAlias(nodeAlias), ), ); } diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index d93a48f34..13fad3183 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -10,10 +10,9 @@ import * as constants from '../core/constants.js'; import {type ProfileManager} from '../core/profile-manager.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; -import {type AnyListrContext, type AnyYargs, type ArgvStruct} from '../types/aliases.js'; +import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {type MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; -import * as helpers from '../core/helpers.js'; +import {MirrorNodeExplorerComponent} from '../core/config/remote/components/mirror-node-explorer-component.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; @@ -29,9 +28,9 @@ import { INGRESS_CONTROLLER_PREFIX, } from '../core/constants.js'; import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; -import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; import {type ClusterReference, type Context} from '../core/config/remote/types.js'; -import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; +import * as helpers from '../core/helpers.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface ExplorerDeployConfigClass { cacheDir: string; @@ -260,7 +259,7 @@ export class ExplorerCommand extends BaseCommand { return ListrLock.newAcquireLockTask(lease, task); }, }, - this.loadRemoteConfigTask(argv), + ListrRemoteConfig.loadRemoteConfig(this.remoteConfigManager, argv), { title: 'Install cert manager', task: async context_ => { @@ -495,7 +494,7 @@ export class ExplorerCommand extends BaseCommand { return ListrLock.newAcquireLockTask(lease, task); }, }, - this.loadRemoteConfigTask(argv), + ListrRemoteConfig.loadRemoteConfig(this.remoteConfigManager, argv), { title: 'Destroy explorer', task: async context_ => { @@ -529,7 +528,7 @@ export class ExplorerCommand extends BaseCommand { }); }, }, - this.disableMirrorNodeExplorerComponents(), + this.removeMirrorNodeExplorerComponents(), ], { concurrent: false, @@ -609,33 +608,14 @@ export class ExplorerCommand extends BaseCommand { }; } - private loadRemoteConfigTask(argv: ArgvStruct): SoloListrTask { - return { - title: 'Load remote config', - task: async (): Promise => { - await this.remoteConfigManager.loadAndValidate(argv); - }, - }; - } - /** Removes the explorer components from remote config. */ - private disableMirrorNodeExplorerComponents(): SoloListrTask { + private removeMirrorNodeExplorerComponents(): SoloListrTask { return { title: 'Remove explorer from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (context_): Promise => { - const clusterReference: ClusterReference = context_.config.clusterReference; - + task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const explorerComponents: MirrorNodeExplorerComponent[] = - remoteConfig.components.getComponentsByClusterReference( - ComponentTypes.MirrorNodeExplorer, - clusterReference, - ); - - for (const explorerComponent of explorerComponents) { - remoteConfig.components.disableComponent(explorerComponent.name, ComponentTypes.MirrorNodeExplorer); - } + remoteConfig.components.remove('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer); }); }, }; @@ -648,11 +628,10 @@ export class ExplorerCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const {namespace, clusterRef} = context_.config; - - remoteConfig.components.addNewComponent( - ComponentFactory.createNewExplorerComponent(this.remoteConfigManager, clusterRef, namespace), - ); + const { + config: {namespace, clusterRef}, + } = context_; + remoteConfig.components.add(new MirrorNodeExplorerComponent('mirrorNodeExplorer', clusterRef, namespace.name)); }); }, }; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 6fe49cafa..71f03ad80 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -17,7 +17,7 @@ import * as helpers from '../core/helpers.js'; import {type AnyYargs, type ArgvStruct} from '../types/aliases.js'; import {type PodName} from '../integration/kube/resources/pod/pod-name.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {type MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; +import {MirrorNodeComponent} from '../core/config/remote/components/mirror-node-component.js'; import * as fs from 'node:fs'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import * as Base64 from 'js-base64'; @@ -41,7 +41,6 @@ import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface MirrorNodeDeployConfigClass { cacheDir: string; @@ -831,7 +830,7 @@ export class MirrorNodeCommand extends BaseCommand { }); }, }, - this.disableMirrorNodeComponents(), + this.removeMirrorNodeComponents(), ], { concurrent: false, @@ -918,23 +917,13 @@ export class MirrorNodeCommand extends BaseCommand { } /** Removes the mirror node components from remote config. */ - public disableMirrorNodeComponents(): SoloListrTask { + public removeMirrorNodeComponents(): SoloListrTask { return { title: 'Remove mirror node from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (context_): Promise => { - const clusterReference: ClusterReference = context_.config.clusterRef; - + task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const mirrorNodeComponents: MirrorNodeComponent[] = - remoteConfig.components.getComponentsByClusterReference( - ComponentTypes.MirrorNode, - clusterReference, - ); - - for (const mirrorNodeComponent of mirrorNodeComponents) { - remoteConfig.components.disableComponent(mirrorNodeComponent.name, ComponentTypes.MirrorNode); - } + remoteConfig.components.remove('mirrorNode', ComponentType.MirrorNode); }); }, }; @@ -947,11 +936,11 @@ export class MirrorNodeCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const {namespace, clusterRef} = context_.config; + const { + config: {namespace, clusterRef}, + } = context_; - remoteConfig.components.addNewComponent( - ComponentFactory.createNewMirrorNodeComponent(this.remoteConfigManager, clusterRef, namespace), - ); + remoteConfig.components.add(new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name)); }); }, }; diff --git a/src/commands/network.ts b/src/commands/network.ts index c955f01e5..0cf64a58e 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -11,7 +11,6 @@ import {UserBreak} from '../core/errors/user-break.js'; import {BaseCommand, type Options} from './base.js'; import {Flags as flags} from './flags.js'; import * as constants from '../core/constants.js'; -import {SOLO_DEPLOYMENT_CHART} from '../core/constants.js'; import {Templates} from '../core/templates.js'; import { addDebugOptions, @@ -28,6 +27,9 @@ import {type ProfileManager} from '../core/profile-manager.js'; import {type CertificateManager} from '../core/certificate-manager.js'; import {type AnyYargs, type IP, type NodeAlias, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; +import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; +import {EnvoyProxyComponent} from '../core/config/remote/components/envoy-proxy-component.js'; +import {HaProxyComponent} from '../core/config/remote/components/ha-proxy-component.js'; import {v4 as uuidv4} from 'uuid'; import {type CommandDefinition, type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; @@ -39,10 +41,10 @@ import {Base64} from 'js-base64'; import {SecretType} from '../integration/kube/resources/secret/secret-type.js'; import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; +import {SOLO_DEPLOYMENT_CHART} from '../core/constants.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; export interface NetworkDeployConfigClass { applicationEnv: string; @@ -1326,30 +1328,28 @@ export class NetworkCommand extends BaseCommand { title: 'Add node and proxies to remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { - const {namespace} = context_.config; + const { + config: {namespace}, + } = context_; await this.remoteConfigManager.modify(async remoteConfig => { for (const consensusNode of context_.config.consensusNodes) { - const nodeAlias: NodeAlias = consensusNode.name; - const clusterReference: ClusterReference = consensusNode.cluster; - - remoteConfig.components.changeNodeState(nodeAlias, ConsensusNodeStates.REQUESTED); - - remoteConfig.components.addNewComponent( - ComponentFactory.createNewEnvoyProxyComponent( - this.remoteConfigManager, - clusterReference, - namespace, - nodeAlias, + remoteConfig.components.edit( + new ConsensusNodeComponent( + consensusNode.name, + consensusNode.cluster, + namespace.name, + ConsensusNodeStates.REQUESTED, + consensusNode.nodeId, ), ); - remoteConfig.components.addNewComponent( - ComponentFactory.createNewHaProxyComponent( - this.remoteConfigManager, - clusterReference, - namespace, - nodeAlias, - ), + + remoteConfig.components.add( + new EnvoyProxyComponent(`envoy-proxy-${consensusNode.name}`, consensusNode.cluster, namespace.name), + ); + + remoteConfig.components.add( + new HaProxyComponent(`haproxy-${consensusNode.name}`, consensusNode.cluster, namespace.name), ); } }); diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index a326a2209..a0a9a5901 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -897,23 +897,52 @@ export class NodeCommandHandlers extends CommandHandler { return true; } + // TODO MOVE TO TASKS + + /** Removes the consensus node, envoy and haproxy components from remote config. */ + public removeNodeAndProxies(): SoloListrTask { + return { + skip: (): boolean => !this.remoteConfigManager.isLoaded(), + title: 'Remove node and proxies from remote config', + task: async (): Promise => { + await this.remoteConfigManager.modify(async remoteConfig => { + remoteConfig.components.remove('Consensus node name', ComponentType.ConsensusNode); + remoteConfig.components.remove('Envoy proxy name', ComponentType.EnvoyProxy); + remoteConfig.components.remove('HaProxy name', ComponentType.HaProxy); + }); + }, + }; + } + /** * Changes the state from all consensus nodes components in remote config. * - * @param nodeState - to which to change the consensus node component + * @param state - to which to change the consensus node component */ - public changeAllNodeStates(nodeState: ConsensusNodeStates): SoloListrTask { + public changeAllNodeStates(state: ConsensusNodeStates): SoloListrTask { interface Context { config: {namespace: NamespaceName; consensusNodes: ConsensusNode[]}; } return { - title: `Change node state to ${nodeState} in remote config`, + title: `Change node state to ${state} in remote config`, skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_: Context): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { + const { + config: {namespace}, + } = context_; + for (const consensusNode of context_.config.consensusNodes) { - remoteConfig.components.changeNodeState(consensusNode.name, nodeState); + remoteConfig.components.edit( + new ConsensusNodeComponent( + consensusNode.name, + consensusNode.cluster, + namespace.name, + state, + consensusNode.nodeId, + ), + ); } }); }, @@ -1029,6 +1058,6 @@ export class NodeCommandHandlers extends CommandHandler { // throw new SoloError(`${nodeAlias} has invalid state - ` + errorMessageData); // } - return nodeComponent.nodeState; + return nodeComponent.state; } } diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index a2a423138..f714d0321 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -77,7 +77,11 @@ import {ContainerReference} from '../../integration/kube/resources/container/con import {NetworkNodes} from '../../core/network-nodes.js'; import {container, inject, injectable} from 'tsyringe-neo'; import {type Optional, type SoloListr, type SoloListrTask, type SoloListrTaskWrapper} from '../../types/index.js'; -import {type ClusterReference, type Context, type DeploymentName} from '../../core/config/remote/types.js'; +import { + type ClusterReference, + type DeploymentName, + type NamespaceNameAsString, +} from '../../core/config/remote/types.js'; import {patchInject} from '../../core/dependency-injection/container-helper.js'; import {ConsensusNode} from '../../core/model/consensus-node.js'; import {type K8} from '../../integration/kube/k8.js'; @@ -86,6 +90,9 @@ import {InjectTokens} from '../../core/dependency-injection/inject-tokens.js'; import {type RemoteConfigManager} from '../../core/config/remote/remote-config-manager.js'; import {type LocalConfig} from '../../core/config/local/local-config.js'; import {BaseCommand} from '../base.js'; +import {ConsensusNodeComponent} from '../../core/config/remote/components/consensus-node-component.js'; +import {EnvoyProxyComponent} from '../../core/config/remote/components/envoy-proxy-component.js'; +import {HaProxyComponent} from '../../core/config/remote/components/ha-proxy-component.js'; import {HEDERA_PLATFORM_VERSION} from '../../../version.js'; import {ShellRunner} from '../../core/shell-runner.js'; import {PathEx} from '../../business/utils/path-ex.js'; @@ -110,8 +117,6 @@ import {type NodeStartConfigClass} from './config-interfaces/node-start-config-c import {type CheckedNodesConfigClass, type CheckedNodesContext} from './config-interfaces/node-common-config-class.js'; import {type NetworkNodeServices} from '../../core/network-node-services.js'; import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js'; -import {Cluster} from '../../core/config/remote/cluster.js'; -import {ComponentFactory} from '../../core/config/remote/components/component-factory.js'; @injectable() export class NodeCommandTasks { @@ -2502,51 +2507,40 @@ export class NodeCommandTasks { return { title: 'Add new node to remote config', task: async (context_, task) => { - const nodeAlias: NodeAlias = context_.config.nodeAlias; - const namespace: NamespaceName = context_.config.namespace; - const clusterReference: ClusterReference = context_.config.clusterRef; - const context: Context = this.localConfig.clusterRefs[clusterReference]; + const nodeAlias = context_.config.nodeAlias; + const namespace: NamespaceNameAsString = context_.config.namespace.name; + const clusterReference = context_.config.clusterRef; + const context = this.localConfig.clusterRefs[clusterReference]; task.title += `: ${nodeAlias}`; await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.addNewComponent( - ComponentFactory.createNewConsensusNodeComponent( + remoteConfig.components.add( + new ConsensusNodeComponent( nodeAlias, clusterReference, namespace, ConsensusNodeStates.STARTED, + Templates.nodeIdFromNodeAlias(nodeAlias), ), ); - remoteConfig.components.addNewComponent( - ComponentFactory.createNewEnvoyProxyComponent( - this.remoteConfigManager, - clusterReference, - namespace, - nodeAlias, - ), - ); - remoteConfig.components.addNewComponent( - ComponentFactory.createNewHaProxyComponent( - this.remoteConfigManager, - clusterReference, - namespace, - nodeAlias, - ), - ); + + remoteConfig.components.add(new EnvoyProxyComponent(`envoy-proxy-${nodeAlias}`, clusterReference, namespace)); + + remoteConfig.components.add(new HaProxyComponent(`haproxy-${nodeAlias}`, clusterReference, namespace)); }); context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes(); // if the consensusNodes does not contain the nodeAlias then add it if (!context_.config.consensusNodes.some((node: ConsensusNode) => node.name === nodeAlias)) { - const cluster: Cluster = this.remoteConfigManager.clusters[clusterReference]; + const cluster = this.remoteConfigManager.clusters[clusterReference]; context_.config.consensusNodes.push( new ConsensusNode( nodeAlias, Templates.nodeIdFromNodeAlias(nodeAlias), - namespace.name, + namespace, clusterReference, context, cluster.dnsBaseDomain, @@ -2554,7 +2548,7 @@ export class NodeCommandTasks { Templates.renderConsensusNodeFullyQualifiedDomainName( nodeAlias, Templates.nodeIdFromNodeAlias(nodeAlias), - namespace.name, + namespace, clusterReference, cluster.dnsBaseDomain, cluster.dnsConsensusNodePattern, diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 1cd2d626f..f71fea09e 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -14,7 +14,7 @@ import {Flags as flags} from './flags.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {type AnyYargs, type ArgvStruct, type NodeAliases} from '../types/aliases.js'; import {ListrLock} from '../core/lock/listr-lock.js'; -import {type RelayComponent} from '../core/config/remote/components/relay-component.js'; +import {RelayComponent} from '../core/config/remote/components/relay-component.js'; import * as Base64 from 'js-base64'; import {NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; @@ -469,7 +469,7 @@ export class RelayCommand extends BaseCommand { }, skip: context_ => !context_.config.isChartInstalled, }, - this.disableRelayComponent(), + this.removeRelayComponent(), ], { concurrent: false, @@ -546,34 +546,25 @@ export class RelayCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (context_): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const {namespace, nodeAliases, clusterRef} = context_.config; + const { + config: {namespace, nodeAliases}, + } = context_; + const cluster = this.remoteConfigManager.currentCluster; - remoteConfig.components.addNewComponent( - ComponentFactory.createNewRelayComponent(this.remoteConfigManager, clusterRef, namespace, nodeAliases), - ); + remoteConfig.components.add(new RelayComponent('relay', cluster, namespace.name, nodeAliases)); }); }, }; } /** Remove the relay component from remote config. */ - public disableRelayComponent(): SoloListrTask { + public removeRelayComponent(): SoloListrTask { return { title: 'Remove relay component from remote config', skip: (): boolean => !this.remoteConfigManager.isLoaded(), - task: async (context_): Promise => { - const clusterReference: ClusterReference = context_.config.clusterRef; - + task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - const relayComponents: RelayComponent[] = - remoteConfig.components.getComponentsByClusterReference( - ComponentTypes.Relay, - clusterReference, - ); - - for (const relayComponent of relayComponents) { - remoteConfig.components.disableComponent(relayComponent.name, ComponentTypes.Relay); - } + remoteConfig.components.remove('relay', ComponentType.Relay); }); }, }; diff --git a/src/core/config/remote/api/components-data-wrapper-api.ts b/src/core/config/remote/api/components-data-wrapper-api.ts deleted file mode 100644 index 0d19f8329..000000000 --- a/src/core/config/remote/api/components-data-wrapper-api.ts +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {type ToObject, type Validate} from '../../../../types/index.js'; -import {type ClusterReference, type ComponentName} from '../types.js'; -import {type CloneTrait} from '../../../../types/traits/clone-trait.js'; -import {type BaseComponent} from '../components/base-component.js'; -import {type ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; -import {type ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentsDataStruct} from '../interfaces/components-data-struct.js'; - -export interface ComponentsDataWrapperApi - extends Validate, - ToObject, - CloneTrait { - /** Used to add new component to their respective group. */ - addNewComponent(component: BaseComponent): void; - - changeNodeState(componentName: ComponentName, nodeState: ConsensusNodeStates): void; - - /** Used to remove specific component from their respective group. */ - disableComponent(componentName: ComponentName, type: ComponentTypes): void; - - getComponent(type: ComponentTypes, componentName: ComponentName): T; - - getComponentsByClusterReference( - type: ComponentTypes, - clusterReference: ClusterReference, - ): T[]; - - /** - * Checks all existing components of specified type and gives you a new unique index - */ - getNewComponentIndex(componentType: ComponentTypes): number; -} diff --git a/src/core/config/remote/api/remote-config-manager-api.ts b/src/core/config/remote/api/remote-config-manager-api.ts deleted file mode 100644 index 5b54aa568..000000000 --- a/src/core/config/remote/api/remote-config-manager-api.ts +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {type RemoteConfigDataWrapper} from '../remote-config-data-wrapper.js'; -import {type ClusterReference, type ClusterReferences, type Context, type DeploymentName} from '../types.js'; -import {type ComponentsDataWrapper} from '../components-data-wrapper.js'; -import {type Cluster} from '../cluster.js'; -import {type AnyObject, type ArgvStruct, type NodeAliases} from '../../../../types/aliases.js'; -import {type DeploymentStates} from '../enumerations/deployment-states.js'; -import {type NamespaceName} from '../../../../integration/kube/resources/namespace/namespace-name.js'; -import {type ConsensusNode} from '../../../model/consensus-node.js'; -import {type ConfigMap} from '../../../../integration/kube/resources/config-map/config-map.js'; - -export interface RemoteConfigManagerApi { - /** @returns the default cluster from kubectl */ - get currentCluster(): ClusterReference; - - /** @returns the components data wrapper cloned */ - get components(): ComponentsDataWrapper; - - /** - * @returns the remote configuration data's clusters cloned - */ - get clusters(): Record; - - /** - * Modifies the loaded remote configuration data using a provided callback function. - * The callback operates on the configuration data, which is then saved to the cluster. - * - * @param callback - an async function that modifies the remote configuration data. - * @throws if the configuration is not loaded before modification, will throw a SoloError {@link SoloError} - */ - modify(callback: (remoteConfig: RemoteConfigDataWrapper) => Promise): Promise; - - /** - * Creates a new remote configuration in the Kubernetes cluster. - * Gathers data from the local configuration and constructs a new ConfigMap - * entry in the cluster with initial command history and metadata. - */ - create( - argv: ArgvStruct, - state: DeploymentStates, - nodeAliases: NodeAliases, - namespace: NamespaceName, - deployment: DeploymentName, - clusterReference: ClusterReference, - context: Context, - dnsBaseDomain: string, - dnsConsensusNodePattern: string, - ): Promise; - - /** - * Loads the remote configuration, performs a validation and returns it - * @returns RemoteConfigDataWrapper - */ - get(context?: Context): Promise; - - /** Unload the remote config from the remote config manager. */ - unload(): void; - - /** - * Performs the loading of the remote configuration. - * Checks if the configuration is already loaded, otherwise loads and adds the command to history. - * - * @param argv - arguments containing command input for historical reference. - * @param validate - whether to validate the remote configuration. - * @param [skipConsensusNodesValidation] - whether or not to validate the consensusNodes - */ - loadAndValidate( - argv: {_: string[]} & AnyObject, - validate: boolean, - skipConsensusNodesValidation: boolean, - ): Promise; - - /** Empties the component data inside the remote config */ - deleteComponents(): Promise; - - /** @returns if the remote config is already loaded */ - isLoaded(): boolean; - - /** - * Retrieves the ConfigMap containing the remote configuration from the Kubernetes cluster. - * - * @param namespace - The namespace to search for the ConfigMap. - * @param context - The context to use for the Kubernetes client. - * @returns the remote configuration data. - * @throws if the ConfigMap could not be read and the error is not a 404 status, will throw a SoloError {@link SoloError} - */ - getConfigMap(namespace?: NamespaceName, context?: Context): Promise; - - /** - * Creates a new ConfigMap entry in the Kubernetes cluster with the remote configuration data. - */ - createConfigMap(context?: Context): Promise; - - /** - * Get the consensus nodes from the remoteConfigManager and use the localConfig to get the context - * @returns an array of ConsensusNode objects - */ - getConsensusNodes(): ConsensusNode[]; - - /** - * Gets a list of distinct contexts from the consensus nodes. - * @returns an array of context strings. - */ - getContexts(): Context[]; - - /** - * Gets a list of distinct cluster references from the consensus nodes. - * @returns an object of cluster references. - */ - getClusterRefs(): ClusterReferences; -} diff --git a/src/core/config/remote/cluster.ts b/src/core/config/remote/cluster.ts index f44807a22..f179fe2fd 100644 --- a/src/core/config/remote/cluster.ts +++ b/src/core/config/remote/cluster.ts @@ -16,7 +16,6 @@ export class Cluster implements ClusterStruct, ToObject { if (!name) { throw new SoloError('name is required'); } - if (typeof name !== 'string') { throw new SoloError(`Invalid type for name: ${typeof name}`); } @@ -24,7 +23,6 @@ export class Cluster implements ClusterStruct, ToObject { if (!namespace) { throw new SoloError('namespace is required'); } - if (typeof namespace !== 'string') { throw new SoloError(`Invalid type for namespace: ${typeof namespace}`); } @@ -32,7 +30,6 @@ export class Cluster implements ClusterStruct, ToObject { if (!deployment) { throw new SoloError('deployment is required'); } - if (typeof deployment !== 'string') { throw new SoloError(`Invalid type for deployment: ${typeof deployment}`); } diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index abf3aebed..e5a34f798 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -43,57 +43,67 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { /* -------- Modifiers -------- */ /** Used to add new component to their respective group. */ - public addNewComponent(component: BaseComponent): void { - const componentName: string = component.name; + public add(component: BaseComponent): void { + const self = this; - if (!componentName || typeof componentName !== 'string') { - throw new SoloError(`Component name is required ${componentName}`); + const serviceName = component.name; + + if (!serviceName || typeof serviceName !== 'string') { + throw new SoloError(`Service name is required ${serviceName}`); } if (!(component instanceof BaseComponent)) { - throw new SoloError('Component must be instance of BaseComponent', undefined, BaseComponent); + throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent); } - const addComponentCallback: (components: Record) => void = components => { - if (this.checkComponentExists(components, component)) { - throw new SoloError('Component exists', undefined, component.toObject()); + function addComponentCallback(components: Record): void { + if (self.exists(components, component)) { + throw new SoloError('Component exists', null, component.toObject()); } - components[componentName] = component; - }; + components[serviceName] = component; + } - this.applyCallbackToComponentGroup(component.type, addComponentCallback, componentName); + self.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); } - public changeNodeState(componentName: ComponentName, nodeState: ConsensusNodeStates): void { - if (!this.consensusNodes[componentName]) { - throw new SoloError(`Consensus node ${componentName} doesn't exist`); + /** Used to edit an existing component from their respective group. */ + public edit(component: BaseComponent): void { + const serviceName: ComponentName = component.name; + + if (!serviceName || typeof serviceName !== 'string') { + throw new SoloError(`Service name is required ${serviceName}`); + } + if (!(component instanceof BaseComponent)) { + throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent); } - if (!isValidEnum(nodeState, ConsensusNodeStates)) { - throw new SoloError(`Invalid node state ${nodeState}`); + function editComponentCallback(components: Record): void { + if (!components[serviceName]) { + throw new SoloError(`Component doesn't exist, name: ${serviceName}`, null, {component}); + } + components[serviceName] = component; } - this.consensusNodes[componentName].changeNodeState(nodeState); + this.applyCallbackToComponentGroup(component.type, editComponentCallback, serviceName); } /** Used to remove specific component from their respective group. */ - public disableComponent(componentName: ComponentName, type: ComponentTypes): void { - if (!componentName || typeof componentName !== 'string') { - throw new SoloError(`Component name is required ${componentName}`); + public remove(serviceName: ComponentName, type: ComponentTypes): void { + if (!serviceName || typeof serviceName !== 'string') { + throw new SoloError(`Service name is required ${serviceName}`); } - - if (!isValidEnum(type, ComponentTypes)) { + if (!Object.values(ComponentTypes).includes(type)) { throw new SoloError(`Invalid component type ${type}`); } - const disableComponentCallback: (components: Record) => void = components => { - if (!components[componentName]) { - throw new SoloError(`Component ${componentName} of type ${type} not found while attempting to remove`); + function deleteComponentCallback(components: Record): void { + if (!components[serviceName]) { + throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`); } - components[componentName].state = ComponentStates.DELETED; - }; + delete components[serviceName]; + } - this.applyCallbackToComponentGroup(type, disableComponentCallback, componentName); + this.applyCallbackToComponentGroup(type, deleteComponentCallback, serviceName); } /* -------- Utilities -------- */ @@ -113,21 +123,6 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { return component; } - public getComponentsByClusterReference( - type: ComponentTypes, - clusterReference: ClusterReference, - ): T[] { - let filteredComponents: T[] = []; - - const getComponentsByClusterReferenceCallback: (components: Record) => void = components => { - filteredComponents = Object.values(components).filter(component => component.cluster === clusterReference); - }; - - this.applyCallbackToComponentGroup(type, getComponentsByClusterReferenceCallback); - - return filteredComponents; - } - /** * Method used to map the type to the specific component group * and pass it to a callback to apply modifications @@ -271,64 +266,54 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { } public static initializeWithNodes( - consensusNodeComponents: Record, + nodeAliases: NodeAliases, + clusterReference: ClusterReference, + namespace: NamespaceNameAsString, ): ComponentsDataWrapper { - return new ComponentsDataWrapper(undefined, undefined, undefined, undefined, consensusNodeComponents); + const consensusNodeComponents: Record = {}; + + for (const nodeAlias of nodeAliases) { + consensusNodeComponents[nodeAlias] = new ConsensusNodeComponent( + nodeAlias, + clusterReference, + namespace, + ConsensusNodeStates.NON_DEPLOYED, + Templates.nodeIdFromNodeAlias(nodeAlias), + ); + } + + return new ComponentsDataWrapper(undefined, undefined, undefined, undefined, consensusNodeComponents, undefined); } /** checks if component exists in the respective group */ - private checkComponentExists(components: Record, newComponent: BaseComponent): boolean { - return Object.values(components).some((component): boolean => BaseComponent.compare(component, newComponent)); + private exists(components: Record, newComponent: BaseComponent): boolean { + return Object.values(components).some(component => BaseComponent.compare(component, newComponent)); } - /** - * Checks all existing components of specified type and gives you a new unique index - */ - public getNewComponentIndex(componentType: ComponentTypes): number { - let newComponentIndex: number = 0; - - const calculateNewComponentIndexCallback: ( - components: Record, - ) => void = components => { - for (const componentName of Object.keys(components)) { - const componentIndex: number = ComponentNameTemplates.parseComponentName(componentName); - if (newComponentIndex <= componentIndex) { - newComponentIndex = componentIndex + 1; + public validate(): void { + function testComponentsObject(components: Record, expectedInstance: any): void { + for (const [name, component] of Object.entries(components)) { + if (!name || typeof name !== 'string') { + throw new SoloError(`Invalid component service name ${{[name]: component?.constructor?.name}}`); } - } - }; - - this.applyCallbackToComponentGroup(componentType, calculateNewComponentIndexCallback); - return newComponentIndex; - } - - /** Validates that the component group mapping has only components from the expected instance */ - private validateComponentTypes(components: Record, expectedInstance: any): void { - for (const [componentName, component] of Object.entries(components)) { - if (!componentName || typeof componentName !== 'string') { - throw new SoloError(`Invalid component name ${{[componentName]: component?.constructor?.name}}`); - } - - if (!(component instanceof expectedInstance)) { - throw new SoloError( - `Invalid component type, component name: ${componentName}, ` + - `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`, - undefined, - {component}, - ); + if (!(component instanceof expectedInstance)) { + throw new SoloError( + `Invalid component type, service name: ${name}, ` + + `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`, + null, + {component}, + ); + } } } - } - public validate(): void { - this.validateComponentTypes(this.relays, RelayComponent); - this.validateComponentTypes(this.haProxies, HaProxyComponent); - this.validateComponentTypes(this.mirrorNodes, MirrorNodeComponent); - this.validateComponentTypes(this.envoyProxies, EnvoyProxyComponent); - this.validateComponentTypes(this.consensusNodes, ConsensusNodeComponent); - this.validateComponentTypes(this.mirrorNodeExplorers, MirrorNodeExplorerComponent); - this.validateComponentTypes(this.blockNodes, BlockNodeComponent); + testComponentsObject(this.relays, RelayComponent); + testComponentsObject(this.haProxies, HaProxyComponent); + testComponentsObject(this.mirrorNodes, MirrorNodeComponent); + testComponentsObject(this.envoyProxies, EnvoyProxyComponent); + testComponentsObject(this.consensusNodes, ConsensusNodeComponent); + testComponentsObject(this.mirrorNodeExplorers, MirrorNodeExplorerComponent); } private transformComponentGroupToObject( diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts index 10fceb025..e2c2a7d7c 100644 --- a/src/core/config/remote/components/base-component.ts +++ b/src/core/config/remote/components/base-component.ts @@ -4,7 +4,6 @@ import {SoloError} from '../../../errors/solo-error.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject, type Validate} from '../../../../types/index.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {ComponentStates} from '../enumerations/component-states.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; @@ -25,7 +24,6 @@ export class BaseComponent implements BaseComponentStruct, Validate, ToObject { - const consensusNodeComponents: Record = {}; - - for (const nodeAlias of nodeAliases) { - consensusNodeComponents[nodeAlias] = ComponentFactory.createNewConsensusNodeComponent( - nodeAlias, - clusterReference, - namespace, - ConsensusNodeStates.NON_DEPLOYED, - ); - } - - return consensusNodeComponents; - } -} diff --git a/src/core/config/remote/components/component-name-templates.ts b/src/core/config/remote/components/component-name-templates.ts deleted file mode 100644 index d6ba59648..000000000 --- a/src/core/config/remote/components/component-name-templates.ts +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {SoloError} from '../../../errors/solo-error.js'; -import {type ComponentName} from '../types.js'; -import {type NodeAlias} from '../../../../types/aliases.js'; - -export class ComponentNameTemplates { - private static BLOCK_NODE_BASE_NAME: string = 'block-node'; - private static RELAY_BASE_NAME: string = 'relay'; - private static EXPLORER_BASE_NAME: string = 'mirror-node-explorer'; - private static MIRROR_NODE_BASE_NAME: string = 'mirror-node'; - private static HA_PROXY_BASE_NAME: (nodeAlias: NodeAlias) => string = nodeAlias => `haproxy-${nodeAlias}`; - private static ENVOY_PROXY_BASE_NAME: (nodeAlias: NodeAlias) => string = nodeAlias => `envoy-proxy-${nodeAlias}`; - - public static renderBlockNodeName(index: number): ComponentName { - return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.BLOCK_NODE_BASE_NAME, index); - } - - public static renderRelayName(index: number): ComponentName { - return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.RELAY_BASE_NAME, index); - } - - public static renderMirrorNodeExplorerName(index: number): ComponentName { - return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.EXPLORER_BASE_NAME, index); - } - - public static renderMirrorNodeName(index: number): ComponentName { - return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.MIRROR_NODE_BASE_NAME, index); - } - - public static renderHaProxyName(index: number, nodeAlias: NodeAlias): ComponentName { - return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.HA_PROXY_BASE_NAME(nodeAlias), index); - } - - public static renderEnvoyProxyName(index: number, nodeAlias: NodeAlias): ComponentName { - return ComponentNameTemplates.renderComponentName(ComponentNameTemplates.ENVOY_PROXY_BASE_NAME(nodeAlias), index); - } - - /** - * Used for rendering component name with additional data. - * - * @param baseName - unique name for the component ( ex. mirror-node ) - * @param index - total number of components from this kind - * @returns a unique name to be used for creating components - */ - private static renderComponentName(baseName: string, index: number): ComponentName { - return `${baseName}-${index}`; - } - - /** - * Extracts the index from a component name by splitting on '-' and taking the last segment. - * - * @param name - full component name (e.g., "mirror-node-node1-42") - * @returns the numeric index (e.g., 42) - */ - public static parseComponentName(name: ComponentName): number { - const parts: string[] = name.split('-'); - const lastPart: string = parts.at(-1); - const componentIndex: number = Number.parseInt(lastPart, 10); - - if (Number.isNaN(componentIndex)) { - throw new SoloError(`Invalid component index in component name: ${name}`); - } - - return componentIndex; - } -} diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts index 1e9569038..49d663dbd 100644 --- a/src/core/config/remote/components/consensus-node-component.ts +++ b/src/core/config/remote/components/consensus-node-component.ts @@ -7,7 +7,6 @@ import {ConsensusNodeStates} from '../enumerations/consensus-node-states.js'; import {isValidEnum} from '../../../util/validation-helpers.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject} from '../../../../types/index.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; import {type ConsensusNodeComponentStruct} from './interfaces/consensus-node-component-struct.js'; /** @@ -20,35 +19,22 @@ export class ConsensusNodeComponent extends BaseComponent implements ConsensusNodeComponentStruct, ToObject { - private _nodeState: ConsensusNodeStates; - /** * @param name - the name to distinguish components. * @param nodeId - node id of the consensus node * @param cluster - associated to component * @param namespace - associated to component - * @param state - the component state - * @param nodeState - of the consensus node + * @param state - of the consensus node */ public constructor( name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString, - state: ComponentStates, - nodeState: ConsensusNodeStates, + public readonly state: ConsensusNodeStates, public readonly nodeId: number, ) { - super(ComponentTypes.ConsensusNode, name, cluster, namespace, state); - this._nodeState = nodeState; - this.validate(); - } - - public get nodeState(): ConsensusNodeStates { - return this._nodeState; - } + super(ComponentTypes.ConsensusNode, name, cluster, namespace); - public changeNodeState(nodeState: ConsensusNodeStates): void { - this._nodeState = nodeState; this.validate(); } @@ -56,15 +42,15 @@ export class ConsensusNodeComponent /** Handles creating instance of the class from plain object. */ public static fromObject(component: ConsensusNodeComponentStruct): ConsensusNodeComponent { - const {name, cluster, state, namespace, nodeState, nodeId} = component; - return new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, nodeId); + const {name, cluster, namespace, state, nodeId} = component; + return new ConsensusNodeComponent(name, cluster, namespace, state, nodeId); } public override validate(): void { super.validate(); - if (!isValidEnum(this.nodeState, ConsensusNodeStates)) { - throw new SoloError(`Invalid consensus node state: ${this.nodeState}`); + if (!isValidEnum(this.state, ConsensusNodeStates)) { + throw new SoloError(`Invalid consensus node state: ${this.state}`); } if (typeof this.nodeId !== 'number') { @@ -79,7 +65,7 @@ export class ConsensusNodeComponent public override toObject(): ConsensusNodeComponentStruct { return { ...super.toObject(), - nodeState: this.nodeState, + state: this.state, nodeId: this.nodeId, }; } diff --git a/src/core/config/remote/components/envoy-proxy-component.ts b/src/core/config/remote/components/envoy-proxy-component.ts index 97e247b77..72faf17b1 100644 --- a/src/core/config/remote/components/envoy-proxy-component.ts +++ b/src/core/config/remote/components/envoy-proxy-component.ts @@ -2,18 +2,12 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class EnvoyProxyComponent extends BaseComponent { - public constructor( - name: ComponentName, - cluster: ClusterReference, - namespace: NamespaceNameAsString, - state: ComponentStates, - ) { - super(ComponentTypes.EnvoyProxy, name, cluster, namespace, state); + public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) { + super(ComponentTypes.EnvoyProxy, name, cluster, namespace); this.validate(); } @@ -21,7 +15,7 @@ export class EnvoyProxyComponent extends BaseComponent { /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStruct): EnvoyProxyComponent { - const {name, cluster, namespace, state} = component; - return new EnvoyProxyComponent(name, cluster, namespace, state); + const {name, cluster, namespace} = component; + return new EnvoyProxyComponent(name, cluster, namespace); } } diff --git a/src/core/config/remote/components/ha-proxy-component.ts b/src/core/config/remote/components/ha-proxy-component.ts index 4eb5710f2..bcfa745b6 100644 --- a/src/core/config/remote/components/ha-proxy-component.ts +++ b/src/core/config/remote/components/ha-proxy-component.ts @@ -3,17 +3,11 @@ import {BaseComponent} from './base-component.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class HaProxyComponent extends BaseComponent { - public constructor( - name: ComponentName, - cluster: ClusterReference, - namespace: NamespaceNameAsString, - state: ComponentStates, - ) { - super(ComponentTypes.HaProxy, name, cluster, namespace, state); + public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) { + super(ComponentTypes.HaProxy, name, cluster, namespace); this.validate(); } @@ -21,7 +15,7 @@ export class HaProxyComponent extends BaseComponent { /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStruct): HaProxyComponent { - const {name, cluster, namespace, state} = component; - return new HaProxyComponent(name, cluster, namespace, state); + const {name, cluster, namespace} = component; + return new HaProxyComponent(name, cluster, namespace); } } diff --git a/src/core/config/remote/components/interfaces/base-component-struct.ts b/src/core/config/remote/components/interfaces/base-component-struct.ts index 9e7ca059a..f2e6b4a20 100644 --- a/src/core/config/remote/components/interfaces/base-component-struct.ts +++ b/src/core/config/remote/components/interfaces/base-component-struct.ts @@ -1,11 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -import {type ComponentStates} from '../../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../types.js'; export interface BaseComponentStruct { name: ComponentName; cluster: ClusterReference; namespace: NamespaceNameAsString; - state: ComponentStates; } diff --git a/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts b/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts index 85aabc1bf..8b10c1bf8 100644 --- a/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts +++ b/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts @@ -5,5 +5,5 @@ import {type ConsensusNodeStates} from '../../enumerations/consensus-node-states export interface ConsensusNodeComponentStruct extends BaseComponentStruct { nodeId: number; - nodeState: ConsensusNodeStates; + state: ConsensusNodeStates; } diff --git a/src/core/config/remote/components/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts index ed9360b7d..ef4ba1a08 100644 --- a/src/core/config/remote/components/mirror-node-component.ts +++ b/src/core/config/remote/components/mirror-node-component.ts @@ -2,18 +2,12 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class MirrorNodeComponent extends BaseComponent { - public constructor( - name: ComponentName, - cluster: ClusterReference, - namespace: NamespaceNameAsString, - state: ComponentStates, - ) { - super(ComponentTypes.MirrorNode, name, cluster, namespace, state); + public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) { + super(ComponentTypes.MirrorNode, name, cluster, namespace); this.validate(); } @@ -21,7 +15,7 @@ export class MirrorNodeComponent extends BaseComponent { /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStruct): MirrorNodeComponent { - const {name, cluster, namespace, state} = component; - return new MirrorNodeComponent(name, cluster, namespace, state); + const {name, cluster, namespace} = component; + return new MirrorNodeComponent(name, cluster, namespace); } } diff --git a/src/core/config/remote/components/mirror-node-explorer-component.ts b/src/core/config/remote/components/mirror-node-explorer-component.ts index 412562d2d..c633fa0e8 100644 --- a/src/core/config/remote/components/mirror-node-explorer-component.ts +++ b/src/core/config/remote/components/mirror-node-explorer-component.ts @@ -2,18 +2,12 @@ import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class MirrorNodeExplorerComponent extends BaseComponent { - public constructor( - name: ComponentName, - cluster: ClusterReference, - namespace: NamespaceNameAsString, - state: ComponentStates, - ) { - super(ComponentTypes.MirrorNodeExplorer, name, cluster, namespace, state); + public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) { + super(ComponentTypes.MirrorNodeExplorer, name, cluster, namespace); this.validate(); } @@ -21,7 +15,7 @@ export class MirrorNodeExplorerComponent extends BaseComponent { /** Handles creating instance of the class from plain object. */ public static fromObject(component: BaseComponentStruct): MirrorNodeExplorerComponent { - const {name, cluster, namespace, state} = component; - return new MirrorNodeExplorerComponent(name, cluster, namespace, state); + const {name, cluster, namespace} = component; + return new MirrorNodeExplorerComponent(name, cluster, namespace); } } diff --git a/src/core/config/remote/components/relay-component.ts b/src/core/config/remote/components/relay-component.ts index e0df62a79..3eeb7a749 100644 --- a/src/core/config/remote/components/relay-component.ts +++ b/src/core/config/remote/components/relay-component.ts @@ -3,7 +3,6 @@ import {SoloError} from '../../../errors/solo-error.js'; import {BaseComponent} from './base-component.js'; import {ComponentTypes} from '../enumerations/component-types.js'; -import {type ComponentStates} from '../enumerations/component-states.js'; import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type NodeAliases} from '../../../../types/aliases.js'; import {type ToObject} from '../../../../types/index.js'; @@ -14,17 +13,15 @@ export class RelayComponent extends BaseComponent implements RelayComponentStruc * @param name - to distinguish components. * @param clusterReference - in which the component is deployed. * @param namespace - associated with the component. - * @param state - the state of the component * @param consensusNodeAliases - list node aliases */ public constructor( name: ComponentName, clusterReference: ClusterReference, namespace: NamespaceNameAsString, - state: ComponentStates, public readonly consensusNodeAliases: NodeAliases = [], ) { - super(ComponentTypes.Relay, name, clusterReference, namespace, state); + super(ComponentTypes.Relay, name, clusterReference, namespace); this.validate(); } @@ -32,8 +29,8 @@ export class RelayComponent extends BaseComponent implements RelayComponentStruc /** Handles creating instance of the class from plain object. */ public static fromObject(component: RelayComponentStruct): RelayComponent { - const {name, cluster, namespace, state, consensusNodeAliases} = component; - return new RelayComponent(name, cluster, namespace, state, consensusNodeAliases); + const {name, cluster, namespace, consensusNodeAliases} = component; + return new RelayComponent(name, cluster, namespace, consensusNodeAliases); } public override validate(): void { diff --git a/src/core/config/remote/enumerations/component-states.ts b/src/core/config/remote/enumerations/component-states.ts deleted file mode 100644 index af7b895fb..000000000 --- a/src/core/config/remote/enumerations/component-states.ts +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -/** - * Represents the possible states of a component in the remote config. - */ -export enum ComponentStates { - ACTIVE = 'active', - DELETED = 'deleted', -} diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts index 18f6b0047..f1fb20749 100644 --- a/src/core/config/remote/remote-config-manager.ts +++ b/src/core/config/remote/remote-config-manager.ts @@ -12,7 +12,6 @@ import {type K8Factory} from '../../../integration/kube/k8-factory.js'; import { type ClusterReference, type ClusterReferences, - type ComponentName, type Context, type DeploymentName, type NamespaceNameAsString, @@ -36,16 +35,13 @@ import {promptTheUserForDeployment, resolveNamespaceFromDeployment} from '../../ import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js'; import {getSoloVersion} from '../../../../version.js'; import {DeploymentStates} from './enumerations/deployment-states.js'; -import {type RemoteConfigManagerApi} from './api/remote-config-manager-api.js'; -import {ComponentFactory} from './components/component-factory.js'; -import {ConsensusNodeComponent} from './components/consensus-node-component.js'; /** * Uses Kubernetes ConfigMaps to manage the remote configuration data by creating, loading, modifying, * and saving the configuration data to and from a Kubernetes cluster. */ @injectable() -export class RemoteConfigManager implements RemoteConfigManagerApi { +export class RemoteConfigManager { /** Stores the loaded remote configuration data. */ private remoteConfig: Optional; @@ -73,16 +69,27 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { return this.k8Factory.default().clusters().readCurrent(); } + /** @returns the components data wrapper cloned */ public get components(): ComponentsDataWrapper { return this.remoteConfig.components.clone(); } + /** + * @returns the remote configuration data's clusters cloned + */ public get clusters(): Record { return structuredClone(this.remoteConfig.clusters); } /* ---------- Readers and Modifiers ---------- */ + /** + * Modifies the loaded remote configuration data using a provided callback function. + * The callback operates on the configuration data, which is then saved to the cluster. + * + * @param callback - an async function that modifies the remote configuration data. + * @throws if the configuration is not loaded before modification, will throw a SoloError {@link SoloError} + */ public async modify(callback: (remoteConfig: RemoteConfigDataWrapper) => Promise): Promise { if (!this.remoteConfig) { throw new SoloError('Attempting to modify remote config without loading it first'); @@ -95,6 +102,11 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { await this.save(); } + /** + * Creates a new remote configuration in the Kubernetes cluster. + * Gathers data from the local configuration and constructs a new ConfigMap + * entry in the cluster with initial command history and metadata. + */ public async create( argv: ArgvStruct, state: DeploymentStates, @@ -121,15 +133,12 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { const soloVersion = getSoloVersion(); const currentCommand = argv._.join(' '); - const consensusNodeComponents: Record = - ComponentFactory.createConsensusNodeComponentsFromNodeAliases(nodeAliases, clusterReference, namespace); - this.remoteConfig = new RemoteConfigDataWrapper({ clusters, metadata: new RemoteConfigMetadata(namespace.name, deployment, state, lastUpdatedAt, email, soloVersion), commandHistory: [currentCommand], lastExecutedCommand: currentCommand, - components: ComponentsDataWrapper.initializeWithNodes(consensusNodeComponents), + components: ComponentsDataWrapper.initializeWithNodes(nodeAliases, clusterReference, namespace.name), flags: await CommonFlagsDataWrapper.initialize(this.configManager, argv), }); @@ -167,6 +176,10 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { } } + /** + * Loads the remote configuration, performs a validation and returns it + * @returns RemoteConfigDataWrapper + */ public async get(context?: Context): Promise { const namespace = this.configManager.getFlag(flags.namespace) ?? (await this.getNamespace()); @@ -188,6 +201,7 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { return this.remoteConfig; } + /** Unload the remote config from the remote config manager. */ public unload(): void { this.remoteConfig = undefined; } @@ -211,6 +225,14 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { /* ---------- Listr Task Builders ---------- */ + /** + * Performs the loading of the remote configuration. + * Checks if the configuration is already loaded, otherwise loads and adds the command to history. + * + * @param argv - arguments containing command input for historical reference. + * @param validate - whether to validate the remote configuration. + * @param [skipConsensusNodesValidation] - whether or not to validate the consensusNodes + */ public async loadAndValidate( argv: {_: string[]} & AnyObject, validate: boolean = true, @@ -302,6 +324,7 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { /* ---------- Utilities ---------- */ + /** Empties the component data inside the remote config */ public async deleteComponents(): Promise { await this.modify(async remoteConfig => { remoteConfig.components = ComponentsDataWrapper.initializeEmpty(); @@ -312,6 +335,14 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { return !!this.remoteConfig; } + /** + * Retrieves the ConfigMap containing the remote configuration from the Kubernetes cluster. + * + * @param namespace - The namespace to search for the ConfigMap. + * @param context - The context to use for the Kubernetes client. + * @returns the remote configuration data. + * @throws if the ConfigMap could not be read and the error is not a 404 status, will throw a SoloError {@link SoloError} + */ public async getConfigMap(namespace?: NamespaceName, context?: Context): Promise { if (!namespace) { namespace = await this.getNamespace(); @@ -339,6 +370,9 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { } } + /** + * Creates a new ConfigMap entry in the Kubernetes cluster with the remote configuration data. + */ public async createConfigMap(context?: Context): Promise { const namespace = await this.getNamespace(); const name = constants.SOLO_REMOTE_CONFIGMAP_NAME; @@ -436,6 +470,10 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { //* Common Commands + /** + * Get the consensus nodes from the remoteConfigManager and use the localConfig to get the context + * @returns an array of ConsensusNode objects + */ public getConsensusNodes(): ConsensusNode[] { if (!this.isLoaded()) { throw new SoloError('Remote configuration is not loaded, and was expected to be loaded'); @@ -472,10 +510,18 @@ export class RemoteConfigManager implements RemoteConfigManagerApi { return consensusNodes; } + /** + * Gets a list of distinct contexts from the consensus nodes. + * @returns an array of context strings. + */ public getContexts(): Context[] { return [...new Set(this.getConsensusNodes().map((node): Context => node.context))]; } + /** + * Gets a list of distinct cluster references from the consensus nodes. + * @returns an object of cluster references. + */ public getClusterRefs(): ClusterReferences { const nodes = this.getConsensusNodes(); const accumulator: ClusterReferences = {}; diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 9a2dda06c..8e665040d 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -2,150 +2,196 @@ import * as constants from '../../constants.js'; import {SoloError} from '../../errors/solo-error.js'; -import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; -import {ComponentStates} from './enumerations/component-states.js'; -import {type ConsensusNodeComponent} from './components/consensus-node-component.js'; +import {type K8Factory} from '../../../integration/kube/k8-factory.js'; import {type ComponentsDataWrapper} from './components-data-wrapper.js'; import {type BaseComponent} from './components/base-component.js'; import {type NamespaceName} from '../../../integration/kube/resources/namespace/namespace-name.js'; import {type LocalConfig} from '../local/local-config.js'; import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; -import {type K8Factory} from '../../../integration/kube/k8-factory.js'; +import {type ConsensusNodeStates} from './enumerations/consensus-node-states.js'; /** * Static class is used to validate that components in the remote config * are present in the kubernetes cluster, and throw errors if there is mismatch. */ export class RemoteConfigValidator { - private static getRelayLabels(): string[] { - return [constants.SOLO_RELAY_LABEL]; - } - - private static getHaProxyLabels(component: BaseComponent): string[] { - const name: string = component.name.split('-').slice(0, -1).join('-'); - return [`app=${name}`]; + /** + * Gathers and handles validation of all components. + * + * @param namespace - namespace to validate the components in. + * @param components - components to validate. + * @param k8Factory - to validate the elements. + * @param localConfig - to get the context from cluster + * @param skipConsensusNodes - whether to validate consensus nodes + */ + public static async validateComponents( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + skipConsensusNodes: boolean, + ): Promise { + await Promise.all([ + ...RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig), + ...RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig), + ...RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig), + ...RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig), + ...RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), + ...RemoteConfigValidator.validateBlockNodes(namespace, components, k8Factory, localConfig), + ...(skipConsensusNodes + ? [] + : RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig)), + ]); } - private static getMirrorNodeLabels(): string[] { - return constants.SOLO_HEDERA_MIRROR_IMPORTER; - } + private static validateRelays( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + ): Promise[] { + return Object.values(components.relays).map(async component => { + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = [constants.SOLO_RELAY_LABEL]; + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - private static getEnvoyProxyLabels(component: BaseComponent): string[] { - const name: string = component.name.split('-').slice(0, -1).join('-'); - return [`app=${name}`]; + if (pods.length === 0) { + throw new Error('Pod not found'); + } // to return the generic error message + } catch (error) { + RemoteConfigValidator.throwValidationError('Relay', component, error); + } + }); } - private static getMirrorNodeExplorerLabels(): string[] { - return [constants.SOLO_HEDERA_EXPLORER_LABEL]; - } + private static validateHaProxies( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + ): Promise[] { + return Object.values(components.haProxies).map(async component => { + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = [`app=${component.name}`]; + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - private static getBlockNodeLabels(component: BaseComponent): string[] { - return [`app.kubernetes.io/instance=${component.name}`]; + if (pods.length === 0) { + throw new Error('Pod not found'); + } // to return the generic error message + } catch (error) { + RemoteConfigValidator.throwValidationError('HaProxy', component, error); + } + }); } - private static getConsensusNodeLabels(component: BaseComponent): string[] { - return [`app=network-${component.name}`]; - } + private static validateMirrorNodes( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + ): Promise[] { + return Object.values(components.mirrorNodes).map(async component => { + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = constants.SOLO_HEDERA_MIRROR_IMPORTER; + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); - private static consensusNodeSkipConditionCallback(nodeComponent: ConsensusNodeComponent): boolean { - return ( - nodeComponent.nodeState === ConsensusNodeStates.REQUESTED || - nodeComponent.nodeState === ConsensusNodeStates.NON_DEPLOYED - ); + if (pods.length === 0) { + throw new Error('Pod not found'); + } // to return the generic error message + } catch (error) { + RemoteConfigValidator.throwValidationError('Mirror node', component, error); + } + }); } - private static componentValidationsMapping: Record< - string, - { - getLabelsCallback: (component: BaseComponent) => string[]; - displayName: string; - skipCondition?: (component: BaseComponent) => boolean; - } - > = { - relays: { - displayName: 'Relay', - getLabelsCallback: RemoteConfigValidator.getRelayLabels, - }, - haProxies: { - displayName: 'HaProxy', - getLabelsCallback: RemoteConfigValidator.getHaProxyLabels, - }, - mirrorNodes: { - displayName: 'Mirror node', - getLabelsCallback: RemoteConfigValidator.getMirrorNodeLabels, - }, - envoyProxies: { - displayName: 'Envoy proxy', - getLabelsCallback: RemoteConfigValidator.getEnvoyProxyLabels, - }, - mirrorNodeExplorers: { - displayName: 'Mirror node explorer', - getLabelsCallback: RemoteConfigValidator.getMirrorNodeExplorerLabels, - }, - blockNodes: { - displayName: 'Block node', - getLabelsCallback: RemoteConfigValidator.getBlockNodeLabels, - }, - consensusNodes: { - displayName: 'Consensus node', - getLabelsCallback: RemoteConfigValidator.getConsensusNodeLabels, - skipCondition: RemoteConfigValidator.consensusNodeSkipConditionCallback, - }, - }; - - public static async validateComponents( + private static validateEnvoyProxies( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, - skipConsensusNodes: boolean, - ): Promise { - const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidationsMapping) - .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes) - .flatMap(([key, {getLabelsCallback, displayName, skipCondition}]): Promise[] => - RemoteConfigValidator.validateComponentGroup( - namespace, - components[key], - k8Factory, - localConfig, - getLabelsCallback, - displayName, - skipCondition, - ), - ); - - await Promise.all(validationPromises); + ): Promise[] { + return Object.values(components.envoyProxies).map(async component => { + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = [`app=${component.name}`]; + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); + + if (pods.length === 0) { + throw new Error('Pod not found'); + } // to return the generic error message + } catch (error) { + RemoteConfigValidator.throwValidationError('Envoy proxy', component, error); + } + }); } - private static validateComponentGroup( + private static validateConsensusNodes( namespace: NamespaceName, - components: Record, + components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, - getLabelsCallback: (component: BaseComponent) => string[], - displayName: string, - skipCondition?: (component: BaseComponent) => boolean, ): Promise[] { - return Object.values(components).map(async (component): Promise => { - if (component.state === ComponentStates.DELETED) { + return Object.values(components.consensusNodes).map(async component => { + if (component.state === ConsensusNodeStates.REQUESTED || component.state === ConsensusNodeStates.NON_DEPLOYED) { return; } - if (skipCondition?.(component)) { - return; + + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = [`app=network-${component.name}`]; + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); + + if (pods.length === 0) { + throw new Error('Pod not found'); + } // to return the generic error message + } catch (error) { + RemoteConfigValidator.throwValidationError('Consensus node', component, error); } + }); + } + private static validateMirrorNodeExplorers( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + ): Promise[] { + return Object.values(components.mirrorNodeExplorers).map(async component => { const context: Context = localConfig.clusterRefs[component.cluster]; - const labels: string[] = getLabelsCallback(component); + const labels: string[] = [constants.SOLO_HEDERA_EXPLORER_LABEL]; + try { + const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); + if (pods.length === 0) { + throw new Error('Pod not found'); + } // to return the generic error message + } catch (error) { + RemoteConfigValidator.throwValidationError('Mirror node explorer', component, error); + } + }); + } + + private static validateBlockNodes( + namespace: NamespaceName, + components: ComponentsDataWrapper, + k8Factory: K8Factory, + localConfig: LocalConfig, + ): Promise[] { + return Object.values(components.blockNodes).map(async component => { + const context: Context = localConfig.clusterRefs[component.cluster]; + const labels: string[] = [constants.SOLO_HEDERA_EXPLORER_LABEL]; // TODO: ADD BLOCK SELECT try { const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (pods.length === 0) { - throw new Error('Pod not found'); // to return the generic error message - } + throw new Error('Pod not found'); + } // to return the generic error message } catch (error) { - throw RemoteConfigValidator.buildValidationError(displayName, component, error); + RemoteConfigValidator.throwValidationError('Block node', component, error); } }); } @@ -153,27 +199,16 @@ export class RemoteConfigValidator { /** * Generic handler that throws errors. * - * @param displayName - name to display in error message + * @param type - name to display in error message * @param component - component which is not found in the cluster * @param error - original error for the kube client */ - private static buildValidationError( - displayName: string, - component: BaseComponent, - error: Error | unknown, - ): SoloError { - return new SoloError( - RemoteConfigValidator.buildValidationErrorMessage(displayName, component), + private static throwValidationError(type: string, component: BaseComponent, error: Error | unknown): never { + throw new SoloError( + `${type} in remote config with name ${component.name} ` + + `was not found in namespace: ${component.namespace}, cluster: ${component.cluster}`, error, - component.toObject(), - ); - } - - public static buildValidationErrorMessage(displayName: string, component: BaseComponent): string { - return ( - `${displayName} in remote config with name ${component.name} was not found in ` + - `namespace: ${component.namespace}, ` + - `cluster: ${component.cluster}` + {component: component.toObject()}, ); } } diff --git a/src/core/templates.ts b/src/core/templates.ts index ea8003582..c9557cf40 100644 --- a/src/core/templates.ts +++ b/src/core/templates.ts @@ -242,19 +242,23 @@ export class Templates { } } + public static renderEnvoyProxyName(nodeAlias: NodeAlias): string { + return `envoy-proxy-${nodeAlias}`; + } + + public static renderHaProxyName(nodeAlias: NodeAlias): string { + return `haproxy-${nodeAlias}`; + } + + public static renderFullyQualifiedHaProxyName(nodeAlias: NodeAlias, namespace: NamespaceName): string { + return `${Templates.renderHaProxyName(nodeAlias)}-svc.${namespace}.svc.cluster.local`; + } + public static parseNodeAliasToIpMapping(unparsed: string): Record { const mapping: Record = {}; for (const data of unparsed.split(',')) { const [nodeAlias, ip] = data.split('=') as [NodeAlias, IP]; - - if (!nodeAlias || typeof nodeAlias !== 'string') { - throw new SoloError(`Can't parse node alias: ${data}`); - } - if (!ip || typeof ip !== 'string') { - throw new SoloError(`Can't parse ip: ${data}`); - } - mapping[nodeAlias] = ip; } diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index 486d11da4..40c575a86 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -1,18 +1,24 @@ // SPDX-License-Identifier: Apache-2.0 -import {beforeEach, describe, it} from 'mocha'; +import {it, describe} from 'mocha'; import {expect} from 'chai'; + +import * as constants from '../../../../src/core/constants.js'; +import {type ConfigManager} from '../../../../src/core/config-manager.js'; +import {Templates} from '../../../../src/core/templates.js'; +import {Flags as flags} from '../../../../src/commands/flags.js'; import {RemoteConfigValidator} from '../../../../src/core/config/remote/remote-config-validator.js'; +import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations.js'; import {ComponentsDataWrapper} from '../../../../src/core/config/remote/components-data-wrapper.js'; import {SoloError} from '../../../../src/core/errors/solo-error.js'; -import {type RelayComponent} from '../../../../src/core/config/remote/components/relay-component.js'; -import {type HaProxyComponent} from '../../../../src/core/config/remote/components/ha-proxy-component.js'; -import {type MirrorNodeComponent} from '../../../../src/core/config/remote/components/mirror-node-component.js'; -import {type ConsensusNodeComponent} from '../../../../src/core/config/remote/components/consensus-node-component.js'; -import {type MirrorNodeExplorerComponent} from '../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; -import {type EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy-proxy-component.js'; - -import {type NodeAlias, type NodeAliases} from '../../../../src/types/aliases.js'; +import {RelayComponent} from '../../../../src/core/config/remote/components/relay-component.js'; +import {HaProxyComponent} from '../../../../src/core/config/remote/components/ha-proxy-component.js'; +import {MirrorNodeComponent} from '../../../../src/core/config/remote/components/mirror-node-component.js'; +import {ConsensusNodeComponent} from '../../../../src/core/config/remote/components/consensus-node-component.js'; +import {MirrorNodeExplorerComponent} from '../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; +import {EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy-proxy-component.js'; + +import {type ArgvStruct, type NodeAlias, type NodeAliases} from '../../../../src/types/aliases.js'; import {container} from 'tsyringe-neo'; import {NamespaceName} from '../../../../src/integration/kube/resources/namespace/namespace-name.js'; import {PodReference} from '../../../../src/integration/kube/resources/pod/pod-reference.js'; @@ -24,101 +30,18 @@ import {LocalConfig} from '../../../../src/core/config/local/local-config.js'; import {getTestCacheDirectory} from '../../../test-utility.js'; import {Duration} from '../../../../src/core/time/duration.js'; import {LocalConfigDataWrapper} from '../../../../src/core/config/local/local-config-data-wrapper.js'; -import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations/consensus-node-states.js'; -import {type ClusterReference, type ComponentName} from '../../../../src/core/config/remote/types.js'; -import {ComponentFactory} from '../../../../src/core/config/remote/components/component-factory.js'; -import {type BlockNodeComponent} from '../../../../src/core/config/remote/components/block-node-component.js'; -import {type BaseComponent} from '../../../../src/core/config/remote/components/base-component.js'; -import {ComponentTypes} from '../../../../src/core/config/remote/enumerations/component-types.js'; - -interface ComponentsRecord { - explorer: MirrorNodeExplorerComponent; - blockNode: BlockNodeComponent; - mirrorNode: MirrorNodeComponent; - relay: RelayComponent; - consensusNode: ConsensusNodeComponent; - haProxy: HaProxyComponent; - envoyProxy: EnvoyProxyComponent; -} - -interface LabelRecord { - explorer: string[]; - blockNode: string[]; - mirrorNode: string[]; - relay: string[]; - consensusNode: string[]; - haProxy: string[]; - envoyProxy: string[]; -} - -interface ComponentsData { - namespace: NamespaceName; - components: ComponentsRecord; - labelRecord: LabelRecord; - componentsDataWrapper: ComponentsDataWrapper; -} - -function prepareComponentsData(namespace: NamespaceName): ComponentsData { - const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; - - const clusterReference: ClusterReference = 'cluster'; - const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; - const nodeAlias: NodeAlias = 'node1'; - - const components: ComponentsRecord = { - explorer: ComponentFactory.createNewExplorerComponent(remoteConfigManagerMock, clusterReference, namespace), - blockNode: ComponentFactory.createNewBlockNodeComponent(remoteConfigManagerMock, clusterReference, namespace), - mirrorNode: ComponentFactory.createNewMirrorNodeComponent(remoteConfigManagerMock, clusterReference, namespace), - relay: ComponentFactory.createNewRelayComponent(remoteConfigManagerMock, clusterReference, namespace, [nodeAlias]), - consensusNode: ComponentFactory.createNewConsensusNodeComponent(nodeAlias, clusterReference, namespace, nodeState), - haProxy: ComponentFactory.createNewHaProxyComponent( - remoteConfigManagerMock, - clusterReference, - namespace, - nodeAlias, - ), - envoyProxy: ComponentFactory.createNewEnvoyProxyComponent( - remoteConfigManagerMock, - clusterReference, - namespace, - nodeAlias, - ), - }; - - const labelRecord: LabelRecord = { - // @ts-expect-error - to access private property - relay: RemoteConfigValidator.getRelayLabels(), - // @ts-expect-error - to access private property - haProxy: RemoteConfigValidator.getHaProxyLabels(components.haProxy), - // @ts-expect-error - to access private property - mirrorNode: RemoteConfigValidator.getMirrorNodeLabels(), - // @ts-expect-error - to access private property - envoyProxy: RemoteConfigValidator.getEnvoyProxyLabels(components.envoyProxy), - // @ts-expect-error - to access private property - explorer: RemoteConfigValidator.getMirrorNodeExplorerLabels(), - // @ts-expect-error - to access private property - blockNode: RemoteConfigValidator.getBlockNodeLabels(components.blockNode), - // @ts-expect-error - to access private property - consensusNode: RemoteConfigValidator.getConsensusNodeLabels(components.consensusNode), - }; - - const componentsDataWrapper: ComponentsDataWrapper = ComponentsDataWrapper.initializeEmpty(); - - return {namespace, components, labelRecord, componentsDataWrapper}; -} describe('RemoteConfigValidator', () => { - const namespace: NamespaceName = NamespaceName.of('remote-config-validator'); + const namespace = NamespaceName.of('remote-config-validator'); + let configManager: ConfigManager; let k8Factory: K8Factory; let localConfig: LocalConfig; - const filePath: string = `${getTestCacheDirectory('LocalConfig')}/localConfig.yaml`; - - let components: ComponentsRecord; - let labelRecord: LabelRecord; - let componentsDataWrapper: ComponentsDataWrapper; + const filePath = `${getTestCacheDirectory('LocalConfig')}/localConfig.yaml`; before(async () => { + configManager = container.resolve(InjectTokens.ConfigManager); + configManager.update({[flags.namespace.name]: namespace} as ArgvStruct); k8Factory = container.resolve(InjectTokens.K8Factory); localConfig = new LocalConfig(filePath); // @ts-expect-error - TS2341: to mock @@ -126,137 +49,179 @@ describe('RemoteConfigValidator', () => { await k8Factory.default().namespaces().create(namespace); }); - beforeEach(() => { - const testData: ComponentsData = prepareComponentsData(namespace); - components = testData.components; - labelRecord = testData.labelRecord; - componentsDataWrapper = testData.componentsDataWrapper; - }); - after(async function () { this.timeout(Duration.ofMinutes(5).toMillis()); await k8Factory.default().namespaces().delete(namespace); }); - async function createPod(name: string, labelsRaw: string[]): Promise { - const labels: Record = {}; - - for (const rawLabel of labelsRaw) { - const [key, value] = rawLabel.split('='); - labels[key] = value; + const cluster = 'cluster'; + const state = ConsensusNodeStates.STARTED; + + const nodeAlias = 'node1' as NodeAlias; + const haProxyName = Templates.renderHaProxyName(nodeAlias); + const envoyProxyName = Templates.renderEnvoyProxyName(nodeAlias); + const relayName = 'relay'; + const mirrorNodeName = 'mirror-node'; + const mirrorNodeExplorerName = 'mirror-node-explorer'; + + const consensusNodeAliases = [nodeAlias] as NodeAliases; + + // @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private + const components = new ComponentsDataWrapper( + {[relayName]: new RelayComponent(relayName, cluster, namespace.name, consensusNodeAliases)}, + {[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name)}, + {[mirrorNodeName]: new MirrorNodeComponent(mirrorNodeName, cluster, namespace.name)}, + {[envoyProxyName]: new EnvoyProxyComponent(envoyProxyName, cluster, namespace.name)}, + { + [nodeAlias]: new ConsensusNodeComponent( + nodeAlias, + cluster, + namespace.name, + state, + Templates.nodeIdFromNodeAlias(nodeAlias), + ), + }, + {[mirrorNodeExplorerName]: new MirrorNodeExplorerComponent(mirrorNodeExplorerName, cluster, namespace.name)}, + ); + + async function createPod(name: string, labels: Record) { + try { + await k8Factory + .default() + .pods() + .create( + PodReference.of(namespace, PodName.of(name)), + labels, + ContainerName.of(name), + 'alpine:latest', + ['/bin/sh', '-c', 'apk update && apk upgrade && apk add --update bash && sleep 7200'], + ['bash', '-c', 'exit 0'], + ); + } catch (error) { + console.error(error); + throw new Error('Error creating pod'); } - - await k8Factory - .default() - .pods() - .create( - PodReference.of(namespace, PodName.of(name)), - labels, - ContainerName.of(name), - 'alpine:latest', - ['/bin/sh', '-c', 'apk update && apk upgrade && apk add --update bash && sleep 7200'], - ['bash', '-c', 'exit 0'], - ); } - const testCasesForIndividualComponents: Array<{ - componentKey: keyof ComponentsRecord; - displayName: string; - }> = [ - {componentKey: 'relay', displayName: 'Relay'}, - {componentKey: 'haProxy', displayName: 'HaProxy'}, - {componentKey: 'mirrorNode', displayName: 'Mirror node'}, - {componentKey: 'envoyProxy', displayName: 'Envoy proxy'}, - {componentKey: 'consensusNode', displayName: 'Consensus node'}, - {componentKey: 'explorer', displayName: 'Mirror node explorer'}, - {componentKey: 'blockNode', displayName: 'Block node'}, - ]; - - for (const {componentKey, displayName} of testCasesForIndividualComponents) { - describe(`${displayName} validation`, () => { - it('should fail if component is not present', async () => { - const component: BaseComponent = components[componentKey]; + describe('Relays validation', () => { + it('should fail if component is not present', async () => { + try { + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig)); + throw new Error(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); + } + }); - componentsDataWrapper.addNewComponent(component); + it('should succeed if component is present', async () => { + const [key, value] = constants.SOLO_RELAY_LABEL.split('='); + await createPod(relayName, {[key]: value}); - try { - await RemoteConfigValidator.validateComponents( - namespace, - componentsDataWrapper, - k8Factory, - localConfig, - false, - ); - expect.fail(); - } catch (error) { - expect(error).to.be.instanceOf(SoloError); - expect(error.message).to.equal(RemoteConfigValidator.buildValidationErrorMessage(displayName, component)); - } - }); + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig)); + }); + }); - it('should succeed if component is present', async () => { - const component: BaseComponent = components[componentKey]; + describe('HaProxies validation', () => { + it('should fail if component is not present', async () => { + try { + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig)); + throw new Error(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); + } + }); - await createPod(component.name, labelRecord[componentKey]); + it('should succeed if component is present', async () => { + await createPod(haProxyName, {app: haProxyName}); - await RemoteConfigValidator.validateComponents(namespace, componentsDataWrapper, k8Factory, localConfig, false); - }); + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig)); }); - } + }); - describe('Additional test cases', () => { - it('Should not validate disabled components', async () => { - const component: BlockNodeComponent = components.blockNode; + describe('Mirror Node Components validation', () => { + it('should fail if component is not present', async () => { + try { + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig)); + throw new Error(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); + } + }); - componentsDataWrapper.addNewComponent(component); - componentsDataWrapper.disableComponent(component.name, ComponentTypes.BlockNode); + it('should succeed if component is present', async () => { + const [key1, value1] = constants.SOLO_HEDERA_MIRROR_IMPORTER[0].split('='); + const [key2, value2] = constants.SOLO_HEDERA_MIRROR_IMPORTER[1].split('='); + await createPod(mirrorNodeName, {[key1]: value1, [key2]: value2}); - await RemoteConfigValidator.validateComponents(namespace, componentsDataWrapper, k8Factory, localConfig, false); + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig)); }); + }); - it('Should not validate consensus nodes if skipConsensusNodes is enabled', async () => { - const skipConsensusNodes: boolean = true; - - const nodeAliases: NodeAliases = ['node1', 'node2', 'node3']; + describe('Envoy Proxies validation', () => { + it('should fail if component is not present', async () => { + try { + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig)); + throw new Error(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); + } + }); - const consensusNodeComponents: Record = - ComponentFactory.createConsensusNodeComponentsFromNodeAliases(nodeAliases, 'cluster-ref', namespace); + it('should succeed if component is present', async () => { + await createPod(envoyProxyName, {app: envoyProxyName}); - const componentsDataWrapper: ComponentsDataWrapper = - ComponentsDataWrapper.initializeWithNodes(consensusNodeComponents); + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig)); + }); + }); - for (const nodeAlias of nodeAliases) { - // Make sure the status is STARTED - componentsDataWrapper.changeNodeState(nodeAlias, ConsensusNodeStates.STARTED); + describe('Consensus Nodes validation', () => { + it('should fail if component is not present', async () => { + try { + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig)); + throw new Error(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); } - - await RemoteConfigValidator.validateComponents( - namespace, - componentsDataWrapper, - k8Factory, - localConfig, - skipConsensusNodes, - ); }); - const nodeStates: ConsensusNodeStates[] = [ConsensusNodeStates.REQUESTED, ConsensusNodeStates.NON_DEPLOYED]; + it('should succeed if component is present', async () => { + await createPod(nodeAlias, {app: `network-${nodeAlias}`}); - for (const nodeState of nodeStates) { - it(`Should not validate consensus nodes if status is ${nodeState} `, async () => { - const nodeAliases: NodeAliases = ['node1', 'node2', 'node3']; - - const consensusNodeComponents: Record = - ComponentFactory.createConsensusNodeComponentsFromNodeAliases(nodeAliases, 'cluster-ref', namespace); + // @ts-expect-error - TS2341: Property is private + await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig)); + }); + }); - const componentsDataWrapper: ComponentsDataWrapper = - ComponentsDataWrapper.initializeWithNodes(consensusNodeComponents); + describe('Mirror Node Explorers validation', () => { + it('should fail if component is not present', async () => { + try { + await Promise.all( + // @ts-expect-error - TS2341: Property is private + RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), + ); + throw new Error(); + } catch (error) { + expect(error).to.be.instanceOf(SoloError); + } + }); - for (const nodeAlias of nodeAliases) { - componentsDataWrapper.changeNodeState(nodeAlias, nodeState); - } + it('should succeed if component is present', async () => { + const [key, value] = constants.SOLO_HEDERA_EXPLORER_LABEL.split('='); + await createPod(mirrorNodeExplorerName, {[key]: value}); - await RemoteConfigValidator.validateComponents(namespace, componentsDataWrapper, k8Factory, localConfig, false); - }); - } + await Promise.all( + // @ts-expect-error - TS2341: Property is private + RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), + ); + }); }); }); diff --git a/test/unit/core/config/remote/cluster.test.ts b/test/unit/core/config/remote/cluster.test.ts index 4a937cda7..ed69e85c6 100644 --- a/test/unit/core/config/remote/cluster.test.ts +++ b/test/unit/core/config/remote/cluster.test.ts @@ -2,94 +2,69 @@ import {it} from 'mocha'; import {expect} from 'chai'; +import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {Cluster} from '../../../../../src/core/config/remote/cluster.js'; import {type ClusterReference} from '../../../../../src/core/config/remote/types.js'; -import {type ClusterStruct} from '../../../../../src/core/config/remote/interfaces/cluster-struct.js'; describe('Cluster', () => { - it('should convert to an object', () => { - const clusterData: ClusterStruct = { - name: 'name', - namespace: 'namespace', - deployment: 'deployment', - dnsBaseDomain: 'cluster.world', - dnsConsensusNodePattern: 'network.svc', - }; + it('should fail if name is not provided', () => { + expect(() => new Cluster(null, 'valid', 'valid')).to.throw(SoloError, 'name is required'); + expect(() => new Cluster('', 'valid', 'valid')).to.throw(SoloError, 'name is required'); + }); - const cluster: Cluster = new Cluster( - clusterData.name, - clusterData.namespace, - clusterData.deployment, - clusterData.dnsBaseDomain, - clusterData.dnsConsensusNodePattern, - ); + it('should fail if name is not a string', () => { + const name = 1; // @ts-ignore + expect(() => new Cluster(name, 'valid', 'valid')).to.throw(SoloError, 'Invalid type for name: number'); + }); - const clusterObject: ClusterStruct = cluster.toObject(); - expect(clusterObject.name).to.equal(clusterData.name); - expect(clusterObject.namespace).to.equal(clusterData.namespace); - expect(clusterObject.deployment).to.equal(clusterData.deployment); - expect(clusterObject.dnsBaseDomain).to.equal(clusterData.dnsBaseDomain); - expect(clusterObject.dnsConsensusNodePattern).to.equal(clusterData.dnsConsensusNodePattern); + it('should fail if namespace is not provided', () => { + expect(() => new Cluster('valid', null, 'valid')).to.throw(SoloError, 'namespace is required'); + expect(() => new Cluster('valid', '', 'valid')).to.throw(SoloError, 'namespace is required'); }); - it('should convert clusters map to an object', () => { - const clusterData1: ClusterStruct = { - name: 'name1', - namespace: 'namespace1', - deployment: 'deployment1', - dnsBaseDomain: 'cluster1.world', - dnsConsensusNodePattern: 'network1.svc', - }; + it('should fail if namespace is not a string', () => { + const namespace = 1; // @ts-ignore + expect(() => new Cluster('valid', namespace, 'valid')).to.throw(SoloError, 'Invalid type for namespace: number'); + }); - const clusterData2: ClusterStruct = { - name: 'name2', - namespace: 'namespace2', - deployment: 'deployment2', - dnsBaseDomain: 'cluster2.world', - dnsConsensusNodePattern: 'network2.svc', - }; + it('should convert to an object', () => { + const c = new Cluster('name', 'namespace', 'deployment', 'cluster.world', 'network.svc'); + const o = c.toObject(); + expect(o.name).to.equal('name'); + expect(o.namespace).to.equal('namespace'); + expect(o.deployment).to.equal('deployment'); + expect(o.dnsBaseDomain).to.equal('cluster.world'); + expect(o.dnsConsensusNodePattern).to.equal('network.svc'); + }); - const clusterMap1: Record = { - cluster1: new Cluster( - clusterData1.name, - clusterData1.namespace, - clusterData1.deployment, - clusterData1.dnsBaseDomain, - clusterData1.dnsConsensusNodePattern, - ), - cluster2: new Cluster( - clusterData2.name, - clusterData2.namespace, - clusterData2.deployment, - clusterData2.dnsBaseDomain, - clusterData2.dnsConsensusNodePattern, - ), + it('should convert clusters map to an object', () => { + const map1: Record = { + cluster1: new Cluster('name1', 'namespace1', 'deployment1', 'cluster1.world', 'network1.svc'), + cluster2: new Cluster('name2', 'namespace2', 'deployment2', 'cluster2.world', 'network2.svc'), }; - const clustersMapObject: any = Cluster.toClustersMapObject(clusterMap1); - expect(clustersMapObject.cluster1.name).to.equal(clusterData1.name); - expect(clustersMapObject.cluster1.namespace).to.equal(clusterData1.namespace); - expect(clustersMapObject.cluster1.deployment).to.equal(clusterData1.deployment); - expect(clustersMapObject.cluster1.dnsBaseDomain).to.equal(clusterData1.dnsBaseDomain); - expect(clustersMapObject.cluster1.dnsConsensusNodePattern).to.equal(clusterData1.dnsConsensusNodePattern); - - expect(clustersMapObject.cluster2.name).to.equal(clusterData2.name); - expect(clustersMapObject.cluster2.namespace).to.equal(clusterData2.namespace); - expect(clustersMapObject.cluster2.deployment).to.equal(clusterData2.deployment); - expect(clustersMapObject.cluster2.dnsBaseDomain).to.equal(clusterData2.dnsBaseDomain); - expect(clustersMapObject.cluster2.dnsConsensusNodePattern).to.equal(clusterData2.dnsConsensusNodePattern); - - const clustersMap2: Record = Cluster.fromClustersMapObject(clustersMapObject); - expect(clustersMap2.cluster1.name).to.equal(clusterMap1.cluster1.name); - expect(clustersMap2.cluster1.namespace).to.equal(clusterMap1.cluster1.namespace); - expect(clustersMap2.cluster1.deployment).to.equal(clusterMap1.cluster1.deployment); - expect(clustersMap2.cluster1.dnsBaseDomain).to.equal(clusterMap1.cluster1.dnsBaseDomain); - expect(clustersMap2.cluster1.dnsConsensusNodePattern).to.equal(clusterMap1.cluster1.dnsConsensusNodePattern); + const o = Cluster.toClustersMapObject(map1); + expect(o.cluster1.name).to.equal('name1'); + expect(o.cluster1.namespace).to.equal('namespace1'); + expect(o.cluster1.deployment).to.equal('deployment1'); + expect(o.cluster1.dnsBaseDomain).to.equal('cluster1.world'); + expect(o.cluster1.dnsConsensusNodePattern).to.equal('network1.svc'); + expect(o.cluster2.name).to.equal('name2'); + expect(o.cluster2.namespace).to.equal('namespace2'); + expect(o.cluster2.deployment).to.equal('deployment2'); + expect(o.cluster2.dnsBaseDomain).to.equal('cluster2.world'); + expect(o.cluster2.dnsConsensusNodePattern).to.equal('network2.svc'); - expect(clustersMap2.cluster2.name).to.equal(clusterMap1.cluster2.name); - expect(clustersMap2.cluster2.namespace).to.equal(clusterMap1.cluster2.namespace); - expect(clustersMap2.cluster2.deployment).to.equal(clusterMap1.cluster2.deployment); - expect(clustersMap2.cluster2.dnsBaseDomain).to.equal(clusterMap1.cluster2.dnsBaseDomain); - expect(clustersMap2.cluster2.dnsConsensusNodePattern).to.equal(clusterMap1.cluster2.dnsConsensusNodePattern); + const map2 = Cluster.fromClustersMapObject(o); + expect(map2.cluster1.name).to.equal(map1.cluster1.name); + expect(map2.cluster1.namespace).to.equal(map1.cluster1.namespace); + expect(map2.cluster1.deployment).to.equal(map1.cluster1.deployment); + expect(map2.cluster1.dnsBaseDomain).to.equal(map1.cluster1.dnsBaseDomain); + expect(map2.cluster1.dnsConsensusNodePattern).to.equal(map1.cluster1.dnsConsensusNodePattern); + expect(map2.cluster2.name).to.equal(map1.cluster2.name); + expect(map2.cluster2.namespace).to.equal(map1.cluster2.namespace); + expect(map2.cluster2.deployment).to.equal(map1.cluster2.deployment); + expect(map2.cluster2.dnsBaseDomain).to.equal(map1.cluster2.dnsBaseDomain); + expect(map2.cluster2.dnsConsensusNodePattern).to.equal(map1.cluster2.dnsConsensusNodePattern); }); }); diff --git a/test/unit/core/config/remote/components-data-wrapper.test.ts b/test/unit/core/config/remote/components-data-wrapper.test.ts index 802d9014b..8cf218dff 100644 --- a/test/unit/core/config/remote/components-data-wrapper.test.ts +++ b/test/unit/core/config/remote/components-data-wrapper.test.ts @@ -11,92 +11,58 @@ import {ConsensusNodeComponent} from '../../../../../src/core/config/remote/comp import {MirrorNodeExplorerComponent} from '../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; import {RelayComponent} from '../../../../../src/core/config/remote/components/relay-component.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; -import {ComponentTypes} from '../../../../../src/core/config/remote/enumerations/component-types.js'; -import {ConsensusNodeStates} from '../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentStates} from '../../../../../src/core/config/remote/enumerations/component-states.js'; import {type NodeAliases} from '../../../../../src/types/aliases.js'; -import { - type ClusterReference, - type ComponentName, - type NamespaceNameAsString, -} from '../../../../../src/core/config/remote/types.js'; -import {BlockNodeComponent} from '../../../../../src/core/config/remote/components/block-node-component.js'; -import {type ComponentsDataStruct} from '../../../../../src/core/config/remote/interfaces/components-data-struct.js'; - -export function createComponentsDataWrapper(): { - values: { - name: string; - cluster: ClusterReference; - namespace: NamespaceNameAsString; - nodeState: ConsensusNodeStates; - consensusNodeAliases: NodeAliases; - state: ComponentStates; - }; - components: { - relays: Record; - haProxies: Record; - mirrorNodes: Record; - envoyProxies: Record; - consensusNodes: Record; - mirrorNodeExplorers: Record; - blockNodes: Record; - }; - wrapper: {componentsDataWrapper: ComponentsDataWrapper}; - componentName: string; -} { - const name: string = 'name'; - const componentName: string = name; - - const cluster: ClusterReference = 'cluster'; - const namespace: NamespaceNameAsString = 'namespace'; - const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; - const consensusNodeAliases: NodeAliases = ['node1', 'node2']; - const state: ComponentStates = ComponentStates.ACTIVE; - - const relays: Record = { - [componentName]: new RelayComponent(name, cluster, namespace, state, consensusNodeAliases), - }; +import {ConsensusNodeStates} from '../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; +import {ComponentTypes} from '../../../../../src/core/config/remote/enumerations/component-types.js'; - const haProxies: Record = { - [componentName]: new HaProxyComponent(name, cluster, namespace, state), - }; +export function createComponentsDataWrapper() { + const name = 'name'; + const serviceName = name; - const mirrorNodes: Record = { - [componentName]: new MirrorNodeComponent(name, cluster, namespace, state), - }; + const cluster = 'cluster'; + const namespace = 'namespace'; + const state = ConsensusNodeStates.STARTED; + const consensusNodeAliases = ['node1', 'node2'] as NodeAliases; - const envoyProxies: Record = { - [componentName]: new EnvoyProxyComponent(name, cluster, namespace, state), + const relays = {[serviceName]: new RelayComponent(name, cluster, namespace, consensusNodeAliases)}; + const haProxies = { + [serviceName]: new HaProxyComponent(name, cluster, namespace), + ['serviceName2']: new HaProxyComponent('name2', 'cluster2', namespace), }; - - const consensusNodes: Record = { - [componentName]: new ConsensusNodeComponent(name, cluster, namespace, state, nodeState, 0), + const mirrorNodes = {[serviceName]: new MirrorNodeComponent(name, cluster, namespace)}; + const envoyProxies = { + [serviceName]: new EnvoyProxyComponent(name, cluster, namespace), + ['serviceName2']: new EnvoyProxyComponent('name2', 'cluster2', namespace), }; - - const mirrorNodeExplorers: Record = { - [componentName]: new MirrorNodeExplorerComponent(name, cluster, namespace, state), - }; - - const blockNodes: Record = { - [componentName]: new BlockNodeComponent(name, cluster, namespace, state), + const consensusNodes = { + [serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, 0), + ['serviceName2']: new ConsensusNodeComponent('node2', 'cluster2', namespace, state, 1), }; + const mirrorNodeExplorers = {[serviceName]: new MirrorNodeExplorerComponent(name, cluster, namespace)}; // @ts-expect-error - TS267: to access private constructor - const componentsDataWrapper: ComponentsDataWrapper = new ComponentsDataWrapper( + const componentsDataWrapper = new ComponentsDataWrapper( relays, haProxies, mirrorNodes, envoyProxies, consensusNodes, mirrorNodeExplorers, - blockNodes, ); - + /* + ? The class after calling the toObject() method + * RELAY: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' consensusNodeAliases: ['node1', 'node2'] } }, + * HAPROXY: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, + * MIRROR_NODE: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, + * ENVOY_PROXY: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, + * CONSENSUS_NODE: { serviceName: { state: 'started', name: 'name', cluster: 'cluster', namespace: 'namespace'} }, + * MIRROR_NODE_EXPLORER: { serviceName: { name: 'name', cluster: 'cluster', namespace: 'namespace' } }, + */ return { - values: {name, cluster, namespace, nodeState, consensusNodeAliases, state}, - components: {consensusNodes, haProxies, envoyProxies, mirrorNodes, mirrorNodeExplorers, relays, blockNodes}, + values: {name, cluster, namespace, state, consensusNodeAliases}, + components: {consensusNodes, haProxies, envoyProxies, mirrorNodes, mirrorNodeExplorers, relays}, wrapper: {componentsDataWrapper}, - componentName, + serviceName, }; } @@ -105,10 +71,7 @@ describe('ComponentsDataWrapper', () => { it('should not be able to create a instance if wrong data is passed to constructor', () => { // @ts-expect-error - TS267: to access private constructor - expect((): ComponentsDataWrapper => new ComponentsDataWrapper({componentName: {}})).to.throw( - SoloError, - 'Invalid component type', - ); + expect(() => new ComponentsDataWrapper({serviceName: {}})).to.throw(SoloError, 'Invalid component type'); }); it('toObject method should return a object that can be parsed with fromObject', () => { @@ -116,11 +79,8 @@ describe('ComponentsDataWrapper', () => { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const newComponentsDataWrapper: ComponentsDataWrapper = ComponentsDataWrapper.fromObject( - componentsDataWrapper.toObject(), - ); - - const componentsDataWrapperObject: ComponentsDataStruct = componentsDataWrapper.toObject(); + const newComponentsDataWrapper = ComponentsDataWrapper.fromObject(componentsDataWrapper.toObject()); + const componentsDataWrapperObject = componentsDataWrapper.toObject(); expect(componentsDataWrapperObject).to.deep.equal(newComponentsDataWrapper.toObject()); @@ -131,140 +91,103 @@ describe('ComponentsDataWrapper', () => { expect(componentsDataWrapper); }); - it('should not be able to add new component with the .addNewComponent() method if it already exist', () => { + it('should not be able to add new component with the .add() method if it already exist', () => { const { wrapper: {componentsDataWrapper}, components: {consensusNodes}, - componentName, + serviceName, } = createComponentsDataWrapper(); - const existingComponent: ConsensusNodeComponent = consensusNodes[componentName]; + const existingComponent = consensusNodes[serviceName]; - expect(() => componentsDataWrapper.addNewComponent(existingComponent)).to.throw(SoloError, 'Component exists'); + expect(() => componentsDataWrapper.add(existingComponent)).to.throw(SoloError, 'Component exists'); }); - it('should be able to add new component with the .addNewComponent() method', () => { + it('should be able to add new component with the .add() method', () => { const { wrapper: {componentsDataWrapper}, - values: {state}, } = createComponentsDataWrapper(); - const newComponentName: string = 'envoy'; + const newServiceName = 'envoy'; const {name, cluster, namespace} = { - name: newComponentName, + name: newServiceName, cluster: 'cluster', namespace: 'new-namespace', }; - const newComponent: EnvoyProxyComponent = new EnvoyProxyComponent(name, cluster, namespace, state); + const newComponent = new EnvoyProxyComponent(name, cluster, namespace); - componentsDataWrapper.addNewComponent(newComponent); + componentsDataWrapper.add(newComponent); - const componentDataWrapperObject: ComponentsDataStruct = componentsDataWrapper.toObject(); + const componentDataWrapperObject = componentsDataWrapper.toObject(); - expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newComponentName); + expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy]).has.own.property(newServiceName); - expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy][newComponentName]).to.deep.equal({ + expect(componentDataWrapperObject[ComponentTypes.EnvoyProxy][newServiceName]).to.deep.equal({ name, cluster, namespace, - state, }); - expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(2); + expect(Object.values(componentDataWrapperObject[ComponentTypes.EnvoyProxy])).to.have.lengthOf(3); }); - it('should be able to change node state with the .changeNodeState(()', () => { + it('should be able to edit component with the .edit()', () => { const { wrapper: {componentsDataWrapper}, - componentName, + components: {relays}, + values: {cluster, namespace}, + serviceName, } = createComponentsDataWrapper(); + const relayComponent = relays[serviceName]; - const newNodeState: ConsensusNodeStates = ConsensusNodeStates.STOPPED; + componentsDataWrapper.edit(relayComponent); - componentsDataWrapper.changeNodeState(componentName, newNodeState); + const newCluster = 'newCluster'; - expect(componentsDataWrapper.consensusNodes[componentName].nodeState).to.equal(newNodeState); - }); + const newReplayComponent = new RelayComponent(relayComponent.name, newCluster, namespace); - it("should not be able to edit component with the .editComponent() if it doesn't exist ", () => { - const { - wrapper: {componentsDataWrapper}, - } = createComponentsDataWrapper(); - const notFoundComponentName: string = 'not_found'; + componentsDataWrapper.edit(newReplayComponent); - expect(() => - componentsDataWrapper.changeNodeState(notFoundComponentName, ConsensusNodeStates.NON_DEPLOYED), - ).to.throw(SoloError, `Consensus node ${notFoundComponentName} doesn't exist`); + expect(componentsDataWrapper.toObject()[ComponentTypes.Relay][relayComponent.name].cluster).to.equal(newCluster); }); - it('should be able to disable component with the .disableComponent()', () => { + it("should not be able to edit component with the .edit() if it doesn't exist ", () => { const { wrapper: {componentsDataWrapper}, components: {relays}, - componentName, + serviceName, } = createComponentsDataWrapper(); + const notFoundServiceName = 'not_found'; + const relay = relays[serviceName]; + relay.name = notFoundServiceName; - componentsDataWrapper.disableComponent(componentName, ComponentTypes.Relay); - - expect(relays[componentName].state).to.equal(ComponentStates.DELETED); - }); - - it("should not be able to disable component with the .disableComponent() if it doesn't exist ", () => { - const { - wrapper: {componentsDataWrapper}, - } = createComponentsDataWrapper(); - - const notFoundComponentName: string = 'not_found'; - - expect(() => componentsDataWrapper.disableComponent(notFoundComponentName, ComponentTypes.Relay)).to.throw( + expect(() => componentsDataWrapper.edit(relay)).to.throw( SoloError, - `Component ${notFoundComponentName} of type ${ComponentTypes.Relay} not found while attempting to remove`, + `Component doesn't exist, name: ${notFoundServiceName}`, ); }); - it('should be able to get components with .getComponent()', () => { + it('should be able to remove component with the .remove()', () => { const { wrapper: {componentsDataWrapper}, - componentName, - components: {blockNodes}, + serviceName, } = createComponentsDataWrapper(); - const blockNodeComponent: BlockNodeComponent = componentsDataWrapper.getComponent( - ComponentTypes.BlockNode, - componentName, - ); + componentsDataWrapper.remove(serviceName, ComponentTypes.Relay); - expect(blockNodes[componentName].toObject()).to.deep.equal(blockNodeComponent.toObject()); + expect(componentsDataWrapper.relays).not.to.have.own.property(serviceName); }); - it("should fail if trying to get component that doesn't exist with .getComponent()", () => { + it("should not be able to remove component with the .remove() if it doesn't exist ", () => { const { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const notFoundComponentName: ComponentName = 'not_found'; - const type: ComponentTypes = ComponentTypes.BlockNode; + const notFoundServiceName = 'not_found'; - expect(() => componentsDataWrapper.getComponent(type, notFoundComponentName)).to.throw( - `Component ${notFoundComponentName} of type ${type} not found while attempting to read`, + expect(() => componentsDataWrapper.remove(notFoundServiceName, ComponentTypes.Relay)).to.throw( + SoloError, + `Component ${notFoundServiceName} of type ${ComponentTypes.Relay} not found while attempting to remove`, ); }); - - it('should be able to get components with .applyCallbackToComponentGroup()', () => { - const { - wrapper: {componentsDataWrapper}, - components: {blockNodes}, - values: {cluster}, - } = createComponentsDataWrapper(); - - const blockNodeComponents: BlockNodeComponent[] = - componentsDataWrapper.getComponentsByClusterReference(ComponentTypes.BlockNode, cluster); - - for (const blockNodeComponent of blockNodeComponents) { - expect(blockNodeComponent.toObject()).to.deep.equal(blockNodes[blockNodeComponent.name].toObject()); - expect(blockNodeComponent.cluster).to.equal(cluster); - } - - expect(Object.keys(blockNodes).length).to.equal(blockNodeComponents.length); - }); }); diff --git a/test/unit/core/config/remote/components/component-names.test.ts b/test/unit/core/config/remote/components/component-names.test.ts deleted file mode 100644 index b260122cc..000000000 --- a/test/unit/core/config/remote/components/component-names.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -import {expect} from 'chai'; -import {describe, it} from 'mocha'; -import {type ComponentName} from '../../../../../../src/core/config/remote/types.js'; -import {type NodeAlias, type NodeAliases} from '../../../../../../src/types/aliases.js'; -import {Templates} from '../../../../../../src/core/templates.js'; -import {ComponentNameTemplates} from '../../../../../../src/core/config/remote/components/component-name-templates.js'; - -describe('ComponentNameTemplates', () => { - const maxTestIndex: number = 10; - const nodeAliases: NodeAliases = Templates.renderNodeAliasesFromCount(maxTestIndex, 0); - - it('should create a valid component name for MirrorNodeComponent', () => { - for (let index: number = 0; index < maxTestIndex; index++) { - const componentName: ComponentName = ComponentNameTemplates.renderMirrorNodeName(index); - expect(componentName).to.equal(`mirror-node-${index}`); - } - }); - - it('should create a valid component name for BlockNodeComponent', () => { - for (let index: number = 0; index < maxTestIndex; index++) { - const componentName: ComponentName = ComponentNameTemplates.renderBlockNodeName(index); - expect(componentName).to.equal(`block-node-${index}`); - } - }); - - it('should create a valid component name for EnvoyProxyComponent', () => { - for (let index: number = 0; index < maxTestIndex; index++) { - const nodeAlias: NodeAlias = nodeAliases[index]; - - const componentName: ComponentName = ComponentNameTemplates.renderEnvoyProxyName(index, nodeAlias); - expect(componentName).to.equal(`envoy-proxy-${nodeAlias}-${index}`); - } - }); - - it('should create a valid component name for HaProxyComponent', () => { - for (let index: number = 0; index < maxTestIndex; index++) { - const nodeAlias: NodeAlias = nodeAliases[index]; - - const componentName: ComponentName = ComponentNameTemplates.renderHaProxyName(index, nodeAlias); - expect(componentName).to.equal(`haproxy-${nodeAlias}-${index}`); - } - }); - - it('should create a valid component name for MirrorNodeExplorerComponent', () => { - for (let index: number = 0; index < maxTestIndex; index++) { - const componentName: ComponentName = ComponentNameTemplates.renderMirrorNodeExplorerName(index); - expect(componentName).to.equal(`mirror-node-explorer-${index}`); - } - }); - - it('should create a valid component name for RelayComponent', () => { - for (let index: number = 0; index < maxTestIndex; index++) { - const componentName: ComponentName = ComponentNameTemplates.renderRelayName(index); - expect(componentName).to.equal(`relay-${index}`); - } - }); -}); diff --git a/test/unit/core/config/remote/components/components.test.ts b/test/unit/core/config/remote/components/components.test.ts index 57e2604f3..a87fbce6c 100644 --- a/test/unit/core/config/remote/components/components.test.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -3,48 +3,75 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; -import {type RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; +import {RelayComponent} from '../../../../../../src/core/config/remote/components/relay-component.js'; import {BaseComponent} from '../../../../../../src/core/config/remote/components/base-component.js'; -import {type ConsensusNodeComponent} from '../../../../../../src/core/config/remote/components/consensus-node-component.js'; +import {ConsensusNodeComponent} from '../../../../../../src/core/config/remote/components/consensus-node-component.js'; import {HaProxyComponent} from '../../../../../../src/core/config/remote/components/ha-proxy-component.js'; import {EnvoyProxyComponent} from '../../../../../../src/core/config/remote/components/envoy-proxy-component.js'; import {MirrorNodeComponent} from '../../../../../../src/core/config/remote/components/mirror-node-component.js'; import {MirrorNodeExplorerComponent} from '../../../../../../src/core/config/remote/components/mirror-node-explorer-component.js'; -import {type NodeAlias} from '../../../../../../src/types/aliases.js'; +import {SoloError} from '../../../../../../src/core/errors/solo-error.js'; +import {type NodeAliases} from '../../../../../../src/types/aliases.js'; import {Templates} from '../../../../../../src/core/templates.js'; import {ConsensusNodeStates} from '../../../../../../src/core/config/remote/enumerations/consensus-node-states.js'; -import {ComponentStates} from '../../../../../../src/core/config/remote/enumerations/component-states.js'; -import {BlockNodeComponent} from '../../../../../../src/core/config/remote/components/block-node-component.js'; -import {type ClusterReference, type ComponentName} from '../../../../../../src/core/config/remote/types.js'; -import {NamespaceName} from '../../../../../../src/integration/kube/resources/namespace/namespace-name.js'; -import {type BaseComponentStruct} from '../../../../../../src/core/config/remote/components/interfaces/base-component-struct.js'; -import {type RelayComponentStruct} from '../../../../../../src/core/config/remote/components/interfaces/relay-component-struct.js'; -import {type ConsensusNodeComponentStruct} from '../../../../../../src/core/config/remote/components/interfaces/consensus-node-component-struct.js'; -import {ComponentFactory} from '../../../../../../src/core/config/remote/components/component-factory.js'; -import {ComponentNameTemplates} from '../../../../../../src/core/config/remote/components/component-name-templates.js'; - -const remoteConfigManagerMock: any = {components: {getNewComponentIndex: (): number => 1}}; - -const componentName: ComponentName = 'componentName'; -const clusterReference: ClusterReference = 'cluster-reference'; -const namespace: NamespaceName = NamespaceName.of('valid'); - -function testBaseComponentData(classComponent: any): void { + +function testBaseComponentData(classComponent: any) { + const validNamespace = 'valid'; + it('should fail if name is not provided', () => { + const name = ''; + expect(() => new classComponent(name, 'valid', validNamespace)).to.throw(SoloError, `Invalid name: ${name}`); + }); + + it('should fail if name is string', () => { + const name = 1; // @ts-ignore + expect(() => new classComponent(name, 'valid', validNamespace)).to.throw(SoloError, `Invalid name: ${name}`); + }); + + it('should fail if cluster is not provided', () => { + const cluster = ''; + expect(() => new classComponent('valid', cluster, validNamespace)).to.throw( + SoloError, + `Invalid cluster: ${cluster}`, + ); + }); + + it('should fail if cluster is string', () => { + const cluster = 1; + expect(() => new classComponent('valid', cluster, validNamespace)).to.throw( + SoloError, + `Invalid cluster: ${cluster}`, + ); + }); + + it('should fail if namespace is not provided', () => { + const namespace = ''; + expect(() => new classComponent('valid', 'valid', namespace)).to.throw( + SoloError, + `Invalid namespace: ${namespace}`, + ); + }); + + it('should fail if namespace is string', () => { + const namespace = 1; + expect(() => new classComponent('valid', 'valid', namespace)).to.throw( + SoloError, + `Invalid namespace: ${namespace}`, + ); + }); + + it('should successfully create ', () => { + new classComponent('valid', 'valid', 'valid'); + }); + it('should be an instance of BaseComponent', () => { - const component: any = new classComponent(componentName, clusterReference, namespace.name, ComponentStates.ACTIVE); + const component = new classComponent('valid', 'valid', validNamespace); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { - const data: BaseComponentStruct = { - name: componentName, - cluster: clusterReference, - namespace: namespace.name, - state: ComponentStates.ACTIVE, - }; - - const component: any = new classComponent(data.name, data.cluster, data.namespace, data.state); - expect(component.toObject()).to.deep.equal(data); + const {name, cluster, namespace} = {name: 'name', cluster: 'cluster', namespace: 'namespace'}; + const component = new classComponent(name, cluster, namespace); + expect(component.toObject()).to.deep.equal({name, cluster, namespace}); }); } @@ -56,84 +83,178 @@ describe('MirrorNodeComponent', () => testBaseComponentData(MirrorNodeComponent) describe('MirrorNodeExplorerComponent', () => testBaseComponentData(MirrorNodeExplorerComponent)); -describe('BlockNodeComponent', () => testBaseComponentData(BlockNodeComponent)); - describe('RelayComponent', () => { + it('should fail if name is not provided', () => { + const name = ''; + expect(() => new RelayComponent(name, 'valid', 'valid', [])).to.throw(SoloError, `Invalid name: ${name}`); + }); + + it('should fail if name is string', () => { + const name = 1; + // @ts-expect-error - TS2345: Argument of type number is not assignable to parameter of type string + expect(() => new RelayComponent(name, 'valid', 'valid', [])).to.throw(SoloError, `Invalid name: ${name}`); + }); + + it('should fail if cluster is not provided', () => { + const cluster = ''; + expect(() => new RelayComponent('valid', cluster, 'valid', [])).to.throw(SoloError, `Invalid cluster: ${cluster}`); + }); + + it('should fail if cluster is string', () => { + const cluster = 1; + // @ts-expect-error - TS2345: Argument of type number is not assignable to parameter of type string + expect(() => new RelayComponent('valid', cluster, 'valid', [])).to.throw(SoloError, `Invalid cluster: ${cluster}`); + }); + + it('should fail if namespace is not provided', () => { + const namespace = null; + expect(() => new RelayComponent('valid', 'valid', namespace, [])).to.throw( + SoloError, + `Invalid namespace: ${namespace}`, + ); + }); + + it('should fail if namespace is string', () => { + const namespace = 1; + // @ts-expect-error - forcefully provide namespace as a number to create an error + expect(() => new RelayComponent('valid', 'valid', namespace, [])).to.throw( + SoloError, + `Invalid namespace: ${namespace}`, + ); + }); + + it('should fail if consensusNodeAliases is not valid', () => { + const consensusNodeAliases = [undefined] as NodeAliases; + expect(() => new RelayComponent('valid', 'valid', 'valid', consensusNodeAliases)).to.throw( + SoloError, + `Invalid consensus node alias: ${consensusNodeAliases[0]}, aliases ${consensusNodeAliases}`, + ); + }); + + it('should fail if consensusNodeAliases is not valid', () => { + const consensusNodeAliases = ['node1', 1] as NodeAliases; + expect(() => new RelayComponent('valid', 'valid', 'valid', consensusNodeAliases)).to.throw( + SoloError, + `Invalid consensus node alias: 1, aliases ${consensusNodeAliases}`, + ); + }); + it('should successfully create ', () => { - ComponentFactory.createNewRelayComponent(remoteConfigManagerMock, clusterReference, namespace, []); + new RelayComponent('valid', 'valid', 'valid'); }); it('should be an instance of BaseComponent', () => { - const component: RelayComponent = ComponentFactory.createNewRelayComponent( - remoteConfigManagerMock, - clusterReference, - namespace, - [], - ); + const component = new RelayComponent('valid', 'valid', 'valid'); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { - const name: ComponentName = ComponentNameTemplates.renderRelayName( - remoteConfigManagerMock.components.getNewComponentIndex(), + const {name, cluster, namespace, consensusNodeAliases} = { + name: 'name', + cluster: 'cluster', + namespace: 'namespace', + consensusNodeAliases: ['node1'] as NodeAliases, + }; + + const component = new RelayComponent(name, cluster, namespace, consensusNodeAliases); + expect(component.toObject()).to.deep.equal({name, cluster, namespace: namespace, consensusNodeAliases}); + }); +}); + +describe('ConsensusNodeComponent', () => { + it('should fail if name is not provided', () => { + const name = ''; + expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( + SoloError, + `Invalid name: ${name}`, ); + }); - const values: RelayComponentStruct = { - name, - cluster: clusterReference, - namespace: namespace.name, - state: ComponentStates.ACTIVE, - consensusNodeAliases: ['node1'], - }; + it('should fail if name is not a string', () => { + const name = 1; // @ts-ignore + expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( + SoloError, + `Invalid name: ${name}`, + ); + }); - const component: RelayComponent = ComponentFactory.createNewRelayComponent( - remoteConfigManagerMock, - values.cluster, - namespace, - values.consensusNodeAliases, + it('should fail if cluster is not provided', () => { + const cluster = ''; + expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( + SoloError, + `Invalid cluster: ${cluster}`, ); + }); - expect(component.toObject()).to.deep.equal(values); + it('should fail if cluster is not a string', () => { + const cluster = 1; // @ts-ignore + expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( + SoloError, + `Invalid cluster: ${cluster}`, + ); }); -}); -describe('ConsensusNodeComponent', () => { - const nodeAlias: NodeAlias = 'node1'; - const nodeState: ConsensusNodeStates = ConsensusNodeStates.STARTED; + it('should fail if namespace is not provided', () => { + const namespace = null; + expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED, 0)).to.throw( + SoloError, + `Invalid namespace: ${namespace}`, + ); + }); - it('should successfully create ', () => { - ComponentFactory.createNewConsensusNodeComponent(nodeAlias, 'valid', namespace, nodeState); + it('should fail if namespace is not a string', () => { + const namespace = 1; // @ts-ignore + expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED, 0)).to.throw( + SoloError, + `Invalid namespace: ${namespace}`, + ); }); - it('should be an instance of BaseComponent', () => { - const component: ConsensusNodeComponent = ComponentFactory.createNewConsensusNodeComponent( - nodeAlias, - clusterReference, - namespace, - nodeState, + it('should fail if state is not valid', () => { + const state = 'invalid' as ConsensusNodeStates.STARTED; + expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', state, 0)).to.throw( + SoloError, + `Invalid consensus node state: ${state}`, ); + }); + + it('should fail if nodeId is not a number', () => { + const nodeId = 'invalid'; // @ts-ignore + expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, nodeId)).to.throw( + SoloError, + `Invalid node id. It must be a number: ${nodeId}`, + ); + }); + it('should fail if nodeId is negative', () => { + const nodeId = -1; // @ts-ignore + expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, nodeId)).to.throw( + SoloError, + `Invalid node id. It cannot be negative: ${nodeId}`, + ); + }); + + it('should successfully create ', () => { + new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, 0); + }); + + it('should be an instance of BaseComponent', () => { + const component = new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, 0); expect(component).to.be.instanceOf(BaseComponent); }); it('calling toObject() should return a valid data', () => { - const nodeAlias: NodeAlias = 'node1'; - const values: ConsensusNodeComponentStruct = { + const nodeAlias = 'node1'; + const nodeInfo = { name: nodeAlias, - cluster: clusterReference, - namespace: namespace.name, - state: ComponentStates.ACTIVE, - nodeState, + cluster: 'cluster', + namespace: 'namespace', + state: ConsensusNodeStates.STARTED, nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; - const component: ConsensusNodeComponent = ComponentFactory.createNewConsensusNodeComponent( - values.name as NodeAlias, - values.cluster, - namespace, - values.nodeState as ConsensusNodeStates.STARTED, - ); - - expect(component.toObject()).to.deep.equal(values); + const {name, cluster, namespace, state, nodeId} = nodeInfo; + const component = new ConsensusNodeComponent(name, cluster, namespace, state, nodeId); + expect(component.toObject()).to.deep.equal(nodeInfo); }); }); diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index 93a5691e3..88599b548 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -5,52 +5,57 @@ import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import {RemoteConfigMetadata} from '../../../../../src/core/config/remote/metadata.js'; -import {type EmailAddress} from '../../../../../src/core/config/remote/types.js'; -import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; -import {type RemoteConfigMetadataStruct} from '../../../../../src/core/config/remote/interfaces/remote-config-metadata-struct.js'; - -interface MetadataTestStructure { - metadata: RemoteConfigMetadata; - migration: Migration; - values: RemoteConfigMetadataStruct; -} - -export function createMetadata(): MetadataTestStructure { +import { + type EmailAddress, + type NamespaceNameAsString, + type Version, +} from '../../../../../src/core/config/remote/types.js'; +import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations.js'; + +export function createMetadata() { + const namespace: NamespaceNameAsString = 'namespace'; + const deploymentName = 'kind-namespace'; + const state = DeploymentStates.PRE_GENESIS; const lastUpdatedAt: Date = new Date(); const lastUpdateBy: EmailAddress = 'test@test.test'; - - const values: RemoteConfigMetadataStruct = { - namespace: 'namespace', - deploymentName: 'kind-namespace', - state: DeploymentStates.PRE_GENESIS, - soloVersion: '0.0.1', - migration: new Migration(lastUpdatedAt, lastUpdateBy, '0.0.0'), - lastUpdatedAt, - lastUpdateBy, - soloChartVersion: '1.0.0', - hederaPlatformVersion: '1.0.0', - hederaMirrorNodeChartVersion: '1.0.0', - hederaExplorerChartVersion: '1.0.0', - hederaJsonRpcRelayChartVersion: '1.0.0', - }; + const soloVersion: Version = '0.0.1'; + const migration = new Migration(lastUpdatedAt, lastUpdateBy, '0.0.0'); + const soloChartVersion = ''; + const hederaPlatformVersion = ''; + const hederaMirrorNodeChartVersion = ''; + const hederaExplorerChartVersion = ''; + const hederaJsonRpcRelayChartVersion = ''; return { metadata: new RemoteConfigMetadata( - values.namespace, - values.deploymentName, - values.state, - values.lastUpdatedAt, - values.lastUpdateBy, - values.soloVersion, - values.soloChartVersion, - values.hederaPlatformVersion, - values.hederaMirrorNodeChartVersion, - values.hederaExplorerChartVersion, - values.hederaJsonRpcRelayChartVersion, - values.migration as Migration, + namespace, + deploymentName, + state, + lastUpdatedAt, + lastUpdateBy, + soloVersion, + '', + '', + '', + '', + '', + migration, ), - migration: values.migration as Migration, - values, + values: { + namespace, + deploymentName, + state, + lastUpdatedAt, + lastUpdateBy, + migration, + soloVersion, + soloChartVersion, + hederaPlatformVersion, + hederaMirrorNodeChartVersion, + hederaExplorerChartVersion, + hederaJsonRpcRelayChartVersion, + }, + migration, }; } @@ -60,42 +65,61 @@ describe('RemoteConfigMetadata', () => { }); it('toObject method should return a valid object', () => { - const {metadata, migration, values} = createMetadata(); + const { + metadata, + migration, + values: { + namespace, + deploymentName, + state, + lastUpdatedAt, + lastUpdateBy, + soloVersion, + soloChartVersion, + hederaPlatformVersion, + hederaMirrorNodeChartVersion, + hederaExplorerChartVersion, + hederaJsonRpcRelayChartVersion, + }, + } = createMetadata(); expect(metadata.toObject()).to.deep.equal({ - namespace: values.namespace, - deploymentName: values.deploymentName, - state: values.state, - lastUpdatedAt: values.lastUpdatedAt, - lastUpdateBy: values.lastUpdateBy, - soloVersion: values.soloVersion, - soloChartVersion: values.soloChartVersion, - hederaPlatformVersion: values.hederaPlatformVersion, - hederaMirrorNodeChartVersion: values.hederaMirrorNodeChartVersion, - hederaExplorerChartVersion: values.hederaExplorerChartVersion, - hederaJsonRpcRelayChartVersion: values.hederaJsonRpcRelayChartVersion, - migration: (migration as Migration).toObject(), + namespace, + deploymentName, + state, + lastUpdatedAt, + lastUpdateBy, + soloVersion, + soloChartVersion, + hederaPlatformVersion, + hederaMirrorNodeChartVersion, + hederaExplorerChartVersion, + hederaJsonRpcRelayChartVersion, + migration: migration.toObject(), }); }); it('should successfully create instance using fromObject', () => { - const {metadata, values} = createMetadata(); + const { + metadata, + values: {namespace, deploymentName, lastUpdatedAt, lastUpdateBy, soloVersion, state}, + } = createMetadata(); // @ts-expect-error - TS234: to access private property delete metadata._migration; - const newMetadata: RemoteConfigMetadata = RemoteConfigMetadata.fromObject({ - namespace: values.namespace, - deploymentName: values.deploymentName, - state: values.state, - lastUpdatedAt: values.lastUpdatedAt, - lastUpdateBy: values.lastUpdateBy, - soloVersion: values.soloVersion, - soloChartVersion: values.soloChartVersion, - hederaPlatformVersion: values.hederaPlatformVersion, - hederaMirrorNodeChartVersion: values.hederaMirrorNodeChartVersion, - hederaExplorerChartVersion: values.hederaExplorerChartVersion, - hederaJsonRpcRelayChartVersion: values.hederaJsonRpcRelayChartVersion, + const newMetadata = RemoteConfigMetadata.fromObject({ + namespace, + deploymentName, + state, + lastUpdatedAt, + lastUpdateBy, + soloVersion, + soloChartVersion: '', + hederaPlatformVersion: '', + hederaMirrorNodeChartVersion: '', + hederaExplorerChartVersion: '', + hederaJsonRpcRelayChartVersion: '', }); expect(newMetadata.toObject()).to.deep.equal(metadata.toObject()); @@ -108,7 +132,7 @@ describe('RemoteConfigMetadata', () => { metadata, values: {lastUpdateBy}, } = createMetadata(); - const version: string = '0.0.1'; + const version = '0.0.1'; metadata.makeMigration(lastUpdateBy, version); @@ -118,23 +142,60 @@ describe('RemoteConfigMetadata', () => { }); describe('Values', () => { - const {values} = createMetadata(); + const { + values: {namespace, deploymentName, lastUpdatedAt, lastUpdateBy, soloVersion, state}, + } = createMetadata(); + + it('should not be able to create new instance of the class with invalid name', () => { + expect( + () => new RemoteConfigMetadata(null, deploymentName, state, lastUpdatedAt, lastUpdateBy, soloVersion), + ).to.throw(SoloError, `Invalid namespace: ${null}`); + + expect( + // @ts-expect-error: TS2345 - to assign unexpected value + () => new RemoteConfigMetadata(1, deploymentName, state, lastUpdatedAt, lastUpdateBy, soloVersion), + ).to.throw(SoloError, `Invalid namespace: ${1}`); + }); + + it('should not be able to create new instance of the class with invalid lastUpdatedAt', () => { + expect( + () => new RemoteConfigMetadata(namespace, deploymentName, state, null, lastUpdateBy, soloVersion), + ).to.throw(SoloError, `Invalid lastUpdatedAt: ${null}`); + + // @ts-expect-error: TS2345 - to assign unexpected value + expect(() => new RemoteConfigMetadata(namespace, deploymentName, state, 1, lastUpdateBy, soloVersion)).to.throw( + SoloError, + `Invalid lastUpdatedAt: ${1}`, + ); + }); + + it('should not be able to create new instance of the class with invalid lastUpdateBy', () => { + expect( + () => new RemoteConfigMetadata(namespace, deploymentName, state, lastUpdatedAt, null, soloVersion), + ).to.throw(SoloError, `Invalid lastUpdateBy: ${null}`); + + // @ts-expect-error: TS2345 - to assign unexpected value + expect(() => new RemoteConfigMetadata(namespace, deploymentName, state, lastUpdatedAt, 1, soloVersion)).to.throw( + SoloError, + `Invalid lastUpdateBy: ${1}`, + ); + }); it('should not be able to create new instance of the class with invalid migration', () => { expect( () => new RemoteConfigMetadata( - values.namespace, - values.deploymentName, - values.state, - values.lastUpdatedAt, - values.lastUpdateBy, - values.soloVersion, - values.soloChartVersion, - values.hederaPlatformVersion, - values.hederaMirrorNodeChartVersion, - values.hederaExplorerChartVersion, - values.hederaJsonRpcRelayChartVersion, + namespace, + deploymentName, + state, + lastUpdatedAt, + lastUpdateBy, + soloVersion, + '', + '', + '', + '', + '', // @ts-expect-error - TS2345: to inject wrong migration {}, ), diff --git a/test/unit/core/config/remote/migration.test.ts b/test/unit/core/config/remote/migration.test.ts index 1e19ec036..088bb91c7 100644 --- a/test/unit/core/config/remote/migration.test.ts +++ b/test/unit/core/config/remote/migration.test.ts @@ -3,18 +3,57 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; import {Migration} from '../../../../../src/core/config/remote/migration.js'; +import {type EmailAddress, type Version} from '../../../../../src/core/config/remote/types.js'; +import {SoloError} from '../../../../../src/core/errors/solo-error.js'; -import {type MigrationStruct} from '../../../../../src/core/config/remote/interfaces/migration-struct.js'; +function createMigration() { + const migratedAt = new Date(); + const migratedBy = 'test@test.test' as EmailAddress; + const fromVersion = '1.0.0' as Version; + return { + migration: new Migration(migratedAt, migratedBy, fromVersion), + values: {migratedAt, migratedBy, fromVersion}, + }; +} describe('Migration', () => { - const values: MigrationStruct = {migratedAt: new Date(), migratedBy: 'test@test.test', fromVersion: '1.0.0'}; - let migration: Migration; - - beforeEach(() => { - migration = new Migration(values.migratedAt, values.migratedBy, values.fromVersion); + it('should be able to create new instance of the class with valid data', () => { + expect(() => createMigration()).not.to.throw(); }); it('toObject method should return a valid object', () => { + const {migration, values} = createMigration(); + expect(migration.toObject()).to.deep.equal(values); }); + + describe('Values', () => { + const migratedAt = new Date(); + const migratedBy = 'test@test.test' as EmailAddress; + const fromVersion = '1.0.0' as Version; + + it('should not be able to create new instance of the class with invalid migratedAt', () => { + // @ts-ignore + expect(() => new Migration(null, migratedBy, fromVersion)).to.throw(SoloError, `Invalid migratedAt: ${null}`); + + // @ts-ignore + expect(() => new Migration(1, migratedBy, fromVersion)).to.throw(SoloError, `Invalid migratedAt: ${1}`); + }); + + it('should not be able to create new instance of the class with invalid migratedBy', () => { + // @ts-ignore + expect(() => new Migration(migratedAt, null, fromVersion)).to.throw(SoloError, `Invalid migratedBy: ${null}`); + + // @ts-ignore + expect(() => new Migration(migratedAt, 1, fromVersion)).to.throw(SoloError, `Invalid migratedBy: ${1}`); + }); + + it('should not be able to create new instance of the class with invalid fromVersion', () => { + // @ts-ignore + expect(() => new Migration(migratedAt, migratedBy, null)).to.throw(SoloError, `Invalid fromVersion: ${null}`); + + // @ts-ignore + expect(() => new Migration(migratedAt, migratedBy, 1)).to.throw(SoloError, `Invalid fromVersion: ${1}`); + }); + }); }); diff --git a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts index d5694b43e..d6af7249e 100644 --- a/test/unit/core/config/remote/remote-config-data-wrapper.test.ts +++ b/test/unit/core/config/remote/remote-config-data-wrapper.test.ts @@ -7,39 +7,41 @@ import * as yaml from 'yaml'; import {RemoteConfigDataWrapper} from '../../../../../src/core/config/remote/remote-config-data-wrapper.js'; import {createMetadata} from './metadata.test.js'; import {createComponentsDataWrapper} from './components-data-wrapper.test.js'; +import {SoloError} from '../../../../../src/core/errors/solo-error.js'; import * as constants from '../../../../../src/core/constants.js'; import {CommonFlagsDataWrapper} from '../../../../../src/core/config/remote/common-flags-data-wrapper.js'; -import {type RemoteConfigData} from '../../../../../src/core/config/remote/remote-config-data.js'; -import {type RemoteConfigDataStruct} from '../../../../../src/core/config/remote/interfaces/remote-config-data-struct.js'; -const configManagerMock: any = { +const configManagerMock = { update: (...arguments_: any) => true, getFlag: (...arguments_: any) => true, hasFlag: (...arguments_: any) => true, setFlag: (...arguments_: any) => true, }; -async function createRemoteConfigDataWrapper(): Promise<{ - values: RemoteConfigData; - dataWrapper: RemoteConfigDataWrapper; -}> { +async function createRemoteConfigDataWrapper() { const {metadata} = createMetadata(); const { wrapper: {componentsDataWrapper}, } = createComponentsDataWrapper(); - const values: RemoteConfigData = { + const clusters = {}; + const components = componentsDataWrapper; + const lastExecutedCommand = 'lastExecutedCommand'; + const commandHistory = []; + const flags = await CommonFlagsDataWrapper.initialize(configManagerMock as any, {}); + + const dataWrapper = new RemoteConfigDataWrapper({ metadata, - clusters: {}, - components: componentsDataWrapper, - lastExecutedCommand: 'lastExecutedCommand', - commandHistory: [], - flags: await CommonFlagsDataWrapper.initialize(configManagerMock, {}), - }; + clusters, + components, + lastExecutedCommand, + commandHistory, + flags, + }); return { - values, - dataWrapper: new RemoteConfigDataWrapper(values), + dataWrapper, + values: {metadata, clusters, components, lastExecutedCommand, commandHistory}, }; } @@ -49,7 +51,7 @@ describe('RemoteConfigDataWrapper', async () => { it('should be able to add new command to history with addCommandToHistory()', async () => { const {dataWrapper} = await createRemoteConfigDataWrapper(); - const command: string = 'command'; + const command = 'command'; dataWrapper.addCommandToHistory(command); @@ -57,7 +59,7 @@ describe('RemoteConfigDataWrapper', async () => { expect(dataWrapper.commandHistory).to.include(command); it('should be able to handle overflow', () => { - for (let index: number = 0; index < constants.SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY; index++) { + for (let index = 0; index < constants.SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY; index++) { dataWrapper.addCommandToHistory(command); } }); @@ -65,9 +67,9 @@ describe('RemoteConfigDataWrapper', async () => { it('should successfully be able to parse yaml and create instance with fromConfigmap()', async () => { const {dataWrapper} = await createRemoteConfigDataWrapper(); - const dataWrapperObject: RemoteConfigDataStruct = dataWrapper.toObject(); + const dataWrapperObject = dataWrapper.toObject(); - const yamlData: string = yaml.stringify({ + const yamlData = yaml.stringify({ metadata: dataWrapperObject.metadata, components: dataWrapperObject.components as any, clusters: dataWrapperObject.clusters, @@ -75,6 +77,30 @@ describe('RemoteConfigDataWrapper', async () => { lastExecutedCommand: dataWrapperObject.lastExecutedCommand, }); - RemoteConfigDataWrapper.fromConfigmap(configManagerMock, {data: {'remote-config-data': yamlData}} as any); + RemoteConfigDataWrapper.fromConfigmap(configManagerMock as any, {data: {'remote-config-data': yamlData}} as any); + }); + + it('should fail if invalid data is passed to setters', async () => { + const {dataWrapper} = await createRemoteConfigDataWrapper(); + + // @ts-expect-error TS2322: Type string is not assignable to type string[] + expect(() => (dataWrapper.commandHistory = '')).to.throw(SoloError); + + // @ts-expect-error TS2341 Property lastExecutedCommand is private and only accessible within class RemoteConfigDataWrapper + expect(() => (dataWrapper.lastExecutedCommand = '')).to.throw(SoloError); + + // @ts-expect-error TS2341 Property lastExecutedCommand is private and only accessible within class RemoteConfigDataWrapper + expect(() => (dataWrapper.lastExecutedCommand = 1)).to.throw(SoloError); + + // @ts-expect-error TS2322 Type number is not assignable to type ComponentsDataWrapper + expect(() => (dataWrapper.components = 1)).to.throw(SoloError); + + // @ts-expect-error TS2322 Type string is not assignable to type ComponentsDataWrapper + expect(() => (dataWrapper.components = '')).to.throw(SoloError); + + expect(() => (dataWrapper.metadata = null)).to.throw(SoloError); + + // @ts-expect-error 2740: Type {} is missing the following properties from type RemoteConfigMetadata + expect(() => (dataWrapper.metadata = {})).to.throw(SoloError); }); }); From 5b5f31abbfc77909101d87233ed2856f35dfa94c Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 24 Apr 2025 11:11:05 +0300 Subject: [PATCH 66/70] lint-fix Signed-off-by: Zhan Milenkov --- src/commands/explorer.ts | 4 +++- src/commands/mirror-node.ts | 1 - src/commands/relay.ts | 2 -- src/core/config/remote/components-data-wrapper.ts | 3 --- src/core/config/remote/components/block-node-component.ts | 6 +----- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 13fad3183..010bcd484 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -631,7 +631,9 @@ export class ExplorerCommand extends BaseCommand { const { config: {namespace, clusterRef}, } = context_; - remoteConfig.components.add(new MirrorNodeExplorerComponent('mirrorNodeExplorer', clusterRef, namespace.name)); + remoteConfig.components.add( + new MirrorNodeExplorerComponent('mirrorNodeExplorer', clusterRef, namespace.name), + ); }); }, }; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index 71f03ad80..bbdbe3526 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -40,7 +40,6 @@ import {KeyManager} from '../core/key-manager.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; -import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface MirrorNodeDeployConfigClass { cacheDir: string; diff --git a/src/commands/relay.ts b/src/commands/relay.ts index f71fea09e..3d779584a 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -20,8 +20,6 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; -import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; interface RelayDestroyConfigClass { chartDirectory: string; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index e5a34f798..0627bb7ae 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -12,14 +12,11 @@ import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-com import {type ClusterReference, type ComponentName} from './types.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; -import {ComponentStates} from './enumerations/component-states.js'; -import {isValidEnum} from '../../util/validation-helpers.js'; import {type BaseComponentStruct} from './components/interfaces/base-component-struct.js'; import {type RelayComponentStruct} from './components/interfaces/relay-component-struct.js'; import {type ConsensusNodeComponentStruct} from './components/interfaces/consensus-node-component-struct.js'; import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; -import {ComponentNameTemplates} from './components/component-name-templates.js'; /** * Represent the components in the remote config and handles: diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts index c9849ee3a..299e9e344 100644 --- a/src/core/config/remote/components/block-node-component.ts +++ b/src/core/config/remote/components/block-node-component.ts @@ -6,11 +6,7 @@ import {ComponentTypes} from '../enumerations/component-types.js'; import {type BaseComponentStruct} from './interfaces/base-component-struct.js'; export class BlockNodeComponent extends BaseComponent { - public constructor( - name: ComponentName, - cluster: ClusterReference, - namespace: NamespaceNameAsString, - ) { + public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) { super(ComponentTypes.BlockNode, name, cluster, namespace); this.validate(); } From ecbf3edd33d71564a8a5b2441963651640664670 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 24 Apr 2025 11:19:48 +0300 Subject: [PATCH 67/70] lint-fix and issues after reverting changes Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 3 +- src/commands/deployment.ts | 1 + src/commands/mirror-node.ts | 3 +- src/commands/node/handlers.ts | 6 ++-- src/commands/relay.ts | 3 +- .../config/remote/components-data-wrapper.ts | 9 +++--- src/core/config/remote/listr-config-tasks.ts | 28 +++++++++++++++++++ .../config/remote/remote-config-validator.ts | 2 +- .../core/remote-config-validator.test.ts | 2 +- test/unit/core/config/remote/metadata.test.ts | 2 +- 10 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 src/core/config/remote/listr-config-tasks.ts diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 6a96fff81..b237412bb 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -24,7 +24,6 @@ import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; import {type BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; -import {ComponentFactory} from '../core/config/remote/components/component-factory.js'; import {ContainerReference} from '../integration/kube/resources/container/container-reference.js'; import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; @@ -167,10 +166,12 @@ export class BlockNodeCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodeDeployConfigClass = context_.config; + // TODO const newBlockNodeIndex: number = this.remoteConfigManager.components.getNewComponentIndex( ComponentTypes.BlockNode, ); + // TODO config.newBlockNodeComponent = ComponentFactory.createNewBlockNodeComponent( this.remoteConfigManager, config.clusterRef, diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 660dd2645..33c874b70 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -20,6 +20,7 @@ import {Cluster} from '../core/config/remote/cluster.js'; import {resolveNamespaceFromDeployment} from '../core/resolvers.js'; import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js'; import {DeploymentStates} from '../core/config/remote/enumerations/deployment-states.js'; +import {ConsensusNodeComponent} from '../core/config/remote/components/consensus-node-component.js'; interface DeploymentAddClusterConfig { quiet: boolean; diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts index bbdbe3526..bc71301a8 100644 --- a/src/commands/mirror-node.ts +++ b/src/commands/mirror-node.ts @@ -40,6 +40,7 @@ import {KeyManager} from '../core/key-manager.js'; import {prepareValuesFiles, showVersionBanner} from '../core/helpers.js'; import {type Pod} from '../integration/kube/resources/pod/pod.js'; import {PathEx} from '../business/utils/path-ex.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface MirrorNodeDeployConfigClass { cacheDir: string; @@ -922,7 +923,7 @@ export class MirrorNodeCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('mirrorNode', ComponentType.MirrorNode); + remoteConfig.components.remove('mirrorNode', ComponentTypes.MirrorNode); }); }, }; diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index a0a9a5901..bebbea375 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -906,9 +906,9 @@ export class NodeCommandHandlers extends CommandHandler { title: 'Remove node and proxies from remote config', task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('Consensus node name', ComponentType.ConsensusNode); - remoteConfig.components.remove('Envoy proxy name', ComponentType.EnvoyProxy); - remoteConfig.components.remove('HaProxy name', ComponentType.HaProxy); + remoteConfig.components.remove('Consensus node name', ComponentTypes.ConsensusNode); + remoteConfig.components.remove('Envoy proxy name', ComponentTypes.EnvoyProxy); + remoteConfig.components.remove('HaProxy name', ComponentTypes.HaProxy); }); }, }; diff --git a/src/commands/relay.ts b/src/commands/relay.ts index 3d779584a..ef6e56838 100644 --- a/src/commands/relay.ts +++ b/src/commands/relay.ts @@ -20,6 +20,7 @@ import {NamespaceName} from '../integration/kube/resources/namespace/namespace-n import {type ClusterReference, type DeploymentName} from '../core/config/remote/types.js'; import {type CommandDefinition, type Optional, type SoloListrTask} from '../types/index.js'; import {HEDERA_JSON_RPC_RELAY_VERSION} from '../../version.js'; +import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; interface RelayDestroyConfigClass { chartDirectory: string; @@ -562,7 +563,7 @@ export class RelayCommand extends BaseCommand { skip: (): boolean => !this.remoteConfigManager.isLoaded(), task: async (): Promise => { await this.remoteConfigManager.modify(async remoteConfig => { - remoteConfig.components.remove('relay', ComponentType.Relay); + remoteConfig.components.remove('relay', ComponentTypes.Relay); }); }, }; diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts index 0627bb7ae..759c3e2ad 100644 --- a/src/core/config/remote/components-data-wrapper.ts +++ b/src/core/config/remote/components-data-wrapper.ts @@ -9,14 +9,15 @@ import {MirrorNodeComponent} from './components/mirror-node-component.js'; import {EnvoyProxyComponent} from './components/envoy-proxy-component.js'; import {ConsensusNodeComponent} from './components/consensus-node-component.js'; import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-component.js'; -import {type ClusterReference, type ComponentName} from './types.js'; +import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from './types.js'; import {ComponentTypes} from './enumerations/component-types.js'; import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; import {type BaseComponentStruct} from './components/interfaces/base-component-struct.js'; import {type RelayComponentStruct} from './components/interfaces/relay-component-struct.js'; import {type ConsensusNodeComponentStruct} from './components/interfaces/consensus-node-component-struct.js'; -import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js'; import {type ComponentsDataStruct} from './interfaces/components-data-struct.js'; +import {Templates} from '../../templates.js'; +import {type NodeAliases} from '../../../types/aliases.js'; /** * Represent the components in the remote config and handles: @@ -24,7 +25,7 @@ import {type ComponentsDataStruct} from './interfaces/components-data-struct.js' * - Validation. * - Conversion FROM and TO plain object. */ -export class ComponentsDataWrapper implements ComponentsDataWrapperApi { +export class ComponentsDataWrapper { private constructor( public readonly relays: Record = {}, public readonly haProxies: Record = {}, @@ -60,7 +61,7 @@ export class ComponentsDataWrapper implements ComponentsDataWrapperApi { components[serviceName] = component; } - self.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); + self.applyCallbackToComponentGroup(component.type, addComponentCallback, serviceName); } /** Used to edit an existing component from their respective group. */ diff --git a/src/core/config/remote/listr-config-tasks.ts b/src/core/config/remote/listr-config-tasks.ts new file mode 100644 index 000000000..5c1ccb551 --- /dev/null +++ b/src/core/config/remote/listr-config-tasks.ts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 + +import {type SoloListrTask} from '../../../types/index.js'; +import {type AnyObject} from '../../../types/aliases.js'; +import {type RemoteConfigManager} from './remote-config-manager.js'; + +/** + * Static class that handles all tasks related to remote config used by other commands. + */ +export class ListrRemoteConfig { + /** + * Loads the remote config from the config class and performs component validation. + * + * @param remoteConfigManager + * @param argv - used to update the last executed command and command history + */ + public static loadRemoteConfig( + remoteConfigManager: RemoteConfigManager, + argv: {_: string[]} & AnyObject, + ): SoloListrTask { + return { + title: 'Load remote config', + task: async (): Promise => { + await remoteConfigManager.loadAndValidate(argv); + }, + }; + } +} diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts index 8e665040d..d8b0404b6 100644 --- a/src/core/config/remote/remote-config-validator.ts +++ b/src/core/config/remote/remote-config-validator.ts @@ -9,7 +9,7 @@ import {type NamespaceName} from '../../../integration/kube/resources/namespace/ import {type LocalConfig} from '../local/local-config.js'; import {type Pod} from '../../../integration/kube/resources/pod/pod.js'; import {type Context} from './types.js'; -import {type ConsensusNodeStates} from './enumerations/consensus-node-states.js'; +import {ConsensusNodeStates} from './enumerations/consensus-node-states.js'; /** * Static class is used to validate that components in the remote config diff --git a/test/e2e/integration/core/remote-config-validator.test.ts b/test/e2e/integration/core/remote-config-validator.test.ts index 40c575a86..ea6531cd7 100644 --- a/test/e2e/integration/core/remote-config-validator.test.ts +++ b/test/e2e/integration/core/remote-config-validator.test.ts @@ -8,7 +8,6 @@ import {type ConfigManager} from '../../../../src/core/config-manager.js'; import {Templates} from '../../../../src/core/templates.js'; import {Flags as flags} from '../../../../src/commands/flags.js'; import {RemoteConfigValidator} from '../../../../src/core/config/remote/remote-config-validator.js'; -import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations.js'; import {ComponentsDataWrapper} from '../../../../src/core/config/remote/components-data-wrapper.js'; import {SoloError} from '../../../../src/core/errors/solo-error.js'; import {RelayComponent} from '../../../../src/core/config/remote/components/relay-component.js'; @@ -30,6 +29,7 @@ import {LocalConfig} from '../../../../src/core/config/local/local-config.js'; import {getTestCacheDirectory} from '../../../test-utility.js'; import {Duration} from '../../../../src/core/time/duration.js'; import {LocalConfigDataWrapper} from '../../../../src/core/config/local/local-config-data-wrapper.js'; +import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations/consensus-node-states.js'; describe('RemoteConfigValidator', () => { const namespace = NamespaceName.of('remote-config-validator'); diff --git a/test/unit/core/config/remote/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts index 88599b548..0caf54f25 100644 --- a/test/unit/core/config/remote/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -10,7 +10,7 @@ import { type NamespaceNameAsString, type Version, } from '../../../../../src/core/config/remote/types.js'; -import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations.js'; +import {DeploymentStates} from '../../../../../src/core/config/remote/enumerations/deployment-states.js'; export function createMetadata() { const namespace: NamespaceNameAsString = 'namespace'; From 864569e9c4ed689dd6968501a14ac3e0bcc41c62 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Thu, 24 Apr 2025 11:39:33 +0300 Subject: [PATCH 68/70] cleanup after removing remote config changes Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 23 ++++++++--------------- src/commands/explorer.ts | 1 + 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index b237412bb..fa9213e52 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -22,8 +22,7 @@ import * as versions from '../../version.js'; import {type CommandFlag, type CommandFlags} from '../types/flag-types.js'; import {type Lock} from '../core/lock/lock.js'; import {type NamespaceName} from '../integration/kube/resources/namespace/namespace-name.js'; -import {type BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; -import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {BlockNodeComponent} from '../core/config/remote/components/block-node-component.js'; import {ContainerReference} from '../integration/kube/resources/container/container-reference.js'; import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; @@ -112,8 +111,8 @@ export class BlockNodeCommand extends BaseCommand { return valuesArgument; } - private getReleaseName(blockNodeIndex: number): string { - return constants.BLOCK_NODE_RELEASE_NAME + '-' + blockNodeIndex; + private getReleaseName(): string { + return constants.BLOCK_NODE_RELEASE_NAME; } private async add(argv: ArgvStruct): Promise { @@ -166,19 +165,13 @@ export class BlockNodeCommand extends BaseCommand { task: async (context_): Promise => { const config: BlockNodeDeployConfigClass = context_.config; - // TODO - const newBlockNodeIndex: number = this.remoteConfigManager.components.getNewComponentIndex( - ComponentTypes.BlockNode, - ); + config.releaseName = this.getReleaseName(); - // TODO - config.newBlockNodeComponent = ComponentFactory.createNewBlockNodeComponent( - this.remoteConfigManager, + config.newBlockNodeComponent = new BlockNodeComponent( + config.releaseName, config.clusterRef, - config.namespace, + config.namespace.name, ); - - config.releaseName = this.getReleaseName(newBlockNodeIndex); }, }, { @@ -283,7 +276,7 @@ export class BlockNodeCommand extends BaseCommand { await this.remoteConfigManager.modify(async remoteConfig => { const config: BlockNodeDeployConfigClass = context_.config; - remoteConfig.components.addNewComponent(config.newBlockNodeComponent); + remoteConfig.components.add(config.newBlockNodeComponent); }); }, }; diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts index 010bcd484..bd7371a31 100644 --- a/src/commands/explorer.ts +++ b/src/commands/explorer.ts @@ -31,6 +31,7 @@ import {INGRESS_CONTROLLER_VERSION} from '../../version.js'; import {type ClusterReference, type Context} from '../core/config/remote/types.js'; import * as helpers from '../core/helpers.js'; import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js'; +import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js'; interface ExplorerDeployConfigClass { cacheDir: string; From 5983d562cc91ecb14cdf71d0f05d6bdb376eb269 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Fri, 25 Apr 2025 11:34:46 +0300 Subject: [PATCH 69/70] added logic for detecting if java sve fix should be applied during 'solo block node add' command Signed-off-by: Zhan Milenkov --- src/commands/block-node.ts | 71 +++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index fa9213e52..5f31b88f9 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -3,7 +3,7 @@ import {Listr} from 'listr2'; import {SoloError} from '../core/errors/solo-error.js'; import * as helpers from '../core/helpers.js'; -import {showVersionBanner, sleep} from '../core/helpers.js'; +import {requiresJavaSveFix, showVersionBanner, sleep} from '../core/helpers.js'; import * as constants from '../core/constants.js'; import {BaseCommand} from './base.js'; import {Flags as flags} from './flags.js'; @@ -28,6 +28,9 @@ import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; import chalk from 'chalk'; import {CommandBuilder, CommandGroup, Subcommand} from '../core/command-path-builders/command-builder.js'; +import {Containers} from '../integration/kube/resources/container/containers.js'; +import {Container} from '../integration/kube/resources/container/container.js'; +import {Pod} from '../integration/kube/resources/pod/pod.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -92,22 +95,6 @@ export class BlockNodeCommand extends BaseCommand { }); } - // Fix for M4 chips (ARM64) - - const chipType: string[] = await helpers.getAppleSiliconChipset(this.logger); - let isAppleM4SeriesChip: boolean = false; - for (const chip of chipType) { - if (chip.includes('M4')) { - isAppleM4SeriesChip = true; - } - } - - if (isAppleM4SeriesChip) { - valuesArgument += helpers.populateHelmArguments({ - 'blockNode.config.JAVA_OPTS': '"-Xms8G -Xmx8G -XX:UseSVE=0"', - }); - } - return valuesArgument; } @@ -228,6 +215,56 @@ export class BlockNodeCommand extends BaseCommand { ); }, }, + { + title: 'Check software', + task: async (context_, task): Promise => { + const config: BlockNodeDeployConfigClass = context_.config; + + const labels: string[] = [`app.kubernetes.io/instance=${config.releaseName}`]; + + const blockNodePods: Pod[] = await this.k8Factory + .getK8(config.context) + .pods() + .list(config.namespace, labels); + + if (blockNodePods.length === 0) { + throw new SoloError('Failed to list block node pod'); + } + + const podReference: PodReference = blockNodePods[0].podReference; + + const containerReference: ContainerReference = ContainerReference.of( + podReference, + constants.BLOCK_NODE_CONTAINER_NAME, + ); + + const k8Containers: Containers = this.k8Factory.getK8(config.context).containers(); + + const container: Container = k8Containers.readByRef(containerReference); + + const shouldApplyJavaSveFix: boolean = await requiresJavaSveFix(container); + + if (!shouldApplyJavaSveFix) { + return; + } + + task.title += ` - ${chalk.grey('applying Java SVE fix')}`; + + const upgradeValuesArgument: string = helpers.populateHelmArguments({ + 'blockNode.config.JAVA_OPTS': '"-Xms8G -Xmx8G -XX:UseSVE=0"', + }); + + await this.chartManager.upgrade( + config.namespace, + config.releaseName, + constants.BLOCK_NODE_CHART, + constants.BLOCK_NODE_CHART_URL, + config.chartVersion, + upgradeValuesArgument, + config.context, + ); + }, + }, { title: 'Check block node pod is ready', task: async (context_): Promise => { From 0bfde3ac61d69a181d2e654cdd353dd5a7746ce3 Mon Sep 17 00:00:00 2001 From: Zhan Milenkov Date: Fri, 25 Apr 2025 11:43:46 +0300 Subject: [PATCH 70/70] apply fixes requested on pr Signed-off-by: Zhan Milenkov --- eslint.config.mjs | 2 +- src/commands/block-node.ts | 23 +++++------------------ src/commands/flags.ts | 11 ++++------- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index ca747b8fb..04a96ce52 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -137,7 +137,7 @@ export default [ 'warn', { allowExpressions: false, - allowTypedFunctionExpressions: true, + allowTypedFunctionExpressions: false, allowHigherOrderFunctions: false, }, ], diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts index 5f31b88f9..865810fb9 100644 --- a/src/commands/block-node.ts +++ b/src/commands/block-node.ts @@ -28,9 +28,9 @@ import {Duration} from '../core/time/duration.js'; import {type PodReference} from '../integration/kube/resources/pod/pod-reference.js'; import chalk from 'chalk'; import {CommandBuilder, CommandGroup, Subcommand} from '../core/command-path-builders/command-builder.js'; -import {Containers} from '../integration/kube/resources/container/containers.js'; -import {Container} from '../integration/kube/resources/container/container.js'; -import {Pod} from '../integration/kube/resources/pod/pod.js'; +import {type Containers} from '../integration/kube/resources/container/containers.js'; +import {type Container} from '../integration/kube/resources/container/container.js'; +import {type Pod} from '../integration/kube/resources/pod/pod.js'; interface BlockNodeDeployConfigClass { chartVersion: string; @@ -45,7 +45,6 @@ interface BlockNodeDeployConfigClass { namespace: NamespaceName; nodeAliases: NodeAliases; // from remote config context: string; - isChartInstalled: boolean; valuesArg: string; newBlockNodeComponent: BlockNodeComponent; releaseName: string; @@ -161,18 +160,6 @@ export class BlockNodeCommand extends BaseCommand { ); }, }, - { - title: 'Check chart is installed', - task: async (context_): Promise => { - const config: BlockNodeDeployConfigClass = context_.config; - - config.isChartInstalled = await this.chartManager.isChartInstalled( - config.namespace, - config.releaseName, - config.context, - ); - }, - }, { title: 'Prepare chart values', task: async (context_): Promise => { @@ -319,7 +306,7 @@ export class BlockNodeCommand extends BaseCommand { }; } - private displayHealthcheckData( + private displayHealthCheckData( task: SoloListrTaskWrapper, ): (attempt: number, maxAttempt: number, color?: 'yellow' | 'green' | 'red', additionalData?: string) => void { const baseTitle: string = task.title; @@ -345,7 +332,7 @@ export class BlockNodeCommand extends BaseCommand { maxAttempt: number, color?: 'yellow' | 'green' | 'red', additionalData?: string, - ) => void = this.displayHealthcheckData(task); + ) => void = this.displayHealthCheckData(task); const blockNodePodReference: PodReference = await this.k8Factory .getK8(config.context) diff --git a/src/commands/flags.ts b/src/commands/flags.ts index 14060e0fa..e387c70df 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -1625,20 +1625,17 @@ export class Flags { name: 'block-node-version', definition: { describe: 'Block nodes chart version', - defaultValue: version.MIRROR_NODE_VERSION, + defaultValue: version.BLOCK_NODE_VERSION, type: 'string', }, - prompt: async function promptMirrorNodeVersion( - task: SoloListrTaskWrapper, - input: boolean, - ): Promise { + prompt: async function (task: SoloListrTaskWrapper, input: boolean): Promise { return await Flags.promptToggle( task, input, - Flags.mirrorNodeVersion.definition.defaultValue as boolean, + Flags.blockNodeVersion.definition.defaultValue as boolean, 'Would you like to choose mirror node version? ', null, - Flags.mirrorNodeVersion.name, + Flags.blockNodeVersion.name, ); }, };