diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 3c06066bd..bc1dc34bd 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -16,11 +16,10 @@ Maintainers are assigned the following scopes in this repository:
| Name | GitHub ID | Scope | LFID | Discord ID | Email | Company Affiliation |
|----- | ------------- | ---------- | ---- | ------------------- | ----- | ------------------- |
-| | jeromy-cannon | Maintainer | | jeromy_at_hashgraph | | Hashgraph |
+| | jeromy-cannon | Maintainer | | jeromy\_at\_hashgraph | | Hashgraph |
| | leninmehedy | Maintainer | | | | Hashgraph |
| | nathanklick | Maintainer | | nathan.hashgraph | | Hashgraph |
-
## Emeritus Maintainers
| Name | GitHub ID | Scope | LFID | Discord ID | Email | Company Affiliation |
@@ -31,24 +30,24 @@ Maintainers are assigned the following scopes in this repository:
Maintainers are expected to perform the following duties for this repository. The duties are listed in more or less priority order:
-- Review, respond, and act on any security vulnerabilities reported against the repository.
-- Review, provide feedback on, and merge or reject GitHub Pull Requests from
+* Review, respond, and act on any security vulnerabilities reported against the repository.
+* Review, provide feedback on, and merge or reject GitHub Pull Requests from
Contributors.
-- Review, triage, comment on, and close GitHub Issues
+* Review, triage, comment on, and close GitHub Issues
submitted by Contributors.
-- When appropriate, lead/facilitate architectural discussions in the community.
-- When appropriate, lead/facilitate the creation of a product roadmap.
-- Create, clarify, and label issues to be worked on by Contributors.
-- Ensure that there is a well defined (and ideally automated) product test and
+* When appropriate, lead/facilitate architectural discussions in the community.
+* When appropriate, lead/facilitate the creation of a product roadmap.
+* Create, clarify, and label issues to be worked on by Contributors.
+* Ensure that there is a well defined (and ideally automated) product test and
release pipeline, including the publication of release artifacts.
-- When appropriate, execute the product release process.
-- Maintain the repository CONTRIBUTING.md file and getting started documents to
+* When appropriate, execute the product release process.
+* Maintain the repository CONTRIBUTING.md file and getting started documents to
give guidance and encouragement to those wanting to contribute to the product, and those wanting to become maintainers.
-- Contribute to the product via GitHub Pull Requests.
-- Monitor requests from the LF Decentralized Trust Technical Advisory Council about the
-contents and management of LFDT repositories, such as branch handling,
-required files in repositories and so on.
-- Contribute to the LFDT Project's Quarterly Report.
+* Contribute to the product via GitHub Pull Requests.
+* Monitor requests from the LF Decentralized Trust Technical Advisory Council about the
+ contents and management of LFDT repositories, such as branch handling,
+ required files in repositories and so on.
+* Contribute to the LFDT Project's Quarterly Report.
## Becoming a Maintainer
@@ -56,21 +55,21 @@ This community welcomes contributions. Interested contributors are encouraged to
progress to become maintainers. To become a maintainer the following steps
occur, roughly in order.
-- The proposed maintainer establishes their reputation in the community,
+* The proposed maintainer establishes their reputation in the community,
including authoring five (5) significant merged pull requests, and expresses
an interest in becoming a maintainer for the repository.
-- A PR is created to update this file to add the proposed maintainer to the list of active maintainers.
-- The PR is authored by an existing maintainer or has a comment on the PR from an existing maintainer supporting the proposal.
-- The PR is authored by the proposed maintainer or has a comment on the PR from the proposed maintainer confirming their interest in being a maintainer.
- - The PR or comment from the proposed maintainer must include their
+* A PR is created to update this file to add the proposed maintainer to the list of active maintainers.
+* The PR is authored by an existing maintainer or has a comment on the PR from an existing maintainer supporting the proposal.
+* The PR is authored by the proposed maintainer or has a comment on the PR from the proposed maintainer confirming their interest in being a maintainer.
+ * The PR or comment from the proposed maintainer must include their
willingness to be a long-term (more than 6 month) maintainer.
-- Once the PR and necessary comments have been received, an approval timeframe begins.
-- The PR **MUST** be communicated on all appropriate communication channels, including relevant community calls, chat channels and mailing lists. Comments of support from the community are welcome.
-- The PR is merged and the proposed maintainer becomes a maintainer if either:
- - Two weeks have passed since at least three (3) Maintainer PR approvals have been recorded, OR
- - An absolute majority of maintainers have approved the PR.
-- If the PR does not get the requisite PR approvals, it may be closed.
-- Once the add maintainer PR has been merged, any necessary updates to the GitHub Teams are made.
+* Once the PR and necessary comments have been received, an approval timeframe begins.
+* The PR **MUST** be communicated on all appropriate communication channels, including relevant community calls, chat channels and mailing lists. Comments of support from the community are welcome.
+* The PR is merged and the proposed maintainer becomes a maintainer if either:
+ * Two weeks have passed since at least three (3) Maintainer PR approvals have been recorded, OR
+ * An absolute majority of maintainers have approved the PR.
+* If the PR does not get the requisite PR approvals, it may be closed.
+* Once the add maintainer PR has been merged, any necessary updates to the GitHub Teams are made.
## Removing Maintainers
@@ -78,28 +77,28 @@ Being a maintainer is not a status symbol or a title to be carried
indefinitely. It will occasionally be necessary and appropriate to move a
maintainer to emeritus status. This can occur in the following situations:
-- Resignation of a maintainer.
-- Violation of the Code of Conduct warranting removal.
-- Inactivity.
- - A general measure of inactivity will be no commits or code review comments
+* Resignation of a maintainer.
+* Violation of the Code of Conduct warranting removal.
+* Inactivity.
+ * A general measure of inactivity will be no commits or code review comments
for one reporting quarter. This will not be strictly enforced if
the maintainer expresses a reasonable intent to continue contributing.
- - Reasonable exceptions to inactivity will be granted for known long term
+ * Reasonable exceptions to inactivity will be granted for known long term
leave such as parental leave and medical leave.
-- Other circumstances at the discretion of the other Maintainers.
+* Other circumstances at the discretion of the other Maintainers.
The process to move a maintainer from active to emeritus status is comparable to the process for adding a maintainer, outlined above. In the case of voluntary
resignation, the Pull Request can be merged following a maintainer PR approval. If the removal is for any other reason, the following steps **SHOULD** be followed:
-- A PR is created to update this file to move the maintainer to the list of emeritus maintainers.
-- The PR is authored by, or has a comment supporting the proposal from, an existing maintainer or a member of the project's Technical Steering Commitee (TSC).
-- Once the PR and necessary comments have been received, the approval timeframe begins.
-- The PR **MAY** be communicated on appropriate communication channels, including relevant community calls, chat channels and mailing lists.
-- The PR is merged and the maintainer transitions to maintainer emeritus if:
- - The PR is approved by the maintainer to be transitioned, OR
- - Two weeks have passed since at least three (3) Maintainer PR approvals have been recorded, OR
- - An absolute majority of maintainers have approved the PR.
-- If the PR does not get the requisite PR approvals, it may be closed.
+* A PR is created to update this file to move the maintainer to the list of emeritus maintainers.
+* The PR is authored by, or has a comment supporting the proposal from, an existing maintainer or a member of the project's Technical Steering Commitee (TSC).
+* Once the PR and necessary comments have been received, the approval timeframe begins.
+* The PR **MAY** be communicated on appropriate communication channels, including relevant community calls, chat channels and mailing lists.
+* The PR is merged and the maintainer transitions to maintainer emeritus if:
+ * The PR is approved by the maintainer to be transitioned, OR
+ * Two weeks have passed since at least three (3) Maintainer PR approvals have been recorded, OR
+ * An absolute majority of maintainers have approved the PR.
+* If the PR does not get the requisite PR approvals, it may be closed.
Returning to active status from emeritus status uses the same steps as adding a
new maintainer. Note that the emeritus maintainer already has the 5 required
diff --git a/docs/site/content/en/_index.md b/docs/site/content/en/_index.md
index 4a79935a7..13d37bb78 100644
--- a/docs/site/content/en/_index.md
+++ b/docs/site/content/en/_index.md
@@ -2,7 +2,7 @@
title: Solo docs
---
-{{< blocks/cover title="Welcome to Solo" image_anchor="top" height="full" >}}
+{{< blocks/cover title="Welcome to Solo" image\_anchor="top" height="full" >}}
Learn More
{{< blocks/link-down color="info" >}}
{{< /blocks/cover >}}
diff --git a/docs/site/content/en/about/index.md b/docs/site/content/en/about/index.md
index 9b06fddbe..0675bac69 100644
--- a/docs/site/content/en/about/index.md
+++ b/docs/site/content/en/about/index.md
@@ -4,7 +4,7 @@ linkTitle: About
menu: {main: {weight: 10}}
---
-{{% blocks/cover title="About Solo" image_anchor="bottom" height="auto" %}}
+{{% blocks/cover title="About Solo" image\_anchor="bottom" height="auto" %}}
### A Hiero Ledger tool for deploying and managing Hiero Ledger networks.
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/business/errors/read-remote-config-before-load-error.ts b/src/business/errors/read-remote-config-before-load-error.ts
new file mode 100644
index 000000000..2b876779a
--- /dev/null
+++ b/src/business/errors/read-remote-config-before-load-error.ts
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {SoloError} from '../../core/errors/solo-error.js';
+
+export class ReadRemoteConfigBeforeLoadError extends SoloError {
+ public constructor(message: string, cause?: Error, meta?: object) {
+ super(message, cause, meta);
+ }
+}
diff --git a/src/business/errors/write-remote-config-before-load-error.ts b/src/business/errors/write-remote-config-before-load-error.ts
new file mode 100644
index 000000000..165073cd0
--- /dev/null
+++ b/src/business/errors/write-remote-config-before-load-error.ts
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {SoloError} from '../../core/errors/solo-error.js';
+
+export class WriteRemoteConfigBeforeLoadError extends SoloError {
+ public constructor(message: string, cause?: Error, meta?: object) {
+ super(message, cause, meta);
+ }
+}
diff --git a/src/business/runtime-state/api/remote-config-runtime-state-api.ts b/src/business/runtime-state/api/remote-config-runtime-state-api.ts
new file mode 100644
index 000000000..13fd7aa1b
--- /dev/null
+++ b/src/business/runtime-state/api/remote-config-runtime-state-api.ts
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {
+ type ClusterReference,
+ type ClusterReferences,
+ type Context,
+ type DeploymentName,
+} from '../../../types/index.js';
+import {type NamespaceName} from '../../../types/namespace/namespace-name.js';
+import {type ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js';
+import {type AnyObject, type ArgvStruct, type NodeAliases} from '../../../types/aliases.js';
+import {type LedgerPhase} from '../../../data/schema/model/remote/ledger-phase.js';
+import {type ConsensusNode} from '../../../core/model/consensus-node.js';
+import {type ComponentFactoryApi} from '../../../core/config/remote/api/component-factory-api.js';
+import {type RemoteConfig} from '../config/remote/remote-config.js';
+
+export interface RemoteConfigRuntimeStateApi {
+ currentCluster: ClusterReference;
+ configuration?: RemoteConfig;
+
+ getClusterRefs(): ClusterReferences;
+ getContexts(): Context[];
+ getConsensusNodes(): ConsensusNode[];
+ deleteComponents(): Promise;
+
+ isLoaded(): boolean;
+ load(namespace?: NamespaceName, context?: Context): Promise;
+ populateRemoteConfig(configMap: ConfigMap): Promise;
+ write(): Promise;
+ persist(): Promise;
+
+ create(
+ argv: ArgvStruct,
+ ledgerPhase: LedgerPhase,
+ nodeAliases: NodeAliases,
+ namespace: NamespaceName,
+ deployment: DeploymentName,
+ clusterReference: ClusterReference,
+ context: Context,
+ dnsBaseDomain: string,
+ dnsConsensusNodePattern: string,
+ ): Promise;
+
+ createFromExisting(
+ namespace: NamespaceName,
+ clusterReference: ClusterReference,
+ deployment: DeploymentName,
+ componentFactory: ComponentFactoryApi,
+ dnsBaseDomain: string,
+ dnsConsensusNodePattern: string,
+ existingClusterContext: Context,
+ argv: ArgvStruct,
+ nodeAliases: NodeAliases,
+ ): Promise;
+
+ addCommandToHistory(command: string): void;
+ createConfigMap(namespace: NamespaceName, context: Context): Promise;
+ getConfigMap(namespace?: NamespaceName, context?: Context): Promise;
+ loadAndValidate(
+ argv: {_: string[]} & AnyObject,
+ validate?: boolean,
+ skipConsensusNodesValidation?: boolean,
+ ): Promise;
+}
diff --git a/src/business/runtime-state/config/local/local-config-runtime-state.ts b/src/business/runtime-state/config/local/local-config-runtime-state.ts
index 409cc5cf6..9f661d3b8 100644
--- a/src/business/runtime-state/config/local/local-config-runtime-state.ts
+++ b/src/business/runtime-state/config/local/local-config-runtime-state.ts
@@ -51,7 +51,8 @@ export class LocalConfigRuntimeState {
// Loads the source data and writes it back in case of migrations.
public async load(): Promise {
if (!this.configFileExists()) {
- return await this.persist();
+ await this.persist();
+ return;
}
try {
diff --git a/src/business/runtime-state/config/remote/remote-config-runtime-state.ts b/src/business/runtime-state/config/remote/remote-config-runtime-state.ts
new file mode 100644
index 000000000..a1086e585
--- /dev/null
+++ b/src/business/runtime-state/config/remote/remote-config-runtime-state.ts
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {inject, injectable} from 'tsyringe-neo';
+import {type ObjectMapper} from '../../../../data/mapper/api/object-mapper.js';
+import {ClassToObjectMapper} from '../../../../data/mapper/impl/class-to-object-mapper.js';
+import {ConfigKeyFormatter} from '../../../../data/key/config-key-formatter.js';
+import {ReadRemoteConfigBeforeLoadError} from '../../../errors/read-remote-config-before-load-error.js';
+import {WriteRemoteConfigBeforeLoadError} from '../../../errors/write-remote-config-before-load-error.js';
+import {RemoteConfigSource} from '../../../../data/configuration/impl/remote-config-source.js';
+import {YamlConfigMapStorageBackend} from '../../../../data/backend/impl/yaml-config-map-storage-backend.js';
+import {type ConfigMap} from '../../../../integration/kube/resources/config-map/config-map.js';
+import {LedgerPhase} from '../../../../data/schema/model/remote/ledger-phase.js';
+import {SemVer} from 'semver';
+import {ComponentsDataWrapperApi} from '../../../../core/config/remote/api/components-data-wrapper-api.js';
+import {InjectTokens} from '../../../../core/dependency-injection/inject-tokens.js';
+import {type K8Factory} from '../../../../integration/kube/k8-factory.js';
+import {type SoloLogger} from '../../../../core/logging/solo-logger.js';
+import {type ConfigManager} from '../../../../core/config-manager.js';
+import {patchInject} from '../../../../core/dependency-injection/container-helper.js';
+import {
+ type ClusterReference,
+ type ClusterReferences,
+ type Context,
+ type DeploymentName,
+ type NamespaceNameAsString,
+} from '../../../../types/index.js';
+import {type AnyObject, type ArgvStruct, type NodeAlias, type NodeAliases} from '../../../../types/aliases.js';
+import {NamespaceName} from '../../../../types/namespace/namespace-name.js';
+import {ComponentStateMetadataSchema} from '../../../../data/schema/model/remote/state/component-state-metadata-schema.js';
+import {Templates} from '../../../../core/templates.js';
+import {DeploymentPhase} from '../../../../data/schema/model/remote/deployment-phase.js';
+import {getSoloVersion} from '../../../../../version.js';
+import * as constants from '../../../../core/constants.js';
+import {SoloError} from '../../../../core/errors/solo-error.js';
+import {Flags as flags} from '../../../../commands/flags.js';
+import {promptTheUserForDeployment} from '../../../../core/resolvers.js';
+import {ConsensusNode} from '../../../../core/model/consensus-node.js';
+import {RemoteConfigRuntimeStateApi} from '../../api/remote-config-runtime-state-api.js';
+import {type RemoteConfigValidatorApi} from '../../../../core/config/remote/api/remote-config-validator-api.js';
+import {ComponentFactoryApi} from '../../../../core/config/remote/api/component-factory-api.js';
+import {ComponentTypes} from '../../../../core/config/remote/enumerations/component-types.js';
+import {LocalConfigRuntimeState} from '../local/local-config-runtime-state.js';
+import {RemoteConfigMetadataSchema} from '../../../../data/schema/model/remote/remote-config-metadata-schema.js';
+import {ApplicationVersionsSchema} from '../../../../data/schema/model/common/application-versions-schema.js';
+import {ClusterSchema} from '../../../../data/schema/model/common/cluster-schema.js';
+import {DeploymentStateSchema} from '../../../../data/schema/model/remote/deployment-state-schema.js';
+import {DeploymentHistorySchema} from '../../../../data/schema/model/remote/deployment-history-schema.js';
+import {RemoteConfigSchemaDefinition} from '../../../../data/schema/migration/impl/remote/remote-config-schema-definition.js';
+import {RemoteConfigSchema} from '../../../../data/schema/model/remote/remote-config-schema.js';
+import {ConsensusNodeStateSchema} from '../../../../data/schema/model/remote/state/consensus-node-state-schema.js';
+import {UserIdentitySchema} from '../../../../data/schema/model/common/user-identity-schema.js';
+import {Deployment} from '../local/deployment.js';
+import {RemoteConfig} from './remote-config.js';
+
+enum RuntimeStatePhase {
+ Loaded = 'loaded',
+ NotLoaded = 'not_loaded',
+}
+
+@injectable()
+export class RemoteConfigRuntimeState implements RemoteConfigRuntimeStateApi {
+ private phase: RuntimeStatePhase = RuntimeStatePhase.NotLoaded;
+
+ public clusterReferences: Map = new Map();
+ private namespace: NamespaceName;
+
+ private source?: RemoteConfigSource;
+ private backend?: YamlConfigMapStorageBackend;
+ private objectMapper?: ObjectMapper;
+
+ private _remoteConfig?: RemoteConfig;
+
+ public constructor(
+ @inject(InjectTokens.K8Factory) private readonly k8Factory?: K8Factory,
+ @inject(InjectTokens.SoloLogger) private readonly logger?: SoloLogger,
+ @inject(InjectTokens.LocalConfigRuntimeState) private readonly localConfig?: LocalConfigRuntimeState,
+ @inject(InjectTokens.ConfigManager) private readonly configManager?: ConfigManager,
+ @inject(InjectTokens.RemoteConfigValidator) private readonly remoteConfigValidator?: RemoteConfigValidatorApi,
+ ) {
+ this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
+ this.logger = patchInject(logger, InjectTokens.SoloLogger, this.constructor.name);
+ this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
+ this.configManager = patchInject(configManager, InjectTokens.ConfigManager, this.constructor.name);
+ }
+
+ public get configuration(): RemoteConfig {
+ this.failIfNotLoaded();
+ return this._remoteConfig;
+ }
+
+ public get components(): Readonly {
+ this.failIfNotLoaded();
+ return this._remoteConfig.components;
+ }
+
+ public get currentCluster(): ClusterReference {
+ return this.k8Factory.default().clusters().readCurrent();
+ }
+
+ public async load(namespace?: NamespaceName, context?: Context): Promise {
+ if (this.isLoaded()) {
+ return;
+ }
+
+ const configMap: ConfigMap = await this.getConfigMap(namespace, context);
+ await this.populateRemoteConfig(configMap);
+ }
+
+ public async populateRemoteConfig(configMap: ConfigMap): Promise {
+ this.backend = new YamlConfigMapStorageBackend(configMap);
+ this.objectMapper = new ClassToObjectMapper(ConfigKeyFormatter.instance());
+ this.source = new RemoteConfigSource(
+ new RemoteConfigSchemaDefinition(this.objectMapper),
+ this.objectMapper,
+ this.backend,
+ );
+ await this.source.load();
+ this._remoteConfig = new RemoteConfig(this.source.modelData);
+ this.phase = RuntimeStatePhase.Loaded;
+ }
+
+ public async write(): Promise {
+ await this.source.persist();
+ const remoteConfigDataBytes: Buffer = await this.backend.readBytes(constants.SOLO_REMOTE_CONFIGMAP_DATA_KEY);
+ const remoteConfigData: Record = {
+ [constants.SOLO_REMOTE_CONFIGMAP_DATA_KEY]: remoteConfigDataBytes.toString('utf8'),
+ };
+
+ const promises: Promise[] = [];
+
+ for (const contexts of this.clusterReferences.keys()) {
+ promises.push(this.updateConfigMap(contexts, this.namespace, remoteConfigData));
+ }
+
+ await Promise.all(promises);
+ }
+
+ private async updateConfigMap(
+ context: Context,
+ namespace: NamespaceName,
+ data: Record,
+ ): Promise {
+ console.log({context, namespace, data});
+ await this.k8Factory.getK8(context).configMaps().update(namespace, constants.SOLO_REMOTE_CONFIGMAP_NAME, data);
+ }
+
+ public isLoaded(): boolean {
+ return this.phase === RuntimeStatePhase.Loaded;
+ }
+
+ private failIfNotLoaded(): void {
+ if (!this.isLoaded()) {
+ throw new ReadRemoteConfigBeforeLoadError('Attempting to read from remote config before loading it');
+ }
+ }
+
+ public async persist(): Promise {
+ if (!this.isLoaded()) {
+ throw new WriteRemoteConfigBeforeLoadError('Attempting to persist remote config before loading it');
+ }
+ await this.write();
+ }
+
+ public async create(
+ argv: ArgvStruct,
+ ledgerPhase: LedgerPhase,
+ nodeAliases: NodeAliases,
+ namespace: NamespaceName,
+ deployment: DeploymentName,
+ clusterReference: ClusterReference,
+ context: Context,
+ dnsBaseDomain: string,
+ dnsConsensusNodePattern: string,
+ ): Promise {
+ const consensusNodeStates: ConsensusNodeStateSchema[] = nodeAliases.map(
+ (nodeAlias: NodeAlias): ConsensusNodeStateSchema => {
+ return new ConsensusNodeStateSchema(
+ new ComponentStateMetadataSchema(
+ Templates.nodeIdFromNodeAlias(nodeAlias),
+ namespace.name,
+ clusterReference,
+ DeploymentPhase.REQUESTED,
+ ),
+ );
+ },
+ );
+
+ const userIdentity: Readonly = this.localConfig.configuration.userIdentity;
+ const cliVersion: SemVer = new SemVer(getSoloVersion());
+ const command: string = argv._.join(' ');
+
+ const cluster: ClusterSchema = new ClusterSchema(
+ clusterReference,
+ namespace.name,
+ deployment,
+ dnsBaseDomain,
+ dnsConsensusNodePattern,
+ );
+
+ const remoteConfig: RemoteConfigSchema = new RemoteConfigSchema(
+ undefined,
+ new RemoteConfigMetadataSchema(new Date(), userIdentity),
+ new ApplicationVersionsSchema(cliVersion),
+ [cluster],
+ new DeploymentStateSchema(ledgerPhase, consensusNodeStates),
+ new DeploymentHistorySchema([command], command),
+ );
+
+ const configMap: ConfigMap = await this.createConfigMap(namespace, context);
+ await this.populateRemoteConfig(configMap);
+ await this.write();
+ }
+
+ public async createFromExisting(
+ namespace: NamespaceName,
+ clusterReference: ClusterReference,
+ deployment: DeploymentName,
+ componentFactory: ComponentFactoryApi,
+ dnsBaseDomain: string,
+ dnsConsensusNodePattern: string,
+ existingClusterContext: Context,
+ argv: ArgvStruct,
+ nodeAliases: NodeAliases,
+ ): Promise {
+ const existingRemoteConfigConfigMap: ConfigMap = await this.getConfigMap(namespace, existingClusterContext);
+ await this.populateRemoteConfig(existingRemoteConfigConfigMap);
+
+ //? Create copy of the existing remote config inside the new cluster
+ await this.createConfigMap(namespace, existingClusterContext);
+ await this.write();
+
+ //* update the command history
+ this.addCommandToHistory(argv._.join(' '));
+
+ //* add the new clusters
+ this.configuration.addCluster(
+ new ClusterSchema(clusterReference, namespace.name, deployment, dnsBaseDomain, dnsConsensusNodePattern),
+ );
+
+ //* add the new nodes to components
+ for (const nodeAlias of nodeAliases) {
+ this.configuration.components.addNewComponent(
+ componentFactory.createNewConsensusNodeComponent(
+ Templates.nodeIdFromNodeAlias(nodeAlias),
+ clusterReference,
+ namespace,
+ DeploymentPhase.REQUESTED,
+ ),
+ ComponentTypes.ConsensusNode,
+ );
+ }
+
+ await this.persist();
+ }
+
+ public addCommandToHistory(command: string): void {
+ this.source.modelData.history.commands.push(command);
+ this.source.modelData.history.lastExecutedCommand = command;
+
+ if (this.source.modelData.history.commands.length > constants.SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY) {
+ this.source.modelData.history.commands.shift();
+ }
+ }
+
+ public async createConfigMap(namespace: NamespaceName, context: Context): Promise {
+ const name: string = constants.SOLO_REMOTE_CONFIGMAP_NAME;
+ const labels: Record = constants.SOLO_REMOTE_CONFIGMAP_LABELS;
+ await this.k8Factory
+ .getK8(context)
+ .configMaps()
+ .create(namespace, name, labels, {[constants.SOLO_REMOTE_CONFIGMAP_DATA_KEY]: '{}'});
+ return await this.k8Factory.getK8(context).configMaps().read(namespace, name);
+ }
+
+ public async getConfigMap(namespace?: NamespaceName, context?: Context): Promise {
+ const configMap: ConfigMap = await this.k8Factory
+ .getK8(context)
+ .configMaps()
+ .read(namespace, constants.SOLO_REMOTE_CONFIGMAP_NAME);
+
+ if (!configMap) {
+ throw new SoloError(`Remote config ConfigMap not found for namespace: ${namespace}, context: ${context}`);
+ }
+
+ return configMap;
+ }
+
+ /**
+ * 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,
+ skipConsensusNodesValidation: boolean = true,
+ ): Promise {
+ await this.setDefaultNamespaceAndDeploymentIfNotSet(argv);
+ this.setDefaultContextIfNotSet();
+
+ const deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment);
+ const deployment: Deployment = this.localConfig.configuration.deploymentByName(deploymentName);
+ this.namespace = NamespaceName.of(deployment.namespace);
+ const context: Context = this.localConfig.configuration.clusterRefs.get(deployment.clusters[0])?.toString();
+
+ for (const clusterReference of deployment.clusters) {
+ const context: Context = this.localConfig.configuration.clusterRefs.get(clusterReference.toString())?.toString();
+ this.clusterReferences.set(context, clusterReference.toString());
+ }
+
+ // TODO: Compare configs from clusterReferences
+
+ await this.load(this.namespace, context);
+
+ this.logger.info('Remote config loaded');
+ if (!validate) {
+ return;
+ }
+
+ await this.remoteConfigValidator.validateComponents(
+ this.namespace,
+ skipConsensusNodesValidation,
+ this.configuration.state,
+ );
+
+ const currentCommand: string = argv._?.join(' ');
+ const commandArguments: string = flags.stringifyArgv(argv);
+
+ this.addCommandToHistory(
+ `Executed by ${this.localConfig.configuration.userIdentity.name}: ${currentCommand} ${commandArguments}`.trim(),
+ );
+
+ this.populateVersionsInMetadata(argv, this.source.modelData);
+
+ await this.persist();
+ }
+
+ private populateVersionsInMetadata(argv: AnyObject, remoteConfig: RemoteConfigSchema): void {
+ const command: string = argv._[0];
+ const subcommand: string = argv._[1];
+
+ const isCommandUsingSoloChartVersionFlag: boolean =
+ (command === 'network' && subcommand === 'deploy') ||
+ (command === 'network' && subcommand === 'refresh') ||
+ (command === 'node' && subcommand === 'update') ||
+ (command === 'node' && subcommand === 'update-execute') ||
+ (command === 'node' && subcommand === 'add') ||
+ (command === 'node' && subcommand === 'add-execute') ||
+ (command === 'node' && subcommand === 'delete') ||
+ (command === 'node' && subcommand === 'delete-execute');
+
+ if (argv[flags.soloChartVersion.name]) {
+ remoteConfig.versions.cli = new SemVer(argv[flags.soloChartVersion.name]);
+ } else if (isCommandUsingSoloChartVersionFlag) {
+ remoteConfig.versions.cli = new SemVer(flags.soloChartVersion.definition.defaultValue as string);
+ }
+
+ const isCommandUsingReleaseTagVersionFlag: boolean =
+ (command === 'node' && subcommand !== 'keys' && subcommand !== 'logs' && subcommand !== 'states') ||
+ (command === 'network' && subcommand === 'deploy');
+
+ if (argv[flags.releaseTag.name]) {
+ remoteConfig.versions.consensusNode = new SemVer(argv[flags.releaseTag.name]);
+ } else if (isCommandUsingReleaseTagVersionFlag) {
+ remoteConfig.versions.consensusNode = new SemVer(flags.releaseTag.definition.defaultValue as string);
+ }
+
+ if (argv[flags.mirrorNodeVersion.name]) {
+ remoteConfig.versions.mirrorNodeChart = new SemVer(argv[flags.mirrorNodeVersion.name]);
+ } else if (command === 'mirror-node' && subcommand === 'deploy') {
+ remoteConfig.versions.mirrorNodeChart = new SemVer(flags.mirrorNodeVersion.definition.defaultValue as string);
+ }
+
+ if (argv[flags.explorerVersion.name]) {
+ remoteConfig.versions.explorerChart = new SemVer(argv[flags.explorerVersion.name]);
+ } else if (command === 'explorer' && subcommand === 'deploy') {
+ remoteConfig.versions.explorerChart = new SemVer(flags.explorerVersion.definition.defaultValue as string);
+ }
+
+ if (argv[flags.relayReleaseTag.name]) {
+ remoteConfig.versions.jsonRpcRelayChart = new SemVer(argv[flags.relayReleaseTag.name]);
+ } else if (command === 'relay' && subcommand === 'deploy') {
+ remoteConfig.versions.jsonRpcRelayChart = new SemVer(flags.relayReleaseTag.definition.defaultValue as string);
+ }
+ }
+
+ public async deleteComponents(): Promise {
+ this._remoteConfig.state.consensusNodes = [];
+ this._remoteConfig.state.blockNodes = [];
+ this._remoteConfig.state.envoyProxies = [];
+ this._remoteConfig.state.haProxies = [];
+ this._remoteConfig.state.explorers = [];
+ this._remoteConfig.state.mirrorNodes = [];
+ this._remoteConfig.state.relayNodes = [];
+ }
+
+ private async setDefaultNamespaceAndDeploymentIfNotSet(argv: AnyObject): Promise {
+ if (this.configManager.hasFlag(flags.namespace)) {
+ return;
+ }
+
+ // TODO: Current quick fix for commands where namespace is not passed
+ let deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment);
+ let currentDeployment: Deployment = this.localConfig.configuration.deploymentByName(deploymentName);
+
+ if (!deploymentName) {
+ deploymentName = await promptTheUserForDeployment(this.configManager);
+ currentDeployment = this.localConfig.configuration.deploymentByName(deploymentName);
+ // TODO: Fix once we have the DataManager,
+ // without this the user will be prompted a second time for the deployment
+ // TODO: we should not be mutating argv
+ argv[flags.deployment.name] = deploymentName;
+ this.logger.warn(
+ `Deployment name not found in flags or local config, setting it in argv and config manager to: ${deploymentName}`,
+ );
+ this.configManager.setFlag(flags.deployment, deploymentName);
+ }
+
+ if (!currentDeployment) {
+ throw new SoloError(`Selected deployment name is not set in local config - ${deploymentName}`);
+ }
+
+ const namespace: NamespaceNameAsString = currentDeployment.namespace;
+
+ this.logger.warn(`Namespace not found in flags, setting it to: ${namespace}`);
+ this.configManager.setFlag(flags.namespace, namespace);
+ argv[flags.namespace.name] = namespace;
+ }
+
+ private setDefaultContextIfNotSet(): void {
+ if (this.configManager.hasFlag(flags.context)) {
+ return;
+ }
+
+ 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");
+ }
+
+ this.logger.warn(`Context not found in flags, setting it to: ${context}`);
+ this.configManager.setFlag(flags.context, context);
+ }
+
+ //* Common Commands
+
+ /**
+ * Get the consensus nodes from the remoteConfig 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');
+ }
+
+ const consensusNodes: ConsensusNode[] = [];
+
+ for (const node of Object.values(this.configuration.state.consensusNodes)) {
+ const cluster: ClusterSchema = this.configuration.clusters.find(
+ (cluster: ClusterSchema): boolean => cluster.name === node.metadata.cluster,
+ );
+ const context: Context = this.localConfig.configuration.clusterRefs.get(node.metadata.cluster)?.toString();
+ const nodeAlias: NodeAlias = Templates.renderNodeAliasFromNumber(node.metadata.id + 1);
+
+ consensusNodes.push(
+ new ConsensusNode(
+ nodeAlias,
+ node.metadata.id,
+ node.metadata.namespace,
+ node.metadata.cluster,
+ context,
+ cluster.dnsBaseDomain,
+ cluster.dnsConsensusNodePattern,
+ Templates.renderConsensusNodeFullyQualifiedDomainName(
+ nodeAlias,
+ node.metadata.id,
+ node.metadata.namespace,
+ node.metadata.cluster,
+ cluster.dnsBaseDomain,
+ cluster.dnsConsensusNodePattern,
+ ),
+ ),
+ );
+ }
+
+ // return the consensus nodes
+ 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: ConsensusNode[] = this.getConsensusNodes();
+ const accumulator: ClusterReferences = new Map();
+
+ for (const node of nodes) {
+ accumulator.set(node.cluster, node.context);
+ }
+
+ return accumulator;
+ }
+
+ private getContextForFirstCluster(): string {
+ const deploymentName: DeploymentName = this.configManager.getFlag(flags.deployment);
+
+ const clusterReference: ClusterReference =
+ this.localConfig.configuration.deploymentByName(deploymentName).clusters[0];
+
+ const context: Context = this.localConfig.configuration.clusterRefs.get(clusterReference)?.toString();
+
+ this.logger.debug(`Using context ${context} for cluster ${clusterReference} for deployment ${deploymentName}`);
+
+ return context;
+ }
+}
diff --git a/src/business/runtime-state/config/remote/remote-config.ts b/src/business/runtime-state/config/remote/remote-config.ts
new file mode 100644
index 000000000..912fa2dc3
--- /dev/null
+++ b/src/business/runtime-state/config/remote/remote-config.ts
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {ComponentsDataWrapper} from '../../../../core/config/remote/components-data-wrapper.js';
+import {type Facade} from '../../facade/facade.js';
+import {type RemoteConfigSchema} from '../../../../data/schema/model/remote/remote-config-schema.js';
+import {type ComponentsDataWrapperApi} from '../../../../core/config/remote/api/components-data-wrapper-api.js';
+import {type RemoteConfigMetadataSchema} from '../../../../data/schema/model/remote/remote-config-metadata-schema.js';
+import {type ApplicationVersionsSchema} from '../../../../data/schema/model/common/application-versions-schema.js';
+import {type ClusterSchema} from '../../../../data/schema/model/common/cluster-schema.js';
+import {type DeploymentStateSchema} from '../../../../data/schema/model/remote/deployment-state-schema.js';
+import {type DeploymentHistorySchema} from '../../../../data/schema/model/remote/deployment-history-schema.js';
+
+export class RemoteConfig implements Facade {
+ private readonly _components: ComponentsDataWrapperApi;
+ private readonly _schemaVersion: number;
+ private readonly _metadata: Readonly;
+ private readonly _versions: Readonly;
+ private readonly _clusters: Readonly[];
+ private readonly _state: Readonly;
+ private readonly _history: Readonly;
+
+ public constructor(public readonly encapsulatedObject: RemoteConfigSchema) {
+ this._components = new ComponentsDataWrapper(encapsulatedObject.state);
+ this._schemaVersion = encapsulatedObject.schemaVersion;
+ this._metadata = encapsulatedObject.metadata;
+ this._versions = encapsulatedObject.versions;
+ this._clusters = encapsulatedObject.clusters;
+ this._state = encapsulatedObject.state;
+ }
+
+ public get components(): ComponentsDataWrapperApi {
+ return this._components;
+ }
+
+ public get schemaVersion(): number {
+ return this._schemaVersion;
+ }
+
+ public get metadata(): Readonly {
+ return this._metadata;
+ }
+
+ public get versions(): Readonly {
+ return this._versions;
+ }
+
+ public get clusters(): Readonly[]> {
+ return this._clusters;
+ }
+
+ public get state(): DeploymentStateSchema {
+ return this._state;
+ }
+
+ public get history(): Readonly {
+ return this._history;
+ }
+
+ public addCluster(cluster: ClusterSchema): void {
+ this._clusters.push(cluster);
+ }
+}
diff --git a/src/commands/account.ts b/src/commands/account.ts
index 26c67d1ee..2b44ef1aa 100644
--- a/src/commands/account.ts
+++ b/src/commands/account.ts
@@ -258,7 +258,7 @@ export class AccountCommand extends BaseCommand {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
nodeAliases: helpers.parseNodeAliases(
this.configManager.getFlag(flags.nodeAliasesUnparsed),
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
),
} as Config;
@@ -278,7 +278,7 @@ export class AccountCommand extends BaseCommand {
await self.accountManager.loadNodeClient(
config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
self.configManager.getFlag(flags.deployment),
self.configManager.getFlag(flags.forcePortForward),
);
@@ -359,7 +359,7 @@ export class AccountCommand extends BaseCommand {
const nodeId = Templates.nodeIdFromNodeAlias(nodeAlias);
const nodeClient = await self.accountManager.refreshNodeClient(
context_.config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
nodeAlias,
context_.config.deployment,
);
@@ -518,7 +518,7 @@ export class AccountCommand extends BaseCommand {
await self.accountManager.loadNodeClient(
context_.config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
config.deployment,
self.configManager.getFlag(flags.forcePortForward),
);
@@ -608,7 +608,7 @@ export class AccountCommand extends BaseCommand {
await self.accountManager.loadNodeClient(
config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
config.deployment,
self.configManager.getFlag(flags.forcePortForward),
);
@@ -710,7 +710,7 @@ export class AccountCommand extends BaseCommand {
await self.accountManager.loadNodeClient(
config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
config.deployment,
self.configManager.getFlag(flags.forcePortForward),
);
diff --git a/src/commands/base.ts b/src/commands/base.ts
index 74893579f..e5e159932 100644
--- a/src/commands/base.ts
+++ b/src/commands/base.ts
@@ -3,7 +3,6 @@
import {SoloError} from '../core/errors/solo-error.js';
import {ShellRunner} from '../core/shell-runner.js';
import {type LockManager} from '../core/lock/lock-manager.js';
-import {type RemoteConfigManager} from '../core/config/remote/remote-config-manager.js';
import {type ChartManager} from '../core/chart-manager.js';
import {type ConfigManager} from '../core/config-manager.js';
import {type DependencyManager} from '../core/dependency-managers/index.js';
@@ -18,6 +17,7 @@ import {PathEx} from '../business/utils/path-ex.js';
import {inject} from 'tsyringe-neo';
import {patchInject} from '../core/dependency-injection/container-helper.js';
import {InjectTokens} from '../core/dependency-injection/inject-tokens.js';
+import {type RemoteConfigRuntimeStateApi} from '../business/runtime-state/api/remote-config-runtime-state-api.js';
export abstract class BaseCommand extends ShellRunner {
constructor(
@@ -28,7 +28,7 @@ export abstract class BaseCommand extends ShellRunner {
@inject(InjectTokens.DependencyManager) protected readonly depManager?: DependencyManager,
@inject(InjectTokens.LockManager) protected readonly leaseManager?: LockManager,
@inject(InjectTokens.LocalConfigRuntimeState) public readonly localConfig?: LocalConfigRuntimeState,
- @inject(InjectTokens.RemoteConfigManager) protected readonly remoteConfigManager?: RemoteConfigManager,
+ @inject(InjectTokens.RemoteConfigRuntimeState) protected readonly remoteConfig?: RemoteConfigRuntimeStateApi,
) {
super();
@@ -39,11 +39,7 @@ export abstract class BaseCommand extends ShellRunner {
this.depManager = patchInject(depManager, InjectTokens.DependencyManager, this.constructor.name);
this.leaseManager = patchInject(leaseManager, InjectTokens.LockManager, this.constructor.name);
this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
- this.remoteConfigManager = patchInject(
- remoteConfigManager,
- InjectTokens.RemoteConfigManager,
- this.constructor.name,
- );
+ this.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name);
}
/**
@@ -53,7 +49,7 @@ export abstract class BaseCommand extends ShellRunner {
* 1. Chart's default values file (if chartDirectory is set)
* 2. Profile values file
* 3. User's values file
- * @param clusterRefs
+ * @param clusterReferences
* @param valuesFileInput - the values file input string
* @param chartDirectory - the chart directory
* @param profileValuesFile - mapping of clusterRef to the profile values file full path
diff --git a/src/commands/block-node.ts b/src/commands/block-node.ts
index dfaff177c..d980e4a64 100644
--- a/src/commands/block-node.ts
+++ b/src/commands/block-node.ts
@@ -16,19 +16,28 @@ import {
type NodeAliases,
} from '../types/aliases.js';
import {ListrLock} from '../core/lock/listr-lock.js';
-import {type ClusterReference, type DeploymentName} from '../types/index.js';
-import {type CommandDefinition, type Optional, type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js';
+import {
+ type ClusterReference,
+ type CommandDefinition,
+ type DeploymentName,
+ 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 '../types/namespace/namespace-name.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';
import chalk from 'chalk';
import {CommandBuilder, CommandGroup, Subcommand} from '../core/command-path-builders/command-builder.js';
import {type Pod} from '../integration/kube/resources/pod/pod.js';
+import {BlockNodeStateSchema} from '../data/schema/model/remote/state/block-node-state-schema.js';
+import {ComponentStateMetadataSchema} from '../data/schema/model/remote/state/component-state-metadata-schema.js';
+import {DeploymentPhase} from '../data/schema/model/remote/deployment-phase.js';
+import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js';
interface BlockNodeDeployConfigClass {
chartVersion: string;
@@ -44,7 +53,7 @@ interface BlockNodeDeployConfigClass {
nodeAliases: NodeAliases; // from remote config
context: string;
valuesArg: string;
- newBlockNodeComponent: BlockNodeComponent;
+ newBlockNodeComponent: BlockNodeStateSchema;
releaseName: string;
}
@@ -81,7 +90,7 @@ export class BlockNodeCommand extends BaseCommand {
valuesArgument += helpers.prepareValuesFiles(config.valuesFile);
}
- valuesArgument += helpers.populateHelmArguments({nameOverride: config.newBlockNodeComponent.name});
+ valuesArgument += helpers.populateHelmArguments({nameOverride: config.newBlockNodeComponent.metadata.id});
if (config.domainName) {
valuesArgument += helpers.populateHelmArguments({
@@ -129,15 +138,13 @@ export class BlockNodeCommand extends BaseCommand {
task,
);
- context_.config.nodeAliases = this.remoteConfigManager
- .getConsensusNodes()
- .map((node): NodeAlias => node.name);
+ context_.config.nodeAliases = this.remoteConfig.getConsensusNodes().map((node): NodeAlias => node.name);
if (!context_.config.clusterRef) {
context_.config.clusterRef = this.k8Factory.default().clusters().readCurrent();
}
- context_.config.context = this.remoteConfigManager.getClusterRefs()[context_.config.clusterRef];
+ context_.config.context = this.remoteConfig.getClusterRefs()[context_.config.clusterRef];
this.logger.debug('Initialized config', {config: context_.config});
@@ -151,10 +158,8 @@ export class BlockNodeCommand extends BaseCommand {
config.releaseName = this.getReleaseName();
- config.newBlockNodeComponent = new BlockNodeComponent(
- config.releaseName,
- config.clusterRef,
- config.namespace.name,
+ config.newBlockNodeComponent = new BlockNodeStateSchema(
+ new ComponentStateMetadataSchema(1, config.namespace.name, config.clusterRef, DeploymentPhase.DEPLOYED),
);
},
},
@@ -260,12 +265,12 @@ export class BlockNodeCommand extends BaseCommand {
private addBlockNodeComponent(): SoloListrTask {
return {
title: 'Add block node component in remote config',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async (context_): Promise => {
- await this.remoteConfigManager.modify(async remoteConfig => {
+ await this.remoteConfig.modify(async (_, components) => {
const config: BlockNodeDeployConfigClass = context_.config;
- remoteConfig.components.add(config.newBlockNodeComponent);
+ components.addNewComponent(config.newBlockNodeComponent, ComponentTypes.BlockNode);
});
},
};
diff --git a/src/commands/deployment.ts b/src/commands/deployment.ts
index 511ce80cb..8e631db2f 100644
--- a/src/commands/deployment.ts
+++ b/src/commands/deployment.ts
@@ -23,12 +23,12 @@ import {container, inject, injectable} 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 {Cluster} from '../core/config/remote/cluster.js';
import {resolveNamespaceFromDeployment} from '../core/resolvers.js';
import {patchInject} from '../core/dependency-injection/container-helper.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';
+import {LedgerPhase} from '../data/schema/model/remote/ledger-phase.js';
+import {type ConfigMap} from '../integration/kube/resources/config-map/config-map.js';
+import {type ComponentFactoryApi} from '../core/config/remote/api/component-factory-api.js';
import {StringFacade} from '../business/runtime-state/facade/string-facade.js';
import {Deployment} from '../business/runtime-state/config/local/deployment.js';
@@ -44,7 +44,7 @@ interface DeploymentAddClusterConfig {
dnsBaseDomain: string;
dnsConsensusNodePattern: string;
- state?: DeploymentStates;
+ ledgerPhase?: LedgerPhase;
nodeAliases: NodeAliases;
existingNodesCount: number;
@@ -57,7 +57,10 @@ export interface DeploymentAddClusterContext {
@injectable()
export class DeploymentCommand extends BaseCommand {
- constructor(@inject(InjectTokens.ClusterCommandTasks) private readonly tasks: ClusterCommandTasks) {
+ public constructor(
+ @inject(InjectTokens.ClusterCommandTasks) private readonly tasks: ClusterCommandTasks,
+ @inject(InjectTokens.ComponentFactory) private readonly componentFactory: ComponentFactoryApi,
+ ) {
super();
this.tasks = patchInject(tasks, InjectTokens.ClusterCommandTasks, this.constructor.name);
@@ -225,7 +228,7 @@ export class DeploymentCommand extends BaseCommand {
for (const clusterReference of clusterReferences) {
const context = self.localConfig.configuration.clusterRefs.get(clusterReference.toString()).toString();
const namespace = NamespaceName.of(self.localConfig.configuration.deploymentByName(deployment).namespace);
- const remoteConfigExists = await self.remoteConfigManager.get(context);
+ const remoteConfigExists: ConfigMap = await self.remoteConfig.getConfigMap(namespace, context);
const namespaceExists = await self.k8Factory.getK8(context).namespaces().has(namespace);
const existingConfigMaps = await self.k8Factory
.getK8(context)
@@ -291,6 +294,7 @@ export class DeploymentCommand extends BaseCommand {
try {
await tasks.run();
} catch (error: Error | unknown) {
+ console.error(error);
throw new SoloError('Error adding cluster to deployment', error);
}
@@ -541,8 +545,8 @@ export class DeploymentCommand extends BaseCommand {
}
/**
- * Checks the network state:
- * - if remote config is found check's the state field to see if it's pre or post genesis.
+ * Checks the ledger phase:
+ * - if remote config is found check's the ledgerPhase field to see if it's pre or post genesis.
* - pre genesis:
* - prompts user if needed.
* - generates node aliases based on '--number-of-consensus-nodes'
@@ -554,15 +558,15 @@ export class DeploymentCommand extends BaseCommand {
*/
public checkNetworkState(): SoloListrTask {
return {
- title: 'check network state',
+ title: 'check ledger phase',
task: async (context_, task) => {
- const {deployment, numberOfConsensusNodes, quiet} = context_.config;
+ const {deployment, numberOfConsensusNodes, quiet, namespace} = context_.config;
const existingClusterReferences = this.localConfig.configuration.deploymentByName(deployment).clusters;
- // if there is no remote config don't validate deployment state
+ // if there is no remote config don't validate deployment ledger phase
if (existingClusterReferences.length === 0) {
- context_.config.state = DeploymentStates.PRE_GENESIS;
+ context_.config.ledgerPhase = LedgerPhase.UNINITIALIZED;
// if the user can't be prompted for '--num-consensus-nodes' fail
if (!numberOfConsensusNodes && quiet) {
@@ -585,22 +589,28 @@ export class DeploymentCommand extends BaseCommand {
?.toString();
context_.config.existingClusterContext = existingClusterContext;
- const remoteConfig = await this.remoteConfigManager.get(existingClusterContext);
+ const remoteConfigConfigMap: ConfigMap = await this.remoteConfig.getConfigMap(
+ namespace,
+ existingClusterContext,
+ );
+
+ await this.remoteConfig.populateRemoteConfig(remoteConfigConfigMap);
- const state = remoteConfig.metadata.state;
- context_.config.state = state;
+ const ledgerPhase: LedgerPhase = this.remoteConfig.state.ledgerPhase;
- const existingNodesCount = Object.keys(remoteConfig.components.consensusNodes).length;
+ context_.config.ledgerPhase = ledgerPhase;
+
+ const existingNodesCount: number = Object.keys(this.remoteConfig.state.consensusNodes).length;
context_.config.nodeAliases = Templates.renderNodeAliasesFromCount(numberOfConsensusNodes, existingNodesCount);
- // If state is pre-genesis and user can't be prompted for the '--num-consensus-nodes' fail
- if (state === DeploymentStates.PRE_GENESIS && !numberOfConsensusNodes && quiet) {
- throw new SoloError(`--${flags.numberOfConsensusNodes} must be specified ${DeploymentStates.PRE_GENESIS}`);
+ // If ledgerPhase is pre-genesis and user can't be prompted for the '--num-consensus-nodes' fail
+ if (ledgerPhase === LedgerPhase.UNINITIALIZED && !numberOfConsensusNodes && quiet) {
+ throw new SoloError(`--${flags.numberOfConsensusNodes} must be specified ${LedgerPhase.UNINITIALIZED}`);
}
- // If state is pre-genesis prompt the user for the '--num-consensus-nodes'
- else if (state === DeploymentStates.PRE_GENESIS && !numberOfConsensusNodes) {
+ // If ledgerPhase is pre-genesis prompt the user for the '--num-consensus-nodes'
+ else if (ledgerPhase === LedgerPhase.UNINITIALIZED && !numberOfConsensusNodes) {
await this.configManager.executePrompt(task, [flags.numberOfConsensusNodes]);
context_.config.numberOfConsensusNodes = this.configManager.getFlag(flags.numberOfConsensusNodes);
context_.config.nodeAliases = Templates.renderNodeAliasesFromCount(
@@ -609,10 +619,10 @@ export class DeploymentCommand extends BaseCommand {
);
}
- // if the state is post-genesis and '--num-consensus-nodes' is specified throw
- else if (state === DeploymentStates.POST_GENESIS && numberOfConsensusNodes) {
+ // if the ledgerPhase is post-genesis and '--num-consensus-nodes' is specified throw
+ else if (ledgerPhase === LedgerPhase.INITIALIZED && numberOfConsensusNodes) {
throw new SoloError(
- `--${flags.numberOfConsensusNodes.name}=${numberOfConsensusNodes} shouldn't be specified ${state}`,
+ `--${flags.numberOfConsensusNodes.name}=${numberOfConsensusNodes} shouldn't be specified ${ledgerPhase}`,
);
}
},
@@ -625,12 +635,12 @@ export class DeploymentCommand extends BaseCommand {
public testClusterConnection(): SoloListrTask {
return {
title: 'Test cluster connection',
- task: async (context_, task) => {
+ task: async (context_, task): Promise => {
const {clusterRef, context} = context_.config;
task.title += `: ${clusterRef}, context: ${context}`;
- const isConnected = await this.k8Factory
+ const isConnected: boolean = await this.k8Factory
.getK8(context)
.namespaces()
.list()
@@ -647,7 +657,7 @@ export class DeploymentCommand extends BaseCommand {
public verifyClusterAddPrerequisites(): SoloListrTask {
return {
title: 'Verify prerequisites',
- task: async () => {
+ task: async (): Promise => {
// TODO: Verifies Kubernetes cluster & namespace-level prerequisites (e.g., cert-manager, HAProxy, etc.)
},
};
@@ -659,7 +669,7 @@ export class DeploymentCommand extends BaseCommand {
public addClusterRefToDeployments(): SoloListrTask {
return {
title: 'add cluster-ref in local config deployments',
- task: async (context_, task) => {
+ task: async (context_, task): Promise => {
const {clusterRef, deployment} = context_.config;
task.title = `add cluster-ref: ${clusterRef} for deployment: ${deployment} in local config`;
@@ -677,12 +687,12 @@ export class DeploymentCommand extends BaseCommand {
public createOrEditRemoteConfigForNewDeployment(argv: ArgvStruct): SoloListrTask {
return {
title: 'create remote config for deployment',
- task: async (context_, task) => {
+ task: async (context_, task): Promise => {
const {
deployment,
clusterRef,
context,
- state,
+ ledgerPhase,
nodeAliases,
namespace,
existingClusterContext,
@@ -698,50 +708,29 @@ export class DeploymentCommand extends BaseCommand {
await this.k8Factory.getK8(context).namespaces().create(namespace);
}
- if (!existingClusterContext) {
- await this.remoteConfigManager.create(
- argv,
- state,
- nodeAliases,
- namespace,
- deployment,
- clusterRef,
- context,
- dnsBaseDomain,
- dnsConsensusNodePattern,
- );
-
- return;
- }
-
- await this.remoteConfigManager.get(existingClusterContext);
-
- //? Create copy of the existing remote config inside the new cluster
- await this.remoteConfigManager.createConfigMap(context);
-
- //? Update remote configs inside the clusters
- await this.remoteConfigManager.modify(async remoteConfig => {
- //* update the command history
- remoteConfig.addCommandToHistory(argv._.join(' '));
-
- //* add the new clusters
- remoteConfig.addCluster(
- new Cluster(clusterRef, namespace.name, deployment, dnsBaseDomain, dnsConsensusNodePattern),
- );
-
- //* add the new nodes to components
- for (const nodeAlias of nodeAliases) {
- remoteConfig.components.add(
- new ConsensusNodeComponent(
- nodeAlias,
- clusterRef,
- namespace.name,
- ConsensusNodeStates.NON_DEPLOYED,
- Templates.nodeIdFromNodeAlias(nodeAlias),
- ),
- );
- }
- });
+ await (existingClusterContext
+ ? this.remoteConfig.createFromExisting(
+ namespace,
+ clusterRef,
+ deployment,
+ this.componentFactory,
+ dnsBaseDomain,
+ dnsConsensusNodePattern,
+ existingClusterContext,
+ argv,
+ nodeAliases,
+ )
+ : this.remoteConfig.create(
+ argv,
+ ledgerPhase,
+ nodeAliases,
+ namespace,
+ deployment,
+ clusterRef,
+ context,
+ dnsBaseDomain,
+ dnsConsensusNodePattern,
+ ));
},
};
}
diff --git a/src/commands/explorer.ts b/src/commands/explorer.ts
index 7954cb20b..c0a86f8b5 100644
--- a/src/commands/explorer.ts
+++ b/src/commands/explorer.ts
@@ -15,9 +15,8 @@ import {
import {type ProfileManager} from '../core/profile-manager.js';
import {BaseCommand} from './base.js';
import {Flags as flags} from './flags.js';
-import {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 {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 {
@@ -36,7 +35,8 @@ import {KeyManager} from '../core/key-manager.js';
import {INGRESS_CONTROLLER_VERSION} from '../../version.js';
import {patchInject} from '../core/dependency-injection/container-helper.js';
import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js';
-import {ListrRemoteConfig} from '../core/config/remote/listr-config-tasks.js';
+import {type MirrorNodeStateSchema} from '../data/schema/model/remote/state/mirror-node-state-schema.js';
+import {type ComponentFactoryApi} from '../core/config/remote/api/component-factory-api.js';
interface ExplorerDeployConfigClass {
cacheDir: string;
@@ -81,6 +81,7 @@ export class ExplorerCommand extends BaseCommand {
public constructor(
@inject(InjectTokens.ProfileManager) private readonly profileManager: ProfileManager,
@inject(InjectTokens.ClusterChecks) private readonly clusterChecks: ClusterChecks,
+ @inject(InjectTokens.ComponentFactory) private readonly componentFactory: ComponentFactoryApi,
) {
super();
@@ -263,7 +264,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_ => {
@@ -476,7 +477,7 @@ export class ExplorerCommand extends BaseCommand {
const clusterReference: ClusterReference = this.configManager.hasFlag(flags.clusterRef)
? this.configManager.getFlag(flags.clusterRef)
- : this.remoteConfigManager.currentCluster;
+ : this.remoteConfig.currentCluster;
const clusterContext: Context = this.localConfig.configuration.clusterRefs
.get(clusterReference)
@@ -500,7 +501,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_ => {
@@ -534,7 +535,7 @@ export class ExplorerCommand extends BaseCommand {
});
},
},
- this.removeMirrorNodeExplorerComponents(),
+ this.disableMirrorNodeExplorerComponents(),
],
{
concurrent: false,
@@ -614,14 +615,33 @@ export class ExplorerCommand extends BaseCommand {
};
}
+ private loadRemoteConfigTask(argv: ArgvStruct): SoloListrTask {
+ return {
+ title: 'Load remote config',
+ task: async (): Promise => {
+ await this.remoteConfig.loadAndValidate(argv);
+ },
+ };
+ }
+
/** 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.remove('mirrorNodeExplorer', ComponentTypes.MirrorNodeExplorer);
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
+ task: async (context_): Promise => {
+ const clusterReference: ClusterReference = context_.config.clusterReference;
+
+ await this.remoteConfig.modify(async (_, components) => {
+ const explorerComponents: MirrorNodeStateSchema[] =
+ components.getComponentsByClusterReference(
+ ComponentTypes.Explorers,
+ clusterReference,
+ );
+
+ for (const explorerComponent of explorerComponents) {
+ components.removeComponent(explorerComponent.metadata.id, ComponentTypes.Explorers);
+ }
});
},
};
@@ -631,14 +651,14 @@ export class ExplorerCommand extends BaseCommand {
private addMirrorNodeExplorerComponents(): SoloListrTask {
return {
title: 'Add explorer to remote config',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async (context_): Promise => {
- await this.remoteConfigManager.modify(async remoteConfig => {
- const {
- config: {namespace, clusterRef},
- } = context_;
- remoteConfig.components.add(
- new MirrorNodeExplorerComponent('mirrorNodeExplorer', clusterRef, namespace.name),
+ await this.remoteConfig.modify(async (_, components) => {
+ const {namespace, clusterRef} = context_.config;
+
+ components.addNewComponent(
+ this.componentFactory.createNewExplorerComponent(clusterRef, namespace),
+ ComponentTypes.Explorers,
);
});
},
diff --git a/src/commands/mirror-node.ts b/src/commands/mirror-node.ts
index d092365c8..8baeaad30 100644
--- a/src/commands/mirror-node.ts
+++ b/src/commands/mirror-node.ts
@@ -22,7 +22,6 @@ import {prepareValuesFiles, 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 * as fs from 'node:fs';
import {
type ClusterReference,
@@ -49,6 +48,8 @@ import {InjectTokens} from '../core/dependency-injection/inject-tokens.js';
import {patchInject} from '../core/dependency-injection/container-helper.js';
import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js';
import {type AccountId} from '@hashgraph/sdk';
+import {type MirrorNodeStateSchema} from '../data/schema/model/remote/state/mirror-node-state-schema.js';
+import {type ComponentFactoryApi} from '../core/config/remote/api/component-factory-api.js';
interface MirrorNodeDeployConfigClass {
cacheDir: string;
@@ -103,6 +104,7 @@ export class MirrorNodeCommand extends BaseCommand {
public constructor(
@inject(InjectTokens.AccountManager) private readonly accountManager?: AccountManager,
@inject(InjectTokens.ProfileManager) private readonly profileManager?: ProfileManager,
+ @inject(InjectTokens.ComponentFactory) private readonly componentFactory?: ComponentFactoryApi,
) {
super();
@@ -365,7 +367,7 @@ export class MirrorNodeCommand extends BaseCommand {
const deploymentName: DeploymentName = self.configManager.getFlag(flags.deployment);
await self.accountManager.loadNodeClient(
context_.config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
deploymentName,
self.configManager.getFlag(flags.forcePortForward),
);
@@ -478,7 +480,7 @@ export class MirrorNodeCommand extends BaseCommand {
const portForward = this.configManager.getFlag(flags.forcePortForward);
context_.addressBook = await self.accountManager.prepareAddressBookBase64(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
deployment,
this.configManager.getFlag(flags.operatorId),
this.configManager.getFlag(flags.operatorKey),
@@ -601,7 +603,7 @@ export class MirrorNodeCommand extends BaseCommand {
const exchangeRatesFileIdNumber = 112;
const timestamp = Date.now();
- const clusterReferences = this.remoteConfigManager.getClusterRefs();
+ const clusterReferences = this.remoteConfig.getClusterRefs();
const deployment = this.configManager.getFlag(flags.deployment);
const fees = await this.accountManager.getFileContents(
namespace,
@@ -770,7 +772,7 @@ export class MirrorNodeCommand extends BaseCommand {
await self.accountManager.loadNodeClient(
context_.config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
self.configManager.getFlag(flags.deployment),
self.configManager.getFlag(flags.forcePortForward),
);
@@ -832,7 +834,7 @@ export class MirrorNodeCommand extends BaseCommand {
});
},
},
- this.removeMirrorNodeComponents(),
+ this.disableMirrorNodeComponents(),
],
{
concurrent: false,
@@ -919,13 +921,23 @@ export class MirrorNodeCommand extends BaseCommand {
}
/** Removes the mirror node components from remote config. */
- public removeMirrorNodeComponents(): SoloListrTask {
+ 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 => {
- remoteConfig.components.remove('mirrorNode', ComponentTypes.MirrorNode);
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
+ task: async (context_): Promise => {
+ const clusterReference: ClusterReference = context_.config.clusterRef;
+
+ await this.remoteConfig.modify(async (_, components) => {
+ const mirrorNodeComponents: MirrorNodeStateSchema[] =
+ components.getComponentsByClusterReference(
+ ComponentTypes.MirrorNode,
+ clusterReference,
+ );
+
+ for (const mirrorNodeComponent of mirrorNodeComponents) {
+ components.removeComponent(mirrorNodeComponent.metadata.id, ComponentTypes.MirrorNode);
+ }
});
},
};
@@ -935,14 +947,15 @@ export class MirrorNodeCommand extends BaseCommand {
public addMirrorNodeComponents(): SoloListrTask {
return {
title: 'Add mirror node to remote config',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async (context_): Promise => {
- await this.remoteConfigManager.modify(async remoteConfig => {
- const {
- config: {namespace, clusterRef},
- } = context_;
+ await this.remoteConfig.modify(async (_, components) => {
+ const {namespace, clusterRef} = context_.config;
- remoteConfig.components.add(new MirrorNodeComponent('mirrorNode', clusterRef, namespace.name));
+ components.addNewComponent(
+ this.componentFactory.createNewMirrorNodeComponent(clusterRef, namespace),
+ ComponentTypes.MirrorNode,
+ );
});
},
};
diff --git a/src/commands/network.ts b/src/commands/network.ts
index 8ef9cdc8f..694ed3904 100644
--- a/src/commands/network.ts
+++ b/src/commands/network.ts
@@ -9,7 +9,6 @@ import {UserBreak} from '../core/errors/user-break.js';
import {BaseCommand} 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,
@@ -24,26 +23,28 @@ import {type KeyManager} from '../core/key-manager.js';
import {type PlatformInstaller} from '../core/platform-installer.js';
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 {
+ type AnyYargs,
+ type ArgvStruct,
+ type IP,
+ type NodeAlias,
+ type NodeAliases,
+ type NodeId,
+} 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 ClusterReference,
type ClusterReferences,
type CommandDefinition,
+ type Context,
type DeploymentName,
type Realm,
type Shard,
+ type SoloListr,
type SoloListrTask,
type SoloListrTaskWrapper,
} from '../types/index.js';
-import {NamespaceName} from '../types/namespace/namespace-name.js';
-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 {Base64} from 'js-base64';
import {SecretType} from '../integration/kube/resources/secret/secret-type.js';
import {Duration} from '../core/time/duration.js';
@@ -53,9 +54,15 @@ import {PathEx} from '../business/utils/path-ex.js';
import {inject, injectable} from 'tsyringe-neo';
import {InjectTokens} from '../core/dependency-injection/inject-tokens.js';
import {patchInject} from '../core/dependency-injection/container-helper.js';
-import {ConsensusNodeStates} from '../core/config/remote/enumerations/consensus-node-states.js';
import {lt as SemVersionLessThan, SemVer} from 'semver';
import {Deployment} from '../business/runtime-state/config/local/deployment.js';
+import {type ComponentFactoryApi} from '../core/config/remote/api/component-factory-api.js';
+import {DeploymentPhase} from '../data/schema/model/remote/deployment-phase.js';
+import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js';
+import {PvcName} from '../integration/kube/resources/pvc/pvc-name.js';
+import {PvcReference} from '../integration/kube/resources/pvc/pvc-reference.js';
+import {NamespaceName} from '../types/namespace/namespace-name.js';
+import {ConsensusNode} from '../core/model/consensus-node.js';
export interface NetworkDeployConfigClass {
applicationEnv: string;
@@ -109,6 +116,12 @@ export interface NetworkDeployConfigClass {
clusterRefs: ClusterReferences;
domainNames?: string;
domainNamesMapping?: Record;
+ debugNodeAlias: NodeAlias;
+ app: string;
+}
+
+interface NetworkDeployContext {
+ config: NetworkDeployConfigClass;
}
export interface NetworkDestroyContext {
@@ -133,6 +146,7 @@ export class NetworkCommand extends BaseCommand {
@inject(InjectTokens.KeyManager) private readonly keyManager: KeyManager,
@inject(InjectTokens.PlatformInstaller) private readonly platformInstaller: PlatformInstaller,
@inject(InjectTokens.ProfileManager) private readonly profileManager: ProfileManager,
+ @inject(InjectTokens.ComponentFactory) private readonly componentFactory: ComponentFactoryApi,
) {
super();
@@ -359,36 +373,7 @@ export class NetworkCommand extends BaseCommand {
* Prepare values args string for each cluster-ref
* @param config
*/
- async prepareValuesArgMap(config: {
- chartDirectory?: string;
- app?: string;
- nodeAliases: string[];
- debugNodeAlias?: NodeAlias;
- enablePrometheusSvcMonitor?: boolean;
- releaseTag?: string;
- persistentVolumeClaims?: string;
- valuesFile?: string;
- haproxyIpsParsed?: Record;
- envoyIpsParsed?: Record;
- storageType: constants.StorageType;
- resolvedThrottlesFile: string;
- gcsWriteAccessKey: string;
- gcsWriteSecrets: string;
- gcsEndpoint: string;
- gcsBucket: string;
- gcsBucketPrefix: string;
- awsWriteAccessKey: string;
- awsWriteSecrets: string;
- awsEndpoint: string;
- awsBucket: string;
- awsBucketPrefix: string;
- backupBucket: string;
- loadBalancerEnabled: boolean;
- clusterRefs: ClusterReferences;
- consensusNodes: ConsensusNode[];
- domainNamesMapping?: Record;
- cacheDir: string;
- }): Promise> {
+ private async prepareValuesArgMap(config: NetworkDeployConfigClass): Promise> {
const valuesArguments: Record = this.prepareValuesArg(config);
// prepare values files for each cluster
@@ -430,33 +415,7 @@ export class NetworkCommand extends BaseCommand {
* Prepare the values argument for the helm chart for a given config
* @param config
*/
- prepareValuesArg(config: {
- chartDirectory?: string;
- app?: string;
- consensusNodes: ConsensusNode[];
- debugNodeAlias?: NodeAlias;
- enablePrometheusSvcMonitor?: boolean;
- releaseTag?: string;
- persistentVolumeClaims?: string;
- valuesFile?: string;
- haproxyIpsParsed?: Record;
- envoyIpsParsed?: Record;
- storageType: constants.StorageType;
- resolvedThrottlesFile: string;
- gcsWriteAccessKey: string;
- gcsWriteSecrets: string;
- gcsEndpoint: string;
- gcsBucket: string;
- gcsBucketPrefix: string;
- awsWriteAccessKey: string;
- awsWriteSecrets: string;
- awsEndpoint: string;
- awsBucket: string;
- awsBucketPrefix: string;
- backupBucket: string;
- loadBalancerEnabled: boolean;
- domainNamesMapping?: Record;
- }): Record {
+ private prepareValuesArg(config: NetworkDeployConfigClass): Record {
const valuesArguments: Record = {};
const clusterReferences: ClusterReference[] = [];
let extraEnvironmentIndex = 0;
@@ -645,7 +604,10 @@ export class NetworkCommand extends BaseCommand {
}
}
- async prepareConfig(task: any, argv: any, promptForNodeAliases: boolean = false) {
+ private async prepareConfig(
+ task: SoloListrTaskWrapper,
+ argv: ArgvStruct,
+ ): Promise {
this.configManager.update(argv);
this.logger.debug('Updated config with argv', {config: this.configManager.config});
@@ -744,9 +706,9 @@ export class NetworkCommand extends BaseCommand {
flags.genesisThrottlesFile.definition.defaultValue as string,
);
- config.consensusNodes = this.remoteConfigManager.getConsensusNodes();
- config.contexts = this.remoteConfigManager.getContexts();
- config.clusterRefs = this.remoteConfigManager.getClusterRefs();
+ config.consensusNodes = this.remoteConfig.getConsensusNodes();
+ config.contexts = this.remoteConfig.getContexts();
+ config.clusterRefs = this.remoteConfig.getClusterRefs();
config.nodeAliases = parseNodeAliases(config.nodeAliasesUnparsed, config.consensusNodes, this.configManager);
argv[flags.nodeAliasesUnparsed.name] = config.nodeAliases.join(',');
@@ -796,7 +758,7 @@ export class NetworkCommand extends BaseCommand {
await Promise.all(
context_.config.contexts.map(async context => {
// Delete all if found
- this.k8Factory
+ await this.k8Factory
.getK8(context)
.configMaps()
.delete(context_.config.namespace, constants.SOLO_REMOTE_CONFIGMAP_NAME);
@@ -850,16 +812,12 @@ export class NetworkCommand extends BaseCommand {
const self = this;
const lease = await self.leaseManager.create();
- interface Context {
- config: NetworkDeployConfigClass;
- }
-
- const tasks = new Listr(
+ const tasks: Listr = new Listr(
[
{
title: 'Initialize',
task: async (context_, task) => {
- context_.config = await self.prepareConfig(task, argv, true);
+ context_.config = await self.prepareConfig(task, argv);
return ListrLock.newAcquireLockTask(lease, task);
},
},
@@ -965,7 +923,7 @@ export class NetworkCommand extends BaseCommand {
config.valuesArgMap[clusterReference],
config.clusterRefs.get(clusterReference),
);
- showVersionBanner(self.logger, SOLO_DEPLOYMENT_CHART, config.soloChartVersion);
+ showVersionBanner(self.logger, constants.SOLO_DEPLOYMENT_CHART, config.soloChartVersion);
}
},
},
@@ -973,9 +931,9 @@ export class NetworkCommand extends BaseCommand {
{
title: 'Check for load balancer',
skip: context_ => context_.config.loadBalancerEnabled === false,
- task: (context_, task) => {
- const subTasks: SoloListrTask[] = [];
- const config = context_.config;
+ task: (context_, task): SoloListr => {
+ const subTasks: SoloListrTask[] = [];
+ const config: NetworkDeployConfigClass = context_.config;
//Add check for network node service to be created and load balancer to be assigned (if load balancer is enabled)
for (const consensusNode of config.consensusNodes) {
@@ -1079,9 +1037,9 @@ export class NetworkCommand extends BaseCommand {
self.waitForNetworkPods(),
{
title: 'Check proxy pods are running',
- task: (context_, task) => {
- const subTasks: SoloListrTask[] = [];
- const config = context_.config;
+ task: (context_, task): SoloListr => {
+ const subTasks: SoloListrTask[] = [];
+ const config: NetworkDeployConfigClass = context_.config;
// HAProxy
for (const consensusNode of config.consensusNodes) {
@@ -1129,7 +1087,7 @@ export class NetworkCommand extends BaseCommand {
{
title: 'Check auxiliary pods are ready',
task: (_, task) => {
- const subTasks: SoloListrTask[] = [];
+ const subTasks: SoloListrTask[] = [];
// minio
subTasks.push({
@@ -1213,7 +1171,7 @@ export class NetworkCommand extends BaseCommand {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
enableTimeout: self.configManager.getFlag(flags.enableTimeout) as boolean,
force: self.configManager.getFlag(flags.force) as boolean,
- contexts: self.remoteConfigManager.getContexts(),
+ contexts: self.remoteConfig.getContexts(),
};
return ListrLock.newAcquireLockTask(lease, task);
@@ -1252,7 +1210,7 @@ export class NetworkCommand extends BaseCommand {
} else {
// If the namespace is not being deleted,
// remove all components data from the remote configuration
- await self.remoteConfigManager.deleteComponents();
+ await self.remoteConfig.deleteComponents();
}
}, constants.NETWORK_DESTROY_WAIT_TIMEOUT * 1000);
@@ -1346,33 +1304,27 @@ export class NetworkCommand extends BaseCommand {
}
/** Adds the consensus node, envoy and haproxy components to remote config. */
- public addNodesAndProxies(): SoloListrTask {
+ public addNodesAndProxies(): SoloListrTask {
return {
title: 'Add node and proxies to remote config',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async (context_): Promise => {
- const {
- config: {namespace},
- } = context_;
+ const {namespace} = context_.config;
- await this.remoteConfigManager.modify(async remoteConfig => {
+ await this.remoteConfig.modify(async (_, components) => {
for (const consensusNode of context_.config.consensusNodes) {
- remoteConfig.components.edit(
- new ConsensusNodeComponent(
- consensusNode.name,
- consensusNode.cluster,
- namespace.name,
- ConsensusNodeStates.REQUESTED,
- consensusNode.nodeId,
- ),
- );
+ const nodeId: NodeId = Templates.nodeIdFromNodeAlias(consensusNode.name);
+ const clusterReference: ClusterReference = consensusNode.cluster;
- remoteConfig.components.add(
- new EnvoyProxyComponent(`envoy-proxy-${consensusNode.name}`, consensusNode.cluster, namespace.name),
- );
+ components.changeNodePhase(nodeId, DeploymentPhase.REQUESTED);
- remoteConfig.components.add(
- new HaProxyComponent(`haproxy-${consensusNode.name}`, consensusNode.cluster, namespace.name),
+ components.addNewComponent(
+ this.componentFactory.createNewEnvoyProxyComponent(clusterReference, namespace),
+ ComponentTypes.EnvoyProxy,
+ );
+ components.addNewComponent(
+ this.componentFactory.createNewHaProxyComponent(clusterReference, namespace),
+ ComponentTypes.HaProxy,
);
}
});
diff --git a/src/commands/node/configs.ts b/src/commands/node/configs.ts
index 7b0f96231..13f9e27b0 100644
--- a/src/commands/node/configs.ts
+++ b/src/commands/node/configs.ts
@@ -18,7 +18,6 @@ import {InjectTokens} from '../../core/dependency-injection/inject-tokens.js';
import {type ConfigManager} from '../../core/config-manager.js';
import {patchInject} from '../../core/dependency-injection/container-helper.js';
import {type AccountManager} from '../../core/account-manager.js';
-import {type RemoteConfigManager} from '../../core/config/remote/remote-config-manager.js';
import {PathEx} from '../../business/utils/path-ex.js';
import {type NodeSetupConfigClass} from './config-interfaces/node-setup-config-class.js';
import {type NodeStartConfigClass} from './config-interfaces/node-start-config-class.js';
@@ -51,6 +50,7 @@ import {type NodeRestartContext} from './config-interfaces/node-restart-context.
import {type NodeSetupContext} from './config-interfaces/node-setup-context.js';
import {type NodePrepareUpgradeContext} from './config-interfaces/node-prepare-upgrade-context.js';
import {LocalConfigRuntimeState} from '../../business/runtime-state/config/local/local-config-runtime-state.js';
+import {type RemoteConfigRuntimeStateApi} from '../../business/runtime-state/api/remote-config-runtime-state-api.js';
const PREPARE_UPGRADE_CONFIGS_NAME = 'prepareUpgradeConfig';
const DOWNLOAD_GENERATED_FILES_CONFIGS_NAME = 'downloadGeneratedFilesConfig';
@@ -68,7 +68,7 @@ export class NodeCommandConfigs {
public constructor(
@inject(InjectTokens.ConfigManager) private readonly configManager: ConfigManager,
@inject(InjectTokens.LocalConfigRuntimeState) private readonly localConfig: LocalConfigRuntimeState,
- @inject(InjectTokens.RemoteConfigManager) private readonly remoteConfigManager: RemoteConfigManager,
+ @inject(InjectTokens.RemoteConfigRuntimeState) private readonly remoteConfig: RemoteConfigRuntimeStateApi,
@inject(InjectTokens.K8Factory) private readonly k8Factory: K8Factory,
@inject(InjectTokens.AccountManager) private readonly accountManager: AccountManager,
) {
@@ -76,11 +76,7 @@ export class NodeCommandConfigs {
this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
this.accountManager = patchInject(accountManager, InjectTokens.AccountManager, this.constructor.name);
- this.remoteConfigManager = patchInject(
- remoteConfigManager,
- InjectTokens.RemoteConfigManager,
- this.constructor.name,
- );
+ this.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name);
}
private async initializeSetup(config: AnyObject, k8Factory: K8Factory): Promise {
@@ -122,7 +118,7 @@ export class NodeCommandConfigs {
await this.initializeSetup(context_.config, this.k8Factory);
context_.config.nodeClient = await this.accountManager.loadNodeClient(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
@@ -181,7 +177,7 @@ export class NodeCommandConfigs {
context_.config.existingNodeAliases = [];
context_.config.nodeAliases = helpers.parseNodeAliases(
context_.config.nodeAliasesUnparsed,
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
);
@@ -190,7 +186,7 @@ export class NodeCommandConfigs {
if (shouldLoadNodeClient) {
context_.config.nodeClient = await this.accountManager.loadNodeClient(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
}
@@ -235,7 +231,7 @@ export class NodeCommandConfigs {
if (shouldLoadNodeClient) {
context_.config.nodeClient = await this.accountManager.loadNodeClient(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
}
@@ -293,7 +289,7 @@ export class NodeCommandConfigs {
if (shouldLoadNodeClient) {
context_.config.nodeClient = await this.accountManager.loadNodeClient(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
}
@@ -356,7 +352,7 @@ export class NodeCommandConfigs {
if (shouldLoadNodeClient) {
context_.config.nodeClient = await this.accountManager.loadNodeClient(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
}
@@ -377,12 +373,12 @@ export class NodeCommandConfigs {
context_.config.serviceMap = await this.accountManager.getNodeServiceMap(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
- context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes();
- context_.config.contexts = this.remoteConfigManager.getContexts();
+ context_.config.consensusNodes = this.remoteConfig.getConsensusNodes();
+ context_.config.contexts = this.remoteConfig.getContexts();
if (!context_.config.clusterRef) {
context_.config.clusterRef = this.k8Factory.default().clusters().readCurrent();
@@ -404,13 +400,13 @@ export class NodeCommandConfigs {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
nodeAliases: helpers.parseNodeAliases(
this.configManager.getFlag(flags.nodeAliasesUnparsed),
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
),
nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed),
deployment: this.configManager.getFlag(flags.deployment),
- consensusNodes: this.remoteConfigManager.getConsensusNodes(),
- contexts: this.remoteConfigManager.getContexts(),
+ consensusNodes: this.remoteConfig.getConsensusNodes(),
+ contexts: this.remoteConfig.getContexts(),
} as NodeLogsConfigClass;
return context_.config;
@@ -421,7 +417,7 @@ export class NodeCommandConfigs {
context_: NodeStatesContext,
task: SoloListrTaskWrapper,
): Promise {
- const consensusNodes: ConsensusNode[] = this.remoteConfigManager.getConsensusNodes();
+ const consensusNodes: ConsensusNode[] = this.remoteConfig.getConsensusNodes();
context_.config = {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
nodeAliases: helpers.parseNodeAliases(
@@ -432,7 +428,7 @@ export class NodeCommandConfigs {
nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed),
deployment: this.configManager.getFlag(flags.deployment),
consensusNodes,
- contexts: this.remoteConfigManager.getContexts(),
+ contexts: this.remoteConfig.getContexts(),
} as NodeStatesConfigClass;
return context_.config;
@@ -454,7 +450,7 @@ export class NodeCommandConfigs {
context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task);
context_.config.nodeAliases = helpers.parseNodeAliases(
context_.config.nodeAliasesUnparsed,
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
);
@@ -479,7 +475,7 @@ export class NodeCommandConfigs {
context_.config.curDate = new Date();
context_.config.nodeAliases = helpers.parseNodeAliases(
context_.config.nodeAliasesUnparsed,
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
);
@@ -496,7 +492,7 @@ export class NodeCommandConfigs {
context_: NodeStopContext,
task: SoloListrTaskWrapper,
): Promise {
- const consensusNodes: ConsensusNode[] = this.remoteConfigManager.getConsensusNodes();
+ const consensusNodes: ConsensusNode[] = this.remoteConfig.getConsensusNodes();
context_.config = {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
nodeAliases: helpers.parseNodeAliases(
@@ -507,7 +503,7 @@ export class NodeCommandConfigs {
nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed),
deployment: this.configManager.getFlag(flags.deployment),
consensusNodes,
- contexts: this.remoteConfigManager.getContexts(),
+ contexts: this.remoteConfig.getContexts(),
} as NodeStopConfigClass;
await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace);
@@ -522,8 +518,8 @@ export class NodeCommandConfigs {
context_.config = {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
deployment: this.configManager.getFlag(flags.deployment),
- consensusNodes: this.remoteConfigManager.getConsensusNodes(),
- contexts: this.remoteConfigManager.getContexts(),
+ consensusNodes: this.remoteConfig.getConsensusNodes(),
+ contexts: this.remoteConfig.getContexts(),
} as NodeFreezeConfigClass;
await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace);
@@ -550,7 +546,7 @@ export class NodeCommandConfigs {
'contexts',
]) as NodeStartConfigClass;
context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task);
- context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes();
+ context_.config.consensusNodes = this.remoteConfig.getConsensusNodes();
for (const consensusNode of context_.config.consensusNodes) {
const k8 = this.k8Factory.getK8(consensusNode.context);
@@ -576,8 +572,8 @@ export class NodeCommandConfigs {
context_.config = {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
deployment: this.configManager.getFlag(flags.deployment),
- consensusNodes: this.remoteConfigManager.getConsensusNodes(),
- contexts: this.remoteConfigManager.getContexts(),
+ consensusNodes: this.remoteConfig.getConsensusNodes(),
+ contexts: this.remoteConfig.getContexts(),
} as NodeRestartConfigClass;
await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace);
@@ -599,7 +595,7 @@ export class NodeCommandConfigs {
]) as NodeSetupConfigClass;
context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task);
- context_.config.consensusNodes = this.remoteConfigManager.getConsensusNodes();
+ context_.config.consensusNodes = this.remoteConfig.getConsensusNodes();
context_.config.nodeAliases = helpers.parseNodeAliases(
context_.config.nodeAliasesUnparsed,
context_.config.consensusNodes,
diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts
index 501b37931..1a09521cb 100644
--- a/src/commands/node/handlers.ts
+++ b/src/commands/node/handlers.ts
@@ -5,17 +5,14 @@ import * as NodeFlags from './flags.js';
import {type NodeCommandConfigs} from './configs.js';
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 {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 {ConsensusNodeComponent} from '../../core/config/remote/components/consensus-node-component.js';
+import {type ArgvStruct, type NodeAlias, type NodeAliases, NodeId} from '../../types/aliases.js';
import {type Listr} from 'listr2';
import chalk from 'chalk';
-import {type ComponentsDataWrapper} from '../../core/config/remote/components-data-wrapper.js';
import {type Optional, type SoloListrTask} from '../../types/index.js';
import {inject, injectable} from 'tsyringe-neo';
import {patchInject} from '../../core/dependency-injection/container-helper.js';
@@ -28,7 +25,11 @@ 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';
+import {DeploymentPhase} from '../../data/schema/model/remote/deployment-phase.js';
+import {Templates} from '../../core/templates.js';
+import {ConsensusNodeStateSchema} from '../../data/schema/model/remote/state/consensus-node-state-schema.js';
+import {type RemoteConfigRuntimeStateApi} from '../../business/runtime-state/api/remote-config-runtime-state-api.js';
+import {ComponentsDataWrapperApi} from '../../core/config/remote/api/components-data-wrapper-api.js';
@injectable()
export class NodeCommandHandlers extends CommandHandler {
@@ -37,18 +38,14 @@ export class NodeCommandHandlers extends CommandHandler {
public constructor(
@inject(InjectTokens.LockManager) private readonly leaseManager: LockManager,
- @inject(InjectTokens.RemoteConfigManager) private readonly remoteConfigManager: RemoteConfigManager,
+ @inject(InjectTokens.RemoteConfigRuntimeState) private readonly remoteConfig: RemoteConfigRuntimeStateApi,
@inject(InjectTokens.NodeCommandTasks) private readonly tasks: NodeCommandTasks,
@inject(InjectTokens.NodeCommandConfigs) private readonly configs: NodeCommandConfigs,
) {
super();
this.leaseManager = patchInject(leaseManager, InjectTokens.LockManager, this.constructor.name);
this.configs = patchInject(configs, InjectTokens.NodeCommandConfigs, this.constructor.name);
- this.remoteConfigManager = patchInject(
- remoteConfigManager,
- InjectTokens.RemoteConfigManager,
- this.constructor.name,
- );
+ this.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name);
this.tasks = patchInject(tasks, InjectTokens.NodeCommandTasks, this.constructor.name);
}
@@ -58,8 +55,8 @@ export class NodeCommandHandlers extends CommandHandler {
private static readonly UPGRADE_CONTEXT_FILE = 'node-upgrade.json';
private init() {
- this.consensusNodes = this.remoteConfigManager.getConsensusNodes();
- this.contexts = this.remoteConfigManager.getContexts();
+ this.consensusNodes = this.remoteConfig.getConsensusNodes();
+ this.contexts = this.remoteConfig.getContexts();
}
/** ******** Task Lists **********/
@@ -67,7 +64,7 @@ export class NodeCommandHandlers extends CommandHandler {
private deletePrepareTaskList(argv: ArgvStruct, lease: Lock): SoloListrTask[] {
return [
this.tasks.initialize(argv, this.configs.deleteConfigBuilder.bind(this.configs), lease),
- this.validateSingleNodeState({excludedStates: []}),
+ this.validateSingleNodeState({excludedPhases: []}),
this.tasks.identifyExistingNodes(),
this.tasks.loadAdminKey(),
this.tasks.prepareUpgradeZip(),
@@ -113,7 +110,7 @@ export class NodeCommandHandlers extends CommandHandler {
this.tasks.initialize(argv, this.configs.addConfigBuilder.bind(this.configs), lease),
// TODO instead of validating the state we need to do a remote config add component, and we will need to manually
// the nodeAlias based on the next available node ID + 1
- // this.validateSingleNodeState({excludedStates: []}),
+ // this.validateSingleNodeState({excludedPhases: []}),
this.tasks.checkPVCsEnabled(),
this.tasks.identifyExistingNodes(),
this.tasks.determineNewNodeAccountNumber(),
@@ -166,7 +163,7 @@ export class NodeCommandHandlers extends CommandHandler {
private updatePrepareTasks(argv: ArgvStruct, lease: Lock): SoloListrTask[] {
return [
this.tasks.initialize(argv, this.configs.updateConfigBuilder.bind(this.configs), lease),
- this.validateSingleNodeState({excludedStates: []}),
+ this.validateSingleNodeState({excludedPhases: []}),
this.tasks.identifyExistingNodes(),
this.tasks.loadAdminKey(),
this.tasks.prepareUpgradeZip(),
@@ -210,7 +207,7 @@ export class NodeCommandHandlers extends CommandHandler {
private upgradePrepareTasks(argv: ArgvStruct, lease: Lock): SoloListrTask[] {
return [
this.tasks.initialize(argv, this.configs.upgradeConfigBuilder.bind(this.configs), lease),
- this.validateAllNodeStates({excludedStates: []}),
+ this.validateAllNodePhases({excludedPhases: []}),
this.tasks.identifyExistingNodes(),
this.tasks.loadAdminKey(),
this.tasks.prepareUpgradeZip(),
@@ -717,8 +714,8 @@ export class NodeCommandHandlers extends CommandHandler {
argv,
[
this.tasks.initialize(argv, this.configs.refreshConfigBuilder.bind(this.configs), lease),
- this.validateAllNodeStates({
- acceptedStates: [ConsensusNodeStates.STARTED, ConsensusNodeStates.SETUP, ConsensusNodeStates.INITIALIZED],
+ this.validateAllNodePhases({
+ acceptedPhases: [DeploymentPhase.STARTED, DeploymentPhase.CONFIGURED, DeploymentPhase.DEPLOYED],
}),
this.tasks.identifyNetworkPods(),
this.tasks.dumpNetworkNodesSaveState(),
@@ -771,12 +768,12 @@ export class NodeCommandHandlers extends CommandHandler {
argv,
[
this.tasks.initialize(argv, this.configs.stopConfigBuilder.bind(this.configs), lease),
- this.validateAllNodeStates({
- acceptedStates: [ConsensusNodeStates.STARTED, ConsensusNodeStates.SETUP],
+ this.validateAllNodePhases({
+ acceptedPhases: [DeploymentPhase.STARTED, DeploymentPhase.CONFIGURED],
}),
this.tasks.identifyNetworkPods(1),
this.tasks.stopNodes('nodeAliases'),
- this.changeAllNodeStates(ConsensusNodeStates.INITIALIZED),
+ this.changeAllNodePhases(DeploymentPhase.STARTED),
],
{
concurrent: false,
@@ -798,14 +795,14 @@ export class NodeCommandHandlers extends CommandHandler {
argv,
[
this.tasks.initialize(argv, this.configs.startConfigBuilder.bind(this.configs), lease),
- this.validateAllNodeStates({acceptedStates: [ConsensusNodeStates.SETUP]}),
+ this.validateAllNodePhases({acceptedPhases: [DeploymentPhase.CONFIGURED]}),
this.tasks.identifyExistingNodes(),
this.tasks.uploadStateFiles(context_ => context_.config.stateFile.length === 0),
this.tasks.startNodes('nodeAliases'),
this.tasks.enablePortForwarding(),
this.tasks.checkAllNodesAreActive('nodeAliases'),
this.tasks.checkNodeProxiesAreActive(),
- this.changeAllNodeStates(ConsensusNodeStates.STARTED),
+ this.changeAllNodePhases(DeploymentPhase.STARTED),
this.tasks.addNodeStakes(),
],
{
@@ -828,13 +825,13 @@ export class NodeCommandHandlers extends CommandHandler {
argv,
[
this.tasks.initialize(argv, this.configs.setupConfigBuilder.bind(this.configs), lease),
- this.validateAllNodeStates({
- acceptedStates: [ConsensusNodeStates.INITIALIZED],
+ this.validateAllNodePhases({
+ acceptedPhases: [DeploymentPhase.DEPLOYED],
}),
this.tasks.identifyNetworkPods(),
this.tasks.fetchPlatformSoftware('nodeAliases'),
this.tasks.setupNetworkNodes('nodeAliases', true),
- this.changeAllNodeStates(ConsensusNodeStates.SETUP),
+ this.changeAllNodePhases(DeploymentPhase.CONFIGURED),
],
{
concurrent: false,
@@ -859,7 +856,7 @@ export class NodeCommandHandlers extends CommandHandler {
this.tasks.sendFreezeTransaction(),
this.tasks.checkAllNodesAreFrozen('existingNodeAliases'),
this.tasks.stopNodes('existingNodeAliases'),
- this.changeAllNodeStates(ConsensusNodeStates.INITIALIZED),
+ this.changeAllNodePhases(DeploymentPhase.FROZEN),
],
{
concurrent: false,
@@ -885,7 +882,7 @@ export class NodeCommandHandlers extends CommandHandler {
this.tasks.enablePortForwarding(),
this.tasks.checkAllNodesAreActive('existingNodeAliases'),
this.tasks.checkNodeProxiesAreActive(),
- this.changeAllNodeStates(ConsensusNodeStates.STARTED),
+ this.changeAllNodePhases(DeploymentPhase.STARTED),
],
{
concurrent: false,
@@ -898,52 +895,24 @@ 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', ComponentTypes.ConsensusNode);
- remoteConfig.components.remove('Envoy proxy name', ComponentTypes.EnvoyProxy);
- remoteConfig.components.remove('HaProxy name', ComponentTypes.HaProxy);
- });
- },
- };
- }
-
/**
* Changes the state from all consensus nodes components in remote config.
*
- * @param state - to which to change the consensus node component
+ * @param phase - to which to change the consensus node component
*/
- public changeAllNodeStates(state: ConsensusNodeStates): SoloListrTask {
+ public changeAllNodePhases(phase: DeploymentPhase): SoloListrTask {
interface Context {
config: {namespace: NamespaceName; consensusNodes: ConsensusNode[]};
}
return {
- title: `Change node state to ${state} in remote config`,
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ title: `Change node state to ${phase} in remote config`,
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async (context_: Context): Promise => {
- await this.remoteConfigManager.modify(async remoteConfig => {
- const {
- config: {namespace},
- } = context_;
-
+ await this.remoteConfig.modify(async (_, components) => {
for (const consensusNode of context_.config.consensusNodes) {
- remoteConfig.components.edit(
- new ConsensusNodeComponent(
- consensusNode.name,
- consensusNode.cluster,
- namespace.name,
- state,
- consensusNode.nodeId,
- ),
- );
+ const nodeId: NodeId = Templates.nodeIdFromNodeAlias(consensusNode.name);
+ components.changeNodePhase(nodeId, phase);
}
});
},
@@ -953,15 +922,15 @@ export class NodeCommandHandlers extends CommandHandler {
/**
* Creates tasks to validate that each node state is either one of the accepted states or not one of the excluded.
*
- * @param acceptedStates - the state at which the nodes can be, not matching any of the states throws an error
- * @param excludedStates - the state at which the nodes can't be, matching any of the states throws an error
+ * @param acceptedPhases - the state at which the nodes can be, not matching any of the states throws an error
+ * @param excludedPhases - the state at which the nodes can't be, matching any of the states throws an error
*/
- public validateAllNodeStates({
- acceptedStates,
- excludedStates,
+ public validateAllNodePhases({
+ acceptedPhases,
+ excludedPhases,
}: {
- acceptedStates?: ConsensusNodeStates[];
- excludedStates?: ConsensusNodeStates[];
+ acceptedPhases?: DeploymentPhase[];
+ excludedPhases?: DeploymentPhase[];
}): SoloListrTask {
interface Context {
config: {namespace: string; nodeAliases: NodeAliases};
@@ -969,16 +938,16 @@ export class NodeCommandHandlers extends CommandHandler {
return {
title: 'Validate nodes states',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: (context_: Context, task): Listr => {
const nodeAliases = context_.config.nodeAliases;
- const components = this.remoteConfigManager.components;
+ const components = this.remoteConfig.components;
const subTasks: SoloListrTask[] = nodeAliases.map(nodeAlias => ({
title: `Validating state for node ${nodeAlias}`,
task: (_, task): void => {
- const state = this.validateNodeState(nodeAlias, components, acceptedStates, excludedStates);
+ const state = this.validateNodeState(nodeAlias, components, acceptedPhases, excludedPhases);
task.title += ` - ${chalk.green('valid state')}: ${chalk.cyan(state)}`;
},
@@ -995,15 +964,15 @@ export class NodeCommandHandlers extends CommandHandler {
/**
* Creates tasks to validate that specific node state is either one of the accepted states or not one of the excluded.
*
- * @param acceptedStates - the state at which the node can be, not matching any of the states throws an error
- * @param excludedStates - the state at which the node can't be, matching any of the states throws an error
+ * @param acceptedPhases - the state at which the node can be, not matching any of the states throws an error
+ * @param excludedPhases - the state at which the node can't be, matching any of the states throws an error
*/
public validateSingleNodeState({
- acceptedStates,
- excludedStates,
+ acceptedPhases,
+ excludedPhases,
}: {
- acceptedStates?: ConsensusNodeStates[];
- excludedStates?: ConsensusNodeStates[];
+ acceptedPhases?: DeploymentPhase[];
+ excludedPhases?: DeploymentPhase[];
}): SoloListrTask {
interface Context {
config: {namespace: string; nodeAlias: NodeAlias};
@@ -1011,15 +980,15 @@ export class NodeCommandHandlers extends CommandHandler {
return {
title: 'Validate nodes state',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: (context_: Context, task): void => {
const nodeAlias = context_.config.nodeAlias;
task.title += ` ${nodeAlias}`;
// TODO: Disabled for now until the node's state mapping is completed
- // const components = this.remoteConfigManager.components;
- // const state = this.validateNodeState(nodeAlias, components, acceptedStates, excludedStates);
+ // const components = this.remoteConfig.components;
+ // const state = this.validateNodeState(nodeAlias, components, acceptedPhases, excludedPhases);
// task.title += ` - ${chalk.green('valid state')}: ${chalk.cyan(state)}`;
},
};
@@ -1028,37 +997,40 @@ export class NodeCommandHandlers extends CommandHandler {
/**
* @param nodeAlias - the alias of the node whose state to validate
* @param components - the component data wrapper
- * @param acceptedStates - the state at which the node can be, not matching any of the states throws an error
- * @param excludedStates - the state at which the node can't be, matching any of the states throws an error
+ * @param acceptedPhases - the state at which the node can be, not matching any of the states throws an error
+ * @param excludedPhases - the state at which the node can't be, matching any of the states throws an error
*/
private validateNodeState(
nodeAlias: NodeAlias,
- components: ComponentsDataWrapper,
- acceptedStates: Optional,
- excludedStates: Optional,
- ): ConsensusNodeStates {
- let nodeComponent: ConsensusNodeComponent;
+ components: ComponentsDataWrapperApi,
+ acceptedPhases: Optional,
+ excludedPhases: Optional,
+ ): DeploymentPhase {
+ let nodeComponent: ConsensusNodeStateSchema;
try {
- nodeComponent = components.getComponent(ComponentTypes.ConsensusNode, nodeAlias);
+ nodeComponent = components.getComponent(
+ ComponentTypes.ConsensusNode,
+ Templates.nodeIdFromNodeAlias(nodeAlias),
+ );
} catch {
throw new SoloError(`${nodeAlias} not found in remote config`);
}
// TODO: Enable once the states have been mapped
- // if (acceptedStates && !acceptedStates.includes(nodeComponent.state)) {
+ // if (acceptedPhases && !acceptedPhases.includes(nodeComponent.state)) {
// const errorMessageData =
- // `accepted states: ${acceptedStates.join(', ')}, ` + `current state: ${nodeComponent.state}`;
+ // `accepted states: ${acceptedPhases.join(', ')}, ` + `current state: ${nodeComponent.state}`;
//
// throw new SoloError(`${nodeAlias} has invalid state - ` + errorMessageData);
// }
//
- // if (excludedStates && excludedStates.includes(nodeComponent.state)) {
+ // if (excludedPhases && excludedPhases.includes(nodeComponent.state)) {
// const errorMessageData =
- // `excluded states: ${excludedStates.join(', ')}, ` + `current state: ${nodeComponent.state}`;
+ // `excluded states: ${excludedPhases.join(', ')}, ` + `current state: ${nodeComponent.state}`;
//
// throw new SoloError(`${nodeAlias} has invalid state - ` + errorMessageData);
// }
- return nodeComponent.state;
+ return nodeComponent.metadata.phase;
}
}
diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts
index 233b457d5..7b2ea15cc 100644
--- a/src/commands/node/tasks.ts
+++ b/src/commands/node/tasks.ts
@@ -79,8 +79,8 @@ import {NetworkNodes} from '../../core/network-nodes.js';
import {container, inject, injectable} from 'tsyringe-neo';
import {
type ClusterReference,
+ type Context,
type DeploymentName,
- type NamespaceNameAsString,
type Optional,
type SoloListr,
type SoloListrTask,
@@ -91,11 +91,7 @@ import {ConsensusNode} from '../../core/model/consensus-node.js';
import {type K8} from '../../integration/kube/k8.js';
import {Base64} from 'js-base64';
import {InjectTokens} from '../../core/dependency-injection/inject-tokens.js';
-import {type RemoteConfigManager} from '../../core/config/remote/remote-config-manager.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';
@@ -119,8 +115,12 @@ 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 {LocalConfigRuntimeState} from '../../business/runtime-state/config/local/local-config-runtime-state.js';
-import {ConsensusNodeStates} from '../../core/config/remote/enumerations/consensus-node-states.js';
+import {ComponentTypes} from '../../core/config/remote/enumerations/component-types.js';
+import {DeploymentPhase} from '../../data/schema/model/remote/deployment-phase.js';
+import {type RemoteConfigRuntimeStateApi} from '../../business/runtime-state/api/remote-config-runtime-state-api.js';
+import {type ComponentFactoryApi} from '../../core/config/remote/api/component-factory-api.js';
+import {type LocalConfigRuntimeState} from '../../business/runtime-state/config/local/local-config-runtime-state.js';
+import {ClusterSchema} from '../../data/schema/model/common/cluster-schema.js';
@injectable()
export class NodeCommandTasks {
@@ -134,8 +134,9 @@ export class NodeCommandTasks {
@inject(InjectTokens.ProfileManager) private readonly profileManager: ProfileManager,
@inject(InjectTokens.ChartManager) private readonly chartManager: ChartManager,
@inject(InjectTokens.CertificateManager) private readonly certificateManager: CertificateManager,
- @inject(InjectTokens.RemoteConfigManager) private readonly remoteConfigManager: RemoteConfigManager,
+ @inject(InjectTokens.RemoteConfigRuntimeState) private readonly remoteConfig: RemoteConfigRuntimeStateApi,
@inject(InjectTokens.LocalConfigRuntimeState) private readonly localConfig: LocalConfigRuntimeState,
+ @inject(InjectTokens.ComponentFactory) private readonly componentFactory: ComponentFactoryApi,
) {
this.logger = patchInject(logger, InjectTokens.SoloLogger, this.constructor.name);
this.accountManager = patchInject(accountManager, InjectTokens.AccountManager, this.constructor.name);
@@ -147,11 +148,7 @@ export class NodeCommandTasks {
this.chartManager = patchInject(chartManager, InjectTokens.ChartManager, this.constructor.name);
this.certificateManager = patchInject(certificateManager, InjectTokens.CertificateManager, this.constructor.name);
this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
- this.remoteConfigManager = patchInject(
- remoteConfigManager,
- InjectTokens.RemoteConfigManager,
- this.constructor.name,
- );
+ this.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name);
}
private getFileUpgradeId(deploymentName: DeploymentName): FileId {
@@ -446,7 +443,7 @@ export class NodeCommandTasks {
const podReference = PodReference.of(namespace, podName);
task.title = `${title} - status ${chalk.yellow('STARTING')}, attempt ${chalk.blueBright(`0/${maxAttempts}`)}`;
- const consensusNodes = this.remoteConfigManager.getConsensusNodes();
+ const consensusNodes = this.remoteConfig.getConsensusNodes();
if (!context) {
context = helpers.extractContextFromConsensusNodes(nodeAlias, consensusNodes);
}
@@ -638,7 +635,7 @@ export class NodeCommandTasks {
const deploymentName = this.configManager.getFlag(flags.deployment);
await this.accountManager.loadNodeClient(
namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
deploymentName,
this.configManager.getFlag(flags.forcePortForward),
);
@@ -849,7 +846,7 @@ export class NodeCommandTasks {
try {
const nodeClient = await this.accountManager.loadNodeClient(
namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
deployment,
);
const futureDate = new Date();
@@ -1078,7 +1075,7 @@ export class NodeCommandTasks {
task: async (context_, task) => {
const config = context_.config;
config.existingNodeAliases = [];
- const clusterReferences = this.remoteConfigManager.getClusterRefs();
+ const clusterReferences = this.remoteConfig.getClusterRefs();
config.serviceMap = await self.accountManager.getNodeServiceMap(
config.namespace,
clusterReferences,
@@ -1192,7 +1189,7 @@ export class NodeCommandTasks {
task: async context_ => {
context_.config.serviceMap = await this.accountManager.getNodeServiceMap(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.deployment,
);
if (!context_.config.serviceMap.has(context_.config.nodeAlias)) {
@@ -1220,7 +1217,7 @@ export class NodeCommandTasks {
context_.config.nodeAliases = helpers.parseNodeAliases(
// @ts-ignore
context_.config.nodeAliasesUnparsed,
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
);
}
@@ -1277,7 +1274,7 @@ export class NodeCommandTasks {
const deploymentName = this.configManager.getFlag(flags.deployment);
const networkNodeServiceMap = await this.accountManager.getNodeServiceMap(
namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
deploymentName,
);
@@ -1305,7 +1302,7 @@ export class NodeCommandTasks {
const deploymentName = this.configManager.getFlag(flags.deployment);
const networkNodeServiceMap = await this.accountManager.getNodeServiceMap(
namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
deploymentName,
);
@@ -1509,7 +1506,7 @@ export class NodeCommandTasks {
config.nodeClient = await self.accountManager.refreshNodeClient(
config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
skipNodeAlias,
this.configManager.getFlag(flags.deployment),
);
@@ -1569,7 +1566,7 @@ export class NodeCommandTasks {
task: async context_ => {
await self.accountManager.refreshNodeClient(
context_.config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
context_.config.nodeAlias,
this.configManager.getFlag(flags.deployment),
this.configManager.getFlag(flags.forcePortForward),
@@ -1866,7 +1863,7 @@ export class NodeCommandTasks {
if (config.existingNodeAliases.length > 1) {
config.nodeClient = await self.accountManager.refreshNodeClient(
config.namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
config.nodeAlias,
this.configManager.getFlag(flags.deployment),
);
@@ -1957,7 +1954,7 @@ export class NodeCommandTasks {
// Prepare parameter and update the network node chart
const config = context_.config;
const consensusNodes = context_.config.consensusNodes as ConsensusNode[];
- const clusterReferences = this.remoteConfigManager.getClusterRefs();
+ const clusterReferences = this.remoteConfig.getClusterRefs();
// Make sure valuesArgMap is initialized with empty strings
const valuesArgumentMap: Record = {};
@@ -2291,7 +2288,7 @@ export class NodeCommandTasks {
title: 'Kill nodes to pick up updated configMaps',
task: async context_ => {
const config = context_.config;
- const clusterReferences = this.remoteConfigManager.getClusterRefs();
+ const clusterReferences = this.remoteConfig.getClusterRefs();
// the updated node will have a new pod ID if its account ID changed which is a label
config.serviceMap = await this.accountManager.getNodeServiceMap(
config.namespace,
@@ -2531,8 +2528,8 @@ export class NodeCommandTasks {
const config = await configInit(argv, context_, task, shouldLoadNodeClient);
context_.config = config;
- config.consensusNodes = this.remoteConfigManager.getConsensusNodes();
- config.contexts = this.remoteConfigManager.getContexts();
+ config.consensusNodes = this.remoteConfig.getConsensusNodes();
+ config.contexts = this.remoteConfig.getContexts();
for (const flag of required) {
if (config[flag.constName] === undefined) {
@@ -2553,48 +2550,53 @@ export class NodeCommandTasks {
return {
title: 'Add new node to remote config',
task: async (context_, task) => {
- const nodeAlias = context_.config.nodeAlias;
- const namespace: NamespaceNameAsString = context_.config.namespace.name;
- const clusterReference = context_.config.clusterRef;
- const context = this.localConfig.configuration.clusterRefs.get(clusterReference);
+ const nodeAlias: NodeAlias = context_.config.nodeAlias;
+ const nodeId: NodeId = Templates.nodeIdFromNodeAlias(nodeAlias);
+ const namespace: NamespaceName = context_.config.namespace;
+ const clusterReference: ClusterReference = context_.config.clusterRef;
+ const context: Context = this.localConfig.configuration.clusterRefs.get(clusterReference)?.toString();
task.title += `: ${nodeAlias}`;
- await this.remoteConfigManager.modify(async remoteConfig => {
- remoteConfig.components.add(
- new ConsensusNodeComponent(
- nodeAlias,
+ await this.remoteConfig.modify(async (_, components) => {
+ components.addNewComponent(
+ this.componentFactory.createNewConsensusNodeComponent(
+ nodeId,
clusterReference,
namespace,
- ConsensusNodeStates.STARTED,
- Templates.nodeIdFromNodeAlias(nodeAlias),
+ DeploymentPhase.STARTED,
),
+ ComponentTypes.ConsensusNode,
+ );
+ components.addNewComponent(
+ this.componentFactory.createNewEnvoyProxyComponent(clusterReference, namespace),
+ ComponentTypes.EnvoyProxy,
+ );
+ components.addNewComponent(
+ this.componentFactory.createNewHaProxyComponent(clusterReference, namespace),
+ ComponentTypes.HaProxy,
);
-
- 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();
+ context_.config.consensusNodes = this.remoteConfig.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: ClusterSchema = this.remoteConfig.clusters.find(cluster => cluster.name === clusterReference);
context_.config.consensusNodes.push(
new ConsensusNode(
nodeAlias,
- Templates.nodeIdFromNodeAlias(nodeAlias),
- namespace,
+ nodeId,
+ namespace.name,
clusterReference,
context.toString(),
cluster.dnsBaseDomain,
cluster.dnsConsensusNodePattern,
Templates.renderConsensusNodeFullyQualifiedDomainName(
nodeAlias,
- Templates.nodeIdFromNodeAlias(nodeAlias),
- namespace,
+ nodeId,
+ namespace.name,
clusterReference,
cluster.dnsBaseDomain,
cluster.dnsConsensusNodePattern,
diff --git a/src/commands/relay.ts b/src/commands/relay.ts
index 762873370..5492e5f72 100644
--- a/src/commands/relay.ts
+++ b/src/commands/relay.ts
@@ -11,11 +11,9 @@ import {type AccountManager} from '../core/account-manager.js';
import {BaseCommand} from './base.js';
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 {type AnyYargs, type ArgvStruct, type NodeAlias, type NodeAliases, type NodeId} 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 '../types/namespace/namespace-name.js';
import {
type ClusterReference,
type CommandDefinition,
@@ -28,6 +26,10 @@ import {inject, injectable} from 'tsyringe-neo';
import {InjectTokens} from '../core/dependency-injection/inject-tokens.js';
import {patchInject} from '../core/dependency-injection/container-helper.js';
import {ComponentTypes} from '../core/config/remote/enumerations/component-types.js';
+import {Templates} from '../core/templates.js';
+import {NamespaceName} from '../types/namespace/namespace-name.js';
+import {type RelayNodeStateSchema} from '../data/schema/model/remote/state/relay-node-state-schema.js';
+import {type ComponentFactoryApi} from '../core/config/remote/api/component-factory-api.js';
interface RelayDestroyConfigClass {
chartDirectory: string;
@@ -75,6 +77,7 @@ export class RelayCommand extends BaseCommand {
public constructor(
@inject(InjectTokens.ProfileManager) private readonly profileManager: ProfileManager,
@inject(InjectTokens.AccountManager) private readonly accountManager: AccountManager,
+ @inject(InjectTokens.ComponentFactory) private readonly componentFactory: ComponentFactoryApi,
) {
super();
@@ -213,7 +216,7 @@ export class RelayCommand extends BaseCommand {
const accountMap = this.accountManager.getNodeAccountMap(nodeAliases, deploymentName);
const networkNodeServicesMap = await this.accountManager.getNodeServiceMap(
namespace,
- this.remoteConfigManager.getClusterRefs(),
+ this.remoteConfig.getClusterRefs(),
deploymentName,
);
for (const nodeAlias of nodeAliases) {
@@ -276,13 +279,13 @@ export class RelayCommand extends BaseCommand {
);
context_.config.nodeAliases = helpers.parseNodeAliases(
context_.config.nodeAliasesUnparsed,
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
);
context_.config.releaseName = self.prepareReleaseName(context_.config.nodeAliases);
if (context_.config.clusterRef) {
- const context = self.remoteConfigManager.getClusterRefs()[context_.config.clusterRef];
+ const context = self.remoteConfig.getClusterRefs()[context_.config.clusterRef];
if (context) {
context_.config.context = context;
}
@@ -311,7 +314,7 @@ export class RelayCommand extends BaseCommand {
const config = context_.config;
await self.accountManager.loadNodeClient(
context_.config.namespace,
- self.remoteConfigManager.getClusterRefs(),
+ self.remoteConfig.getClusterRefs(),
self.configManager.getFlag(flags.deployment),
self.configManager.getFlag(flags.forcePortForward),
);
@@ -430,14 +433,14 @@ export class RelayCommand extends BaseCommand {
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
nodeAliases: helpers.parseNodeAliases(
self.configManager.getFlag(flags.nodeAliasesUnparsed) as string,
- this.remoteConfigManager.getConsensusNodes(),
+ this.remoteConfig.getConsensusNodes(),
this.configManager,
),
clusterRef: self.configManager.getFlag(flags.clusterRef) as string,
} as RelayDestroyConfigClass;
if (context_.config.clusterRef) {
- const context = self.remoteConfigManager.getClusterRefs()[context_.config.clusterRef];
+ const context = self.remoteConfig.getClusterRefs()[context_.config.clusterRef];
if (context) {
context_.config.context = context;
}
@@ -460,7 +463,7 @@ export class RelayCommand extends BaseCommand {
task: async context_ => {
const config = context_.config;
- await this.chartManager.uninstall(config.namespace, config.releaseName, context_.config.context);
+ await this.chartManager.uninstall(config.namespace, config.releaseName, config.context);
this.logger.showList(
'Destroyed Relays',
@@ -472,7 +475,7 @@ export class RelayCommand extends BaseCommand {
},
skip: context_ => !context_.config.isChartInstalled,
},
- this.removeRelayComponent(),
+ this.disableRelayComponent(),
],
{
concurrent: false,
@@ -546,29 +549,44 @@ export class RelayCommand extends BaseCommand {
public addRelayComponent(): SoloListrTask {
return {
title: 'Add relay component in remote config',
- skip: (): boolean => !this.remoteConfigManager.isLoaded(),
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async (context_): Promise => {
- await this.remoteConfigManager.modify(async remoteConfig => {
- const {
- config: {namespace, nodeAliases},
- } = context_;
- const cluster = this.remoteConfigManager.currentCluster;
-
- remoteConfig.components.add(new RelayComponent('relay', cluster, namespace.name, nodeAliases));
- });
+ const {namespace, nodeAliases, clusterRef} = context_.config;
+
+ const nodeIds: NodeId[] = nodeAliases.map((nodeAlias: NodeAlias) => Templates.nodeIdFromNodeAlias(nodeAlias));
+
+ this.remoteConfig.configuration.components.addNewComponent(
+ this.componentFactory.createNewRelayComponent(clusterRef, namespace, nodeIds),
+ ComponentTypes.RelayNodes,
+ );
+
+ await this.remoteConfig.persist();
},
};
}
/** 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.remove('relay', ComponentTypes.Relay);
- });
+ skip: (): boolean => !this.remoteConfig.isLoaded(),
+ task: async (context_): Promise => {
+ const clusterReference: ClusterReference = context_.config.clusterRef;
+
+ const relayComponents: RelayNodeStateSchema[] =
+ this.remoteConfig.configuration.components.getComponentsByClusterReference(
+ ComponentTypes.RelayNodes,
+ clusterReference,
+ );
+
+ for (const relayComponent of relayComponents) {
+ this.remoteConfig.configuration.components.removeComponent(
+ relayComponent.metadata.id,
+ ComponentTypes.RelayNodes,
+ );
+ }
+
+ await this.remoteConfig.persist();
},
};
}
diff --git a/src/core/account-manager.ts b/src/core/account-manager.ts
index f06c06bad..ad9b85803 100644
--- a/src/core/account-manager.ts
+++ b/src/core/account-manager.ts
@@ -46,12 +46,12 @@ import {InjectTokens} from './dependency-injection/inject-tokens.js';
import {type ClusterReferences, type DeploymentName, Realm, Shard} from './../types/index.js';
import {type Service} from '../integration/kube/resources/service/service.js';
import {SoloService} from './model/solo-service.js';
-import {type RemoteConfigManager} from './config/remote/remote-config-manager.js';
import {PathEx} from '../business/utils/path-ex.js';
import {type NodeServiceMapping} from '../types/mappings/node-service-mapping.js';
import {type ConsensusNode} from './model/consensus-node.js';
import {NetworkNodeServicesBuilder} from './network-node-services-builder.js';
import {LocalConfigRuntimeState} from '../business/runtime-state/config/local/local-config-runtime-state.js';
+import {type RemoteConfigRuntimeStateApi} from '../business/runtime-state/api/remote-config-runtime-state-api.js';
const REASON_FAILED_TO_GET_KEYS = 'failed to get keys for accountId';
const REASON_SKIPPED = 'skipped since it does not have a genesis key';
@@ -69,16 +69,12 @@ export class AccountManager {
constructor(
@inject(InjectTokens.SoloLogger) private readonly logger?: SoloLogger,
@inject(InjectTokens.K8Factory) private readonly k8Factory?: K8Factory,
- @inject(InjectTokens.RemoteConfigManager) private readonly remoteConfigManager?: RemoteConfigManager,
+ @inject(InjectTokens.RemoteConfigRuntimeState) private readonly remoteConfig?: RemoteConfigRuntimeStateApi,
@inject(InjectTokens.LocalConfigRuntimeState) private readonly localConfig?: LocalConfigRuntimeState,
) {
this.logger = patchInject(logger, InjectTokens.SoloLogger, this.constructor.name);
this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
- this.remoteConfigManager = patchInject(
- remoteConfigManager,
- InjectTokens.RemoteConfigManager,
- this.constructor.name,
- );
+ this.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name);
this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
this._portForwards = [];
@@ -94,7 +90,7 @@ export class AccountManager {
accountId: string,
namespace: NamespaceName,
): Promise {
- const contexts = this.remoteConfigManager.getContexts();
+ const contexts = this.remoteConfig.getContexts();
for (const context of contexts) {
try {
@@ -585,7 +581,7 @@ export class AccountManager {
break;
}
}
- const consensusNode: ConsensusNode = this.remoteConfigManager
+ const consensusNode: ConsensusNode = this.remoteConfig
.getConsensusNodes()
.find(node => node.name === serviceBuilder.nodeAlias);
serviceBuilder.withExternalAddress(
@@ -754,7 +750,7 @@ export class AccountManager {
};
try {
- const contexts = this.remoteConfigManager.getContexts();
+ const contexts = this.remoteConfig.getContexts();
for (const context of contexts) {
const secretName = Templates.renderAccountKeySecretName(accountId);
const secretLabels = Templates.renderAccountKeySecretLabelObject(accountId);
diff --git a/src/core/chart-manager.ts b/src/core/chart-manager.ts
index 15b70fa59..7344b7304 100644
--- a/src/core/chart-manager.ts
+++ b/src/core/chart-manager.ts
@@ -46,7 +46,7 @@ export class ChartManager {
}
return await Promise.all(promises); // urls
- } catch (error: Error | any) {
+ } catch (error) {
throw new SoloError(`failed to setup chart repositories: ${error.message}`, error);
}
}
diff --git a/src/core/config/remote/api/component-factory-api.ts b/src/core/config/remote/api/component-factory-api.ts
new file mode 100644
index 000000000..91e3f60f7
--- /dev/null
+++ b/src/core/config/remote/api/component-factory-api.ts
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {type ClusterReference} from '../../../../types/index.js';
+import {type NodeId} from '../../../../types/aliases.js';
+import {type NamespaceName} from '../../../../types/namespace/namespace-name.js';
+import {type DeploymentPhase} from '../../../../data/schema/model/remote/deployment-phase.js';
+import {type ExplorerStateSchema} from '../../../../data/schema/model/remote/state/explorer-state-schema.js';
+import {type MirrorNodeStateSchema} from '../../../../data/schema/model/remote/state/mirror-node-state-schema.js';
+import {type HAProxyStateSchema} from '../../../../data/schema/model/remote/state/haproxy-state-schema.js';
+import {type EnvoyProxyStateSchema} from '../../../../data/schema/model/remote/state/envoy-proxy-state-schema.js';
+import {type ConsensusNodeStateSchema} from '../../../../data/schema/model/remote/state/consensus-node-state-schema.js';
+import {type RelayNodeStateSchema} from '../../../../data/schema/model/remote/state/relay-node-state-schema.js';
+
+export interface ComponentFactoryApi {
+ createNewRelayComponent(
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ nodeIds: NodeId[],
+ ): RelayNodeStateSchema;
+
+ createNewExplorerComponent(clusterReference: ClusterReference, namespace: NamespaceName): ExplorerStateSchema;
+
+ createNewMirrorNodeComponent(clusterReference: ClusterReference, namespace: NamespaceName): MirrorNodeStateSchema;
+
+ createNewHaProxyComponent(clusterReference: ClusterReference, namespace: NamespaceName): HAProxyStateSchema;
+
+ createNewEnvoyProxyComponent(clusterReference: ClusterReference, namespace: NamespaceName): EnvoyProxyStateSchema;
+
+ createNewConsensusNodeComponent(
+ nodeId: NodeId,
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ phase: DeploymentPhase.REQUESTED | DeploymentPhase.STARTED,
+ ): ConsensusNodeStateSchema;
+
+ createConsensusNodeComponentsFromNodeIds(
+ nodeIds: NodeId[],
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ ): ConsensusNodeStateSchema[];
+}
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..b3e69b2e6
--- /dev/null
+++ b/src/core/config/remote/api/components-data-wrapper-api.ts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {type BaseStateSchema} from '../../../../data/schema/model/remote/state/base-state-schema.js';
+import {type ComponentTypes} from '../enumerations/component-types.js';
+import {type DeploymentPhase} from '../../../../data/schema/model/remote/deployment-phase.js';
+import {type ClusterReference, type ComponentId} from '../../../../types/index.js';
+import {type DeploymentStateSchema} from '../../../../data/schema/model/remote/deployment-state-schema.js';
+
+export interface ComponentsDataWrapperApi {
+ state: DeploymentStateSchema;
+
+ addNewComponent(component: BaseStateSchema, type: ComponentTypes): void;
+
+ changeNodePhase(componentId: ComponentId, phase: DeploymentPhase): void;
+
+ removeComponent(componentId: ComponentId, type: ComponentTypes): void;
+
+ getComponent(type: ComponentTypes, componentId: ComponentId): T;
+
+ getComponentsByClusterReference(
+ type: ComponentTypes,
+ clusterReference: ClusterReference,
+ ): T[];
+
+ getComponentById(type: ComponentTypes, id: number): T;
+
+ getNewComponentId(componentType: ComponentTypes): number;
+}
diff --git a/src/core/config/remote/api/remote-config-validator-api.ts b/src/core/config/remote/api/remote-config-validator-api.ts
new file mode 100644
index 000000000..fde95fb61
--- /dev/null
+++ b/src/core/config/remote/api/remote-config-validator-api.ts
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {type NamespaceName} from '../../../../types/namespace/namespace-name.js';
+import {type DeploymentStateSchema} from '../../../../data/schema/model/remote/deployment-state-schema.js';
+
+export interface RemoteConfigValidatorApi {
+ validateComponents(
+ namespace: NamespaceName,
+ skipConsensusNodes: boolean,
+ state: Readonly,
+ ): Promise;
+}
diff --git a/src/core/config/remote/cluster.ts b/src/core/config/remote/cluster.ts
deleted file mode 100644
index 7f1d83669..000000000
--- a/src/core/config/remote/cluster.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type ToObject} from '../../../types/index.js';
-import {type ClusterReference, type DeploymentName, type NamespaceNameAsString} from '../../../types/index.js';
-import {SoloError} from '../../errors/solo-error.js';
-import {type ClusterStruct} from './interfaces/cluster-struct.js';
-
-export class Cluster implements ClusterStruct, ToObject {
- public constructor(
- public readonly name: string,
- public readonly namespace: NamespaceNameAsString,
- public readonly deployment: DeploymentName,
- public readonly dnsBaseDomain: string = 'cluster.local', // example: 'us-west-2.gcp.charlie.sphere'`
- public readonly dnsConsensusNodePattern: string = 'network-{nodeAlias}-svc.{namespace}.svc', // example: '{nodeId}.consensus.prod'`
- ) {
- 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}`);
- }
-
- if (!deployment) {
- throw new SoloError('deployment is required');
- }
- if (typeof deployment !== 'string') {
- throw new SoloError(`Invalid type for deployment: ${typeof deployment}`);
- }
- }
-
- public toObject(): ClusterStruct {
- return {
- name: this.name,
- namespace: this.namespace,
- deployment: this.deployment,
- dnsBaseDomain: this.dnsBaseDomain,
- dnsConsensusNodePattern: this.dnsConsensusNodePattern,
- };
- }
-
- public static fromObject(cluster: ClusterStruct) {
- return new Cluster(
- cluster.name,
- cluster.namespace,
- cluster.deployment,
- cluster.dnsBaseDomain,
- cluster.dnsConsensusNodePattern,
- );
- }
-
- public static toClustersMapObject(clustersMap: Record) {
- const entries = Object.entries(clustersMap).map(([reference, cluster]) => [reference, cluster.toObject()]);
- return Object.fromEntries(entries);
- }
-
- public static fromClustersMapObject(object: any): Record {
- const clusters: Record = {};
-
- for (const [reference, cluster] of Object.entries(object)) {
- clusters[reference] = Cluster.fromObject(cluster as ClusterStruct);
- }
-
- return clusters;
- }
-}
diff --git a/src/core/config/remote/common-flags-data-wrapper.ts b/src/core/config/remote/common-flags-data-wrapper.ts
deleted file mode 100644
index a7a5e81b6..000000000
--- a/src/core/config/remote/common-flags-data-wrapper.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {Flags as flags} from '../../../commands/flags.js';
-import {type ToObject} from '../../../types/index.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[] = [
- flags.releaseTag,
- flags.chartDirectory,
- flags.relayReleaseTag,
- flags.soloChartVersion,
- flags.mirrorNodeVersion,
- flags.nodeAliasesUnparsed,
- flags.explorerVersion,
- ];
-
- private constructor(
- private readonly configManager: ConfigManager,
- private readonly flags: RemoteConfigCommonFlagsStruct,
- ) {}
-
- /**
- * Updates the flags or populates them inside the remote config
- */
- public async handleFlags(argv: AnyObject): Promise {
- for (const flag of CommonFlagsDataWrapper.COMMON_FLAGS) {
- await this.handleFlag(flag, argv);
- }
- }
-
- private async handleFlag(flag: CommandFlag, argv: AnyObject): Promise {
- const detectFlagMismatch = async () => {
- const oldValue = this.flags[flag.constName] as string;
- const newValue = this.configManager.getFlag(flag);
-
- // if the old value is not present, override it with the new one
- if (!oldValue && newValue) {
- this.flags[flag.constName] = newValue;
- return;
- }
-
- // if its present but there is a mismatch warn user
- else if (oldValue && oldValue !== newValue) {
- const isQuiet = this.configManager.getFlag(flags.quiet);
- const isForced = this.configManager.getFlag(flags.force);
-
- // if the quiet or forced flag is passed don't prompt the user
- if (isQuiet === true || isForced === true) {
- return;
- }
-
- const answer = await selectPrompt({
- message: 'Value in remote config differs with the one you are passing, choose which you want to use',
- choices: [
- {
- name: `[old value] ${oldValue}`,
- value: oldValue,
- },
- {
- name: `[new value] ${newValue}`,
- value: newValue,
- },
- ],
- });
-
- // Override if user chooses new the new value, else override and keep the old one
- if (answer === newValue) {
- this.flags[flag.constName] = newValue;
- } else {
- this.configManager.setFlag(flag, oldValue);
- argv[flag.constName] = oldValue;
- }
- }
- };
-
- // if the flag is set, inspect the value
- if (this.configManager.hasFlag(flag)) {
- await detectFlagMismatch();
- }
-
- // use remote config value if no user supplied value
- else if (this.flags[flag.constName]) {
- argv[flag.constName] = this.flags[flag.constName];
- this.configManager.setFlag(flag, this.flags[flag.constName]);
- }
- }
-
- public static async initialize(configManager: ConfigManager, argv: AnyObject): Promise {
- const commonFlagsDataWrapper = new CommonFlagsDataWrapper(configManager, {});
- await commonFlagsDataWrapper.handleFlags(argv);
- return commonFlagsDataWrapper;
- }
-
- public static fromObject(configManager: ConfigManager, data: RemoteConfigCommonFlagsStruct): CommonFlagsDataWrapper {
- return new CommonFlagsDataWrapper(configManager, data);
- }
-
- public toObject(): RemoteConfigCommonFlagsStruct {
- return {
- nodeAliasesUnparsed: this.flags.nodeAliasesUnparsed,
- releaseTag: this.flags.releaseTag,
- relayReleaseTag: this.flags.relayReleaseTag,
- explorerVersion: this.flags.explorerVersion,
- mirrorNodeVersion: this.flags.mirrorNodeVersion,
- };
- }
-}
diff --git a/src/core/config/remote/component-factory.ts b/src/core/config/remote/component-factory.ts
new file mode 100644
index 000000000..dfcdbef38
--- /dev/null
+++ b/src/core/config/remote/component-factory.ts
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import {ComponentTypes} from './enumerations/component-types.js';
+import {DeploymentPhase} from '../../../data/schema/model/remote/deployment-phase.js';
+import {type NodeId} from '../../../types/aliases.js';
+import {ComponentStateMetadataSchema} from '../../../data/schema/model/remote/state/component-state-metadata-schema.js';
+import {type NamespaceName} from '../../../types/namespace/namespace-name.js';
+import {type ClusterReference, type ComponentId} from '../../../types/index.js';
+import {type RemoteConfigRuntimeStateApi} from '../../../business/runtime-state/api/remote-config-runtime-state-api.js';
+import {inject, injectable} from 'tsyringe-neo';
+import {patchInject} from '../../dependency-injection/container-helper.js';
+import {InjectTokens} from '../../dependency-injection/inject-tokens.js';
+import {type ComponentFactoryApi} from './api/component-factory-api.js';
+import {RelayNodeStateSchema} from '../../../data/schema/model/remote/state/relay-node-state-schema.js';
+import {ExplorerStateSchema} from '../../../data/schema/model/remote/state/explorer-state-schema.js';
+import {MirrorNodeStateSchema} from '../../../data/schema/model/remote/state/mirror-node-state-schema.js';
+import {HAProxyStateSchema} from '../../../data/schema/model/remote/state/haproxy-state-schema.js';
+import {EnvoyProxyStateSchema} from '../../../data/schema/model/remote/state/envoy-proxy-state-schema.js';
+import {ConsensusNodeStateSchema} from '../../../data/schema/model/remote/state/consensus-node-state-schema.js';
+
+@injectable()
+export class ComponentFactory implements ComponentFactoryApi {
+ public constructor(
+ @inject(InjectTokens.RemoteConfigRuntimeState) private readonly remoteConfig: RemoteConfigRuntimeStateApi,
+ ) {
+ this.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name);
+ }
+
+ public createNewRelayComponent(
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ nodeIds: NodeId[],
+ ): RelayNodeStateSchema {
+ const id: ComponentId = this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.RelayNodes);
+ const phase: DeploymentPhase.DEPLOYED = DeploymentPhase.DEPLOYED;
+ const metadata: ComponentStateMetadataSchema = new ComponentStateMetadataSchema(
+ id,
+ namespace.name,
+ clusterReference,
+ phase,
+ );
+
+ return new RelayNodeStateSchema(metadata, nodeIds);
+ }
+
+ public createNewExplorerComponent(clusterReference: ClusterReference, namespace: NamespaceName): ExplorerStateSchema {
+ const id: ComponentId = this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.Explorers);
+ const phase: DeploymentPhase.DEPLOYED = DeploymentPhase.DEPLOYED;
+ const metadata: ComponentStateMetadataSchema = new ComponentStateMetadataSchema(
+ id,
+ namespace.name,
+ clusterReference,
+ phase,
+ );
+
+ return new ExplorerStateSchema(metadata);
+ }
+
+ public createNewMirrorNodeComponent(
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ ): MirrorNodeStateSchema {
+ const id: ComponentId = this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.MirrorNode);
+ const phase: DeploymentPhase.DEPLOYED = DeploymentPhase.DEPLOYED;
+ const metadata: ComponentStateMetadataSchema = new ComponentStateMetadataSchema(
+ id,
+ namespace.name,
+ clusterReference,
+ phase,
+ );
+
+ return new MirrorNodeStateSchema(metadata);
+ }
+
+ public createNewHaProxyComponent(clusterReference: ClusterReference, namespace: NamespaceName): HAProxyStateSchema {
+ const id: ComponentId = this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.HaProxy);
+ const phase: DeploymentPhase.DEPLOYED = DeploymentPhase.DEPLOYED;
+ const metadata: ComponentStateMetadataSchema = new ComponentStateMetadataSchema(
+ id,
+ namespace.name,
+ clusterReference,
+ phase,
+ );
+
+ return new HAProxyStateSchema(metadata);
+ }
+
+ public createNewEnvoyProxyComponent(
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ ): EnvoyProxyStateSchema {
+ const id: ComponentId = this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.EnvoyProxy);
+ const phase: DeploymentPhase.DEPLOYED = DeploymentPhase.DEPLOYED;
+ const metadata: ComponentStateMetadataSchema = new ComponentStateMetadataSchema(
+ id,
+ clusterReference,
+ namespace.name,
+ phase,
+ );
+
+ return new EnvoyProxyStateSchema(metadata);
+ }
+
+ public createNewConsensusNodeComponent(
+ nodeId: NodeId,
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ phase: DeploymentPhase.REQUESTED | DeploymentPhase.STARTED,
+ ): ConsensusNodeStateSchema {
+ const metadata: ComponentStateMetadataSchema = new ComponentStateMetadataSchema(
+ nodeId,
+ namespace.name,
+ clusterReference,
+ phase,
+ );
+
+ return new ConsensusNodeStateSchema(metadata);
+ }
+
+ public createConsensusNodeComponentsFromNodeIds(
+ nodeIds: NodeId[],
+ clusterReference: ClusterReference,
+ namespace: NamespaceName,
+ ): ConsensusNodeStateSchema[] {
+ return nodeIds.map((nodeId: NodeId) =>
+ this.createNewConsensusNodeComponent(nodeId, clusterReference, namespace, DeploymentPhase.REQUESTED),
+ );
+ }
+}
diff --git a/src/core/config/remote/components-data-wrapper.ts b/src/core/config/remote/components-data-wrapper.ts
index 302463899..63af7c142 100644
--- a/src/core/config/remote/components-data-wrapper.ts
+++ b/src/core/config/remote/components-data-wrapper.ts
@@ -1,346 +1,189 @@
// SPDX-License-Identifier: Apache-2.0
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';
-import {MirrorNodeExplorerComponent} from './components/mirror-node-explorer-component.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../types/index.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 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:
- * - CRUD operations on the components.
- * - Validation.
- * - Conversion FROM and TO plain object.
- */
-export class ComponentsDataWrapper {
- private constructor(
- public readonly relays: Record = {},
- public readonly haProxies: Record = {},
- public readonly mirrorNodes: Record = {},
- public readonly envoyProxies: Record = {},
- public readonly consensusNodes: Record = {},
- public readonly mirrorNodeExplorers: Record = {},
- public readonly blockNodes: Record = {},
- ) {
- this.validate();
- }
+import {BaseStateSchema} from '../../../data/schema/model/remote/state/base-state-schema.js';
+import {isValidEnum} from '../../util/validation-helpers.js';
+import {type DeploymentPhase} from '../../../data/schema/model/remote/deployment-phase.js';
+import {type ClusterReference, type ComponentId} from '../../../types/index.js';
+import {type ComponentsDataWrapperApi} from './api/components-data-wrapper-api.js';
+import {type DeploymentStateSchema} from '../../../data/schema/model/remote/deployment-state-schema.js';
+
+export class ComponentsDataWrapper implements ComponentsDataWrapperApi {
+ public constructor(public state: DeploymentStateSchema) {}
/* -------- Modifiers -------- */
/** Used to add new component to their respective group. */
- public add(component: BaseComponent): void {
- const self = this;
-
- const serviceName = component.name;
+ public addNewComponent(component: BaseStateSchema, type: ComponentTypes): void {
+ const componentId: ComponentId = component.metadata.id;
- if (!serviceName || typeof serviceName !== 'string') {
- throw new SoloError(`Service name is required ${serviceName}`);
+ if (typeof componentId !== 'number' || componentId < 0) {
+ throw new SoloError(`Component id is required ${componentId}`);
}
- if (!(component instanceof BaseComponent)) {
- throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent);
+ if (!(component instanceof BaseStateSchema)) {
+ throw new SoloError('Component must be instance of BaseState', undefined, BaseStateSchema);
}
- function addComponentCallback(components: Record): void {
- if (self.exists(components, component)) {
- throw new SoloError('Component exists', null, component.toObject());
+ const addComponentCallback: (components: BaseStateSchema[]) => void = components => {
+ if (this.checkComponentExists(components, component)) {
+ throw new SoloError('Component exists', undefined, component);
}
- components[serviceName] = component;
- }
+ components[componentId] = component;
+ };
- self.applyCallbackToComponentGroup(component.type, addComponentCallback, serviceName);
+ this.applyCallbackToComponentGroup(type, addComponentCallback, componentId);
}
- /** 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);
- }
-
- function editComponentCallback(components: Record): void {
- if (!components[serviceName]) {
- throw new SoloError(`Component doesn't exist, name: ${serviceName}`, null, {component});
- }
- components[serviceName] = component;
+ public changeNodePhase(componentId: ComponentId, phase: DeploymentPhase): void {
+ if (!this.state.consensusNodes[componentId]) {
+ throw new SoloError(`Consensus node ${componentId} doesn't exist`);
}
- this.applyCallbackToComponentGroup(component.type, editComponentCallback, serviceName);
+ this.state.consensusNodes[componentId].metadata.phase = phase;
}
/** Used to remove specific component from their respective group. */
- public remove(serviceName: ComponentName, type: ComponentTypes): void {
- if (!serviceName || typeof serviceName !== 'string') {
- throw new SoloError(`Service name is required ${serviceName}`);
+ public removeComponent(componentId: ComponentId, type: ComponentTypes): void {
+ if (typeof componentId !== 'number' || componentId < 0) {
+ throw new SoloError(`Component id is required ${componentId}`);
}
- if (!Object.values(ComponentTypes).includes(type)) {
+
+ if (!isValidEnum(type, ComponentTypes)) {
throw new SoloError(`Invalid component type ${type}`);
}
- function deleteComponentCallback(components: Record): void {
- if (!components[serviceName]) {
- throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`);
+ const removeComponentCallback: (components: BaseStateSchema[]) => void = components => {
+ const index: number = components.findIndex(component => component.metadata.id === componentId);
+ if (index === -1) {
+ throw new SoloError(`Component ${componentId} of type ${type} not found while attempting to remove`);
}
- delete components[serviceName];
- }
- this.applyCallbackToComponentGroup(type, deleteComponentCallback, serviceName);
+ components.splice(index, 1);
+ };
+
+ this.applyCallbackToComponentGroup(type, removeComponentCallback, componentId);
}
/* -------- Utilities -------- */
- public getComponent(type: ComponentTypes, componentName: ComponentName): T {
+ public getComponent(type: ComponentTypes, componentId: ComponentId): T {
let component: T;
- const getComponentCallback: (components: Record) => void = components => {
- if (!components[componentName]) {
- throw new SoloError(`Component ${componentName} of type ${type} not found while attempting to read`);
+ const getComponentCallback: (components: BaseStateSchema[]) => void = components => {
+ component = components.find(component => component.metadata.id === componentId) as T;
+
+ if (!component) {
+ throw new SoloError(`Component ${componentId} of type ${type} not found while attempting to read`);
}
- component = components[componentName] as T;
};
- this.applyCallbackToComponentGroup(type, getComponentCallback, componentName);
+ this.applyCallbackToComponentGroup(type, getComponentCallback, componentId);
return component;
}
+ public getComponentsByClusterReference(
+ type: ComponentTypes,
+ clusterReference: ClusterReference,
+ ): T[] {
+ let filteredComponents: T[] = [];
+
+ const getComponentsByClusterReferenceCallback: (components: T[]) => void = components => {
+ filteredComponents = components.filter(component => component.metadata.cluster === clusterReference);
+ };
+
+ this.applyCallbackToComponentGroup(type, getComponentsByClusterReferenceCallback);
+
+ return filteredComponents;
+ }
+
+ public getComponentById(type: ComponentTypes, id: number): T {
+ let filteredComponent: T;
+
+ const getComponentByIdCallback: (components: T[]) => void = components => {
+ filteredComponent = components.find(component => +component.metadata.id === id);
+ };
+
+ this.applyCallbackToComponentGroup(type, getComponentByIdCallback);
+
+ if (!filteredComponent) {
+ throw new SoloError(`Component of type ${type} with id ${id} was not found in remote config`);
+ }
+
+ return filteredComponent;
+ }
+
/**
* Method used to map the type to the specific component group
* and pass it to a callback to apply modifications
*/
private applyCallbackToComponentGroup(
componentType: ComponentTypes,
- callback: (components: Record) => void,
- componentName?: ComponentName,
+ callback: (components: BaseStateSchema[]) => void,
+ componentId?: ComponentId,
): void {
switch (componentType) {
- case ComponentTypes.Relay: {
- callback(this.relays);
+ case ComponentTypes.RelayNodes: {
+ callback(this.state.relayNodes);
break;
}
case ComponentTypes.HaProxy: {
- callback(this.haProxies);
+ callback(this.state.haProxies);
break;
}
case ComponentTypes.MirrorNode: {
- callback(this.mirrorNodes);
+ callback(this.state.mirrorNodes);
break;
}
case ComponentTypes.EnvoyProxy: {
- callback(this.envoyProxies);
+ callback(this.state.envoyProxies);
break;
}
case ComponentTypes.ConsensusNode: {
- callback(this.consensusNodes);
+ callback(this.state.consensusNodes);
break;
}
- case ComponentTypes.MirrorNodeExplorer: {
- callback(this.mirrorNodeExplorers);
- break;
- }
-
- case ComponentTypes.BlockNode: {
- callback(this.blockNodes);
+ case ComponentTypes.Explorers: {
+ callback(this.state.explorers);
break;
}
default: {
- throw new SoloError(`Unknown component type ${componentType}, component name: ${componentName}`);
+ throw new SoloError(`Unknown component type ${componentType}, component id: ${componentId}`);
}
}
+ }
- this.validate();
+ /** checks if component exists in the respective group */
+ private checkComponentExists(components: BaseStateSchema[], newComponent: BaseStateSchema): boolean {
+ return components.some((component): boolean => component.metadata.id === newComponent.metadata.id);
}
/**
- * Handles creating instance of the class from plain object.
- *
- * @param components - component groups distinguished by their type.
+ * Checks all existing components of specified type and gives you a new unique index
*/
- public static fromObject(components: ComponentsDataStruct): ComponentsDataWrapper {
- const relays: Record = {};
- const haProxies: Record = {};
- const mirrorNodes: Record = {};
- const envoyProxies: Record = {};
- const consensusNodes: Record = {};
- const mirrorNodeExplorers: Record = {};
- const blockNodes: Record = {};
-
- for (const [componentType, subComponents] of Object.entries(components)) {
- switch (componentType) {
- case ComponentTypes.Relay: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- relays[componentName] = RelayComponent.fromObject(component as RelayComponentStruct);
- }
- break;
- }
+ public getNewComponentId(componentType: ComponentTypes): number {
+ let newComponentId: number = 0;
- case ComponentTypes.HaProxy: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- haProxies[componentName] = HaProxyComponent.fromObject(component);
- }
- break;
- }
+ const calculateNewComponentIndexCallback: (components: BaseStateSchema[]) => void = components => {
+ const componentIds: ComponentId[] = components.map(component => component.metadata.id);
- case ComponentTypes.MirrorNode: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- mirrorNodes[componentName] = MirrorNodeComponent.fromObject(component);
- }
- break;
- }
-
- case ComponentTypes.EnvoyProxy: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- envoyProxies[componentName] = EnvoyProxyComponent.fromObject(component);
- }
- break;
- }
-
- case ComponentTypes.ConsensusNode: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- consensusNodes[componentName] = ConsensusNodeComponent.fromObject(
- component as ConsensusNodeComponentStruct,
- );
- }
- break;
- }
-
- case ComponentTypes.MirrorNodeExplorer: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- mirrorNodeExplorers[componentName] = MirrorNodeExplorerComponent.fromObject(component);
- }
- break;
- }
-
- case ComponentTypes.BlockNode: {
- for (const [componentName, component] of Object.entries(subComponents)) {
- blockNodes[componentName] = BlockNodeComponent.fromObject(component);
- }
- break;
- }
-
- default: {
- throw new SoloError(`Unknown component type ${componentType}`);
+ for (const componentId of componentIds) {
+ if (newComponentId <= +componentId) {
+ newComponentId = +componentId + 1;
}
}
- }
-
- return new ComponentsDataWrapper(
- relays,
- haProxies,
- mirrorNodes,
- envoyProxies,
- consensusNodes,
- mirrorNodeExplorers,
- blockNodes,
- );
- }
-
- /** Used to create an empty instance used to keep the constructor private */
- public static initializeEmpty(): ComponentsDataWrapper {
- return new ComponentsDataWrapper();
- }
-
- public static initializeWithNodes(
- nodeAliases: NodeAliases,
- clusterReference: ClusterReference,
- namespace: NamespaceNameAsString,
- ): ComponentsDataWrapper {
- 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 exists(components: Record, newComponent: BaseComponent): boolean {
- return Object.values(components).some(component => 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}}`);
- }
-
- if (!(component instanceof expectedInstance)) {
- throw new SoloError(
- `Invalid component type, service name: ${name}, ` +
- `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`,
- null,
- {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);
- }
-
- private transformComponentGroupToObject(
- components: Record,
- ): Record {
- const transformedComponents: Record = {};
-
- for (const [componentName, component] of Object.entries(components)) {
- transformedComponents[componentName] = component.toObject() as BaseComponentStruct;
- }
-
- return transformedComponents;
- }
-
- public toObject(): ComponentsDataStruct {
- return {
- [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),
};
- }
- public clone(): ComponentsDataWrapper {
- const data: ComponentsDataStruct = this.toObject();
+ this.applyCallbackToComponentGroup(componentType, calculateNewComponentIndexCallback);
- return ComponentsDataWrapper.fromObject(data);
+ return newComponentId;
}
}
diff --git a/src/core/config/remote/components/base-component.ts b/src/core/config/remote/components/base-component.ts
deleted file mode 100644
index 7bb535563..000000000
--- a/src/core/config/remote/components/base-component.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {SoloError} from '../../../errors/solo-error.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {type ToObject, type Validate} from '../../../../types/index.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {isValidEnum} from '../../../util/validation-helpers.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 BaseComponentStruct, Validate, ToObject {
- /**
- * @param type - type for identifying.
- * @param name - the name to distinguish components.
- * @param cluster - the cluster in which the component is deployed.
- * @param namespace - the namespace associated with the component.
- * @param state - the state of the component
- */
- protected constructor(
- public readonly type: ComponentTypes,
- public readonly name: ComponentName,
- public readonly cluster: ClusterReference,
- public readonly namespace: NamespaceNameAsString,
- ) {}
-
- /* -------- Utilities -------- */
-
- /**
- * Compares two BaseComponent instances for equality.
- *
- * @param x - The first component to compare
- * @param y - The second component to compare
- * @returns boolean - true if the components are equal
- */
- public static compare(x: BaseComponent, y: BaseComponent): boolean {
- return x.name === y.name && x.type === y.type && x.cluster === y.cluster && x.namespace === y.namespace;
- }
-
- public validate(): void {
- if (!this.name || typeof this.name !== 'string') {
- throw new SoloError(`Invalid name: ${this.name}`);
- }
-
- if (!this.cluster || typeof this.cluster !== 'string') {
- throw new SoloError(`Invalid cluster: ${this.cluster}`);
- }
-
- if (!this.namespace || typeof this.namespace !== 'string') {
- throw new SoloError(
- `Invalid namespace: ${this.namespace}, is typeof 'string': ${typeof this.namespace !== 'string'}`,
- );
- }
-
- if (!isValidEnum(this.type, ComponentTypes)) {
- throw new SoloError(`Invalid component type: ${this.type}`);
- }
- }
-
- public toObject(): BaseComponentStruct {
- return {
- name: this.name,
- cluster: this.cluster,
- namespace: this.namespace,
- };
- }
-}
diff --git a/src/core/config/remote/components/block-node-component.ts b/src/core/config/remote/components/block-node-component.ts
deleted file mode 100644
index 4177e12ef..000000000
--- a/src/core/config/remote/components/block-node-component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {BaseComponent} from './base-component.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-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) {
- super(ComponentTypes.BlockNode, name, cluster, namespace);
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: BaseComponentStruct): BlockNodeComponent {
- const {name, cluster, namespace} = component;
- return new BlockNodeComponent(name, cluster, namespace);
- }
-}
diff --git a/src/core/config/remote/components/consensus-node-component.ts b/src/core/config/remote/components/consensus-node-component.ts
deleted file mode 100644
index 549d60d12..000000000
--- a/src/core/config/remote/components/consensus-node-component.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {BaseComponent} from './base-component.js';
-import {SoloError} from '../../../errors/solo-error.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {ConsensusNodeStates} from '../enumerations/consensus-node-states.js';
-import {isValidEnum} from '../../../util/validation-helpers.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {type ToObject} from '../../../../types/index.js';
-import {type ConsensusNodeComponentStruct} from './interfaces/consensus-node-component-struct.js';
-
-/**
- * Represents a consensus node component within the system.
- *
- * A `ConsensusNodeComponent` extends the functionality of `BaseComponent` and includes additional properties and behaviors
- * specific to consensus nodes, such as maintaining and validating the node's state.
- */
-export class ConsensusNodeComponent
- extends BaseComponent
- implements ConsensusNodeComponentStruct, ToObject
-{
- /**
- * @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 - of the consensus node
- */
- public constructor(
- name: ComponentName,
- cluster: ClusterReference,
- namespace: NamespaceNameAsString,
- public readonly state: ConsensusNodeStates,
- public readonly nodeId: number,
- ) {
- super(ComponentTypes.ConsensusNode, name, cluster, namespace);
-
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: ConsensusNodeComponentStruct): ConsensusNodeComponent {
- const {name, cluster, namespace, state, nodeId} = component;
- return new ConsensusNodeComponent(name, cluster, namespace, state, nodeId);
- }
-
- public override validate(): void {
- super.validate();
-
- if (!isValidEnum(this.state, ConsensusNodeStates)) {
- throw new SoloError(`Invalid consensus node state: ${this.state}`);
- }
-
- if (typeof this.nodeId !== 'number') {
- throw new SoloError(`Invalid node id. It must be a number: ${this.nodeId}`);
- }
-
- if (this.nodeId < 0) {
- throw new SoloError(`Invalid node id. It cannot be negative: ${this.nodeId}`);
- }
- }
-
- public override toObject(): ConsensusNodeComponentStruct {
- return {
- ...super.toObject(),
- 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
deleted file mode 100644
index 833e687b2..000000000
--- a/src/core/config/remote/components/envoy-proxy-component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {BaseComponent} from './base-component.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {type BaseComponentStruct} from './interfaces/base-component-struct.js';
-
-export class EnvoyProxyComponent extends BaseComponent {
- public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) {
- super(ComponentTypes.EnvoyProxy, name, cluster, namespace);
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: BaseComponentStruct): EnvoyProxyComponent {
- 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
deleted file mode 100644
index 36915b0d9..000000000
--- a/src/core/config/remote/components/ha-proxy-component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {BaseComponent} from './base-component.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {type BaseComponentStruct} from './interfaces/base-component-struct.js';
-
-export class HaProxyComponent extends BaseComponent {
- public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) {
- super(ComponentTypes.HaProxy, name, cluster, namespace);
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: BaseComponentStruct): HaProxyComponent {
- 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
deleted file mode 100644
index 8d9f31c97..000000000
--- a/src/core/config/remote/components/interfaces/base-component-struct.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../../types/index.js';
-
-export interface BaseComponentStruct {
- name: ComponentName;
- cluster: ClusterReference;
- namespace: NamespaceNameAsString;
-}
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
deleted file mode 100644
index 8b10c1bf8..000000000
--- a/src/core/config/remote/components/interfaces/consensus-node-component-struct.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type BaseComponentStruct} from './base-component-struct.js';
-import {type ConsensusNodeStates} from '../../enumerations/consensus-node-states.js';
-
-export interface ConsensusNodeComponentStruct extends BaseComponentStruct {
- nodeId: number;
- state: 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
deleted file mode 100644
index 358a14a54..000000000
--- a/src/core/config/remote/components/interfaces/relay-component-struct.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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/mirror-node-component.ts b/src/core/config/remote/components/mirror-node-component.ts
deleted file mode 100644
index b15223a80..000000000
--- a/src/core/config/remote/components/mirror-node-component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {BaseComponent} from './base-component.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {type BaseComponentStruct} from './interfaces/base-component-struct.js';
-
-export class MirrorNodeComponent extends BaseComponent {
- public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) {
- super(ComponentTypes.MirrorNode, name, cluster, namespace);
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: BaseComponentStruct): MirrorNodeComponent {
- 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
deleted file mode 100644
index 0565d265f..000000000
--- a/src/core/config/remote/components/mirror-node-explorer-component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {BaseComponent} from './base-component.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {type BaseComponentStruct} from './interfaces/base-component-struct.js';
-
-export class MirrorNodeExplorerComponent extends BaseComponent {
- public constructor(name: ComponentName, cluster: ClusterReference, namespace: NamespaceNameAsString) {
- super(ComponentTypes.MirrorNodeExplorer, name, cluster, namespace);
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: BaseComponentStruct): MirrorNodeExplorerComponent {
- 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
deleted file mode 100644
index 2c4aaedb2..000000000
--- a/src/core/config/remote/components/relay-component.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {SoloError} from '../../../errors/solo-error.js';
-import {BaseComponent} from './base-component.js';
-import {ComponentTypes} from '../enumerations/component-types.js';
-import {type ClusterReference, type ComponentName, type NamespaceNameAsString} from '../../../../types/index.js';
-import {type NodeAliases} from '../../../../types/aliases.js';
-import {type ToObject} from '../../../../types/index.js';
-import {type RelayComponentStruct} from './interfaces/relay-component-struct.js';
-
-export class RelayComponent extends BaseComponent implements RelayComponentStruct, ToObject {
- /**
- * @param name - to distinguish components.
- * @param clusterReference - in which the component is deployed.
- * @param namespace - associated with the component.
- * @param consensusNodeAliases - list node aliases
- */
- public constructor(
- name: ComponentName,
- clusterReference: ClusterReference,
- namespace: NamespaceNameAsString,
- public readonly consensusNodeAliases: NodeAliases = [],
- ) {
- super(ComponentTypes.Relay, name, clusterReference, namespace);
- this.validate();
- }
-
- /* -------- Utilities -------- */
-
- /** Handles creating instance of the class from plain object. */
- public static fromObject(component: RelayComponentStruct): RelayComponent {
- const {name, cluster, namespace, consensusNodeAliases} = component;
- return new RelayComponent(name, cluster, namespace, consensusNodeAliases);
- }
-
- public override validate(): void {
- super.validate();
-
- for (const nodeAlias of this.consensusNodeAliases) {
- if (!nodeAlias || typeof nodeAlias !== 'string') {
- throw new SoloError(`Invalid consensus node alias: ${nodeAlias}, aliases ${this.consensusNodeAliases}`);
- }
- }
- }
-
- public override toObject(): RelayComponentStruct {
- return {
- consensusNodeAliases: this.consensusNodeAliases,
- ...super.toObject(),
- };
- }
-}
diff --git a/src/core/config/remote/enumerations/component-types.ts b/src/core/config/remote/enumerations/component-types.ts
index 4c01bcc88..985b94da0 100644
--- a/src/core/config/remote/enumerations/component-types.ts
+++ b/src/core/config/remote/enumerations/component-types.ts
@@ -9,6 +9,6 @@ export enum ComponentTypes {
HaProxy = 'haProxies',
EnvoyProxy = 'envoyProxies',
MirrorNode = 'mirrorNodes',
- MirrorNodeExplorer = 'mirrorNodeExplorers',
- Relay = 'relays',
+ Explorers = 'explorers',
+ RelayNodes = 'relayNodes',
}
diff --git a/src/core/config/remote/enumerations/consensus-node-states.ts b/src/core/config/remote/enumerations/consensus-node-states.ts
deleted file mode 100644
index b429d8944..000000000
--- a/src/core/config/remote/enumerations/consensus-node-states.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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/interfaces/cluster-struct.ts b/src/core/config/remote/interfaces/cluster-struct.ts
deleted file mode 100644
index ecf16eec4..000000000
--- a/src/core/config/remote/interfaces/cluster-struct.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type DeploymentName} from '../../../../types/index.js';
-
-export interface ClusterStruct {
- name: string;
- namespace: string;
- deployment: DeploymentName;
- dnsBaseDomain: string;
- dnsConsensusNodePattern: string;
-}
diff --git a/src/core/config/remote/interfaces/components-data-struct.ts b/src/core/config/remote/interfaces/components-data-struct.ts
deleted file mode 100644
index 24eb61524..000000000
--- a/src/core/config/remote/interfaces/components-data-struct.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type ComponentTypes} from '../enumerations/component-types.js';
-import {type BaseComponentStruct} from '../components/interfaces/base-component-struct.js';
-import {type ComponentName} from '../../../../types/index.js';
-
-export type ComponentsDataStruct = Record>;
diff --git a/src/core/config/remote/interfaces/migration-struct.ts b/src/core/config/remote/interfaces/migration-struct.ts
deleted file mode 100644
index 9e6e1a525..000000000
--- a/src/core/config/remote/interfaces/migration-struct.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type Version} from '../../../../types/index.js';
-import {type UserIdentitySchema} from '../../../../data/schema/model/common/user-identity-schema.js';
-
-export interface MigrationStruct {
- migratedAt: Date;
- migratedBy: UserIdentitySchema;
- 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
deleted file mode 100644
index 80ed4d656..000000000
--- a/src/core/config/remote/interfaces/remote-config-common-flags-struct.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-export type RemoteConfigCommonFlagsStruct = {
- releaseTag?: string;
- chartDirectory?: string;
- relayReleaseTag?: string;
- soloChartVersion?: string;
- mirrorNodeVersion?: string;
- nodeAliasesUnparsed?: string;
- explorerVersion?: string;
-};
diff --git a/src/core/config/remote/interfaces/remote-config-data-struct.ts b/src/core/config/remote/interfaces/remote-config-data-struct.ts
deleted file mode 100644
index 9734382c4..000000000
--- a/src/core/config/remote/interfaces/remote-config-data-struct.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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/index.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-metadata-struct.ts b/src/core/config/remote/interfaces/remote-config-metadata-struct.ts
deleted file mode 100644
index 7a77d95e7..000000000
--- a/src/core/config/remote/interfaces/remote-config-metadata-struct.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {type DeploymentStates} from '../enumerations/deployment-states.js';
-import {type MigrationStruct} from './migration-struct.js';
-import {type DeploymentName, type NamespaceNameAsString, type Version} from '../../../../types/index.js';
-import {type UserIdentitySchema} from '../../../../data/schema/model/common/user-identity-schema.js';
-
-export interface RemoteConfigMetadataStruct {
- namespace: NamespaceNameAsString;
- state: DeploymentStates;
- deploymentName: DeploymentName;
- lastUpdatedAt: Date;
- lastUpdateBy: UserIdentitySchema;
- soloVersion: Version;
- soloChartVersion: Version;
- hederaPlatformVersion: Version;
- hederaMirrorNodeChartVersion: Version;
- explorerChartVersion: Version;
- hederaJsonRpcRelayChartVersion: Version;
- migration?: MigrationStruct;
-}
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);
- },
- };
- }
-}
diff --git a/src/core/config/remote/metadata.ts b/src/core/config/remote/metadata.ts
deleted file mode 100644
index c6bdd0c4a..000000000
--- a/src/core/config/remote/metadata.ts
+++ /dev/null
@@ -1,152 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {Migration} from './migration.js';
-import {SoloError} from '../../errors/solo-error.js';
-import {
- type DeploymentName,
- type NamespaceNameAsString,
- type Optional,
- type ToObject,
- type Validate,
- type Version,
-} from '../../../types/index.js';
-import {type UserIdentitySchema} from '../../../data/schema/model/common/user-identity-schema.js';
-import {DeploymentStates} from './enumerations/deployment-states.js';
-import {isValidEnum} from '../../util/validation-helpers.js';
-import {type RemoteConfigMetadataStruct} from './interfaces/remote-config-metadata-struct.js';
-
-/**
- * Represent the remote config metadata object and handles:
- * - Validation
- * - Reading
- * - Making a migration
- * - Converting from and to plain object
- */
-export class RemoteConfigMetadata
- implements RemoteConfigMetadataStruct, Validate, ToObject
-{
- private _migration?: Migration;
-
- public constructor(
- public readonly namespace: NamespaceNameAsString,
- public readonly deploymentName: DeploymentName,
- public readonly state: DeploymentStates,
- public readonly lastUpdatedAt: Date,
- public readonly lastUpdateBy: UserIdentitySchema,
- public readonly soloVersion: Version,
- public soloChartVersion: Version = '',
- public hederaPlatformVersion: Version = '',
- public hederaMirrorNodeChartVersion: Version = '',
- public explorerChartVersion: Version = '',
- public hederaJsonRpcRelayChartVersion: Version = '',
- migration?: Migration,
- ) {
- this._migration = migration;
- this.validate();
- }
-
- /* -------- Modifiers -------- */
-
- /** Simplifies making a migration */
- public makeMigration(userIdentity: UserIdentitySchema, fromVersion: Version): void {
- this._migration = new Migration(new Date(), userIdentity, fromVersion);
- }
-
- /* -------- Getters -------- */
-
- /** Retrieves the migration if such exists */
- public get migration(): Optional {
- return this._migration;
- }
-
- /* -------- Utilities -------- */
-
- /** Handles conversion from a plain object to instance */
- public static fromObject(metadata: RemoteConfigMetadataStruct): RemoteConfigMetadata {
- let migration: Optional = undefined;
-
- if (metadata.migration) {
- const {
- migration: {migratedAt, migratedBy, fromVersion},
- } = metadata;
- migration = new Migration(new Date(migratedAt), migratedBy, fromVersion);
- }
-
- return new RemoteConfigMetadata(
- metadata.namespace,
- metadata.deploymentName,
- metadata.state,
- new Date(metadata.lastUpdatedAt),
- metadata.lastUpdateBy,
- metadata.soloVersion,
- metadata.soloChartVersion,
- metadata.hederaPlatformVersion,
- metadata.hederaMirrorNodeChartVersion,
- metadata.explorerChartVersion,
- metadata.hederaJsonRpcRelayChartVersion,
- migration,
- );
- }
-
- public validate(): void {
- if (!this.namespace || !(typeof this.namespace === 'string')) {
- throw new SoloError(
- `Invalid namespace: ${this.namespace}, is type string: ${typeof this.namespace === 'string'}`,
- );
- }
-
- if (!this.deploymentName || !(typeof this.deploymentName === 'string')) {
- throw new SoloError(
- `Invalid deploymentName: ${this.deploymentName}, is type string: ${typeof this.deploymentName === 'string'}`,
- );
- }
-
- if (!(this.lastUpdatedAt instanceof Date)) {
- throw new SoloError(`Invalid lastUpdatedAt: ${this.lastUpdatedAt}`);
- }
-
- if (
- !this.lastUpdateBy ||
- !this.lastUpdateBy.name ||
- !this.lastUpdateBy.hostname ||
- typeof this.lastUpdateBy.name !== 'string' ||
- typeof this.lastUpdateBy.hostname !== 'string'
- ) {
- throw new SoloError(`Invalid lastUpdateBy: ${JSON.stringify(this.lastUpdateBy)}`);
- }
-
- if (!this.soloVersion || typeof this.soloVersion !== 'string') {
- throw new SoloError(`Invalid soloVersion: ${this.soloVersion}`);
- }
-
- if (!isValidEnum(this.state, DeploymentStates)) {
- throw new SoloError(`Invalid cluster state: ${this.state}`);
- }
-
- if (this.migration && !(this.migration instanceof Migration)) {
- throw new SoloError(`Invalid migration: ${this.migration}`);
- }
- }
-
- public toObject(): RemoteConfigMetadataStruct {
- const data: RemoteConfigMetadataStruct = {
- namespace: this.namespace,
- deploymentName: this.deploymentName,
- state: this.state,
- lastUpdatedAt: this.lastUpdatedAt,
- lastUpdateBy: this.lastUpdateBy,
- soloChartVersion: this.soloChartVersion,
- hederaPlatformVersion: this.hederaPlatformVersion,
- hederaMirrorNodeChartVersion: this.hederaMirrorNodeChartVersion,
- explorerChartVersion: this.explorerChartVersion,
- hederaJsonRpcRelayChartVersion: this.hederaJsonRpcRelayChartVersion,
- soloVersion: this.soloVersion,
- } as RemoteConfigMetadataStruct;
-
- if (this.migration) {
- data.migration = this.migration.toObject();
- }
-
- return data;
- }
-}
diff --git a/src/core/config/remote/migration.ts b/src/core/config/remote/migration.ts
deleted file mode 100644
index 46a7895c1..000000000
--- a/src/core/config/remote/migration.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {SoloError} from '../../errors/solo-error.js';
-import {type Version} from '../../../types/index.js';
-import {type MigrationStruct} from './interfaces/migration-struct.js';
-import {type UserIdentitySchema} from '../../../data/schema/model/common/user-identity-schema.js';
-
-export class Migration implements MigrationStruct {
- private readonly _migratedAt: Date;
- private readonly _migratedBy: UserIdentitySchema;
- private readonly _fromVersion: Version;
-
- public constructor(migratedAt: Date, migratedBy: UserIdentitySchema, fromVersion: Version) {
- this._migratedAt = migratedAt;
- this._migratedBy = migratedBy;
- this._fromVersion = fromVersion;
- this.validate();
- }
-
- /* -------- Getters -------- */
-
- public get migratedAt(): Date {
- return this._migratedAt;
- }
- public get migratedBy(): UserIdentitySchema {
- return this._migratedBy;
- }
- public get fromVersion(): Version {
- return this._fromVersion;
- }
-
- /* -------- Utilities -------- */
-
- public validate(): void {
- if (!(this.migratedAt instanceof Date)) {
- throw new SoloError(`Invalid migratedAt: ${this.migratedAt}`);
- }
-
- if (
- !this.migratedBy ||
- !this.migratedBy.name ||
- !this.migratedBy.hostname ||
- typeof this.migratedBy.name !== 'string' ||
- typeof this.migratedBy.hostname !== 'string'
- ) {
- throw new SoloError(`Invalid migratedBy: ${this.migratedBy}`);
- }
-
- if (!this.fromVersion || typeof this.fromVersion !== 'string') {
- throw new SoloError(`Invalid fromVersion: ${this.fromVersion}`);
- }
- }
-
- public toObject(): MigrationStruct {
- return {
- migratedAt: this.migratedAt,
- migratedBy: this.migratedBy,
- fromVersion: this.fromVersion,
- };
- }
-}
diff --git a/src/core/config/remote/remote-config-data-wrapper.ts b/src/core/config/remote/remote-config-data-wrapper.ts
deleted file mode 100644
index 25c6b1884..000000000
--- a/src/core/config/remote/remote-config-data-wrapper.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import {SoloError} from '../../errors/solo-error.js';
-import * as yaml from 'yaml';
-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 Version} from '../../../types/index.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 RemoteConfigDataStruct} from './interfaces/remote-config-data-struct.js';
-
-export class RemoteConfigDataWrapper implements Validate, ToObject {
- private readonly _version: Version = '1.0.0';
- private _metadata: RemoteConfigMetadata;
- private readonly _clusters: Record;
- private _components: ComponentsDataWrapper;
- private _commandHistory: string[];
- private _lastExecutedCommand: string;
- private readonly _flags: CommonFlagsDataWrapper;
-
- public constructor(data: RemoteConfigData) {
- this._metadata = data.metadata;
- this._clusters = Cluster.fromClustersMapObject(data.clusters);
- this._components = data.components;
- this._commandHistory = data.commandHistory;
- this._lastExecutedCommand = data.lastExecutedCommand ?? '';
- this._flags = data.flags;
- this.validate();
- }
-
- //! -------- Modifiers -------- //
-
- public addCommandToHistory(command: string): void {
- this._commandHistory.push(command);
- this.lastExecutedCommand = command;
-
- if (this._commandHistory.length > constants.SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY) {
- this._commandHistory.shift();
- }
-
- this.validate();
- }
-
- //! -------- Getters & Setters -------- //
-
- private get version(): Version {
- return this._version;
- }
-
- public get metadata(): RemoteConfigMetadata {
- return this._metadata;
- }
-
- public set metadata(metadata: RemoteConfigMetadata) {
- this._metadata = metadata;
- this.validate();
- }
-
- public get clusters(): Record {
- return this._clusters;
- }
-
- public addCluster(cluster: Cluster): void {
- this._clusters[cluster.name] = cluster;
- this.validate();
- }
-
- public get components(): ComponentsDataWrapper {
- return this._components;
- }
-
- public set components(components: ComponentsDataWrapper) {
- this._components = components;
- this.validate();
- }
-
- public get lastExecutedCommand(): string {
- return this._lastExecutedCommand;
- }
-
- private set lastExecutedCommand(lastExecutedCommand: string) {
- this._lastExecutedCommand = lastExecutedCommand;
- this.validate();
- }
-
- public get commandHistory(): string[] {
- return this._commandHistory;
- }
-
- private set commandHistory(commandHistory: string[]) {
- this._commandHistory = commandHistory;
- this.validate();
- }
-
- public get flags() {
- return this._flags;
- }
-
- //! -------- Utilities -------- //
-
- public static fromConfigmap(configManager: ConfigManager, configMap: ConfigMap): RemoteConfigDataWrapper {
- const data: any = yaml.parse(configMap.data['remote-config-data']);
-
- return new RemoteConfigDataWrapper({
- metadata: RemoteConfigMetadata.fromObject(data.metadata),
- components: ComponentsDataWrapper.fromObject(data.components),
- clusters: data.clusters,
- commandHistory: data.commandHistory,
- lastExecutedCommand: data.lastExecutedCommand,
- flags: CommonFlagsDataWrapper.fromObject(configManager, data.flags),
- });
- }
-
- public validate(): void {
- if (!this._version || typeof this._version !== 'string') {
- throw new SoloError(`Invalid remote config version: ${this._version}`);
- }
-
- if (!this.metadata || !(this.metadata instanceof RemoteConfigMetadata)) {
- throw new SoloError(`Invalid remote config metadata: ${this.metadata}`);
- }
-
- if (!this.lastExecutedCommand || typeof this.lastExecutedCommand !== 'string') {
- throw new SoloError(`Invalid remote config last executed command: ${this.lastExecutedCommand}`);
- }
-
- if (!Array.isArray(this.commandHistory) || this.commandHistory.some(c => typeof c !== 'string')) {
- throw new SoloError(`Invalid remote config command history: ${this.commandHistory}`);
- }
-
- for (const [clusterReference, cluster] of Object.entries(this.clusters)) {
- if (!clusterReference || typeof clusterReference !== 'string') {
- throw new SoloError(`Invalid remote config cluster-ref: ${clusterReference}`);
- }
-
- if (!cluster) {
- throw new SoloError(`No cluster info is found for cluster-ref: ${clusterReference}`);
- }
-
- if (!cluster.name || typeof cluster.name !== 'string') {
- throw new SoloError(`Invalid remote config cluster name: ${cluster.name} for cluster-ref: ${clusterReference}`);
- }
-
- if (!cluster.namespace || typeof cluster.namespace !== 'string') {
- throw new SoloError(
- `Invalid remote config namespace: ${cluster.namespace} for cluster-ref: ${clusterReference}`,
- );
- }
- }
- }
-
- public toObject(): RemoteConfigDataStruct {
- return {
- metadata: this.metadata.toObject(),
- version: this.version,
- clusters: Cluster.toClustersMapObject(this.clusters),
- components: this.components.toObject(),
- commandHistory: this.commandHistory,
- lastExecutedCommand: this.lastExecutedCommand,
- flags: this.flags.toObject(),
- };
- }
-}
diff --git a/src/core/config/remote/remote-config-data.ts b/src/core/config/remote/remote-config-data.ts
deleted file mode 100644
index c10012d9a..000000000
--- a/src/core/config/remote/remote-config-data.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-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} from './cluster.js';
-import {type ClusterReference} from '../../../types/index.js';
-
-export interface RemoteConfigData {
- metadata: RemoteConfigMetadata;
- clusters: Record;
- components: ComponentsDataWrapper;
- lastExecutedCommand: string;
- commandHistory: string[];
- flags: CommonFlagsDataWrapper;
-}
diff --git a/src/core/config/remote/remote-config-manager.ts b/src/core/config/remote/remote-config-manager.ts
deleted file mode 100644
index d12382e59..000000000
--- a/src/core/config/remote/remote-config-manager.ts
+++ /dev/null
@@ -1,550 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-import * as constants from '../../constants.js';
-import {SoloError} from '../../errors/solo-error.js';
-import {RemoteConfigDataWrapper} from './remote-config-data-wrapper.js';
-import {RemoteConfigMetadata} from './metadata.js';
-import {Flags as flags} from '../../../commands/flags.js';
-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 Context,
- type DeploymentName,
- type NamespaceNameAsString,
- type Optional,
- type Version,
-} from '../../../types/index.js';
-import {type SoloLogger} from '../../logging/solo-logger.js';
-import {type ConfigManager} from '../../config-manager.js';
-import {inject, injectable} from 'tsyringe-neo';
-import {patchInject} from '../../dependency-injection/container-helper.js';
-import {ErrorMessages} from '../../error-messages.js';
-import {CommonFlagsDataWrapper} from './common-flags-data-wrapper.js';
-import {type AnyObject, type ArgvStruct, type NodeAlias, type NodeAliases} from '../../../types/aliases.js';
-import {type NamespaceName} from '../../../types/namespace/namespace-name.js';
-import {InjectTokens} from '../../dependency-injection/inject-tokens.js';
-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 ConfigMap} from '../../../integration/kube/resources/config-map/config-map.js';
-import {getSoloVersion} from '../../../../version.js';
-import {LocalConfigRuntimeState} from '../../../business/runtime-state/config/local/local-config-runtime-state.js';
-import {DeploymentStates} from './enumerations/deployment-states.js';
-import {FacadeArray} from '../../../business/runtime-state/collection/facade-array.js';
-import {StringFacade} from '../../../business/runtime-state/facade/string-facade.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 {
- /** Stores the loaded remote configuration data. */
- private remoteConfig: Optional;
-
- /**
- * @param k8Factory - The Kubernetes client used for interacting with ConfigMaps.
- * @param logger - The logger for recording activity and errors.
- * @param localConfig - Local configuration for the remote config.
- * @param configManager - Manager to retrieve application flags and settings.
- */
- public constructor(
- @inject(InjectTokens.K8Factory) private readonly k8Factory?: K8Factory,
- @inject(InjectTokens.SoloLogger) private readonly logger?: SoloLogger,
- @inject(InjectTokens.LocalConfigRuntimeState) private readonly localConfig?: LocalConfigRuntimeState,
- @inject(InjectTokens.ConfigManager) private readonly configManager?: ConfigManager,
- ) {
- this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
- this.logger = patchInject(logger, InjectTokens.SoloLogger, this.constructor.name);
- this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
- this.configManager = patchInject(configManager, InjectTokens.ConfigManager, this.constructor.name);
- }
-
- /* ---------- Getters ---------- */
-
- public get currentCluster(): ClusterReference {
- 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');
- }
-
- // Call the callback function to modify the remote config
- await callback(this.remoteConfig);
-
- // Save the modified version of the remote config
- 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,
- nodeAliases: NodeAliases,
- namespace: NamespaceName,
- deployment: DeploymentName,
- clusterReference: ClusterReference,
- context: Context,
- dnsBaseDomain: string,
- dnsConsensusNodePattern: string,
- ): Promise {
- const clusters: Record = {
- [clusterReference]: new Cluster(
- clusterReference,
- namespace.name,
- deployment,
- dnsBaseDomain,
- dnsConsensusNodePattern,
- ),
- };
-
- const lastUpdatedAt = new Date();
- const userIdentity = this.localConfig.configuration.userIdentity.encapsulatedObject;
- const soloVersion = getSoloVersion();
- const currentCommand = argv._.join(' ');
-
- this.remoteConfig = new RemoteConfigDataWrapper({
- clusters,
- metadata: new RemoteConfigMetadata(namespace.name, deployment, state, lastUpdatedAt, userIdentity, soloVersion),
- commandHistory: [currentCommand],
- lastExecutedCommand: currentCommand,
- components: ComponentsDataWrapper.initializeWithNodes(nodeAliases, clusterReference, namespace.name),
- flags: await CommonFlagsDataWrapper.initialize(this.configManager, argv),
- });
-
- await this.createConfigMap(context);
- }
-
- /**
- * Saves the currently loaded remote configuration data to the Kubernetes cluster.
- * @throws {@link SoloError} if there is no remote configuration data to save.
- */
- private async save(): Promise {
- if (!this.remoteConfig) {
- throw new SoloError('Attempted to save remote config without data');
- }
-
- await this.replaceConfigMap();
- }
-
- /**
- * Loads the remote configuration from the Kubernetes cluster if it exists.
- * @param namespace - The namespace to search for the ConfigMap.
- * @param context - The context to use for the Kubernetes client.
- * @returns true if the configuration is loaded successfully.
- */
- private async load(namespace?: NamespaceName, context?: Context): Promise {
- if (this.remoteConfig) {
- return;
- }
- try {
- const configMap = await this.getConfigMap(namespace, context);
-
- this.remoteConfig = RemoteConfigDataWrapper.fromConfigmap(this.configManager, configMap);
- } catch (error) {
- throw new SoloError('Failed to load remote config from cluster', error);
- }
- }
-
- /**
- * 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());
-
- await this.load(namespace, context);
- try {
- await RemoteConfigValidator.validateComponents(
- namespace,
- this.remoteConfig.components,
- this.k8Factory,
- this.localConfig,
- false,
- );
- } catch (error) {
- throw new SoloError(
- ErrorMessages.REMOTE_CONFIG_IS_INVALID(this.k8Factory.getK8(context).clusters().readCurrent()),
- error,
- );
- }
- return this.remoteConfig;
- }
-
- /** Unload the remote config from the remote config manager. */
- public unload(): void {
- this.remoteConfig = undefined;
- }
-
- public static compare(remoteConfig1: RemoteConfigDataWrapper, remoteConfig2: RemoteConfigDataWrapper): boolean {
- // Compare clusters
- const clusters1 = Object.keys(remoteConfig1.clusters);
- const clusters2 = Object.keys(remoteConfig2.clusters);
- if (clusters1.length !== clusters2.length) {
- return false;
- }
-
- for (const index in clusters1) {
- if (clusters1[index] !== clusters2[index]) {
- return false;
- }
- }
-
- return true;
- }
-
- /* ---------- 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,
- skipConsensusNodesValidation: boolean = true,
- ): Promise {
- await this.setDefaultNamespaceAndDeploymentIfNotSet(argv);
- this.setDefaultContextIfNotSet();
-
- await this.load();
-
- this.logger.info('Remote config loaded');
- if (!validate) {
- return;
- }
-
- await RemoteConfigValidator.validateComponents(
- this.configManager.getFlag(flags.namespace),
- this.remoteConfig.components,
- this.k8Factory,
- this.localConfig,
- skipConsensusNodesValidation,
- );
-
- const currentCommand = argv._?.join(' ');
- const commandArguments = flags.stringifyArgv(argv);
-
- this.remoteConfig!.addCommandToHistory(
- `Executed by ${this.localConfig.configuration.userIdentity.name}: ${currentCommand} ${commandArguments}`.trim(),
- );
-
- this.populateVersionsInMetadata(argv);
-
- await this.remoteConfig.flags.handleFlags(argv);
-
- await this.save();
- }
-
- private populateVersionsInMetadata(argv: AnyObject): void {
- const command: string = argv._[0];
- const subcommand: string = argv._[1];
-
- const isCommandUsingSoloChartVersionFlag =
- (command === 'network' && subcommand === 'deploy') ||
- (command === 'network' && subcommand === 'refresh') ||
- (command === 'node' && subcommand === 'update') ||
- (command === 'node' && subcommand === 'update-execute') ||
- (command === 'node' && subcommand === 'add') ||
- (command === 'node' && subcommand === 'add-execute') ||
- (command === 'node' && subcommand === 'delete') ||
- (command === 'node' && subcommand === 'delete-execute');
-
- if (argv[flags.soloChartVersion.name]) {
- this.remoteConfig.metadata.soloChartVersion = argv[flags.soloChartVersion.name] as Version;
- } else if (isCommandUsingSoloChartVersionFlag) {
- this.remoteConfig.metadata.soloChartVersion = flags.soloChartVersion.definition.defaultValue as Version;
- }
-
- const isCommandUsingReleaseTagVersionFlag =
- (command === 'node' && subcommand !== 'keys' && subcommand !== 'logs' && subcommand !== 'states') ||
- (command === 'network' && subcommand === 'deploy');
-
- if (argv[flags.releaseTag.name]) {
- this.remoteConfig.metadata.hederaPlatformVersion = argv[flags.releaseTag.name] as Version;
- } else if (isCommandUsingReleaseTagVersionFlag) {
- this.remoteConfig.metadata.hederaPlatformVersion = flags.releaseTag.definition.defaultValue as Version;
- }
-
- if (argv[flags.mirrorNodeVersion.name]) {
- this.remoteConfig.metadata.hederaMirrorNodeChartVersion = argv[flags.mirrorNodeVersion.name] as Version;
- } else if (command === 'mirror-node' && subcommand === 'deploy') {
- this.remoteConfig.metadata.hederaMirrorNodeChartVersion = flags.mirrorNodeVersion.definition
- .defaultValue as Version;
- }
-
- if (argv[flags.explorerVersion.name]) {
- this.remoteConfig.metadata.explorerChartVersion = argv[flags.explorerVersion.name] as Version;
- } else if (command === 'explorer' && subcommand === 'deploy') {
- this.remoteConfig.metadata.explorerChartVersion = flags.explorerVersion.definition.defaultValue as Version;
- }
-
- if (argv[flags.relayReleaseTag.name]) {
- this.remoteConfig.metadata.hederaJsonRpcRelayChartVersion = argv[flags.relayReleaseTag.name] as Version;
- } else if (command === 'relay' && subcommand === 'deploy') {
- this.remoteConfig.metadata.hederaJsonRpcRelayChartVersion = flags.relayReleaseTag.definition
- .defaultValue as Version;
- }
- }
-
- /* ---------- Utilities ---------- */
-
- /** Empties the component data inside the remote config */
- public async deleteComponents(): Promise {
- await this.modify(async remoteConfig => {
- remoteConfig.components = ComponentsDataWrapper.initializeEmpty();
- });
- }
-
- public isLoaded(): boolean {
- 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();
- }
- if (!context) {
- context = this.configManager.getFlag(flags.context) ?? this.getContextForFirstCluster();
- }
-
- try {
- const configMap = await this.k8Factory
- .getK8(context)
- .configMaps()
- .read(namespace, constants.SOLO_REMOTE_CONFIGMAP_NAME);
-
- if (!configMap) {
- throw new SoloError(`Remote config ConfigMap not found for namespace: ${namespace}, context: ${context}`);
- }
-
- return configMap;
- } catch (error) {
- throw new SoloError(
- `Failed to read remote config from cluster for namespace: ${namespace}, context: ${context}`,
- error,
- );
- }
- }
-
- /**
- * 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;
- 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);
- }
-
- /**
- * 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 deploymentName = this.configManager.getFlag(flags.deployment);
-
- if (!deploymentName) {
- throw new SoloError('Failed to get deployment');
- }
-
- const clusterReferences: FacadeArray =
- this.localConfig.configuration.deploymentByName(deploymentName).clusters;
-
- if (!clusterReferences) {
- throw new SoloError(`Failed to get get cluster refs from local config for deployment ${deploymentName}`);
- }
-
- const contexts: Context[] = clusterReferences.map((clusterReference): string =>
- this.localConfig.configuration.clusterRefs.get(clusterReference.toString())?.toString(),
- );
-
- await Promise.all(
- contexts.map(context => this.k8Factory.getK8(context).configMaps().replace(namespace, name, labels, data)),
- );
- }
-
- private async setDefaultNamespaceAndDeploymentIfNotSet(argv: AnyObject): Promise {
- if (this.configManager.hasFlag(flags.namespace)) {
- return;
- }
-
- // TODO: Current quick fix for commands where namespace is not passed
- let deploymentName = this.configManager.getFlag(flags.deployment);
- let currentDeployment = this.localConfig.configuration.deploymentByName(deploymentName);
-
- if (!deploymentName) {
- deploymentName = await promptTheUserForDeployment(this.configManager);
- currentDeployment = this.localConfig.configuration.deploymentByName(deploymentName);
- // TODO: Fix once we have the DataManager,
- // without this the user will be prompted a second time for the deployment
- // TODO: we should not be mutating argv
- argv[flags.deployment.name] = deploymentName;
- this.logger.warn(
- `Deployment name not found in flags or local config, setting it in argv and config manager to: ${deploymentName}`,
- );
- this.configManager.setFlag(flags.deployment, deploymentName);
- }
-
- if (!currentDeployment) {
- throw new SoloError(`Selected deployment name is not set in local config - ${deploymentName}`);
- }
-
- const namespace: NamespaceNameAsString = currentDeployment.namespace;
-
- this.logger.warn(`Namespace not found in flags, setting it to: ${namespace}`);
- this.configManager.setFlag(flags.namespace, namespace);
- argv[flags.namespace.name] = namespace;
- }
-
- private setDefaultContextIfNotSet(): void {
- if (this.configManager.hasFlag(flags.context)) {
- return;
- }
-
- 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");
- }
-
- this.logger.warn(`Context not found in flags, setting it to: ${context}`);
- this.configManager.setFlag(flags.context, context);
- }
-
- /**
- * Retrieves the namespace value from the configuration manager's flags.
- * @returns string - The namespace value if set.
- */
- private async getNamespace(): Promise {
- return await resolveNamespaceFromDeployment(this.localConfig, this.configManager);
- }
-
- //* 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');
- }
-
- const consensusNodes: ConsensusNode[] = [];
-
- for (const node of Object.values(this.components.consensusNodes)) {
- const cluster: Cluster = this.clusters[node.cluster];
- const context: Context = this.localConfig.configuration.clusterRefs.get(node.cluster)?.toString();
-
- consensusNodes.push(
- new ConsensusNode(
- node.name as NodeAlias,
- node.nodeId,
- node.namespace,
- node.cluster,
- context,
- cluster.dnsBaseDomain,
- cluster.dnsConsensusNodePattern,
- Templates.renderConsensusNodeFullyQualifiedDomainName(
- node.name as NodeAlias,
- node.nodeId,
- node.namespace,
- node.cluster,
- cluster.dnsBaseDomain,
- cluster.dnsConsensusNodePattern,
- ),
- ),
- );
- }
-
- // return the consensus nodes
- 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 = new Map();
-
- for (const node of nodes) {
- accumulator.set(node.cluster, node.context);
- }
-
- return accumulator;
- }
-
- private getContextForFirstCluster(): string {
- const deploymentName = this.configManager.getFlag(flags.deployment);
-
- const clusterReference: ClusterReference =
- this.localConfig.configuration.deploymentByName(deploymentName).clusters[0];
-
- const context: Context = this.localConfig.configuration.clusterRefs.get(clusterReference)?.toString();
-
- this.logger.debug(`Using context ${context} for cluster ${clusterReference} for deployment ${deploymentName}`);
-
- return context;
- }
-}
diff --git a/src/core/config/remote/remote-config-validator.ts b/src/core/config/remote/remote-config-validator.ts
index 9d6849baa..e8e5a6ea0 100644
--- a/src/core/config/remote/remote-config-validator.ts
+++ b/src/core/config/remote/remote-config-validator.ts
@@ -3,196 +3,153 @@
import * as constants from '../../constants.js';
import {SoloError} from '../../errors/solo-error.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 '../../../types/namespace/namespace-name.js';
import {type Pod} from '../../../integration/kube/resources/pod/pod.js';
import {type LocalConfigRuntimeState} from '../../../business/runtime-state/config/local/local-config-runtime-state.js';
-import {ConsensusNodeStates} from './enumerations/consensus-node-states.js';
import {type Context} from '../../../types/index.js';
+import {type NodeAlias} from '../../../types/aliases.js';
+import {Templates} from '../../templates.js';
+import {DeploymentPhase} from '../../../data/schema/model/remote/deployment-phase.js';
+import {type ConsensusNodeStateSchema} from '../../../data/schema/model/remote/state/consensus-node-state-schema.js';
+import {type BaseStateSchema} from '../../../data/schema/model/remote/state/base-state-schema.js';
+import {inject, injectable} from 'tsyringe-neo';
+import {type RemoteConfigRuntimeStateApi} from '../../../business/runtime-state/api/remote-config-runtime-state-api.js';
+import {patchInject} from '../../dependency-injection/container-helper.js';
+import {InjectTokens} from '../../dependency-injection/inject-tokens.js';
+import {RemoteConfigValidatorApi} from './api/remote-config-validator-api.js';
+import {DeploymentStateSchema} from '../../../data/schema/model/remote/deployment-state-schema.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 {
- /**
- * 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: LocalConfigRuntimeState,
- 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)),
- ]);
+@injectable()
+export class RemoteConfigValidator implements RemoteConfigValidatorApi {
+ public constructor(
+ @inject(InjectTokens.K8Factory) private readonly k8Factory?: K8Factory,
+ @inject(InjectTokens.LocalConfigRuntimeState) private readonly localConfig?: LocalConfigRuntimeState,
+ ) {
+ this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
+ this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
}
- private static validateRelays(
- namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
- ): Promise[] {
- return Object.values(components.relays).map(async component => {
- const context: Context = localConfig.configuration.clusterRefs.get(component.cluster)?.toString();
- const labels: string[] = [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 getRelayLabels(): string[] {
+ // TODO:
+ // https://github.com/hashgraph/solo/issues/1823
+ // Add logic for selecting by specific label,
+ // when multiple instances can be deployed at the same time.
+ return [constants.SOLO_RELAY_LABEL];
}
- private static validateHaProxies(
- namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
- ): Promise[] {
- return Object.values(components.haProxies).map(async component => {
- const context: Context = localConfig.configuration.clusterRefs.get(component.cluster)?.toString();
- const labels: string[] = [`app=${component.name}`];
- try {
- const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
+ private static getHaProxyLabels(component: BaseStateSchema): string[] {
+ const nodeAlias: NodeAlias = Templates.renderNodeAliasFromNumber(component.metadata.id + 1);
+ return [`app=haproxy-${nodeAlias}`, 'solo.hedera.com/type=haproxy'];
+ }
- 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 getMirrorNodeLabels(): string[] {
+ // TODO:
+ // https://github.com/hashgraph/solo/issues/1823
+ // Add logic for selecting by specific label,
+ // when multiple instances can be deployed at the same time.
+ return constants.SOLO_HEDERA_MIRROR_IMPORTER;
}
- private static validateMirrorNodes(
- namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
- ): Promise[] {
- return Object.values(components.mirrorNodes).map(async component => {
- const context: Context = localConfig.configuration.clusterRefs.get(component.cluster)?.toString();
- const labels: string[] = constants.SOLO_HEDERA_MIRROR_IMPORTER;
- try {
- const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
+ private static getEnvoyProxyLabels(component: BaseStateSchema): string[] {
+ const nodeAlias: NodeAlias = Templates.renderNodeAliasFromNumber(component.metadata.id + 1);
+ return [`solo.hedera.com/node-name=${nodeAlias}`, 'solo.hedera.com/type=envoy-proxy'];
+ }
- 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 getMirrorNodeExplorerLabels(): string[] {
+ // TODO:
+ // https://github.com/hashgraph/solo/issues/1823
+ // Add logic for selecting by specific label,
+ // when multiple instances can be deployed at the same time.
+ return [constants.SOLO_EXPLORER_LABEL];
}
- private static validateEnvoyProxies(
- namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
- ): Promise[] {
- return Object.values(components.envoyProxies).map(async component => {
- const context: Context = localConfig.configuration.clusterRefs.get(component.cluster)?.toString();
- const labels: string[] = [`app=${component.name}`];
- try {
- const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
+ private static getConsensusNodeLabels(component: BaseStateSchema): string[] {
+ return [`app=network-${Templates.renderNodeAliasFromNumber(component.metadata.id + 1)}`];
+ }
- 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 consensusNodeSkipConditionCallback(nodeComponent: ConsensusNodeStateSchema): boolean {
+ return (
+ nodeComponent.metadata.phase === DeploymentPhase.REQUESTED ||
+ nodeComponent.metadata.phase === DeploymentPhase.STOPPED
+ );
}
- private static validateConsensusNodes(
+ private static componentValidationsMapping: Record<
+ string,
+ {
+ getLabelsCallback: (component: BaseStateSchema) => string[];
+ displayName: string;
+ skipCondition?: (component: BaseStateSchema) => 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,
+ },
+ consensusNodes: {
+ displayName: 'Consensus node',
+ getLabelsCallback: RemoteConfigValidator.getConsensusNodeLabels,
+ skipCondition: RemoteConfigValidator.consensusNodeSkipConditionCallback,
+ },
+ };
+
+ public async validateComponents(
namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
- ): Promise[] {
- return Object.values(components.consensusNodes).map(async component => {
- if (component.state === ConsensusNodeStates.REQUESTED || component.state === ConsensusNodeStates.NON_DEPLOYED) {
- return;
- }
-
- const context: Context = localConfig.configuration.clusterRefs.get(component.cluster)?.toString();
- const labels: string[] = [`app=network-${component.name}`];
- try {
- const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
+ skipConsensusNodes: boolean,
+ state: Readonly,
+ ): Promise {
+ const validationPromises: Promise[] = Object.entries(RemoteConfigValidator.componentValidationsMapping)
+ .filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes)
+ .flatMap(([key, {getLabelsCallback, displayName, skipCondition}]): Promise[] =>
+ this.validateComponentGroup(namespace, state[key], getLabelsCallback, displayName, skipCondition),
+ );
- if (pods.length === 0) {
- throw new Error('Pod not found');
- } // to return the generic error message
- } catch (error) {
- RemoteConfigValidator.throwValidationError('Consensus node', component, error);
- }
- });
+ await Promise.all(validationPromises);
}
- private static validateMirrorNodeExplorers(
+ private validateComponentGroup(
namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
+ state: Record,
+ getLabelsCallback: (component: BaseStateSchema) => string[],
+ displayName: string,
+ skipCondition?: (component: BaseStateSchema) => boolean,
): Promise[] {
- return Object.values(components.mirrorNodeExplorers).map(async component => {
- const context: Context = localConfig.configuration.clusterRefs.get(component.cluster)?.toString();
- const labels: string[] = [constants.SOLO_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);
+ return Object.values(state).map(async (component): Promise => {
+ if (skipCondition?.(component)) {
+ return;
}
- });
- }
- private static validateBlockNodes(
- namespace: NamespaceName,
- components: ComponentsDataWrapper,
- k8Factory: K8Factory,
- localConfig: LocalConfigRuntimeState,
- ): Promise[] {
- return Object.values(components.blockNodes).map(async component => {
- const context: Context = localConfig.configuration.clusterRefs[component.cluster];
- const labels: string[] = [constants.SOLO_EXPLORER_LABEL]; // TODO: ADD BLOCK SELECT
+ const context: Context = this.localConfig.configuration.clusterRefs.get(component.metadata.cluster)?.toString();
+ const labels: string[] = getLabelsCallback(component);
+
try {
- const pods: Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
+ const pods: Pod[] = await this.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) {
- RemoteConfigValidator.throwValidationError('Block node', component, error);
+ throw RemoteConfigValidator.buildValidationError(displayName, component, error);
}
});
}
@@ -200,16 +157,23 @@ 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 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: component.toObject()},
+ private static buildValidationError(
+ displayName: string,
+ component: BaseStateSchema,
+ error: Error | unknown,
+ ): SoloError {
+ return new SoloError(RemoteConfigValidator.buildValidationErrorMessage(displayName, component), error, component);
+ }
+
+ public static buildValidationErrorMessage(displayName: string, component: BaseStateSchema): string {
+ return (
+ `${displayName} in remote config with id ${component.metadata.id} was not found in ` +
+ `namespace: ${component.metadata.namespace}, ` +
+ `cluster: ${component.metadata.cluster}`
);
}
}
diff --git a/src/core/constants.ts b/src/core/constants.ts
index d31f720ae..b99fc138f 100644
--- a/src/core/constants.ts
+++ b/src/core/constants.ts
@@ -24,6 +24,7 @@ export const SOLO_REMOTE_CONFIGMAP_NAME = 'solo-remote-config';
export const SOLO_REMOTE_CONFIGMAP_LABELS = {'solo.hedera.com/type': 'remote-config'};
export const SOLO_REMOTE_CONFIG_MAX_COMMAND_IN_HISTORY = 50;
export const SOLO_REMOTE_CONFIGMAP_LABEL_SELECTOR = 'solo.hedera.com/type=remote-config';
+export const SOLO_REMOTE_CONFIGMAP_DATA_KEY = 'remote-config-data';
export const NODE_COPY_CONCURRENT = Number(process.env.NODE_COPY_CONCURRENT) || 4;
export const SKIP_NODE_PING = Boolean(process.env.SKIP_NODE_PING) || false;
export const DEFAULT_LOCK_ACQUIRE_ATTEMPTS = +process.env.SOLO_LEASE_ACQUIRE_ATTEMPTS || 10;
diff --git a/src/core/dependency-injection/container-init.ts b/src/core/dependency-injection/container-init.ts
index 4755df2e1..0cd877c7e 100644
--- a/src/core/dependency-injection/container-init.ts
+++ b/src/core/dependency-injection/container-init.ts
@@ -16,7 +16,6 @@ import {ProfileManager} from '../profile-manager.js';
import {IntervalLockRenewalService} from '../lock/interval-lock-renewal.js';
import {LockManager} from '../lock/lock-manager.js';
import {CertificateManager} from '../certificate-manager.js';
-import {RemoteConfigManager} from '../config/remote/remote-config-manager.js';
import os from 'node:os';
import * as version from '../../../version.js';
import {NetworkNodes} from '../network-nodes.js';
@@ -30,6 +29,7 @@ import {NodeCommandTasks} from '../../commands/node/tasks.js';
import {ClusterCommandConfigs} from '../../commands/cluster/configs.js';
import {NodeCommandConfigs} from '../../commands/node/configs.js';
import {ErrorHandler} from '../error-handler.js';
+import {ClassToObjectMapper} from '../../data/mapper/impl/class-to-object-mapper.js';
import {HelmExecutionBuilder} from '../../integration/helm/execution/helm-execution-builder.js';
import {DefaultHelmClient} from '../../integration/helm/impl/default-helm-client.js';
import {HelpRenderer} from '../help-renderer.js';
@@ -51,7 +51,9 @@ import {ValueContainer} from './value-container.js';
import {BlockNodeCommand} from '../../commands/block-node.js';
import {LocalConfigRuntimeState} from '../../business/runtime-state/config/local/local-config-runtime-state.js';
import {LocalConfigSource} from '../../data/configuration/impl/local-config-source.js';
-import {ClassToObjectMapper} from '../../data/mapper/impl/class-to-object-mapper.js';
+import {RemoteConfigRuntimeState} from '../../business/runtime-state/config/remote/remote-config-runtime-state.js';
+import {ComponentFactory} from '../config/remote/component-factory.js';
+import {RemoteConfigValidator} from '../config/remote/remote-config-validator.js';
export type InstanceOverrides = Map;
@@ -115,7 +117,7 @@ export class Container {
new SingletonContainer(InjectTokens.CertificateManager, CertificateManager),
new SingletonContainer(InjectTokens.LocalConfigRuntimeState, LocalConfigRuntimeState),
new SingletonContainer(InjectTokens.LocalConfigSource, LocalConfigSource),
- new SingletonContainer(InjectTokens.RemoteConfigManager, RemoteConfigManager),
+ new SingletonContainer(InjectTokens.RemoteConfigRuntimeState, RemoteConfigRuntimeState),
new SingletonContainer(InjectTokens.ClusterChecks, ClusterChecks),
new SingletonContainer(InjectTokens.NetworkNodes, NetworkNodes),
new SingletonContainer(InjectTokens.Middlewares, Middlewares),
@@ -139,6 +141,8 @@ export class Container {
new SingletonContainer(InjectTokens.NodeCommandConfigs, NodeCommandConfigs),
new SingletonContainer(InjectTokens.ErrorHandler, ErrorHandler),
new SingletonContainer(InjectTokens.ObjectMapper, ClassToObjectMapper),
+ new SingletonContainer(InjectTokens.ComponentFactory, ComponentFactory),
+ new SingletonContainer(InjectTokens.RemoteConfigValidator, RemoteConfigValidator),
];
const valueContainers: ValueContainer[] = [
diff --git a/src/core/dependency-injection/inject-tokens.ts b/src/core/dependency-injection/inject-tokens.ts
index 2791b0dc1..bf5c5c276 100644
--- a/src/core/dependency-injection/inject-tokens.ts
+++ b/src/core/dependency-injection/inject-tokens.ts
@@ -4,6 +4,8 @@
* Dependency injection tokens
*/
export const InjectTokens = {
+ ComponentFactory: Symbol.for('ComponentFactory'),
+ RemoteConfigValidator: Symbol.for('RemoteConfigValidator'),
LogLevel: Symbol.for('LogLevel'),
DevelopmentMode: Symbol.for('DevelopmentMode'),
OsPlatform: Symbol.for('OsPlatform'),
@@ -29,7 +31,7 @@ export const InjectTokens = {
KeyManager: Symbol.for('KeyManager'),
ProfileManager: Symbol.for('ProfileManager'),
CertificateManager: Symbol.for('CertificateManager'),
- RemoteConfigManager: Symbol.for('RemoteConfigManager'),
+ RemoteConfigRuntimeState: Symbol.for('RemoteConfigRuntimeState'),
ClusterChecks: Symbol.for('ClusterChecks'),
NetworkNodes: Symbol.for('NetworkNodes'),
AccountCommand: Symbol.for('AccountCommand'),
diff --git a/src/core/dependency-managers/dependency-manager.ts b/src/core/dependency-managers/dependency-manager.ts
index b4c8809fa..ff0813148 100644
--- a/src/core/dependency-managers/dependency-manager.ts
+++ b/src/core/dependency-managers/dependency-manager.ts
@@ -13,7 +13,7 @@ import {type SoloListrTask} from '../../types/index.js';
export class DependencyManager extends ShellRunner {
private readonly depManagerMap: Map;
- constructor(@inject(InjectTokens.HelmDependencyManager) helmDepManager?: HelmDependencyManager) {
+ public constructor(@inject(InjectTokens.HelmDependencyManager) helmDepManager?: HelmDependencyManager) {
super();
this.depManagerMap = helmDepManager
? new Map().set(constants.HELM, helmDepManager)
@@ -25,13 +25,12 @@ export class DependencyManager extends ShellRunner {
* @param dep - is the name of the program
* @param [shouldInstall] - Whether or not install the dependency if not installed
*/
- async checkDependency(dep: string, shouldInstall = true) {
+ public async checkDependency(dep: string, shouldInstall: boolean = true): Promise