From 55494a2a3a7610958ee31a09526f9b52892bc689 Mon Sep 17 00:00:00 2001 From: Lenin Mehedy Date: Tue, 11 Feb 2025 13:50:55 +1100 Subject: [PATCH 1/2] fix: update RemoteConfig to handle multi-cluster networking and add support for CusterRef type Signed-off-by: Lenin Mehedy --- src/commands/deployment.ts | 6 +- src/commands/network.ts | 8 +- src/commands/node/handlers.ts | 10 +- src/core/config/local_config_data.ts | 6 +- src/core/config/remote/cluster.ts | 91 +++++++++++++++++++ .../remote/components/base_component.ts | 4 +- .../components/consensus_node_component.ts | 24 ++++- src/core/config/remote/listr_config_tasks.ts | 14 +-- src/core/config/remote/remote_config_data.ts | 5 +- .../remote/remote_config_data_wrapper.ts | 31 ++++--- .../config/remote/remote_config_manager.ts | 16 ++-- src/core/config/remote/types.ts | 14 ++- .../core/remote_config_validator.test.ts | 10 +- test/unit/core/config/remote/cluster.test.ts | 66 ++++++++++++++ .../remote/components/components.test.ts} | 72 +++++++++------ .../remote}/components_data_wrapper.test.ts | 38 ++++---- .../remote}/metadata.test.ts | 8 +- .../remote}/migration.test.ts | 6 +- .../remote_config_data_wrapper.test.ts | 12 +-- 19 files changed, 336 insertions(+), 105 deletions(-) create mode 100644 src/core/config/remote/cluster.ts create mode 100644 test/unit/core/config/remote/cluster.test.ts rename test/unit/core/{remote_config/components.ts => config/remote/components/components.test.ts} (74%) rename test/unit/core/{remote_config => config/remote}/components_data_wrapper.test.ts (76%) rename test/unit/core/{remote_config => config/remote}/metadata.test.ts (93%) rename test/unit/core/{remote_config => config/remote}/migration.test.ts (89%) rename test/unit/core/{remote_config => config/remote}/remote_config_data_wrapper.test.ts (89%) diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts index 98f3aab90..18545aafb 100644 --- a/src/commands/deployment.ts +++ b/src/commands/deployment.ts @@ -9,7 +9,7 @@ import * as constants from '../core/constants.js'; import chalk from 'chalk'; import {ListrRemoteConfig} from '../core/config/remote/listr_config_tasks.js'; import {ClusterCommandTasks} from './cluster/tasks.js'; -import {type DeploymentName, type NamespaceNameAsString, type Cluster} from '../core/config/remote/types.js'; +import {type DeploymentName, type NamespaceNameAsString, type ClusterRef} from '../core/config/remote/types.js'; import {type CommandFlag} from '../types/flag_types.js'; import {type CommandBuilder} from '../types/aliases.js'; import {type SoloListrTask} from '../types/index.js'; @@ -168,7 +168,7 @@ export class DeploymentCommand extends BaseCommand { const self = this; interface Config { - clusterName: Cluster; + clusterName: ClusterRef; } interface Context { @@ -186,7 +186,7 @@ export class DeploymentCommand extends BaseCommand { await self.configManager.executePrompt(task, [flags.clusterName]); ctx.config = { - clusterName: self.configManager.getFlag(flags.clusterName), + clusterName: self.configManager.getFlag(flags.clusterName), } as Config; self.logger.debug('Prepared config', {config: ctx.config, cachedConfig: self.configManager.config}); diff --git a/src/commands/network.ts b/src/commands/network.ts index d10f9884f..7dda6cf85 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -964,7 +964,13 @@ export class NetworkCommand extends BaseCommand { for (const nodeAlias of nodeAliases) { remoteConfig.components.add( nodeAlias, - new ConsensusNodeComponent(nodeAlias, cluster, namespace.name, ConsensusNodeStates.INITIALIZED), + new ConsensusNodeComponent( + nodeAlias, + cluster, + namespace.name, + ConsensusNodeStates.INITIALIZED, + Templates.nodeIdFromNodeAlias(nodeAlias), + ), ); remoteConfig.components.add( diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index d182fa587..a755bff11 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -40,8 +40,8 @@ import {type Listr, type ListrTask} from 'listr2'; import chalk from 'chalk'; import {type ComponentsDataWrapper} from '../../core/config/remote/components_data_wrapper.js'; import {type Optional} from '../../types/index.js'; -import {type NamespaceNameAsString} from '../../core/config/remote/types.js'; import {type NamespaceName} from '../../core/kube/resources/namespace/namespace_name.js'; +import {Templates} from '../../core/templates.js'; export class NodeCommandHandlers implements CommandHandlers { private readonly accountManager: AccountManager; @@ -879,7 +879,13 @@ export class NodeCommandHandlers implements CommandHandlers { for (const nodeAlias of nodeAliases) { remoteConfig.components.edit( nodeAlias, - new ConsensusNodeComponent(nodeAlias, cluster, namespace.name, state), + new ConsensusNodeComponent( + nodeAlias, + cluster, + namespace.name, + state, + Templates.nodeIdFromNodeAlias(nodeAlias), + ), ); } }); diff --git a/src/core/config/local_config_data.ts b/src/core/config/local_config_data.ts index d20b44d06..0df3c3191 100644 --- a/src/core/config/local_config_data.ts +++ b/src/core/config/local_config_data.ts @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import { - type Cluster, + type ClusterRef, type Context, type EmailAddress, type NamespaceNameAsString, @@ -11,11 +11,11 @@ import { export interface DeploymentStructure { // A list of clusters on which the deployment is deployed - clusters: Cluster[]; + clusters: ClusterRef[]; namespace: NamespaceNameAsString; } -export type ClusterContextMapping = Record; +export type ClusterContextMapping = Record; export type Deployments = Record; diff --git a/src/core/config/remote/cluster.ts b/src/core/config/remote/cluster.ts new file mode 100644 index 000000000..1649e61c6 --- /dev/null +++ b/src/core/config/remote/cluster.ts @@ -0,0 +1,91 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ +import {type ToObject} from '../../../types/index.js'; +import {type ClusterRef, type ICluster, type NamespaceNameAsString} from './types.js'; +import {SoloError} from '../../errors.js'; + +export class Cluster implements ICluster, ToObject { + private readonly _name: string; + private readonly _namespace: string; + private readonly _dnsBaseDomain: string = 'cluster.local'; // example: 'us-west-2.gcp.charlie.sphere'` + private readonly _dnsConsensusNodePattern: string = 'network-${nodeAlias}-svc.${namespace}.svc'; // example: '${nodeId}.consensus.prod'` + + public constructor( + name: string, + namespace: NamespaceNameAsString, + dnsBaseDomain?: string, + dnsConsensusNodePattern?: string, + ) { + if (!name) { + throw new SoloError('name is required'); + } + + if (typeof name !== 'string') { + throw new SoloError(`Invalid type for name: ${typeof name}`); + } + + if (!namespace) { + throw new SoloError('namespace is required'); + } + + if (typeof namespace !== 'string') { + throw new SoloError(`Invalid type for namespace: ${typeof namespace}`); + } + + this._name = name; + this._namespace = namespace; + + if (dnsBaseDomain) { + this._dnsBaseDomain = dnsBaseDomain; + } + + if (dnsConsensusNodePattern) { + this._dnsConsensusNodePattern = dnsConsensusNodePattern; + } + } + + public get name(): string { + return this._name; + } + + public get namespace(): string { + return this._namespace; + } + + public get dnsBaseDomain(): string { + return this._dnsBaseDomain; + } + + public get dnsConsensusNodePattern(): string { + return this._dnsConsensusNodePattern; + } + + public toObject(): ICluster { + return { + name: this.name, + namespace: this.namespace, + dnsBaseDomain: this.dnsBaseDomain, + dnsConsensusNodePattern: this.dnsConsensusNodePattern, + }; + } + + public static fromObject(cluster: ICluster) { + return new Cluster(cluster.name, cluster.namespace, cluster.dnsBaseDomain, cluster.dnsConsensusNodePattern); + } + + public static toClustersMapObject(clustersMap: Record) { + const entries = Object.entries(clustersMap).map(([ref, cluster]) => [ref, cluster.toObject()]); + return Object.fromEntries(entries); + } + + public static fromClustersMapObject(obj: any): Record { + const clusters: Record = {}; + + for (const [ref, cluster] of Object.entries(obj)) { + clusters[ref] = Cluster.fromObject(cluster as ICluster); + } + + return clusters; + } +} diff --git a/src/core/config/remote/components/base_component.ts b/src/core/config/remote/components/base_component.ts index ca45579b9..b9f4743d9 100644 --- a/src/core/config/remote/components/base_component.ts +++ b/src/core/config/remote/components/base_component.ts @@ -3,7 +3,7 @@ */ import {ComponentType} from '../enumerations.js'; import {SoloError} from '../../../errors.js'; -import {type Cluster, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; +import {type ClusterRef, type Component, type ComponentName, type NamespaceNameAsString} from '../types.js'; import {type ToObject, type Validate} from '../../../../types/index.js'; /** @@ -20,7 +20,7 @@ export abstract class BaseComponent implements Component, Validate, ToObject { return { - title: `Create remote config in cluster: ${chalk.cyan(cluster)}`, + title: `Create remote config in cluster: ${chalk.cyan(clusterRef)}`, task: async (): Promise => { - await command.getRemoteConfigManager().createAndValidate(cluster, context, namespace.name, argv); + await command.getRemoteConfigManager().createAndValidate(clusterRef, context, namespace.name, argv); }, }; } @@ -66,11 +66,11 @@ export class ListrRemoteConfig { task: async (ctx, task) => { const subTasks: SoloListrTask[] = []; - for (const cluster of command.localConfig.deployments[ctx.config.deployment].clusters) { - const context = command.localConfig.clusterContextMapping?.[cluster]; + for (const clusterRef of command.localConfig.deployments[ctx.config.deployment].clusters) { + const context = command.localConfig.clusterContextMapping?.[clusterRef]; if (!context) continue; - subTasks.push(ListrRemoteConfig.createRemoteConfig(command, cluster, context, ctx.config.namespace, argv)); + subTasks.push(ListrRemoteConfig.createRemoteConfig(command, clusterRef, context, ctx.config.namespace, argv)); } return task.newListr(subTasks, { diff --git a/src/core/config/remote/remote_config_data.ts b/src/core/config/remote/remote_config_data.ts index edb4f9705..0471551d4 100644 --- a/src/core/config/remote/remote_config_data.ts +++ b/src/core/config/remote/remote_config_data.ts @@ -4,11 +4,12 @@ import {type RemoteConfigMetadata} from './metadata.js'; import {type ComponentsDataWrapper} from './components_data_wrapper.js'; import {type CommonFlagsDataWrapper} from './common_flags_data_wrapper.js'; -import {type Cluster, type NamespaceNameAsString} from './types.js'; +import {type ClusterRef} from './types.js'; +import {type Cluster} from './cluster.js'; export interface RemoteConfigData { metadata: RemoteConfigMetadata; - clusters: Record; + clusters: Record; components: ComponentsDataWrapper; lastExecutedCommand: string; commandHistory: string[]; diff --git a/src/core/config/remote/remote_config_data_wrapper.ts b/src/core/config/remote/remote_config_data_wrapper.ts index bb52b1700..15fd3ce8b 100644 --- a/src/core/config/remote/remote_config_data_wrapper.ts +++ b/src/core/config/remote/remote_config_data_wrapper.ts @@ -7,16 +7,17 @@ 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 Cluster, type Version, type RemoteConfigDataStructure, type NamespaceNameAsString} from './types.js'; +import {type ClusterRef, type RemoteConfigDataStructure, type Version} from './types.js'; import type * as k8s from '@kubernetes/client-node'; 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'; export class RemoteConfigDataWrapper implements Validate, ToObject { private readonly _version: Version = '1.0.0'; private _metadata: RemoteConfigMetadata; - private _clusters: Record; + private _clusters: Record; private _components: ComponentsDataWrapper; private _commandHistory: string[]; private _lastExecutedCommand: string; @@ -24,7 +25,7 @@ export class RemoteConfigDataWrapper implements Validate, ToObject { + public get clusters(): Record { return this._clusters; } - public set clusters(clusters: Record) { + public set clusters(clusters: Record) { this._clusters = clusters; this.validate(); } @@ -132,15 +133,21 @@ export class RemoteConfigDataWrapper implements Validate, ToObject { - const clusterDataString = `cluster: { name: ${cluster}, namespace: ${namespace} }`; + Object.entries(this.clusters).forEach(([clusterRef, cluster]: [ClusterRef, Cluster]): void => { + if (!clusterRef || typeof clusterRef !== 'string') { + throw new SoloError(`Invalid remote config cluster-ref: ${clusterRef}`); + } + + if (!cluster) { + throw new SoloError(`No cluster info is found for cluster-ref: ${clusterRef}`); + } - if (!cluster || typeof cluster !== 'string') { - throw new SoloError(`Invalid remote config clusters name: ${clusterDataString}`); + if (!cluster.name || typeof cluster.name !== 'string') { + throw new SoloError(`Invalid remote config cluster name: ${cluster.name} for cluster-ref: ${clusterRef}`); } - if (!namespace || typeof namespace !== 'string') { - throw new SoloError(`Invalid remote config clusters namespace: ${clusterDataString}`); + if (!cluster.namespace || typeof cluster.namespace !== 'string') { + throw new SoloError(`Invalid remote config namespace: ${cluster.namespace} for cluster-ref: ${clusterRef}`); } }); } @@ -149,7 +156,7 @@ export class RemoteConfigDataWrapper implements Validate, ToObject { - const clusters: Record = {}; + const clusters: Record = {}; Object.entries(this.localConfig.deployments).forEach( - ([deploymentName, deploymentStructure]: [DeploymentName, DeploymentStructure]) => { - deploymentStructure.clusters.forEach(cluster => (clusters[cluster] = deploymentStructure.namespace)); + ([, deploymentStructure]: [DeploymentName, DeploymentStructure]) => { + const namespace = deploymentStructure.namespace.toString(); + deploymentStructure.clusters.forEach(cluster => (clusters[cluster] = new Cluster(cluster, namespace))); }, ); @@ -217,7 +219,7 @@ export class RemoteConfigManager { } public async createAndValidate( - cluster: Cluster, + clusterRef: ClusterRef, context: Context, namespace: NamespaceNameAsString, argv: AnyObject, diff --git a/src/core/config/remote/types.ts b/src/core/config/remote/types.ts index 815c5471f..62f3d8345 100644 --- a/src/core/config/remote/types.ts +++ b/src/core/config/remote/types.ts @@ -9,7 +9,7 @@ export type Version = string; /// TODO - see if we can use NamespaceName and use some annotations and overrides to covert to strings export type NamespaceNameAsString = string; export type DeploymentName = string; -export type Cluster = string; +export type ClusterRef = string; export type Context = string; export type ComponentName = string; @@ -21,7 +21,7 @@ export interface IMigration { export interface Component { name: ComponentName; - cluster: Cluster; + cluster: ClusterRef; namespace: NamespaceNameAsString; } @@ -30,9 +30,17 @@ export interface IRelayComponent extends Component { } export interface IConsensusNodeComponent extends Component { + nodeId: number; state: ConsensusNodeStates; } +export interface ICluster { + name: string; + namespace: string; + dnsBaseDomain: string; + dnsConsensusNodePattern: string; +} + export type ComponentsDataStructure = Record>; export type RemoteConfigCommonFlagsStruct = { @@ -48,7 +56,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/e2e/integration/core/remote_config_validator.test.ts b/test/e2e/integration/core/remote_config_validator.test.ts index 72ac7fbf6..569551ee5 100644 --- a/test/e2e/integration/core/remote_config_validator.test.ts +++ b/test/e2e/integration/core/remote_config_validator.test.ts @@ -63,7 +63,15 @@ describe('RemoteConfigValidator', () => { {[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)}, + { + [nodeAlias]: new ConsensusNodeComponent( + nodeAlias, + cluster, + namespace.name, + state, + Templates.nodeIdFromNodeAlias(nodeAlias), + ), + }, {[mirrorNodeExplorerName]: new MirrorNodeExplorerComponent(mirrorNodeExplorerName, cluster, namespace.name)}, ); diff --git a/test/unit/core/config/remote/cluster.test.ts b/test/unit/core/config/remote/cluster.test.ts new file mode 100644 index 000000000..3d7f14207 --- /dev/null +++ b/test/unit/core/config/remote/cluster.test.ts @@ -0,0 +1,66 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ +import {it} from 'mocha'; +import {expect} from 'chai'; +import {SoloError} from '../../../../../src/core/errors.js'; +import {Cluster} from '../../../../../src/core/config/remote/cluster.js'; +import {type ClusterRef} from '../../../../../src/core/config/remote/types.js'; + +describe('Cluster', () => { + it('should fail if name is not provided', () => { + expect(() => new Cluster(null, 'valid')).to.throw(SoloError, 'name is required'); + expect(() => new Cluster('', 'valid')).to.throw(SoloError, 'name is required'); + }); + + it('should fail if name is not a string', () => { + const name = 1; // @ts-ignore + expect(() => new Cluster(name, 'valid')).to.throw(SoloError, 'Invalid type for name: number'); + }); + + it('should fail if namespace is not provided', () => { + expect(() => new Cluster('valid', null)).to.throw(SoloError, 'namespace is required'); + expect(() => new Cluster('valid', '')).to.throw(SoloError, 'namespace is required'); + }); + + it('should fail if namespace is not a string', () => { + const namespace = 1; // @ts-ignore + expect(() => new Cluster('valid', namespace)).to.throw(SoloError, 'Invalid type for namespace: number'); + }); + + it('should convert to an object', () => { + const c = new Cluster('name', 'namespace', 'cluster.world', 'network.svc'); + const o = c.toObject(); + expect(o.name).to.equal('name'); + expect(o.namespace).to.equal('namespace'); + expect(o.dnsBaseDomain).to.equal('cluster.world'); + expect(o.dnsConsensusNodePattern).to.equal('network.svc'); + }); + + it('should convert clusters map to an object', () => { + const map1: Record = { + cluster1: new Cluster('name1', 'namespace1', 'cluster1.world', 'network1.svc'), + cluster2: new Cluster('name2', 'namespace2', 'cluster2.world', 'network2.svc'), + }; + + const o = Cluster.toClustersMapObject(map1); + expect(o.cluster1.name).to.equal('name1'); + expect(o.cluster1.namespace).to.equal('namespace1'); + 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.dnsBaseDomain).to.equal('cluster2.world'); + expect(o.cluster2.dnsConsensusNodePattern).to.equal('network2.svc'); + + 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.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.dnsBaseDomain).to.equal(map1.cluster2.dnsBaseDomain); + expect(map2.cluster2.dnsConsensusNodePattern).to.equal(map1.cluster2.dnsConsensusNodePattern); + }); +}); diff --git a/test/unit/core/remote_config/components.ts b/test/unit/core/config/remote/components/components.test.ts similarity index 74% rename from test/unit/core/remote_config/components.ts rename to test/unit/core/config/remote/components/components.test.ts index 277e12e7a..3e57af37f 100644 --- a/test/unit/core/remote_config/components.ts +++ b/test/unit/core/config/remote/components/components.test.ts @@ -4,16 +4,17 @@ import {expect} from 'chai'; 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 {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 {SoloError} from '../../../../src/core/errors.js'; -import {ConsensusNodeStates} from '../../../../src/core/config/remote/enumerations.js'; -import {type NodeAliases} from '../../../../src/types/aliases.js'; +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 {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 {SoloError} from '../../../../../../src/core/errors.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'; function testBaseComponentData(classComponent: any) { const validNamespace = 'valid'; @@ -164,15 +165,15 @@ describe('RelayComponent', () => { describe('ConsensusNodeComponent', () => { it('should fail if name is not provided', () => { const name = ''; - expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED)).to.throw( + expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( SoloError, `Invalid name: ${name}`, ); }); - it('should fail if name is string', () => { + it('should fail if name is not a string', () => { const name = 1; // @ts-ignore - expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED)).to.throw( + expect(() => new ConsensusNodeComponent(name, 'valid', 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( SoloError, `Invalid name: ${name}`, ); @@ -180,15 +181,15 @@ describe('ConsensusNodeComponent', () => { it('should fail if cluster is not provided', () => { const cluster = ''; - expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED)).to.throw( + expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( SoloError, `Invalid cluster: ${cluster}`, ); }); - it('should fail if cluster is string', () => { + it('should fail if cluster is not a string', () => { const cluster = 1; // @ts-ignore - expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED)).to.throw( + expect(() => new ConsensusNodeComponent('valid', cluster, 'valid', ConsensusNodeStates.STARTED, 0)).to.throw( SoloError, `Invalid cluster: ${cluster}`, ); @@ -196,15 +197,15 @@ describe('ConsensusNodeComponent', () => { it('should fail if namespace is not provided', () => { const namespace = null; - expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED)).to.throw( + expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED, 0)).to.throw( SoloError, `Invalid namespace: ${namespace}`, ); }); - it('should fail if namespace is string', () => { + it('should fail if namespace is not a string', () => { const namespace = 1; // @ts-ignore - expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED)).to.throw( + expect(() => new ConsensusNodeComponent('valid', 'valid', namespace, ConsensusNodeStates.STARTED, 0)).to.throw( SoloError, `Invalid namespace: ${namespace}`, ); @@ -212,30 +213,49 @@ describe('ConsensusNodeComponent', () => { it('should fail if state is not valid', () => { const state = 'invalid' as ConsensusNodeStates.STARTED; - expect(() => new ConsensusNodeComponent('valid', 'valid', 'valid', state)).to.throw( + 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); + new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED, 0); }); it('should be an instance of BaseComponent', () => { - const component = new ConsensusNodeComponent('valid', 'valid', 'valid', ConsensusNodeStates.STARTED); + 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 {name, cluster, namespace, state} = { - name: 'name', + const nodeAlias = 'node1'; + const nodeInfo = { + name: nodeAlias, cluster: 'cluster', namespace: 'namespace', state: ConsensusNodeStates.STARTED, + nodeId: Templates.nodeIdFromNodeAlias(nodeAlias), }; - const component = new ConsensusNodeComponent(name, cluster, namespace, state); - expect(component.toObject()).to.deep.equal({name, cluster, namespace, state}); + 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/remote_config/components_data_wrapper.test.ts b/test/unit/core/config/remote/components_data_wrapper.test.ts similarity index 76% rename from test/unit/core/remote_config/components_data_wrapper.test.ts rename to test/unit/core/config/remote/components_data_wrapper.test.ts index 8c1a3a0f8..473891f4e 100644 --- a/test/unit/core/remote_config/components_data_wrapper.test.ts +++ b/test/unit/core/config/remote/components_data_wrapper.test.ts @@ -4,16 +4,16 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; -import {ComponentsDataWrapper} from '../../../../src/core/config/remote/components_data_wrapper.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 {EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy_proxy_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 {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.js'; -import {type NodeAliases} from '../../../../src/types/aliases.js'; +import {ComponentsDataWrapper} from '../../../../../src/core/config/remote/components_data_wrapper.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 {EnvoyProxyComponent} from '../../../../../src/core/config/remote/components/envoy_proxy_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 {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.js'; +import {type NodeAliases} from '../../../../../src/types/aliases.js'; export function createComponentsDataWrapper() { const serviceName = 'serviceName'; @@ -28,7 +28,7 @@ export function createComponentsDataWrapper() { const haProxies = {[serviceName]: new HaProxyComponent(name, cluster, namespace)}; const mirrorNodes = {[serviceName]: new MirrorNodeComponent(name, cluster, namespace)}; const envoyProxies = {[serviceName]: new EnvoyProxyComponent(name, cluster, namespace)}; - const consensusNodes = {[serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state)}; + const consensusNodes = {[serviceName]: new ConsensusNodeComponent(name, cluster, namespace, state, 1)}; const mirrorNodeExplorers = {[serviceName]: new MirrorNodeExplorerComponent(name, cluster, namespace)}; // @ts-ignore @@ -41,14 +41,14 @@ export function createComponentsDataWrapper() { mirrorNodeExplorers, ); /* - ? 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' } }, - */ + ? 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}, diff --git a/test/unit/core/remote_config/metadata.test.ts b/test/unit/core/config/remote/metadata.test.ts similarity index 93% rename from test/unit/core/remote_config/metadata.test.ts rename to test/unit/core/config/remote/metadata.test.ts index 2206e68cb..25d5ed975 100644 --- a/test/unit/core/remote_config/metadata.test.ts +++ b/test/unit/core/config/remote/metadata.test.ts @@ -3,10 +3,10 @@ */ import {expect} from 'chai'; import {describe, it} from 'mocha'; -import {Migration} from '../../../../src/core/config/remote/migration.js'; -import {SoloError} from '../../../../src/core/errors.js'; -import {RemoteConfigMetadata} from '../../../../src/core/config/remote/metadata.js'; -import {type EmailAddress, type NamespaceNameAsString} from '../../../../src/core/config/remote/types.js'; +import {Migration} from '../../../../../src/core/config/remote/migration.js'; +import {SoloError} from '../../../../../src/core/errors.js'; +import {RemoteConfigMetadata} from '../../../../../src/core/config/remote/metadata.js'; +import {type EmailAddress, type NamespaceNameAsString} from '../../../../../src/core/config/remote/types.js'; export function createMetadata() { const name: NamespaceNameAsString = 'namespace'; diff --git a/test/unit/core/remote_config/migration.test.ts b/test/unit/core/config/remote/migration.test.ts similarity index 89% rename from test/unit/core/remote_config/migration.test.ts rename to test/unit/core/config/remote/migration.test.ts index 812017a30..6b7561693 100644 --- a/test/unit/core/remote_config/migration.test.ts +++ b/test/unit/core/config/remote/migration.test.ts @@ -3,9 +3,9 @@ */ 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.js'; +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.js'; function createMigration() { const migratedAt = new Date(); diff --git a/test/unit/core/remote_config/remote_config_data_wrapper.test.ts b/test/unit/core/config/remote/remote_config_data_wrapper.test.ts similarity index 89% rename from test/unit/core/remote_config/remote_config_data_wrapper.test.ts rename to test/unit/core/config/remote/remote_config_data_wrapper.test.ts index 7a1a3ba26..e102cae3f 100644 --- a/test/unit/core/remote_config/remote_config_data_wrapper.test.ts +++ b/test/unit/core/config/remote/remote_config_data_wrapper.test.ts @@ -5,13 +5,13 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; import * as yaml from 'yaml'; -import {RemoteConfigDataWrapper} from '../../../../src/core/config/remote/remote_config_data_wrapper.js'; +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.js'; -import * as constants from '../../../../src/core/constants.js'; -import {CommonFlagsDataWrapper} from '../../../../src/core/config/remote/common_flags_data_wrapper.js'; -import {NamespaceName} from '../../../../src/core/kube/resources/namespace/namespace_name.js'; +import {SoloError} from '../../../../../src/core/errors.js'; +import * as constants from '../../../../../src/core/constants.js'; +import {CommonFlagsDataWrapper} from '../../../../../src/core/config/remote/common_flags_data_wrapper.js'; +import {Cluster} from '../../../../../src/core/config/remote/cluster.js'; const configManagerMock = { update: (...args: any) => true, @@ -113,6 +113,6 @@ describe('RemoteConfigDataWrapper', async () => { expect(() => (dataWrapper.clusters = {null: null})).to.throw(SoloError); expect(() => (dataWrapper.clusters = {namespace: null})).to.throw(SoloError); - expect(() => (dataWrapper.clusters = {null: 'namespace'})).to.throw(SoloError); + expect(() => (dataWrapper.clusters = {null: new Cluster('cluster', 'namespace')})).to.throw(SoloError); }); }); From aa67689d9ca63f27523a733e726838fbeb0b9bdd Mon Sep 17 00:00:00 2001 From: Lenin Mehedy Date: Tue, 11 Feb 2025 19:19:00 +1100 Subject: [PATCH 2/2] fix: fix typo Signed-off-by: Lenin Mehedy --- src/core/config/remote/enumerations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/config/remote/enumerations.ts b/src/core/config/remote/enumerations.ts index 5e091adf7..23d247c68 100644 --- a/src/core/config/remote/enumerations.ts +++ b/src/core/config/remote/enumerations.ts @@ -12,7 +12,7 @@ export enum ComponentType { EnvoyProxy = 'envoyProxies', MirrorNode = 'mirrorNodes', MirrorNodeExplorer = 'mirrorNodeExplorers', - Relay = 'replays', + Relay = 'relays', } /**