UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

689 lines 35.7 kB
// SPDX-License-Identifier: Apache-2.0 var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var ExplorerCommand_1; import { ListrInquirerPromptAdapter } from '@listr2/prompt-adapter-inquirer'; import { confirm as confirmPrompt } from '@inquirer/prompts'; import { SoloError } from '../core/errors/solo-error.js'; import { UserBreak } from '../core/errors/user-break.js'; import * as constants from '../core/constants.js'; import { BaseCommand } from './base.js'; import { Flags as flags } from './flags.js'; import { ListrLock } from '../core/lock/listr-lock.js'; import * as helpers from '../core/helpers.js'; import { prepareValuesFiles, showVersionBanner, sleep } from '../core/helpers.js'; import { NamespaceName } from '../types/namespace/namespace-name.js'; import { inject, injectable } from 'tsyringe-neo'; import { InjectTokens } from '../core/dependency-injection/inject-tokens.js'; 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 { Templates } from '../core/templates.js'; import { SemanticVersion } from '../business/utils/semantic-version.js'; import { assertUpgradeVersionNotOlder } from '../core/upgrade-version-guard.js'; import { Duration } from '../core/time/duration.js'; import { createHash } from 'node:crypto'; import { DeploymentPhase } from '../data/schema/model/remote/deployment-phase.js'; import { optionFromFlag } from './command-helpers.js'; var ExplorerCommandType; (function (ExplorerCommandType) { ExplorerCommandType["ADD"] = "add"; ExplorerCommandType["UPGRADE"] = "upgrade"; ExplorerCommandType["DESTROY"] = "destroy"; })(ExplorerCommandType || (ExplorerCommandType = {})); let ExplorerCommand = class ExplorerCommand extends BaseCommand { static { ExplorerCommand_1 = this; } clusterChecks; constructor(clusterChecks) { super(); this.clusterChecks = clusterChecks; this.clusterChecks = patchInject(clusterChecks, InjectTokens.ClusterChecks, this.constructor.name); } static DEPLOY_CONFIGS_NAME = 'deployConfigs'; static UPGRADE_CONFIGS_NAME = 'upgradeConfigs'; static DEPLOY_FLAGS_LIST = { required: [flags.deployment], optional: [ flags.cacheDir, flags.chartDirectory, flags.explorerChartDirectory, flags.clusterRef, flags.enableIngress, flags.ingressControllerValueFile, flags.enableExplorerTls, flags.explorerTlsHostName, flags.explorerStaticIp, flags.explorerVersion, flags.namespace, flags.quiet, flags.soloChartVersion, flags.tlsClusterIssuerType, flags.valuesFile, flags.clusterSetupNamespace, flags.domainName, flags.forcePortForward, flags.externalAddress, // Mirror Node flags.mirrorNodeId, flags.mirrorNamespace, ], }; static UPGRADE_FLAGS_LIST = { required: [flags.deployment], optional: [ flags.clusterRef, flags.cacheDir, flags.chartDirectory, flags.explorerChartDirectory, flags.enableIngress, flags.ingressControllerValueFile, flags.enableExplorerTls, flags.explorerTlsHostName, flags.explorerStaticIp, flags.explorerVersion, flags.namespace, flags.quiet, flags.soloChartVersion, flags.tlsClusterIssuerType, flags.valuesFile, flags.clusterSetupNamespace, flags.domainName, flags.forcePortForward, flags.externalAddress, flags.id, // Mirror Node flags.mirrorNodeId, flags.mirrorNamespace, ], }; static DESTROY_FLAGS_LIST = { required: [flags.deployment], optional: [flags.chartDirectory, flags.clusterRef, flags.force, flags.quiet, flags.devMode], }; async prepareHederaExplorerValuesArg(config) { let valuesArgument = ''; if (config.valuesFile) { valuesArgument += prepareValuesFiles(config.valuesFile); } if (config.enableIngress) { valuesArgument += ' --set ingress.enabled=true'; valuesArgument += ` --set ingressClassName=${config.ingressReleaseName}`; } valuesArgument += ` --set fullnameOverride=${config.releaseName}-${config.namespace.name}`; valuesArgument += ` --set proxyPass./api="http://${config.mirrorNodeReleaseName}-rest.${config.mirrorNamespace}.svc.cluster.local" `; if (config.domainName) { valuesArgument += helpers.populateHelmArguments({ 'ingress.enabled': true, 'ingress.hosts[0].host': config.domainName, }); if (config.tlsClusterIssuerType === 'self-signed') { // Create TLS secret for Explorer await KeyManager.createTlsSecret(this.k8Factory, config.namespace, config.domainName, config.cacheDir, constants.EXPLORER_INGRESS_TLS_SECRET_NAME); if (config.enableIngress) { valuesArgument += ` --set ingress.tls[0].hosts[0]=${config.domainName}`; } } } return valuesArgument; } async prepareCertManagerChartValuesArg(config) { const { tlsClusterIssuerType, namespace } = config; let valuesArgument = ' --install '; if (!['acme-staging', 'acme-prod', 'self-signed'].includes(tlsClusterIssuerType)) { throw new Error(`Invalid TLS cluster issuer type: ${tlsClusterIssuerType}, must be one of: "acme-staging", "acme-prod", or "self-signed"`); } if (!(await this.clusterChecks.isCertManagerInstalled())) { valuesArgument += ' --set cert-manager.installCRDs=true'; } if (tlsClusterIssuerType === 'self-signed') { valuesArgument += ' --set selfSignedClusterIssuer.enabled=true'; } else { valuesArgument += ` --set global.explorerNamespace=${namespace}`; valuesArgument += ' --set acmeClusterIssuer.enabled=true'; valuesArgument += ` --set certClusterIssuerType=${tlsClusterIssuerType}`; } if (config.valuesFile) { valuesArgument += prepareValuesFiles(config.valuesFile); } return valuesArgument; } async prepareValuesArg(config) { let valuesArgument = ''; if (config.valuesFile) { valuesArgument += prepareValuesFiles(config.valuesFile); } return valuesArgument; } installCertManagerTask(commandType) { return { title: 'Install cert manager', skip: ({ config }) => !config.enableExplorerTls, task: async ({ config }) => { config.soloChartVersion = SemanticVersion.getValidSemanticVersion(config.soloChartVersion, false, 'Solo chart version'); const { soloChartVersion } = config; const soloCertManagerValuesArgument = await this.prepareCertManagerChartValuesArg(config); // check if CRDs of cert-manager are already installed let needInstall = false; for (const crd of constants.CERT_MANAGER_CRDS) { const crdExists = await this.k8Factory.getK8(config.clusterContext).crds().ifExists(crd); if (!crdExists) { needInstall = true; break; } } if (needInstall) { // if cert-manager isn't already installed we want to install it separate from the certificate issuers // as they will fail to be created due to the order of the installation being dependent on the cert-manager // being installed first await this.chartManager.upgrade(NamespaceName.of(constants.CERT_MANAGER_NAME_SPACE), constants.SOLO_CERT_MANAGER_CHART, constants.SOLO_CERT_MANAGER_CHART, config.chartDirectory || constants.SOLO_TESTING_CHART_URL, soloChartVersion, ' --install --create-namespace --set cert-manager.installCRDs=true', config.clusterContext, commandType !== ExplorerCommandType.ADD); showVersionBanner(this.logger, constants.SOLO_CERT_MANAGER_CHART, soloChartVersion); } // wait cert-manager to be ready to proceed, otherwise may get error of "failed calling webhook" await this.k8Factory .getK8(config.clusterContext) .pods() .waitForReadyStatus(constants.DEFAULT_CERT_MANAGER_NAMESPACE, ['app.kubernetes.io/component=webhook', `app.kubernetes.io/instance=${constants.SOLO_CERT_MANAGER_CHART}`], constants.PODS_READY_MAX_ATTEMPTS, constants.PODS_READY_DELAY); // sleep for a few seconds to allow cert-manager to be ready if (commandType === ExplorerCommandType.UPGRADE) { await sleep(Duration.ofSeconds(10)); } await this.chartManager.upgrade(NamespaceName.of(constants.CERT_MANAGER_NAME_SPACE), constants.SOLO_CERT_MANAGER_CHART, constants.SOLO_CERT_MANAGER_CHART, config.chartDirectory || constants.SOLO_TESTING_CHART_URL, soloChartVersion, soloCertManagerValuesArgument, config.clusterContext); showVersionBanner(this.logger, constants.SOLO_CERT_MANAGER_CHART, soloChartVersion, 'Upgraded'); }, }; } installExplorerTask(commandType) { return { title: 'Install explorer', task: async ({ config }) => { config.explorerVersion = SemanticVersion.getValidSemanticVersion(config.explorerVersion, false, 'Explorer version'); let exploreValuesArgument = ' --install '; exploreValuesArgument += prepareValuesFiles(constants.EXPLORER_VALUES_FILE); exploreValuesArgument += await this.prepareHederaExplorerValuesArg(config); // Local chart checkouts can keep appVersion/tag at placeholder values (for example 0.0.1), // so pin the runtime image tag explicitly to the requested explorer version. if (config.explorerChartDirectory) { exploreValuesArgument += helpers.populateHelmArguments({ 'image.tag': config.explorerVersion }); } await this.chartManager.upgrade(config.namespace, config.releaseName, '', config.explorerChartDirectory || constants.EXPLORER_CHART_URL, config.explorerVersion, exploreValuesArgument, config.clusterContext); if (commandType === ExplorerCommandType.ADD) { this.remoteConfig.configuration.components.changeComponentPhase(config.newExplorerComponent.metadata.id, ComponentTypes.Explorer, DeploymentPhase.DEPLOYED); await this.remoteConfig.persist(); } else if (commandType === ExplorerCommandType.UPGRADE) { // update explorer version in remote config after successful upgrade this.remoteConfig.updateComponentVersion(ComponentTypes.Explorer, new SemanticVersion(config.explorerVersion)); await this.remoteConfig.persist(); } showVersionBanner(this.logger, config.releaseName, config.explorerVersion); }, }; } installExplorerIngressControllerTask() { return { title: 'Install explorer ingress controller', skip: ({ config }) => !config.enableIngress, task: async ({ config }) => { let explorerIngressControllerValuesArgument = ' --install '; if (config.explorerStaticIp !== '') { explorerIngressControllerValuesArgument += ` --set controller.service.loadBalancerIP=${config.explorerStaticIp}`; } explorerIngressControllerValuesArgument += ` --set fullnameOverride=${config.ingressReleaseName}`; explorerIngressControllerValuesArgument += ` --set controller.ingressClass=${config.ingressReleaseName}`; explorerIngressControllerValuesArgument += ` --set controller.extraArgs.controller-class=${config.ingressReleaseName}`; if (config.tlsClusterIssuerType === 'self-signed') { explorerIngressControllerValuesArgument += prepareValuesFiles(config.ingressControllerValueFile); } await this.chartManager.upgrade(config.namespace, config.ingressReleaseName, constants.INGRESS_CONTROLLER_RELEASE_NAME, constants.INGRESS_CONTROLLER_RELEASE_NAME, INGRESS_CONTROLLER_VERSION, explorerIngressControllerValuesArgument, config.clusterContext); showVersionBanner(this.logger, config.ingressReleaseName, INGRESS_CONTROLLER_VERSION); const k8 = this.k8Factory.getK8(config.clusterContext); // patch explorer ingress to use h1 protocol, haproxy ingress controller default backend protocol is h2 // to support grpc over http/2 await k8.ingresses().update(config.namespace, config.releaseName, { metadata: { annotations: { 'haproxy-ingress.github.io/backend-protocol': 'h1', }, }, }); const ingressClasses = await k8.ingressClasses().list(); if (ingressClasses.some((ingressClass) => ingressClass.name === config.ingressReleaseName)) { return; } await k8 .ingressClasses() .create(config.ingressReleaseName, constants.INGRESS_CONTROLLER_PREFIX + config.ingressReleaseName); }, }; } checkExplorerPodIsReadyTask() { return { title: 'Check explorer pod is ready', task: async ({ config }) => { await this.k8Factory .getK8(config.clusterContext) .pods() .waitForReadyStatus(config.namespace, Templates.renderExplorerLabels(config.id, config.isLegacyChartInstalled ? config.releaseName : undefined), constants.PODS_READY_MAX_ATTEMPTS, constants.PODS_READY_DELAY); }, }; } checkExplorerIngressControllerPodIsReadyTask() { return { title: 'Check haproxy ingress controller pod is ready', skip: ({ config }) => !config.enableIngress, task: async ({ config }) => { await this.k8Factory .getK8(config.clusterContext) .pods() .waitForReadyStatus(config.namespace, [ `app.kubernetes.io/name=${constants.INGRESS_CONTROLLER_RELEASE_NAME}`, `app.kubernetes.io/instance=${config.ingressReleaseName}`, ], constants.PODS_READY_MAX_ATTEMPTS, constants.PODS_READY_DELAY); }, }; } enablePortForwardingTask() { return { title: 'Enable port forwarding for explorer', skip: ({ config }) => !config.forcePortForward, task: async ({ config }) => { const externalAddress = this.configManager.getFlag(flags.externalAddress); const pods = await this.k8Factory .getK8(config.clusterContext) .pods() .list(config.namespace, Templates.renderExplorerLabels(config.id, config.isLegacyChartInstalled ? config.releaseName : undefined)); if (pods.length === 0) { throw new SoloError('No Hiero Explorer pod found'); } const podReference = pods[0].podReference; await this.remoteConfig.configuration.components.stopPortForwards(config.clusterRef, podReference, constants.EXPLORER_PORT, // Pod port constants.EXPLORER_LOCAL_PORT, // Local port this.k8Factory.getK8(config.clusterContext), this.logger, ComponentTypes.Explorer, 'Explorer'); await this.remoteConfig.persist(); await this.remoteConfig.configuration.components.managePortForward(config.clusterRef, podReference, constants.EXPLORER_PORT, // Pod port constants.EXPLORER_LOCAL_PORT, // Local port this.k8Factory.getK8(config.clusterContext), this.logger, ComponentTypes.Explorer, 'Explorer', config.isChartInstalled, // Reuse existing port if chart is already installed undefined, true, // persist: auto-restart on failure using persist-port-forward.js externalAddress); await this.remoteConfig.persist(); }, }; } getReleaseName() { return this.renderReleaseName(this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.Explorer)); } getIngressReleaseName(namespaceName) { return this.renderIngressReleaseName(this.remoteConfig.configuration.components.getNewComponentId(ComponentTypes.Explorer), namespaceName); } renderReleaseName(id) { if (typeof id !== 'number') { throw new SoloError(`Invalid component id: ${id}, type: ${typeof id}`); } return `${constants.EXPLORER_RELEASE_NAME}-${id}`; } renderIngressReleaseName(id, namespaceName) { if (typeof id !== 'number') { throw new SoloError(`Invalid component id: ${id}, type: ${typeof id}`); } const maxHelmReleaseNameLength = 53; const baseReleaseName = `${constants.EXPLORER_INGRESS_CONTROLLER_RELEASE_NAME}-${id}-${namespaceName.name}`; if (baseReleaseName.length <= maxHelmReleaseNameLength) { return baseReleaseName; } // Keep names deterministic and short enough for Helm while preserving readability. const hashSuffixLength = 8; const namespaceHash = createHash('sha256') .update(namespaceName.name) .digest('hex') .slice(0, hashSuffixLength); const prefix = `${constants.EXPLORER_INGRESS_CONTROLLER_RELEASE_NAME}-${id}`; const availableNamespaceLength = maxHelmReleaseNameLength - prefix.length - 1 - hashSuffixLength - 1; /* - */ if (availableNamespaceLength <= 0) { return `${prefix}-${namespaceHash}`; } const shortenedNamespace = namespaceName.name.slice(0, availableNamespaceLength); return `${prefix}-${shortenedNamespace}-${namespaceHash}`; } async add(argv) { let lease; const tasks = this.taskList.newTaskList([ { title: 'Initialize', task: async (context_, task) => { await this.localConfig.load(); await this.remoteConfig.loadAndValidate(argv); if (!this.oneShotState.isActive()) { lease = await this.leaseManager.create(); } this.configManager.update(argv); flags.disablePrompts(ExplorerCommand_1.DEPLOY_FLAGS_LIST.optional); const allFlags = [ ...ExplorerCommand_1.DEPLOY_FLAGS_LIST.optional, ...ExplorerCommand_1.DEPLOY_FLAGS_LIST.required, ]; await this.configManager.executePrompt(task, allFlags); const config = this.configManager.getConfig(ExplorerCommand_1.DEPLOY_CONFIGS_NAME, allFlags, []); // In concurrent one-shot execution, configManager may have stale data due to // interleaved updates from other sub-commands. Override with argv values directly. if (this.oneShotState.isActive() && argv[flags.explorerVersion.name]) { config.explorerVersion = argv[flags.explorerVersion.name]; } config.isLegacyChartInstalled = false; context_.config = config; config.clusterRef = this.getClusterReference(); config.clusterContext = this.getClusterContext(config.clusterRef); config.releaseName = this.getReleaseName(); config.ingressReleaseName = this.getIngressReleaseName(config.namespace); const { mirrorNodeId, mirrorNamespace, mirrorNodeReleaseName } = await this.inferMirrorNodeData(config.namespace, config.clusterContext); config.mirrorNodeId = mirrorNodeId; config.mirrorNamespace = mirrorNamespace; config.mirrorNodeReleaseName = mirrorNodeReleaseName; config.newExplorerComponent = this.componentFactory.createNewExplorerComponent(config.clusterRef, config.namespace); config.newExplorerComponent.metadata.phase = DeploymentPhase.REQUESTED; config.id = config.newExplorerComponent.metadata.id; config.valuesArg = await this.prepareValuesArg(context_.config); await this.throwIfNamespaceIsMissing(config.clusterContext, config.namespace); if (!this.oneShotState.isActive()) { return ListrLock.newAcquireLockTask(lease, task); } return ListrLock.newSkippedLockTask(task); }, }, this.loadRemoteConfigTask(argv), this.addExplorerComponents(), this.installCertManagerTask(ExplorerCommandType.ADD), this.installExplorerTask(ExplorerCommandType.ADD), this.installExplorerIngressControllerTask(), this.checkExplorerPodIsReadyTask(), this.checkExplorerIngressControllerPodIsReadyTask(), this.enablePortForwardingTask(), { title: 'Show user messages', skip: () => !this.oneShotState.isActive(), task: () => { this.logger.showAllMessageGroups(); }, }, ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT, undefined, 'explorer node add'); if (tasks.isRoot()) { try { await tasks.run(); this.logger.debug('explorer deployment has completed'); } catch (error) { throw new SoloError(`Error deploying explorer: ${error.message}`, error); } finally { if (!this.oneShotState.isActive()) { await lease?.release(); } } } else { this.taskList.registerCloseFunction(async () => { if (!this.oneShotState.isActive()) { await lease?.release(); } }); } return true; } async upgrade(argv) { let lease; const tasks = this.taskList.newTaskList([ { title: 'Initialize', task: async (context_, task) => { await this.localConfig.load(); await this.remoteConfig.loadAndValidate(argv); if (!this.oneShotState.isActive()) { lease = await this.leaseManager.create(); } this.configManager.update(argv); flags.disablePrompts(ExplorerCommand_1.UPGRADE_FLAGS_LIST.optional); const allFlags = [ ...ExplorerCommand_1.UPGRADE_FLAGS_LIST.optional, ...ExplorerCommand_1.UPGRADE_FLAGS_LIST.required, ]; await this.configManager.executePrompt(task, allFlags); const config = this.configManager.getConfig(ExplorerCommand_1.UPGRADE_CONFIGS_NAME, allFlags, []); context_.config = config; config.clusterRef = this.getClusterReference(); config.clusterContext = this.getClusterContext(config.clusterRef); const { id, releaseName, ingressReleaseName, isChartInstalled, isLegacyChartInstalled } = await this.inferExplorerData(config.namespace, config.clusterContext); config.id = id; config.releaseName = releaseName; config.ingressReleaseName = ingressReleaseName; config.isChartInstalled = isChartInstalled; config.isLegacyChartInstalled = isLegacyChartInstalled; const { mirrorNodeId, mirrorNamespace, mirrorNodeReleaseName } = await this.inferMirrorNodeData(config.namespace, config.clusterContext); config.mirrorNodeId = mirrorNodeId; config.mirrorNamespace = mirrorNamespace; config.mirrorNodeReleaseName = mirrorNodeReleaseName; config.valuesArg = await this.prepareValuesArg(context_.config); assertUpgradeVersionNotOlder('Explorer', config.explorerVersion, this.remoteConfig.getComponentVersion(ComponentTypes.Explorer), optionFromFlag(flags.explorerVersion)); await this.throwIfNamespaceIsMissing(config.clusterContext, config.namespace); if (!this.oneShotState.isActive()) { return ListrLock.newAcquireLockTask(lease, task); } return ListrLock.newSkippedLockTask(task); }, }, this.loadRemoteConfigTask(argv), this.installCertManagerTask(ExplorerCommandType.UPGRADE), this.installExplorerTask(ExplorerCommandType.UPGRADE), this.installExplorerIngressControllerTask(), this.checkExplorerPodIsReadyTask(), this.checkExplorerIngressControllerPodIsReadyTask(), this.enablePortForwardingTask(), ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT, undefined, 'explorer node upgrade'); if (tasks.isRoot()) { try { await tasks.run(); this.logger.debug('explorer upgrading has completed'); } catch (error) { throw new SoloError(`Error upgrading explorer: ${error.message}`, error); } finally { if (!this.oneShotState.isActive()) { await lease?.release(); } } } else { this.taskList.registerCloseFunction(async () => { if (!this.oneShotState.isActive()) { await lease?.release(); } }); } return true; } async destroy(argv) { let lease; const tasks = this.taskList.newTaskList([ { title: 'Initialize', task: async (context_, task) => { await this.localConfig.load(); await this.loadRemoteConfigOrWarn(argv); if (!this.oneShotState.isActive()) { lease = await this.leaseManager.create(); } if (!argv.force) { const confirmResult = await task.prompt(ListrInquirerPromptAdapter).run(confirmPrompt, { default: false, message: 'Are you sure you would like to destroy the explorer?', }); if (!confirmResult) { throw new UserBreak('Aborted application by user prompt'); } } this.configManager.update(argv); const namespace = await this.getNamespace(task); const clusterReference = this.getClusterReference(); const clusterContext = this.getClusterContext(clusterReference); const { id, releaseName, ingressReleaseName, isChartInstalled, isLegacyChartInstalled } = await this.inferExplorerData(namespace, clusterContext); context_.config = { namespace, clusterContext, clusterReference, id, releaseName, ingressReleaseName, isChartInstalled, isLegacyChartInstalled, }; await this.throwIfNamespaceIsMissing(clusterContext, namespace); if (!this.oneShotState.isActive()) { return ListrLock.newAcquireLockTask(lease, task); } return ListrLock.newSkippedLockTask(task); }, }, this.loadRemoteConfigTask(argv, true), this.loadRemoteConfigTask(argv), { title: 'Destroy explorer', task: async (context_) => { await this.chartManager.uninstall(context_.config.namespace, context_.config.releaseName, context_.config.clusterContext); }, skip: (context_) => !context_.config.isChartInstalled, }, { title: 'Uninstall explorer ingress controller', task: async (context_) => { await this.chartManager.uninstall(context_.config.namespace, context_.config.ingressReleaseName); // destroy ingress class if found one const existingIngressClasses = await this.k8Factory .getK8(context_.config.clusterContext) .ingressClasses() .list(); existingIngressClasses.map((ingressClass) => { if (ingressClass.name === context_.config.ingressReleaseName) { this.k8Factory .getK8(context_.config.clusterContext) .ingressClasses() .delete(context_.config.ingressReleaseName); } }); }, }, this.disableMirrorNodeExplorerComponents(), ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT, undefined, 'explorer node destroy'); if (tasks.isRoot()) { try { await tasks.run(); } catch (error) { throw new SoloError(`Error destroy explorer: ${error.message}`, error); } finally { if (!this.oneShotState.isActive()) { await lease?.release(); } } } else { this.taskList.registerCloseFunction(async () => { if (!this.oneShotState.isActive()) { await lease?.release(); } }); } return true; } loadRemoteConfigTask(argv, safe = false) { return { title: 'Load remote config', task: async () => { if (safe) { await this.loadRemoteConfigOrWarn(argv); return; } await this.remoteConfig.loadAndValidate(argv); }, }; } /** Removes the explorer components from remote config. */ disableMirrorNodeExplorerComponents() { return { title: 'Remove explorer from remote config', skip: () => !this.remoteConfig.isLoaded(), task: async ({ config }) => { this.remoteConfig.configuration.components.removeComponent(config.id, ComponentTypes.Explorer); await this.remoteConfig.persist(); }, }; } /** Adds the explorer components to remote config. */ addExplorerComponents() { return { title: 'Add explorer to remote config', skip: () => !this.remoteConfig.isLoaded() || this.oneShotState.isActive(), task: async ({ config }) => { this.remoteConfig.configuration.components.addNewComponent(config.newExplorerComponent, ComponentTypes.Explorer); // update explorer version in remote config this.remoteConfig.updateComponentVersion(ComponentTypes.Explorer, new SemanticVersion(config.explorerVersion)); await this.remoteConfig.persist(); }, }; } async close() { } // no-op async checkIfLegacyChartIsInstalled(id, namespace, context) { return id <= 1 ? await this.chartManager.isChartInstalled(namespace, constants.EXPLORER_RELEASE_NAME, context) : false; } inferExplorerId() { const id = this.configManager.getFlag(flags.id); if (typeof id === 'number') { return id; } if (!this.remoteConfig.configuration.components.state.explorers[0]) { throw new SoloError('No explorer component found in remote config'); } return this.remoteConfig.configuration.components.state.explorers[0].metadata.id; } async inferExplorerData(namespace, context) { const id = this.inferExplorerId(); const isLegacyChartInstalled = await this.checkIfLegacyChartIsInstalled(id, namespace, context); if (isLegacyChartInstalled) { return { id, releaseName: constants.EXPLORER_RELEASE_NAME, isChartInstalled: true, ingressReleaseName: constants.EXPLORER_INGRESS_CONTROLLER_RELEASE_NAME, isLegacyChartInstalled, }; } const releaseName = this.renderReleaseName(id); return { id, releaseName, ingressReleaseName: this.renderIngressReleaseName(id, namespace), isChartInstalled: await this.chartManager.isChartInstalled(namespace, releaseName, context), isLegacyChartInstalled, }; } }; ExplorerCommand = ExplorerCommand_1 = __decorate([ injectable(), __param(0, inject(InjectTokens.ClusterChecks)), __metadata("design:paramtypes", [Function]) ], ExplorerCommand); export { ExplorerCommand }; //# sourceMappingURL=explorer.js.map