UNPKG

@hashgraph/solo

Version:

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

421 lines 23.8 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); } }; import { Templates } from '../../core/templates.js'; import * as constants from '../../core/constants.js'; import { PrivateKey } from '@hiero-ledger/sdk'; import { SoloError } from '../../core/errors/solo-error.js'; import * as helpers from '../../core/helpers.js'; import { checkNamespace } from '../../core/helpers.js'; import fs from 'node:fs'; import { resolveNamespaceFromDeployment } from '../../core/resolvers.js'; import { Flags as flags } from '../flags.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 { PathEx } from '../../business/utils/path-ex.js'; import { SemanticVersion } from '../../business/utils/semantic-version.js'; import { assertUpgradeVersionNotOlder } from '../../core/upgrade-version-guard.js'; import { SOLO_USER_AGENT_HEADER } from '../../core/constants.js'; import { optionFromFlag } from '../command-helpers.js'; const PREPARE_UPGRADE_CONFIGS_NAME = 'prepareUpgradeConfig'; const ADD_CONFIGS_NAME = 'addConfigs'; const DESTROY_CONFIGS_NAME = 'destroyConfigs'; const UPDATE_CONFIGS_NAME = 'updateConfigs'; const UPGRADE_CONFIGS_NAME = 'upgradeConfigs'; const REFRESH_CONFIGS_NAME = 'refreshConfigs'; const KEYS_CONFIGS_NAME = 'keyConfigs'; const SETUP_CONFIGS_NAME = 'setupConfigs'; const START_CONFIGS_NAME = 'startConfigs'; let NodeCommandConfigs = class NodeCommandConfigs { configManager; localConfig; remoteConfig; k8Factory; accountManager; constructor(configManager, localConfig, remoteConfig, k8Factory, accountManager) { this.configManager = configManager; this.localConfig = localConfig; this.remoteConfig = remoteConfig; this.k8Factory = k8Factory; this.accountManager = accountManager; this.configManager = patchInject(configManager, InjectTokens.ConfigManager, this.constructor.name); 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.remoteConfig = patchInject(remoteConfig, InjectTokens.RemoteConfigRuntimeState, this.constructor.name); } async initializeSetup(config, k8Factory) { // compute other config parameters config.keysDir = PathEx.join(config.cacheDir, 'keys'); config.stagingDir = Templates.renderStagingDir(config.cacheDir, config.releaseTag); config.stagingKeysDir = PathEx.join(config.stagingDir, 'keys'); if (!(await k8Factory.default().namespaces().has(config.namespace))) { throw new SoloError(`namespace ${config.namespace} does not exist`); } // prepare staging keys directory if (!fs.existsSync(config.stagingKeysDir)) { fs.mkdirSync(config.stagingKeysDir, { recursive: true }); } // create cached keys dir if it does not exist yet if (!fs.existsSync(config.keysDir)) { fs.mkdirSync(config.keysDir); } } async prepareUpgradeConfigBuilder(argv, context_, task) { context_.config = this.configManager.getConfig(PREPARE_UPGRADE_CONFIGS_NAME, argv.flags, [ 'nodeClient', 'freezeAdminPrivateKey', 'namespace', 'consensusNodes', 'contexts', ]); context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); await this.initializeSetup(context_.config, this.k8Factory); context_.config.nodeClient = await this.accountManager.refreshNodeClient(context_.config.namespace, this.remoteConfig.getClusterRefs(), context_.config.skipNodeAlias, context_.config.deployment); const freezeAdminAccountId = this.accountManager.getFreezeAccountId(context_.config.deployment); const accountKeys = await this.accountManager.getAccountKeysFromSecret(freezeAdminAccountId.toString(), context_.config.namespace); context_.config.freezeAdminPrivateKey = accountKeys.privateKey; return context_.config; } async upgradeConfigBuilder(argv, context_, task, shouldLoadNodeClient = true) { context_.config = this.configManager.getConfig(UPGRADE_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'existingNodeAliases', 'keysDir', 'nodeClient', 'podRefs', 'stagingDir', 'stagingKeysDir', 'namespace', 'consensusNodes', 'contexts', ]); context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); context_.config.curDate = new Date(); context_.config.existingNodeAliases = []; context_.config.nodeAliases = helpers.parseNodeAliases(context_.config.nodeAliasesUnparsed, this.remoteConfig.getConsensusNodes(), this.configManager); // check if the intended package version exists if (context_.config.upgradeVersion) { const semVersion = new SemanticVersion(context_.config.upgradeVersion); const HEDERA_BUILDS_URL = 'https://builds.hedera.com'; const BUILD_ZIP_URL = `${HEDERA_BUILDS_URL}/node/software/v${semVersion.major}.${semVersion.minor}/build-${context_.config.upgradeVersion}.zip`; try { // do not fetch or download, just check if URL exists or not const response = await fetch(BUILD_ZIP_URL, { method: 'HEAD', headers: { 'User-Agent': SOLO_USER_AGENT_HEADER, }, }); if (!response.ok) { throw new SoloError(`Upgrade version ${context_.config.upgradeVersion} does not exist.`); } } catch (error) { throw new SoloError(`Failed to fetch upgrade version ${context_.config.upgradeVersion}: ${error.message}`); } // Compare target version against the version stored in remote config assertUpgradeVersionNotOlder('Consensus node', context_.config.upgradeVersion, this.remoteConfig.configuration.versions.consensusNode, optionFromFlag(flags.upgradeVersion)); } await this.initializeSetup(context_.config, this.k8Factory); if (shouldLoadNodeClient) { context_.config.nodeClient = await this.accountManager.loadNodeClient(context_.config.namespace, this.remoteConfig.getClusterRefs(), context_.config.deployment); } const freezeAdminAccountId = this.accountManager.getFreezeAccountId(context_.config.deployment); const accountKeys = await this.accountManager.getAccountKeysFromSecret(freezeAdminAccountId.toString(), context_.config.namespace); context_.config.freezeAdminPrivateKey = accountKeys.privateKey; return context_.config; } async updateConfigBuilder(argv, context_, task, shouldLoadNodeClient = true) { context_.config = this.configManager.getConfig(UPDATE_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'existingNodeAliases', 'freezeAdminPrivateKey', 'keysDir', 'nodeClient', 'podRefs', 'serviceMap', 'stagingDir', 'stagingKeysDir', 'treasuryKey', 'namespace', 'consensusNodes', 'contexts', ]); context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); context_.config.curDate = new Date(); context_.config.existingNodeAliases = []; await this.initializeSetup(context_.config, this.k8Factory); if (shouldLoadNodeClient) { context_.config.nodeClient = await this.accountManager.loadNodeClient(context_.config.namespace, this.remoteConfig.getClusterRefs(), context_.config.deployment); } // check consensus releaseTag to make sure it is a valid semantic version string starting with 'v' context_.config.releaseTag = SemanticVersion.getValidSemanticVersion(context_.config.releaseTag, true, 'Consensus release tag'); const freezeAdminAccountId = this.accountManager.getFreezeAccountId(context_.config.deployment); const accountKeys = await this.accountManager.getAccountKeysFromSecret(freezeAdminAccountId.toString(), context_.config.namespace); context_.config.freezeAdminPrivateKey = accountKeys.privateKey; const treasuryAccount = await this.accountManager.getTreasuryAccountKeys(context_.config.namespace, context_.config.deployment); const treasuryAccountPrivateKey = treasuryAccount.privateKey; context_.config.treasuryKey = PrivateKey.fromStringED25519(treasuryAccountPrivateKey); if (context_.config.domainNames) { context_.config.domainNamesMapping = Templates.parseNodeAliasToDomainNameMapping(context_.config.domainNames); } return context_.config; } async destroyConfigBuilder(argv, context_, task, shouldLoadNodeClient = true) { context_.config = this.configManager.getConfig(DESTROY_CONFIGS_NAME, argv.flags, [ 'adminKey', 'allNodeAliases', 'existingNodeAliases', 'freezeAdminPrivateKey', 'keysDir', 'nodeClient', 'podRefs', 'serviceMap', 'stagingDir', 'stagingKeysDir', 'treasuryKey', 'namespace', 'consensusNodes', 'contexts', ]); context_.config.curDate = new Date(); context_.config.existingNodeAliases = []; context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); await this.initializeSetup(context_.config, this.k8Factory); if (shouldLoadNodeClient) { context_.config.nodeClient = await this.accountManager.loadNodeClient(context_.config.namespace, this.remoteConfig.getClusterRefs(), context_.config.deployment); } const freezeAdminAccountId = this.accountManager.getFreezeAccountId(context_.config.deployment); const accountKeys = await this.accountManager.getAccountKeysFromSecret(freezeAdminAccountId.toString(), context_.config.namespace); context_.config.freezeAdminPrivateKey = accountKeys.privateKey; const treasuryAccount = await this.accountManager.getTreasuryAccountKeys(context_.config.namespace, context_.config.deployment); const treasuryAccountPrivateKey = treasuryAccount.privateKey; context_.config.treasuryKey = PrivateKey.fromStringED25519(treasuryAccountPrivateKey); if (context_.config.domainNames) { context_.config.domainNamesMapping = Templates.parseNodeAliasToDomainNameMapping(context_.config.domainNames); } return context_.config; } async addConfigBuilder(argv, context_, task, shouldLoadNodeClient = true) { context_.config = this.configManager.getConfig(ADD_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'newNodeAliases', 'curDate', 'existingNodeAliases', 'freezeAdminPrivateKey', 'keysDir', 'lastStateZipPath', 'nodeClient', 'podRefs', 'serviceMap', 'stagingDir', 'stagingKeysDir', 'treasuryKey', 'namespace', 'consensusNodes', 'contexts', ]); context_.adminKey = argv[flags.adminKey?.name] ? PrivateKey.fromStringED25519(argv[flags.adminKey?.name]) : PrivateKey.fromStringED25519(constants.GENESIS_KEY); context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); context_.config.curDate = new Date(); context_.config.existingNodeAliases = []; await this.initializeSetup(context_.config, this.k8Factory); if (shouldLoadNodeClient) { context_.config.nodeClient = await this.accountManager.loadNodeClient(context_.config.namespace, this.remoteConfig.getClusterRefs(), context_.config.deployment); } const freezeAdminAccountId = this.accountManager.getFreezeAccountId(context_.config.deployment); const accountKeys = await this.accountManager.getAccountKeysFromSecret(freezeAdminAccountId.toString(), context_.config.namespace); context_.config.freezeAdminPrivateKey = accountKeys.privateKey; const treasuryAccount = await this.accountManager.getTreasuryAccountKeys(context_.config.namespace, context_.config.deployment); const treasuryAccountPrivateKey = treasuryAccount.privateKey; context_.config.treasuryKey = PrivateKey.fromStringED25519(treasuryAccountPrivateKey); context_.config.serviceMap = await this.accountManager.getNodeServiceMap(context_.config.namespace, this.remoteConfig.getClusterRefs(), context_.config.deployment); context_.config.consensusNodes = this.remoteConfig.getConsensusNodes(); context_.config.contexts = this.remoteConfig.getContexts(); if (!context_.config.clusterRef) { context_.config.clusterRef = this.remoteConfig.getClusterRefs()?.entries()?.next()?.value[0]; if (!context_.config.clusterRef) { throw new SoloError('Error during initialization, cluster ref could not be determined'); } } if (context_.config.domainNames) { context_.config.domainNamesMapping = Templates.parseNodeAliasToDomainNameMapping(context_.config.domainNames); } return context_.config; } async logsConfigBuilder(_argv, context_, task) { context_.config = { namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed), this.remoteConfig.getConsensusNodes(), this.configManager), nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.remoteConfig.getConsensusNodes(), contexts: this.remoteConfig.getContexts(), }; return context_.config; } async connectionsConfigBuilder(_argv, context_, task) { context_.config = { deployment: this.configManager.getFlag(flags.deployment), namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), contexts: this.remoteConfig.getContexts()[0], }; return context_.config; } async statesConfigBuilder(_argv, context_, task) { const consensusNodes = this.remoteConfig.getConsensusNodes(); context_.config = { namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed), consensusNodes, this.configManager), nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed), deployment: this.configManager.getFlag(flags.deployment), consensusNodes, contexts: this.remoteConfig.getContexts(), }; return context_.config; } async refreshConfigBuilder(argv, context_, task) { context_.config = this.configManager.getConfig(REFRESH_CONFIGS_NAME, argv.flags, [ 'nodeAliases', 'podRefs', 'namespace', 'consensusNodes', 'contexts', ]); context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); context_.config.nodeAliases = helpers.parseNodeAliases(context_.config.nodeAliasesUnparsed, this.remoteConfig.getConsensusNodes(), this.configManager); await this.initializeSetup(context_.config, this.k8Factory); if (context_.config.domainNames) { context_.config.domainNamesMapping = Templates.parseNodeAliasToDomainNameMapping(context_.config.domainNames); } return context_.config; } async keysConfigBuilder(argv, context_) { context_.config = this.configManager.getConfig(KEYS_CONFIGS_NAME, argv.flags, [ 'curDate', 'keysDir', 'nodeAliases', 'consensusNodes', 'contexts', ]); context_.config.curDate = new Date(); context_.config.nodeAliases = helpers.parseNodeAliases(context_.config.nodeAliasesUnparsed, this.remoteConfig.getConsensusNodes(), this.configManager); context_.config.keysDir = PathEx.join(this.configManager.getFlag(flags.cacheDir), 'keys'); if (!fs.existsSync(context_.config.keysDir)) { fs.mkdirSync(context_.config.keysDir); } return context_.config; } async stopConfigBuilder(_argv, context_, task) { const consensusNodes = this.remoteConfig.getConsensusNodes(); context_.config = { namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed), consensusNodes, this.configManager), nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed), deployment: this.configManager.getFlag(flags.deployment), consensusNodes, contexts: this.remoteConfig.getContexts(), }; await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace); return context_.config; } async freezeConfigBuilder(_argv, context_, task) { context_.config = { namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.remoteConfig.getConsensusNodes(), contexts: this.remoteConfig.getContexts(), }; await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace); const freezeAdminAccountId = this.accountManager.getFreezeAccountId(context_.config.deployment); const accountKeys = await this.accountManager.getAccountKeysFromSecret(freezeAdminAccountId.toString(), context_.config.namespace); context_.config.freezeAdminPrivateKey = accountKeys.privateKey; return context_.config; } async startConfigBuilder(argv, context_, task) { context_.config = this.configManager.getConfig(START_CONFIGS_NAME, argv.flags, [ 'nodeAliases', 'namespace', 'consensusNodes', 'contexts', ]); context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); context_.config.consensusNodes = this.remoteConfig.getConsensusNodes(); for (const consensusNode of context_.config.consensusNodes) { const k8 = this.k8Factory.getK8(consensusNode.context); if (!(await k8.namespaces().has(context_.config.namespace))) { throw new SoloError(`namespace ${context_.config.namespace} does not exist`); } } context_.config.nodeAliases = helpers.parseNodeAliases(context_.config.nodeAliasesUnparsed, context_.config.consensusNodes, this.configManager); return context_.config; } async restartConfigBuilder(_argv, context_, task) { context_.config = { namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.remoteConfig.getConsensusNodes(), contexts: this.remoteConfig.getContexts(), }; await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace); return context_.config; } async collectJfrConfigBuilder(_argv, context_, task) { context_.config = { namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.remoteConfig.getConsensusNodes(), contexts: this.remoteConfig.getContexts(), nodeAlias: this.configManager.getFlag(flags.nodeAlias), }; await checkNamespace(context_.config.consensusNodes, this.k8Factory, context_.config.namespace); return context_.config; } async setupConfigBuilder(argv, context_, task) { context_.config = this.configManager.getConfig(SETUP_CONFIGS_NAME, argv.flags, [ 'nodeAliases', 'podRefs', 'namespace', 'consensusNodes', 'contexts', ]); const savedVersion = this.remoteConfig.configuration.versions.consensusNode; if (!savedVersion.equals(context_.config.releaseTag) && // allow different versions only for local builds !context_.config.localBuildPath) { throw new SoloError(`Consensus node version saved in remote config ${savedVersion} is different from ${context_.config.releaseTag}`); } context_.config.namespace = await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task); context_.config.consensusNodes = this.remoteConfig.getConsensusNodes(); context_.config.nodeAliases = helpers.parseNodeAliases(context_.config.nodeAliasesUnparsed, context_.config.consensusNodes, this.configManager); await this.initializeSetup(context_.config, this.k8Factory); if (context_.config.domainNames) { context_.config.domainNamesMapping = Templates.parseNodeAliasToDomainNameMapping(context_.config.domainNames); } return context_.config; } }; NodeCommandConfigs = __decorate([ injectable(), __param(0, inject(InjectTokens.ConfigManager)), __param(1, inject(InjectTokens.LocalConfigRuntimeState)), __param(2, inject(InjectTokens.RemoteConfigRuntimeState)), __param(3, inject(InjectTokens.K8Factory)), __param(4, inject(InjectTokens.AccountManager)), __metadata("design:paramtypes", [Function, Function, Object, Object, Function]) ], NodeCommandConfigs); export { NodeCommandConfigs }; //# sourceMappingURL=configs.js.map