UNPKG

@hashgraph/solo

Version:

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

635 lines (554 loc) 20 kB
/** * SPDX-License-Identifier: Apache-2.0 */ import {FREEZE_ADMIN_ACCOUNT} from '../../core/constants.js'; import {Templates} from '../../core/templates.js'; import * as constants from '../../core/constants.js'; import {PrivateKey} from '@hashgraph/sdk'; import {SoloError} from '../../core/errors.js'; import * as helpers from '../../core/helpers.js'; import path from 'path'; import fs from 'fs'; import {validatePath} from '../../core/helpers.js'; import {resolveNamespaceFromDeployment} from '../../core/resolvers.js'; import {Flags as flags} from '../flags.js'; import {type NodeAlias, type NodeAliases} from '../../types/aliases.js'; import {type NetworkNodeServices} from '../../core/network_node_services.js'; import {type NodeAddConfigClass} from './node_add_config.js'; import {type NamespaceName} from '../../core/kube/resources/namespace/namespace_name.js'; import {type PodRef} from '../../core/kube/resources/pod/pod_ref.js'; import {type K8Factory} from '../../core/kube/k8_factory.js'; import {type ConsensusNode} from '../../core/model/consensus_node.js'; import {type DeploymentName} from '../../core/config/remote/types.js'; export const PREPARE_UPGRADE_CONFIGS_NAME = 'prepareUpgradeConfig'; export const DOWNLOAD_GENERATED_FILES_CONFIGS_NAME = 'downloadGeneratedFilesConfig'; export const ADD_CONFIGS_NAME = 'addConfigs'; export const DELETE_CONFIGS_NAME = 'deleteConfigs'; export const UPDATE_CONFIGS_NAME = 'updateConfigs'; export const UPGRADE_CONFIGS_NAME = 'upgradeConfigs'; export const REFRESH_CONFIGS_NAME = 'refreshConfigs'; export const KEYS_CONFIGS_NAME = 'keyConfigs'; export const SETUP_CONFIGS_NAME = 'setupConfigs'; export const START_CONFIGS_NAME = 'startConfigs'; const initializeSetup = async (config: any, k8Factory: K8Factory) => { // compute other config parameters config.keysDir = path.join(validatePath(config.cacheDir), 'keys'); config.stagingDir = Templates.renderStagingDir(config.cacheDir, config.releaseTag); config.stagingKeysDir = path.join(validatePath(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); } }; export const prepareUpgradeConfigBuilder = async function (argv, ctx, task) { const config = this.getConfig(PREPARE_UPGRADE_CONFIGS_NAME, argv.flags, [ 'nodeClient', 'freezeAdminPrivateKey', 'namespace', 'consensusNodes', 'contexts', ]) as NodePrepareUpgradeConfigClass; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); await initializeSetup(config, this.k8Factory); config.nodeClient = await this.accountManager.loadNodeClient( config.namespace, this.parent.getClusterRefs(), config.deployment, ); const accountKeys = await this.accountManager.getAccountKeysFromSecret(FREEZE_ADMIN_ACCOUNT, config.namespace); config.freezeAdminPrivateKey = accountKeys.privateKey; return config; }; export const downloadGeneratedFilesConfigBuilder = async function (argv, ctx, task) { const config = this.getConfig(DOWNLOAD_GENERATED_FILES_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'existingNodeAliases', 'serviceMap', 'namespace', 'consensusNodes', 'contexts', ]) as NodeDownloadGeneratedFilesConfigClass; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); config.existingNodeAliases = []; await initializeSetup(config, this.k8Factory); return config; }; export const upgradeConfigBuilder = async function (argv, ctx, task, shouldLoadNodeClient = true) { const config = this.getConfig(UPGRADE_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'existingNodeAliases', 'keysDir', 'nodeClient', 'podRefs', 'stagingDir', 'stagingKeysDir', 'namespace', 'consensusNodes', 'contexts', ]) as NodeUpgradeConfigClass; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); config.curDate = new Date(); config.existingNodeAliases = []; config.nodeAliases = helpers.parseNodeAliases(config.nodeAliasesUnparsed); await initializeSetup(config, this.k8Factory); // set config in the context for later tasks to use ctx.config = config; ctx.config.chartPath = await this.prepareChartPath( ctx.config.chartDirectory, constants.SOLO_TESTING_CHART_URL, constants.SOLO_DEPLOYMENT_CHART, ); if (shouldLoadNodeClient) { ctx.config.nodeClient = await this.accountManager.loadNodeClient( ctx.config.namespace, this.parent.getClusterRefs(), config.deployment, ); } const accountKeys = await this.accountManager.getAccountKeysFromSecret(FREEZE_ADMIN_ACCOUNT, config.namespace); config.freezeAdminPrivateKey = accountKeys.privateKey; return config; }; export const updateConfigBuilder = async function (argv, ctx, task, shouldLoadNodeClient = true) { const config = this.getConfig(UPDATE_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'existingNodeAliases', 'freezeAdminPrivateKey', 'keysDir', 'nodeClient', 'podRefs', 'serviceMap', 'stagingDir', 'stagingKeysDir', 'treasuryKey', 'namespace', 'consensusNodes', 'contexts', ]) as NodeUpdateConfigClass; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); config.curDate = new Date(); config.existingNodeAliases = []; await initializeSetup(config, this.k8Factory); // set config in the context for later tasks to use ctx.config = config; ctx.config.chartPath = await this.prepareChartPath( ctx.config.chartDirectory, constants.SOLO_TESTING_CHART_URL, constants.SOLO_DEPLOYMENT_CHART, ); if (shouldLoadNodeClient) { ctx.config.nodeClient = await this.accountManager.loadNodeClient( ctx.config.namespace, this.parent.getClusterRefs(), config.deployment, ); } const accountKeys = await this.accountManager.getAccountKeysFromSecret(FREEZE_ADMIN_ACCOUNT, config.namespace); config.freezeAdminPrivateKey = accountKeys.privateKey; const treasuryAccount = await this.accountManager.getTreasuryAccountKeys(config.namespace); const treasuryAccountPrivateKey = treasuryAccount.privateKey; config.treasuryKey = PrivateKey.fromStringED25519(treasuryAccountPrivateKey); return config; }; export const deleteConfigBuilder = async function (argv, ctx, task, shouldLoadNodeClient = true) { const config = this.getConfig(DELETE_CONFIGS_NAME, argv.flags, [ 'adminKey', 'allNodeAliases', 'existingNodeAliases', 'freezeAdminPrivateKey', 'keysDir', 'nodeClient', 'podRefs', 'serviceMap', 'stagingDir', 'stagingKeysDir', 'treasuryKey', 'namespace', 'consensusNodes', 'contexts', ]) as NodeDeleteConfigClass; config.curDate = new Date(); config.existingNodeAliases = []; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); await initializeSetup(config, this.k8Factory); // set config in the context for later tasks to use ctx.config = config; ctx.config.chartPath = await this.prepareChartPath( ctx.config.chartDirectory, constants.SOLO_TESTING_CHART_URL, constants.SOLO_DEPLOYMENT_CHART, ); if (shouldLoadNodeClient) { ctx.config.nodeClient = await this.accountManager.loadNodeClient( ctx.config.namespace, this.parent.getClusterRefs(), config.deployment, ); } const accountKeys = await this.accountManager.getAccountKeysFromSecret(FREEZE_ADMIN_ACCOUNT, config.namespace); config.freezeAdminPrivateKey = accountKeys.privateKey; const treasuryAccount = await this.accountManager.getTreasuryAccountKeys(config.namespace); const treasuryAccountPrivateKey = treasuryAccount.privateKey; config.treasuryKey = PrivateKey.fromStringED25519(treasuryAccountPrivateKey); return config; }; export const addConfigBuilder = async function (argv, ctx, task, shouldLoadNodeClient = true) { const config = this.getConfig(ADD_CONFIGS_NAME, argv.flags, [ 'allNodeAliases', 'chartPath', 'curDate', 'existingNodeAliases', 'freezeAdminPrivateKey', 'keysDir', 'lastStateZipPath', 'nodeClient', 'podRefs', 'serviceMap', 'stagingDir', 'stagingKeysDir', 'treasuryKey', 'namespace', 'consensusNodes', 'contexts', ]) as NodeAddConfigClass; ctx.adminKey = argv[flags.adminKey.name] ? PrivateKey.fromStringED25519(argv[flags.adminKey.name]) : PrivateKey.fromStringED25519(constants.GENESIS_KEY); config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); config.curDate = new Date(); config.existingNodeAliases = []; await initializeSetup(config, this.k8Factory); // set config in the context for later tasks to use ctx.config = config; ctx.config.chartPath = await this.prepareChartPath( ctx.config.chartDirectory, constants.SOLO_TESTING_CHART_URL, constants.SOLO_DEPLOYMENT_CHART, ); if (shouldLoadNodeClient) { ctx.config.nodeClient = await this.accountManager.loadNodeClient( ctx.config.namespace, this.parent.getClusterRefs(), config.deployment, ); } const accountKeys = await this.accountManager.getAccountKeysFromSecret(FREEZE_ADMIN_ACCOUNT, config.namespace); config.freezeAdminPrivateKey = accountKeys.privateKey; const treasuryAccount = await this.accountManager.getTreasuryAccountKeys(config.namespace); const treasuryAccountPrivateKey = treasuryAccount.privateKey; config.treasuryKey = PrivateKey.fromStringED25519(treasuryAccountPrivateKey); config.serviceMap = await this.accountManager.getNodeServiceMap( config.namespace, this.parent.getClusterRefs(), config.deployment, ); config.consensusNodes = this.parent.getConsensusNodes(); config.contexts = this.parent.getContexts(); return config; }; export const logsConfigBuilder = async function (argv, ctx, task) { const config = { namespace: await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)), nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.parent.getConsensusNodes(), contexts: this.parent.getContexts(), } as NodeLogsConfigClass; ctx.config = config; return config; }; export const statesConfigBuilder = async function (argv, ctx, task) { const config = { namespace: await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)), nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.parent.getConsensusNodes(), contexts: this.parent.getContexts(), }; ctx.config = config; return config; }; export const refreshConfigBuilder = async function (argv, ctx, task) { ctx.config = this.getConfig(REFRESH_CONFIGS_NAME, argv.flags, [ 'nodeAliases', 'podRefs', 'namespace', 'consensusNodes', 'contexts', ]) as NodeRefreshConfigClass; ctx.config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); ctx.config.nodeAliases = helpers.parseNodeAliases(ctx.config.nodeAliasesUnparsed); await initializeSetup(ctx.config, this.k8Factory); return ctx.config; }; export const keysConfigBuilder = function (argv, ctx, task) { const config: NodeKeysConfigClass = this.getConfig(KEYS_CONFIGS_NAME, argv.flags, [ 'curDate', 'keysDir', 'nodeAliases', 'consensusNodes', 'contexts', ]) as NodeKeysConfigClass; config.curDate = new Date(); config.nodeAliases = helpers.parseNodeAliases(config.nodeAliasesUnparsed); if (config.nodeAliases.length === 0) { config.nodeAliases = this.consensusNodes.map((node: {name: string}) => { return node.name; }); if (config.nodeAliases.length === 0) { throw new SoloError('no node aliases provided via flags or RemoteConfig'); } } config.keysDir = path.join(this.configManager.getFlag(flags.cacheDir), 'keys'); if (!fs.existsSync(config.keysDir)) { fs.mkdirSync(config.keysDir); } return config; }; export const stopConfigBuilder = async function (argv, ctx, task) { ctx.config = { namespace: await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)), nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed), deployment: this.configManager.getFlag(flags.deployment), consensusNodes: this.parent.getConsensusNodes(), contexts: this.parent.getContexts(), }; if (!(await this.k8Factory.default().namespaces().has(ctx.config.namespace))) { throw new SoloError(`namespace ${ctx.config.namespace} does not exist`); } return ctx.config; }; export const startConfigBuilder = async function (argv, ctx, task) { const config = this.getConfig(START_CONFIGS_NAME, argv.flags, [ 'nodeAliases', 'namespace', 'consensusNodes', 'contexts', ]) as NodeStartConfigClass; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); config.consensusNodes = this.parent.getConsensusNodes(); for (const consensusNode of config.consensusNodes) { const k8 = this.k8Factory.getK8(consensusNode.context); if (!(await k8.namespaces().has(config.namespace))) { throw new SoloError(`namespace ${config.namespace} does not exist`); } } config.nodeAliases = helpers.parseNodeAliases(config.nodeAliasesUnparsed); return config; }; export const setupConfigBuilder = async function (argv, ctx, task) { const config = this.getConfig(SETUP_CONFIGS_NAME, argv.flags, [ 'nodeAliases', 'podRefs', 'namespace', 'consensusNodes', 'contexts', ]) as NodeSetupConfigClass; config.namespace = await resolveNamespaceFromDeployment(this.parent.localConfig, this.configManager, task); config.nodeAliases = helpers.parseNodeAliases(config.nodeAliasesUnparsed); config.consensusNodes = this.parent.getConsensusNodes(); await initializeSetup(config, this.k8Factory); // set config in the context for later tasks to use ctx.config = config; return ctx.config; }; export interface NodeLogsConfigClass { namespace: NamespaceName; deployment: string; nodeAliases: string[]; consensusNodes: ConsensusNode[]; contexts: string[]; } export interface NodeRefreshConfigClass { app: string; cacheDir: string; devMode: boolean; localBuildPath: string; namespace: NamespaceName; deployment: string; nodeAliasesUnparsed: string; releaseTag: string; nodeAliases: NodeAliases; podRefs: Record<NodeAlias, PodRef>; getUnusedConfigs: () => string[]; consensusNodes: ConsensusNode[]; contexts: string[]; } export interface NodeKeysConfigClass { cacheDir: string; devMode: boolean; generateGossipKeys: boolean; generateTlsKeys: boolean; nodeAliasesUnparsed: string; curDate: Date; keysDir: string; nodeAliases: NodeAliases; getUnusedConfigs: () => string[]; consensusNodes: ConsensusNode[]; contexts: string[]; } export interface NodeStartConfigClass { app: string; cacheDir: string; consensusNodes: ConsensusNode[]; debugNodeAlias: NodeAlias; namespace: NamespaceName; deployment: string; nodeAliases: NodeAliases; stagingDir: string; podRefs: Record<NodeAlias, PodRef>; nodeAliasesUnparsed: string; contexts: string[]; } export interface NodeDeleteConfigClass { app: string; cacheDir: string; chartDirectory: string; devMode: boolean; debugNodeAlias: NodeAlias; endpointType: string; soloChartVersion: string; localBuildPath: string; namespace: NamespaceName; deployment: string; nodeAlias: NodeAlias; releaseTag: string; adminKey: PrivateKey; allNodeAliases: NodeAliases; chartPath: string; existingNodeAliases: NodeAliases; freezeAdminPrivateKey: string; keysDir: string; nodeClient: any; podRefs: Record<NodeAlias, PodRef>; serviceMap: Map<string, NetworkNodeServices>; stagingDir: string; stagingKeysDir: string; treasuryKey: PrivateKey; getUnusedConfigs: () => string[]; curDate: Date; consensusNodes: ConsensusNode[]; contexts: string[]; } export interface NodeSetupConfigClass { app: string; appConfig: string; adminKey: string; cacheDir: string; devMode: boolean; localBuildPath: string; namespace: NamespaceName; deployment: string; nodeAliasesUnparsed: string; releaseTag: string; nodeAliases: NodeAliases; podRefs: Record<NodeAlias, PodRef>; consensusNodes: ConsensusNode[]; skipStop?: boolean; keysDir: string; stagingDir: string; getUnusedConfigs: () => string[]; contexts: string[]; } export interface NodeUpgradeConfigClass { nodeAliasesUnparsed: string; nodeAliases: NodeAliases; app: string; cacheDir: string; chartDirectory: string; devMode: boolean; debugNodeAlias: NodeAlias; soloChartVersion: string; localBuildPath: string; namespace: NamespaceName; deployment: string; releaseTag: string; adminKey: PrivateKey; allNodeAliases: NodeAliases; chartPath: string; existingNodeAliases: NodeAliases; freezeAdminPrivateKey: PrivateKey | string; keysDir: string; nodeClient: any; podRefs: Record<NodeAlias, PodRef>; stagingDir: string; stagingKeysDir: string; treasuryKey: PrivateKey; getUnusedConfigs: () => string[]; curDate: Date; consensusNodes: ConsensusNode[]; contexts: string[]; } export interface NodeUpdateConfigClass { app: string; cacheDir: string; chartDirectory: string; devMode: boolean; debugNodeAlias: NodeAlias; endpointType: string; soloChartVersion: string; gossipEndpoints: string; gossipPrivateKey: string; gossipPublicKey: string; grpcEndpoints: string; localBuildPath: string; namespace: NamespaceName; deployment: string; newAccountNumber: string; newAdminKey: PrivateKey; nodeAlias: NodeAlias; releaseTag: string; tlsPrivateKey: string; tlsPublicKey: string; adminKey: PrivateKey; allNodeAliases: NodeAliases; chartPath: string; existingNodeAliases: NodeAliases; freezeAdminPrivateKey: PrivateKey | string; keysDir: string; nodeClient: any; podRefs: Record<NodeAlias, PodRef>; serviceMap: Map<string, NetworkNodeServices>; stagingDir: string; stagingKeysDir: string; treasuryKey: PrivateKey; getUnusedConfigs: () => string[]; curDate: Date; consensusNodes: ConsensusNode[]; contexts: string[]; } interface NodePrepareUpgradeConfigClass { cacheDir: string; namespace: NamespaceName; deployment: string; releaseTag: string; freezeAdminPrivateKey: string; nodeClient: any; getUnusedConfigs: () => string[]; consensusNodes: ConsensusNode[]; contexts: string[]; } interface NodeDownloadGeneratedFilesConfigClass { cacheDir: string; namespace: NamespaceName; deployment: string; releaseTag: string; freezeAdminPrivateKey: string; nodeClient: any; getUnusedConfigs: () => string[]; existingNodeAliases: NodeAliases[]; allNodeAliases: NodeAliases[]; serviceMap: Map<string, NetworkNodeServices>; consensusNodes: ConsensusNode[]; contexts: string[]; }