Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5ee7675
added general functionality, dirty version, attempting to fix refresh
instamenta Dec 16, 2025
8330ff6
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Dec 18, 2025
d894747
refactor strategy
instamenta Dec 18, 2025
3c78c9b
pre cleanup version that is not failing on post and pre genesis block…
instamenta Dec 19, 2025
79969ca
cleanup
instamenta Dec 19, 2025
a57cc83
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Dec 19, 2025
8892036
small fixes
instamenta Dec 23, 2025
be9d906
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Jan 6, 2026
8a1e8dd
increase readability
instamenta Jan 6, 2026
c31045e
got post genesis block node deployments to work
instamenta Jan 8, 2026
619a63a
bit of cleanup
instamenta Jan 9, 2026
bf07138
working on external block node support, and itroducing some helpers t…
instamenta Jan 12, 2026
f80afc3
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Jan 13, 2026
b85bf8f
general clean-up, got post genesis working again, and laid groundwork…
instamenta Jan 13, 2026
1bf972b
clean-up and improvements to 'solo consensus node add'
instamenta Jan 14, 2026
95e992c
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Jan 14, 2026
9f9f099
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Jan 16, 2026
e63adca
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Jan 19, 2026
0f78a2a
added logic for external block node support, improved existing block …
instamenta Jan 19, 2026
ad68f4b
add to new command solo block node add-external to command definitions
instamenta Jan 19, 2026
b4be5a5
added tests for external block node and fixed some logic
instamenta Jan 19, 2026
11452fe
lint-fix
instamenta Jan 19, 2026
306328e
reduce lint warning to satisfy new rule
instamenta Jan 19, 2026
80436fc
removed unusued k8 helper inside the intergration/kube dir, and remov…
instamenta Jan 20, 2026
7cdc990
Merge remote-tracking branch 'origin/main' into 3032-ability-to-provi…
instamenta Jan 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ export class RemoteConfigRuntimeState implements RemoteConfigRuntimeStateApi {
cluster.dnsConsensusNodePattern,
),
node.blockNodeMap,
node.externalBlockNodeMap,
),
);
}
Expand Down
174 changes: 166 additions & 8 deletions src/commands/block-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {DeploymentStateSchema} from '../data/schema/model/remote/deployment-stat
import {ConsensusNode} from '../core/model/consensus-node.js';
import {NetworkCommand} from './network.js';
import {type ClusterSchema} from '../data/schema/model/common/cluster-schema.js';
import {ExternalBlockNodeStateSchema} from '../data/schema/model/remote/state/external-block-node-state-schema.js';

interface BlockNodeDeployConfigClass {
chartVersion: string;
Expand Down Expand Up @@ -109,6 +110,22 @@ interface BlockNodeUpgradeContext {
config: BlockNodeUpgradeConfigClass;
}

interface BlockNodeAddExternalConfigClass {
clusterRef: ClusterReferenceName;
deployment: DeploymentName;
devMode: boolean;
quiet: boolean;
context: string;
externalBlockNodeAddress: string;
newExternalBlockNodeComponent: ExternalBlockNodeStateSchema;
namespace: NamespaceName;
priorityMapping: Record<NodeAlias, number>;
}

interface BlockNodeAddExternalContext {
config: BlockNodeAddExternalConfigClass;
}

@injectable()
export class BlockNodeCommand extends BaseCommand {
public constructor() {
Expand All @@ -121,6 +138,8 @@ export class BlockNodeCommand extends BaseCommand {

private static readonly UPGRADE_CONFIGS_NAME: string = 'upgradeConfigs';

private static readonly ADD_EXTERNAL_CONFIGS_NAME: string = 'addConfigs';

public static readonly ADD_FLAGS_LIST: CommandFlags = {
required: [flags.deployment],
optional: [
Expand All @@ -139,6 +158,11 @@ export class BlockNodeCommand extends BaseCommand {
],
};

public static readonly ADD_EXTERNAL_FLAGS_LIST: CommandFlags = {
required: [flags.deployment, flags.externalBlockNodeAddress],
optional: [flags.clusterRef, flags.devMode, flags.quiet, flags.priorityMapping],
};

public static readonly DESTROY_FLAGS_LIST: CommandFlags = {
required: [flags.deployment],
optional: [flags.chartDirectory, flags.clusterRef, flags.devMode, flags.force, flags.quiet, flags.id],
Expand Down Expand Up @@ -252,21 +276,32 @@ export class BlockNodeCommand extends BaseCommand {
private updateConsensusNodesPostGenesis(): SoloListrTask<BlockNodeDeployContext> {
return {
title: 'Copy block-nodes.json to consensus nodes',
task: async ({config: {priorityMapping, namespace}}): Promise<void> => {
task: async ({config: {priorityMapping}}): Promise<void> => {
const nodeAliases: string[] = Object.keys(priorityMapping);

const filteredConsensusNodes: ConsensusNode[] = this.remoteConfig
.getConsensusNodes()
.filter((node): boolean => nodeAliases.includes(node.name));

for (const node of filteredConsensusNodes) {
await NetworkCommand.createAndCopyBlockNodeJsonFileForConsensusNode(
node,
namespace,
this.logger,
this.k8Factory,
this.remoteConfig,
);
await NetworkCommand.createAndCopyBlockNodeJsonFileForConsensusNode(node, this.logger, this.k8Factory);
}
},
};
}

private updateConsensusNodesPostGenesisForExternal(): SoloListrTask<BlockNodeAddExternalContext> {
return {
title: 'Copy block-nodes.json to consensus nodes',
task: async ({config: {priorityMapping}}): Promise<void> => {
const nodeAliases: string[] = Object.keys(priorityMapping);

const filteredConsensusNodes: ConsensusNode[] = this.remoteConfig
.getConsensusNodes()
.filter((node): boolean => nodeAliases.includes(node.name));

for (const node of filteredConsensusNodes) {
await NetworkCommand.createAndCopyBlockNodeJsonFileForConsensusNode(node, this.logger, this.k8Factory);
}
},
};
Expand All @@ -287,6 +322,45 @@ export class BlockNodeCommand extends BaseCommand {
};
}

private updateConsensusNodesInRemoteConfigForExternalBlockNode(): SoloListrTask<BlockNodeAddExternalContext> {
return {
title: 'Update consensus nodes in remote config',
task: async ({config: {newExternalBlockNodeComponent, priorityMapping}}): Promise<void> => {
const state: DeploymentStateSchema = this.remoteConfig.configuration.state;
const nodeAliases: string[] = Object.keys(priorityMapping);

for (const node of state.consensusNodes.filter((node): boolean =>
nodeAliases.includes(Templates.renderNodeAliasFromNumber(node.metadata.id)),
)) {
const priority: number = priorityMapping[Templates.renderNodeAliasFromNumber(node.metadata.id)];

node.externalBlockNodeMap.push([newExternalBlockNodeComponent.id, priority]);
}

this.remoteConfig.configuration.state.consensusNodes = state.consensusNodes;

await this.remoteConfig.persist();
},
};
}

private handleConsensusNodeUpdatingForExternalBlockNode(): SoloListrTask<BlockNodeAddExternalContext> {
return {
title: 'Update consensus nodes',
task: (_, task): SoloListr<BlockNodeAddExternalContext> => {
const subTasks: SoloListrTask<BlockNodeAddExternalContext>[] = [
this.updateConsensusNodesInRemoteConfigForExternalBlockNode(),
];

if (this.remoteConfig.configuration.state.ledgerPhase !== LedgerPhase.UNINITIALIZED) {
subTasks.push(this.updateConsensusNodesPostGenesisForExternal());
}

return task.newListr(subTasks, constants.LISTR_DEFAULT_OPTIONS.DEFAULT);
},
};
}

public async add(argv: ArgvStruct): Promise<boolean> {
// eslint-disable-next-line @typescript-eslint/typedef,unicorn/no-this-assignment
const self = this;
Expand Down Expand Up @@ -716,6 +790,77 @@ export class BlockNodeCommand extends BaseCommand {
return true;
}

public async addExternal(argv: ArgvStruct): Promise<boolean> {
let lease: Lock;

const tasks: SoloListr<BlockNodeAddExternalContext> = this.taskList.newTaskList<BlockNodeAddExternalContext>(
[
{
title: 'Initialize',
task: async (context_, task): Promise<Listr<AnyListrContext>> => {
await this.localConfig.load();
await this.remoteConfig.loadAndValidate(argv);
lease = await this.leaseManager.create();

this.configManager.update(argv);

flags.disablePrompts(BlockNodeCommand.ADD_EXTERNAL_FLAGS_LIST.optional);

const allFlags: CommandFlag[] = [
...BlockNodeCommand.ADD_EXTERNAL_FLAGS_LIST.required,
...BlockNodeCommand.ADD_EXTERNAL_FLAGS_LIST.optional,
];

await this.configManager.executePrompt(task, allFlags);

const config: BlockNodeAddExternalConfigClass = this.configManager.getConfig(
BlockNodeCommand.ADD_EXTERNAL_CONFIGS_NAME,
allFlags,
) as BlockNodeAddExternalConfigClass;

context_.config = config;

config.clusterRef = this.getClusterReference();
config.context = this.getClusterContext(config.clusterRef);
config.namespace = await this.getNamespace(task);

config.priorityMapping = Templates.parseBlockNodePriorityMapping(
config.priorityMapping as any,
this.remoteConfig.getConsensusNodes(),
);

const id: ComponentId = this.remoteConfig.configuration.state.externalBlockNodes.length + 1;
const [address, port] = Templates.parseExternalBlockAddress(config.externalBlockNodeAddress);
config.newExternalBlockNodeComponent = new ExternalBlockNodeStateSchema(id, address, port);

return ListrLock.newAcquireLockTask(lease, task);
},
},
this.addExternalBlockNodeComponent(),
this.handleConsensusNodeUpdatingForExternalBlockNode(),
],
constants.LISTR_DEFAULT_OPTIONS.DEFAULT,
undefined,
'block node add',
);

if (tasks.isRoot()) {
try {
await tasks.run();
} catch (error) {
throw new SoloError(`Error deploying block node: ${error.message}`, error);
} finally {
await lease?.release();
}
} else {
this.taskList.registerCloseFunction(async (): Promise<void> => {
await lease?.release();
});
}

return true;
}

/**
* Gives the port used for liveness check based on the chart version and image tag (if set)
*/
Expand Down Expand Up @@ -777,6 +922,19 @@ export class BlockNodeCommand extends BaseCommand {
};
}

/** Adds the block node component to remote config. */
private addExternalBlockNodeComponent(): SoloListrTask<BlockNodeAddExternalContext> {
return {
title: 'Add external block node component in remote config',
skip: (): boolean => !this.remoteConfig.isLoaded(),
task: async ({config: {newExternalBlockNodeComponent}}): Promise<void> => {
this.remoteConfig.configuration.state.externalBlockNodes.push(newExternalBlockNodeComponent);

await this.remoteConfig.persist();
},
};
}

/** Adds the block node component to remote config. */
private removeBlockNodeComponent(): SoloListrTask<BlockNodeDestroyContext> {
return {
Expand Down
16 changes: 13 additions & 3 deletions src/commands/command-definitions/block-command-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export class BlockCommandDefinition extends BaseCommandDefinition {
public static readonly NODE_DESTROY = 'destroy';
public static readonly NODE_UPGRADE = 'upgrade';

public static readonly NODE_ADD_EXTERNAL = 'add-external';

public static readonly ADD_COMMAND: string =
`${BlockCommandDefinition.COMMAND_NAME} ${BlockCommandDefinition.NODE_SUBCOMMAND_NAME} ${BlockCommandDefinition.NODE_ADD}` as const;
public static readonly DESTROY_COMMAND: string =
Expand All @@ -58,7 +60,6 @@ export class BlockCommandDefinition extends BaseCommandDefinition {
this.blockNodeCommand.add,
BlockNodeCommand.ADD_FLAGS_LIST,
[constants.HELM, constants.KUBECTL],
false,
),
)
.addSubcommand(
Expand All @@ -70,7 +71,6 @@ export class BlockCommandDefinition extends BaseCommandDefinition {
this.blockNodeCommand.destroy,
BlockNodeCommand.DESTROY_FLAGS_LIST,
[constants.HELM, constants.KUBECTL],
false,
),
)
.addSubcommand(
Expand All @@ -82,7 +82,17 @@ export class BlockCommandDefinition extends BaseCommandDefinition {
this.blockNodeCommand.upgrade,
BlockNodeCommand.UPGRADE_FLAGS_LIST,
[constants.HELM, constants.KUBECTL],
false,
),
)
.addSubcommand(
new Subcommand(
BlockCommandDefinition.NODE_ADD_EXTERNAL,
'Add an external block node for the specified deployment. ' +
'You can specify the priority and consensus nodes to which to connect or use the default settings.',
this.blockNodeCommand,
this.blockNodeCommand.addExternal,
BlockNodeCommand.ADD_EXTERNAL_FLAGS_LIST,
[constants.HELM, constants.KUBECTL],
),
),
)
Expand Down
13 changes: 13 additions & 0 deletions src/commands/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,18 @@ export class Flags {
prompt: undefined,
};

public static readonly externalBlockNodeAddress: CommandFlag = {
constName: 'externalBlockNodeAddress',
name: 'address',
definition: {
describe:
"Configure external block node addresses, port can be provided after ':'" +
`(default port: ${constants.BLOCK_NODE_PORT})`,
type: 'string',
},
prompt: undefined,
};

public static readonly applicationProperties: CommandFlag = {
constName: 'applicationProperties',
name: 'application-properties',
Expand Down Expand Up @@ -2953,6 +2965,7 @@ export class Flags {
Flags.blockNodeChartVersion,
Flags.blockNodeCfg,
Flags.priorityMapping,
Flags.externalBlockNodeAddress,
Flags.realm,
Flags.shard,
Flags.username,
Expand Down
Loading
Loading