Skip to content

Commit

Permalink
feat(multi-cluster): Update 'relay destroy' and 'network destroy' to …
Browse files Browse the repository at this point in the history
…support multiple clusters (#1424)

Signed-off-by: instamenta <[email protected]>
Signed-off-by: Zhan Milenkov <[email protected]>
  • Loading branch information
instamenta authored Feb 20, 2025
1 parent 66bbaab commit 19db9d4
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 62 deletions.
2 changes: 2 additions & 0 deletions src/commands/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ export abstract class BaseCommand extends ShellRunner {
/**
* Gets a list of distinct contexts from the consensus nodes
* @returns an array of context strings
* @deprecated use one inside remote config
*/
public getContexts(): string[] {
const contexts: string[] = [];
Expand All @@ -421,6 +422,7 @@ export abstract class BaseCommand extends ShellRunner {
/**
* Gets a list of distinct cluster references from the consensus nodes
* @returns an object of cluster references
* @deprecated use one inside remote config
*/
public getClusterRefs(): ClusterRefs {
const clustersRefs: ClusterRefs = {};
Expand Down
4 changes: 2 additions & 2 deletions src/commands/mirror_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ import chalk from 'chalk';
import {type CommandFlag} from '../types/flag_types.js';
import {PvcRef} from '../core/kube/resources/pvc/pvc_ref.js';
import {PvcName} from '../core/kube/resources/pvc/pvc_name.js';
import {type DeploymentName} from '../core/config/remote/types.js';
import {type ClusterRef, type DeploymentName} from '../core/config/remote/types.js';
import {extractContextFromConsensusNodes} from '../core/helpers.js';
import {node} from 'globals';

interface MirrorNodeDeployConfigClass {
chartDirectory: string;
Expand Down Expand Up @@ -623,6 +622,7 @@ export class MirrorNodeCommand extends BaseCommand {
namespace: NamespaceName;
clusterContext: string;
isChartInstalled: boolean;
clusterRef?: Optional<ClusterRef>;
};
}

Expand Down
107 changes: 67 additions & 40 deletions src/commands/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {ConsensusNodeStates} from '../core/config/remote/enumerations.js';
import {EnvoyProxyComponent} from '../core/config/remote/components/envoy_proxy_component.js';
import {HaProxyComponent} from '../core/config/remote/components/ha_proxy_component.js';
import {v4 as uuidv4} from 'uuid';
import {type SoloListrTask} from '../types/index.js';
import {type SoloListrTask, type SoloListrTaskWrapper} from '../types/index.js';
import {NamespaceName} from '../core/kube/resources/namespace/namespace_name.js';
import {PvcRef} from '../core/kube/resources/pvc/pvc_ref.js';
import {PvcName} from '../core/kube/resources/pvc/pvc_name.js';
Expand Down Expand Up @@ -87,6 +87,18 @@ export interface NetworkDeployConfigClass {
clusterRefs: ClusterRefs;
}

interface NetworkDestroyContext {
config: {
deletePvcs: boolean;
deleteSecrets: boolean;
namespace: NamespaceName;
enableTimeout: boolean;
force: boolean;
contexts: string[];
};
checkTimeout: boolean;
}

export class NetworkCommand extends BaseCommand {
private readonly keyManager: KeyManager;
private readonly platformInstaller: PlatformInstaller;
Expand Down Expand Up @@ -709,37 +721,58 @@ export class NetworkCommand extends BaseCommand {
return config;
}

async destroyTask(ctx: any, task: any) {
async destroyTask(ctx: NetworkDestroyContext, task: SoloListrTaskWrapper<NetworkDestroyContext>) {
const self = this;
task.title = `Uninstalling chart ${constants.SOLO_DEPLOYMENT_CHART}`;
await self.chartManager.uninstall(
ctx.config.namespace,
constants.SOLO_DEPLOYMENT_CHART,
this.k8Factory.default().contexts().readCurrent(),

// Uninstall all 'solo deployment' charts for each cluster using the contexts
await Promise.all(
ctx.config.contexts.map(context => {
return self.chartManager.uninstall(
ctx.config.namespace,
constants.SOLO_DEPLOYMENT_CHART,
this.k8Factory.getK8(context).contexts().readCurrent(),
);
}),
);

// Delete PVCs inside each cluster
if (ctx.config.deletePvcs) {
const pvcs = await self.k8Factory.default().pvcs().list(ctx.config.namespace, []);
task.title = `Deleting PVCs in namespace ${ctx.config.namespace}`;
if (pvcs) {
for (const pvc of pvcs) {
await self.k8Factory
.default()
.pvcs()
.delete(PvcRef.of(ctx.config.namespace, PvcName.of(pvc)));
}
}

await Promise.all(
ctx.config.contexts.map(async context => {
// Fetch all PVCs inside the namespace using the context
const pvcs = await this.k8Factory.getK8(context).pvcs().list(ctx.config.namespace, []);

// Delete all if found
return Promise.all(
pvcs.map(pvc =>
this.k8Factory
.getK8(context)
.pvcs()
.delete(PvcRef.of(ctx.config.namespace, PvcName.of(pvc))),
),
);
}),
);
}

// Delete Secrets inside each cluster
if (ctx.config.deleteSecrets) {
task.title = `Deleting secrets in namespace ${ctx.config.namespace}`;
const secrets = await self.k8Factory.default().secrets().list(ctx.config.namespace);

if (secrets) {
for (const secret of secrets) {
await self.k8Factory.default().secrets().delete(ctx.config.namespace, secret.name);
}
}
await Promise.all(
ctx.config.contexts.map(async context => {
// Fetch all Secrets inside the namespace using the context
const secrets = await this.k8Factory.getK8(context).secrets().list(ctx.config.namespace);

// Delete all if found
return Promise.all(
secrets.map(secret => this.k8Factory.getK8(context).secrets().delete(ctx.config.namespace, secret.name)),
);
}),
);
}
}

Expand Down Expand Up @@ -1077,19 +1110,8 @@ export class NetworkCommand extends BaseCommand {
const self = this;
const lease = await self.leaseManager.create();

interface Context {
config: {
deletePvcs: boolean;
deleteSecrets: boolean;
namespace: NamespaceName;
enableTimeout: boolean;
force: boolean;
};
checkTimeout: boolean;
}

let networkDestroySuccess = true;
const tasks = new Listr<Context>(
const tasks = new Listr<NetworkDestroyContext>(
[
{
title: 'Initialize',
Expand All @@ -1108,14 +1130,14 @@ export class NetworkCommand extends BaseCommand {

self.configManager.update(argv);
await self.configManager.executePrompt(task, [flags.deletePvcs, flags.deleteSecrets]);
const namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task);

ctx.config = {
deletePvcs: self.configManager.getFlag<boolean>(flags.deletePvcs) as boolean,
deleteSecrets: self.configManager.getFlag<boolean>(flags.deleteSecrets) as boolean,
namespace,
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
enableTimeout: self.configManager.getFlag<boolean>(flags.enableTimeout) as boolean,
force: self.configManager.getFlag<boolean>(flags.force) as boolean,
contexts: this.getContexts(),
};

return ListrLease.newAcquireLeaseTask(lease, task);
Expand All @@ -1125,18 +1147,22 @@ export class NetworkCommand extends BaseCommand {
title: 'Running sub-tasks to destroy network',
task: async (ctx, task) => {
if (ctx.config.enableTimeout) {
const timeoutId = setTimeout(() => {
const timeoutId = setTimeout(async () => {
const message = `\n\nUnable to finish network destroy in ${constants.NETWORK_DESTROY_WAIT_TIMEOUT} seconds\n\n`;
self.logger.error(message);
self.logger.showUser(chalk.red(message));
networkDestroySuccess = false;

if (ctx.config.deletePvcs && ctx.config.deleteSecrets && ctx.config.force) {
self.k8Factory.default().namespaces().delete(ctx.config.namespace);
await Promise.all(
ctx.config.contexts.map(context =>
self.k8Factory.getK8(context).namespaces().delete(ctx.config.namespace),
),
);
} else {
// If the namespace is not being deleted,
// remove all components data from the remote configuration
self.remoteConfigManager.deleteComponents();
await self.remoteConfigManager.deleteComponents();
}
}, constants.NETWORK_DESTROY_WAIT_TIMEOUT * 1_000);

Expand All @@ -1157,10 +1183,11 @@ export class NetworkCommand extends BaseCommand {

try {
await tasks.run();
} catch (e: Error | unknown) {
} catch (e) {
throw new SoloError('Error destroying network', e);
} finally {
await lease.release();
// If the namespace is deleted, the lease can't be released
await lease.release().catch();
}

return networkDestroySuccess;
Expand Down
27 changes: 20 additions & 7 deletions src/commands/relay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class RelayCommand extends BaseCommand {
}

static get DESTROY_FLAGS_LIST() {
return [flags.chartDirectory, flags.deployment, flags.nodeAliasesUnparsed];
return [flags.chartDirectory, flags.deployment, flags.nodeAliasesUnparsed, flags.clusterRef];
}

async prepareValuesArg(
Expand Down Expand Up @@ -357,6 +357,8 @@ export class RelayCommand extends BaseCommand {
nodeAliases: NodeAliases;
releaseName: string;
isChartInstalled: boolean;
clusterRef: Optional<ClusterRef>;
context: Optional<string>;
}

interface Context {
Expand All @@ -370,24 +372,32 @@ export class RelayCommand extends BaseCommand {
task: async (ctx, task) => {
// reset nodeAlias
self.configManager.setFlag(flags.nodeAliasesUnparsed, '');

self.configManager.update(argv);

flags.disablePrompts([flags.clusterRef]);

await self.configManager.executePrompt(task, RelayCommand.DESTROY_FLAGS_LIST);
const namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task);

// prompt if inputs are empty and set it in the context
ctx.config = {
chartDirectory: self.configManager.getFlag<string>(flags.chartDirectory) as string,
namespace: namespace,
namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
nodeAliases: helpers.parseNodeAliases(
self.configManager.getFlag<string>(flags.nodeAliasesUnparsed) as string,
),
clusterRef: self.configManager.getFlag<string>(flags.clusterRef) as string,
} as RelayDestroyConfigClass;

if (ctx.config.clusterRef) {
const context = self.getClusterRefs()[ctx.config.clusterRef];
if (context) ctx.config.context = context;
}

ctx.config.releaseName = this.prepareReleaseName(ctx.config.nodeAliases);
ctx.config.isChartInstalled = await this.chartManager.isChartInstalled(
ctx.config.namespace,
ctx.config.releaseName,
ctx.config.context,
);

self.logger.debug('Initialized config', {config: ctx.config});
Expand All @@ -403,10 +413,13 @@ export class RelayCommand extends BaseCommand {
await this.chartManager.uninstall(
config.namespace,
config.releaseName,
this.k8Factory.default().contexts().readCurrent(),
this.k8Factory.getK8(ctx.config.context).contexts().readCurrent(),
);

this.logger.showList('Destroyed Relays', await self.chartManager.getInstalledCharts(config.namespace));
this.logger.showList(
'Destroyed Relays',
await self.chartManager.getInstalledCharts(config.namespace, config.context),
);

// reset nodeAliasesUnparsed
self.configManager.setFlag(flags.nodeAliasesUnparsed, '');
Expand All @@ -423,7 +436,7 @@ export class RelayCommand extends BaseCommand {

try {
await tasks.run();
} catch (e: Error | any) {
} catch (e) {
throw new SoloError('Error uninstalling relays', e);
} finally {
await lease.release();
Expand Down
1 change: 0 additions & 1 deletion src/core/account_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,6 @@ export class AccountManager {
.pods()
.list(namespace, ['solo.hedera.com/type=network-node']);
for (const pod of pods) {
// eslint-disable-next-line no-prototype-builtins
if (!pod.metadata?.labels?.hasOwnProperty('solo.hedera.com/node-name')) {
// TODO Review why this fixes issue
continue;
Expand Down
Loading

0 comments on commit 19db9d4

Please sign in to comment.