UNPKG

@hashgraph/solo

Version:

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

1,220 lines 65 kB
/** * SPDX-License-Identifier: Apache-2.0 */ import * as constants from '../core/constants.js'; import * as version from '../../version.js'; import path from 'path'; import fs from 'fs'; import { IllegalArgumentError, SoloError } from '../core/errors.js'; import { ListrEnquirerPromptAdapter } from '@listr2/prompt-adapter-enquirer'; import * as helpers from '../core/helpers.js'; import validator from 'validator'; export class Flags { static KEY_COMMON = '_COMMON_'; static async prompt(type, task, input, defaultValue, promptMessage, emptyCheckMessage, flagName) { try { let needsPrompt = type === 'toggle' ? input === undefined || typeof input !== 'boolean' : !input; needsPrompt = type === 'number' ? typeof input !== 'number' : needsPrompt; if (needsPrompt) { if (!process.stdout.isTTY || !process.stdin.isTTY) { // this is to help find issues with prompts running in non-interactive mode, user should supply quite mode, // or provide all flags required for command throw new SoloError('Cannot prompt for input in non-interactive mode'); } input = await task.prompt(ListrEnquirerPromptAdapter).run({ type, default: defaultValue, message: promptMessage, }); } if (emptyCheckMessage && !input) { throw new SoloError(emptyCheckMessage); } return input; } catch (e) { throw new SoloError(`input failed: ${flagName}: ${e.message}`, e); } } static async promptText(task, input, defaultValue, promptMessage, emptyCheckMessage, flagName) { return await Flags.prompt('text', task, input, defaultValue, promptMessage, emptyCheckMessage, flagName); } static async promptToggle(task, input, defaultValue, promptMessage, emptyCheckMessage, flagName) { return await Flags.prompt('toggle', task, input, defaultValue, promptMessage, emptyCheckMessage, flagName); } /** * Disable prompts for the given set of flags * @param flags list of flags to disable prompts for */ static disablePrompts(flags) { Flags.resetDisabledPrompts(); for (const flag of flags) { if (flag.definition) { flag.definition.disablePrompt = true; } } } /** * Set flag from the flag option * @param y instance of yargs * @param commandFlags a set of command flags * */ static setCommandFlags(y, ...commandFlags) { commandFlags.forEach(flag => { y.option(flag.name, flag.definition); }); } static devMode = { constName: 'devMode', name: 'dev', definition: { describe: 'Enable developer mode', defaultValue: false, type: 'boolean', }, prompt: undefined, }; static forcePortForward = { constName: 'forcePortForward', name: 'force-port-forward', definition: { describe: 'Force port forward to access the network services', defaultValue: true, // always use local port-forwarding by default type: 'boolean', }, prompt: undefined, }; // list of common flags across commands. command specific flags are defined in the command's module. static clusterRef = { constName: 'clusterRef', name: 'cluster-ref', definition: { describe: 'The cluster reference that will be used for referencing the Kubernetes cluster and stored in the local and ' + 'remote configuration for the deployment. For commands that take multiple clusters they can be separated by commas.', alias: 'c', type: 'string', }, prompt: async function promptClusterRef(task, input) { return await Flags.promptText(task, input, Flags.clusterRef.definition.defaultValue, 'Enter cluster reference: ', 'cluster reference cannot be empty', Flags.clusterRef.name); }, }; static clusterSetupNamespace = { constName: 'clusterSetupNamespace', name: 'cluster-setup-namespace', definition: { describe: 'Cluster Setup Namespace', defaultValue: constants.SOLO_SETUP_NAMESPACE.name, alias: 's', type: 'string', }, prompt: async function promptClusterSetupNamespace(task, input) { return await Flags.promptText(task, input, 'solo-cluster', 'Enter cluster setup namespace name: ', 'cluster setup namespace cannot be empty', Flags.clusterSetupNamespace.name); }, }; static namespace = { constName: 'namespace', name: 'namespace', definition: { describe: 'Namespace', alias: 'n', type: 'string', }, prompt: async function promptNamespace(task, input) { return await Flags.promptText(task, input, 'solo', 'Enter namespace name: ', 'namespace cannot be empty', Flags.namespace.name); }, }; /** * Parse the values files input string that includes the cluster reference and the values file path * <p>It supports input as below: * <p>--values-file aws-cluster=aws/solo-values.yaml,aws-cluster=aws/solo-values2.yaml,gcp-cluster=gcp/solo-values.yaml,gcp-cluster=gcp/solo-values2.yaml * @param input */ static parseValuesFilesInput(input) { const valuesFiles = {}; if (input) { const inputItems = input.split(','); inputItems.forEach(v => { const parts = v.split('='); let clusterRef = ''; let valuesFile = ''; if (parts.length !== 2) { valuesFile = path.resolve(v); clusterRef = Flags.KEY_COMMON; } else { clusterRef = parts[0]; valuesFile = path.resolve(parts[1]); } if (!valuesFiles[clusterRef]) { valuesFiles[clusterRef] = []; } valuesFiles[clusterRef].push(valuesFile); }); } return valuesFiles; } static valuesFile = { constName: 'valuesFile', name: 'values-file', definition: { describe: 'Comma separated chart values file', defaultValue: '', alias: 'f', type: 'string', }, prompt: async function promptValuesFile(task, input) { return input; // no prompt is needed for values file }, }; static networkDeploymentValuesFile = { constName: 'valuesFile', name: 'values-file', definition: { describe: 'Comma separated chart values file paths for each cluster (e.g. values.yaml,cluster-1=./a/b/values1.yaml,cluster-2=./a/b/values2.yaml)', defaultValue: '', alias: 'f', type: 'string', }, prompt: async function promptValuesFile(task, input) { if (input) { Flags.parseValuesFilesInput(input); // validate input as early as possible by parsing it } return input; // no prompt is needed for values file }, }; static profileFile = { constName: 'profileFile', name: 'profile-file', definition: { describe: 'Resource profile definition (e.g. custom-spec.yaml)', defaultValue: constants.DEFAULT_PROFILE_FILE, type: 'string', }, prompt: async function promptProfileFile(task, input) { if (input && !fs.existsSync(input)) { input = await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'text', default: Flags.valuesFile.definition.defaultValue, message: 'Enter path to custom resource profile definition file: ', }); } if (input && !fs.existsSync(input)) { throw new IllegalArgumentError(`Invalid profile definition file: ${input}}`, input); } return input; }, }; static profileName = { constName: 'profileName', name: 'profile', definition: { describe: `Resource profile (${constants.ALL_PROFILES.join(' | ')})`, defaultValue: constants.PROFILE_LOCAL, type: 'string', }, prompt: async function promptProfile(task, input, choices = constants.ALL_PROFILES) { try { const initial = choices.indexOf(input); if (initial < 0) { const input = await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'select', message: 'Select profile for solo network deployment', choices: helpers.cloneArray(choices), }); if (!input) { throw new SoloError('key-format cannot be empty'); } return input; } return input; } catch (e) { throw new SoloError(`input failed: ${Flags.profileName.name}`, e); } }, }; static deployPrometheusStack = { constName: 'deployPrometheusStack', name: 'prometheus-stack', definition: { describe: 'Deploy prometheus stack', defaultValue: false, type: 'boolean', }, prompt: async function promptDeployPrometheusStack(task, input) { return await Flags.promptToggle(task, input, Flags.deployPrometheusStack.definition.defaultValue, 'Would you like to deploy prometheus stack? ', null, Flags.deployPrometheusStack.name); }, }; static enablePrometheusSvcMonitor = { constName: 'enablePrometheusSvcMonitor', name: 'prometheus-svc-monitor', definition: { describe: 'Enable prometheus service monitor for the network nodes', defaultValue: false, type: 'boolean', }, prompt: async function promptEnablePrometheusSvcMonitor(task, input) { return await Flags.promptToggle(task, input, Flags.enablePrometheusSvcMonitor.definition.defaultValue, 'Would you like to enable the Prometheus service monitor for the network nodes? ', null, Flags.enablePrometheusSvcMonitor.name); }, }; static deployMinio = { constName: 'deployMinio', name: 'minio', definition: { describe: 'Deploy minio operator', defaultValue: true, type: 'boolean', }, prompt: async function promptDeployMinio(task, input) { return await Flags.promptToggle(task, input, Flags.deployMinio.definition.defaultValue, 'Would you like to deploy MinIO? ', null, Flags.deployMinio.name); }, }; static deployCertManager = { constName: 'deployCertManager', name: 'cert-manager', definition: { describe: 'Deploy cert manager, also deploys acme-cluster-issuer', defaultValue: false, type: 'boolean', }, prompt: async function promptDeployCertManager(task, input) { return await Flags.promptToggle(task, input, Flags.deployCertManager.definition.defaultValue, 'Would you like to deploy Cert Manager? ', null, Flags.deployCertManager.name); }, }; /* Deploy cert manager CRDs separately from cert manager itself. Cert manager CRDs are required for cert manager to deploy successfully. */ static deployCertManagerCrds = { constName: 'deployCertManagerCrds', name: 'cert-manager-crds', definition: { describe: 'Deploy cert manager CRDs', defaultValue: false, type: 'boolean', }, prompt: async function promptDeployCertManagerCrds(task, input) { return await Flags.promptToggle(task, input, Flags.deployCertManagerCrds.definition.defaultValue, 'Would you like to deploy Cert Manager CRDs? ', null, Flags.deployCertManagerCrds.name); }, }; static deployJsonRpcRelay = { constName: 'deployJsonRpcRelay', name: 'json-rpc-relay', definition: { describe: 'Deploy JSON RPC Relay', defaultValue: false, alias: 'j', type: 'boolean', }, prompt: undefined, }; static stateFile = { constName: 'stateFile', name: 'state-file', definition: { describe: 'A zipped state file to be used for the network', defaultValue: '', type: 'string', }, prompt: undefined, }; static upgradeZipFile = { constName: 'upgradeZipFile', name: 'upgrade-zip-file', definition: { describe: 'A zipped file used for network upgrade', defaultValue: '', type: 'string', }, prompt: undefined, }; static releaseTag = { constName: 'releaseTag', name: 'release-tag', definition: { describe: `Release tag to be used (e.g. ${version.HEDERA_PLATFORM_VERSION})`, alias: 't', defaultValue: version.HEDERA_PLATFORM_VERSION, type: 'string', }, prompt: async function promptReleaseTag(task, input) { return await Flags.promptText(task, input, version.HEDERA_PLATFORM_VERSION, 'Enter release version: ', undefined, Flags.releaseTag.name); }, }; static relayReleaseTag = { constName: 'relayReleaseTag', name: 'relay-release', definition: { describe: 'Relay release tag to be used (e.g. v0.48.0)', defaultValue: version.HEDERA_JSON_RPC_RELAY_VERSION, type: 'string', }, prompt: async function promptRelayReleaseTag(task, input) { return await Flags.promptText(task, input, Flags.relayReleaseTag.definition.defaultValue, 'Enter relay release version: ', 'relay-release-tag cannot be empty', Flags.relayReleaseTag.name); }, }; static cacheDir = { constName: 'cacheDir', name: 'cache-dir', definition: { describe: 'Local cache directory', defaultValue: constants.SOLO_CACHE_DIR, type: 'string', }, prompt: async function promptCacheDir(task, input) { return await Flags.promptText(task, input, constants.SOLO_CACHE_DIR, 'Enter local cache directory path: ', null, Flags.cacheDir.name); }, }; static nodeAliasesUnparsed = { constName: 'nodeAliasesUnparsed', name: 'node-aliases', definition: { describe: 'Comma separated node aliases (empty means all nodes)', alias: 'i', type: 'string', }, prompt: async function promptNodeAliases(task, input) { return await Flags.prompt('input', task, input, 'node1,node2,node3', 'Enter list of node IDs (comma separated list): ', null, Flags.nodeAliasesUnparsed.name); }, }; static force = { constName: 'force', name: 'force', definition: { describe: 'Force actions even if those can be skipped', defaultValue: false, alias: 'f', type: 'boolean', }, prompt: async function promptForce(task, input) { return await Flags.promptToggle(task, input, Flags.force.definition.defaultValue, 'Would you like to force changes? ', null, Flags.force.name); }, }; static chartDirectory = { constName: 'chartDirectory', name: 'chart-dir', definition: { describe: 'Local chart directory path (e.g. ~/solo-charts/charts', defaultValue: '', alias: 'd', type: 'string', }, prompt: async function promptChartDir(task, input) { try { if (input === 'false') { return ''; } if (input && !fs.existsSync(input)) { input = await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'text', default: Flags.chartDirectory.definition.defaultValue, message: 'Enter local charts directory path: ', }); if (!fs.existsSync(input)) { throw new IllegalArgumentError('Invalid chart directory', input); } } return input; } catch (e) { throw new SoloError(`input failed: ${Flags.chartDirectory.name}`, e); } }, }; static replicaCount = { constName: 'replicaCount', name: 'replica-count', definition: { describe: 'Replica count', defaultValue: 1, alias: '', type: 'number', }, prompt: async function promptReplicaCount(task, input) { return await Flags.prompt('number', task, input, Flags.replicaCount.definition.defaultValue, 'How many replica do you want? ', null, Flags.replicaCount.name); }, }; static chainId = { constName: 'chainId', name: 'ledger-id', definition: { describe: 'Ledger ID (a.k.a. Chain ID)', defaultValue: constants.HEDERA_CHAIN_ID, // Ref: https://github.com/hashgraph/hedera-json-rpc-relay#configuration alias: 'l', type: 'string', }, prompt: async function promptChainId(task, input) { return await Flags.promptText(task, input, Flags.chainId.definition.defaultValue, 'Enter chain ID: ', null, Flags.chainId.name); }, }; // Ref: https://github.com/hashgraph/hedera-json-rpc-relay/blob/main/docs/configuration.md static operatorId = { constName: 'operatorId', name: 'operator-id', definition: { describe: 'Operator ID', defaultValue: undefined, type: 'string', }, prompt: async function promptOperatorId(task, input) { return await Flags.promptText(task, input, Flags.operatorId.definition.defaultValue, 'Enter operator ID: ', null, Flags.operatorId.name); }, }; // Ref: https://github.com/hashgraph/hedera-json-rpc-relay/blob/main/docs/configuration.md static operatorKey = { constName: 'operatorKey', name: 'operator-key', definition: { describe: 'Operator Key', defaultValue: undefined, type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: async function promptOperatorKey(task, input) { return await Flags.promptText(task, input, Flags.operatorKey.definition.defaultValue, 'Enter operator private key: ', null, Flags.operatorKey.name); }, }; static privateKey = { constName: 'privateKey', name: 'private-key', definition: { describe: 'Show private key information', defaultValue: false, type: 'boolean', dataMask: constants.STANDARD_DATAMASK, }, prompt: async function promptPrivateKey(task, input) { return await Flags.promptText(task, input, Flags.ed25519PrivateKey.definition.defaultValue, 'Enter the private key: ', null, Flags.ed25519PrivateKey.name); }, }; static generateGossipKeys = { constName: 'generateGossipKeys', name: 'gossip-keys', definition: { describe: 'Generate gossip keys for nodes', defaultValue: false, type: 'boolean', }, prompt: async function promptGenerateGossipKeys(task, input) { return await Flags.promptToggle(task, input, Flags.generateGossipKeys.definition.defaultValue, `Would you like to generate Gossip keys? ${typeof input} ${input} `, null, Flags.generateGossipKeys.name); }, }; static generateTlsKeys = { constName: 'generateTlsKeys', name: 'tls-keys', definition: { describe: 'Generate gRPC TLS keys for nodes', defaultValue: false, type: 'boolean', }, prompt: async function promptGenerateTLSKeys(task, input) { return await Flags.promptToggle(task, input, Flags.generateTlsKeys.definition.defaultValue, 'Would you like to generate TLS keys? ', null, Flags.generateTlsKeys.name); }, }; static enableTimeout = { constName: 'enableTimeout', name: 'enable-timeout', definition: { describe: 'enable time out for running a command', defaultValue: false, type: 'boolean', }, prompt: undefined, }; static tlsClusterIssuerType = { constName: 'tlsClusterIssuerType', name: 'tls-cluster-issuer-type', definition: { describe: 'The TLS cluster issuer type to use for hedera explorer, defaults to "self-signed", the available options are: "acme-staging", "acme-prod", or "self-signed"', defaultValue: 'self-signed', type: 'string', }, prompt: async function promptTlsClusterIssuerType(task, input) { try { if (!input) { input = await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'text', default: Flags.tlsClusterIssuerType.definition.defaultValue, message: 'Enter TLS cluster issuer type, available options are: "acme-staging", "acme-prod", or "self-signed":', }); } if (!input || !['acme-staging', 'acme-prod', 'self-signed'].includes(input)) { throw new SoloError('must be one of: "acme-staging", "acme-prod", or "self-signed"'); } return input; } catch (e) { throw new SoloError(`input failed: ${Flags.tlsClusterIssuerType.name}`, e); } }, }; static enableHederaExplorerTls = { constName: 'enableHederaExplorerTls', name: 'enable-hedera-explorer-tls', definition: { describe: 'Enable the Hedera Explorer TLS, defaults to false, requires certManager and certManagerCrds, which can be deployed through solo-cluster-setup chart or standalone', defaultValue: false, type: 'boolean', }, prompt: async function promptEnableHederaExplorerTls(task, input) { return await Flags.promptToggle(task, input, Flags.enableHederaExplorerTls.definition.defaultValue, 'Would you like to enable the Hedera Explorer TLS? ', null, Flags.enableHederaExplorerTls.name); }, }; static hederaExplorerStaticIp = { constName: 'hederaExplorerStaticIp', name: 'hedera-explorer-static-ip', definition: { describe: 'The static IP address to use for the Hedera Explorer load balancer, defaults to ""', defaultValue: '', type: 'string', }, prompt: undefined, }; static hederaExplorerTlsHostName = { constName: 'hederaExplorerTlsHostName', name: 'hedera-explorer-tls-host-name', definition: { describe: 'The host name to use for the Hedera Explorer TLS, defaults to "explorer.solo.local"', defaultValue: 'explorer.solo.local', type: 'string', }, prompt: async function promptHederaExplorerTlsHostName(task, input) { return await Flags.promptText(task, input, Flags.hederaExplorerTlsHostName.definition.defaultValue, 'Enter the host name to use for the Hedera Explorer TLS: ', null, Flags.hederaExplorerTlsHostName.name); }, }; static deletePvcs = { constName: 'deletePvcs', name: 'delete-pvcs', definition: { describe: 'Delete the persistent volume claims', defaultValue: false, type: 'boolean', }, prompt: async function promptDeletePvcs(task, input) { return await Flags.promptToggle(task, input, Flags.deletePvcs.definition.defaultValue, 'Would you like to delete persistent volume claims upon uninstall? ', null, Flags.deletePvcs.name); }, }; static deleteSecrets = { constName: 'deleteSecrets', name: 'delete-secrets', definition: { describe: 'Delete the network secrets', defaultValue: false, type: 'boolean', }, prompt: async function promptDeleteSecrets(task, input) { return await Flags.promptToggle(task, input, Flags.deleteSecrets.definition.defaultValue, 'Would you like to delete secrets upon uninstall? ', null, Flags.deleteSecrets.name); }, }; static soloChartVersion = { constName: 'soloChartVersion', name: 'solo-chart-version', definition: { describe: 'Solo testing chart version', defaultValue: version.SOLO_CHART_VERSION, type: 'string', }, prompt: async function promptSoloChartVersion(task, input) { return await Flags.promptText(task, input, Flags.soloChartVersion.definition.defaultValue, 'Enter solo testing chart version: ', null, Flags.soloChartVersion.name); }, }; static applicationProperties = { constName: 'applicationProperties', name: 'application-properties', definition: { describe: 'application.properties file for node', defaultValue: path.join(constants.SOLO_CACHE_DIR, 'templates', 'application.properties'), type: 'string', }, prompt: undefined, }; static applicationEnv = { constName: 'applicationEnv', name: 'application-env', definition: { describe: 'the application.env file for the node provides environment variables to the solo-container' + ' to be used when the hedera platform is started', defaultValue: path.join(constants.SOLO_CACHE_DIR, 'templates', 'application.env'), type: 'string', }, prompt: undefined, }; static apiPermissionProperties = { constName: 'apiPermissionProperties', name: 'api-permission-properties', definition: { describe: 'api-permission.properties file for node', defaultValue: path.join(constants.SOLO_CACHE_DIR, 'templates', 'api-permission.properties'), type: 'string', }, prompt: undefined, }; static bootstrapProperties = { constName: 'bootstrapProperties', name: 'bootstrap-properties', definition: { describe: 'bootstrap.properties file for node', defaultValue: path.join(constants.SOLO_CACHE_DIR, 'templates', 'bootstrap.properties'), type: 'string', }, prompt: undefined, }; static genesisThrottlesFile = { constName: 'genesisThrottlesFile', name: 'genesis-throttles-file', definition: { describe: 'throttles.json file used during network genesis', defaultValue: '', type: 'string', }, prompt: undefined, }; static settingTxt = { constName: 'settingTxt', name: 'settings-txt', definition: { describe: 'settings.txt file for node', defaultValue: path.join(constants.SOLO_CACHE_DIR, 'templates', 'settings.txt'), type: 'string', }, prompt: undefined, }; static app = { constName: 'app', name: 'app', definition: { describe: 'Testing app name', defaultValue: constants.HEDERA_APP_NAME, type: 'string', }, prompt: undefined, }; static appConfig = { constName: 'appConfig', name: 'app-config', definition: { describe: 'json config file of testing app', defaultValue: '', type: 'string', }, prompt: undefined, }; static localBuildPath = { constName: 'localBuildPath', name: 'local-build-path', definition: { describe: 'path of hedera local repo', defaultValue: '', type: 'string', }, prompt: undefined, }; static newAccountNumber = { constName: 'newAccountNumber', name: 'new-account-number', definition: { describe: 'new account number for node update transaction', defaultValue: '', type: 'string', }, prompt: undefined, }; static newAdminKey = { constName: 'newAdminKey', name: 'new-admin-key', definition: { describe: 'new admin key for the Hedera account', defaultValue: '', type: 'string', }, prompt: undefined, }; static gossipPublicKey = { constName: 'gossipPublicKey', name: 'gossip-public-key', definition: { describe: 'path and file name of the public key for signing gossip in PEM key format to be used', defaultValue: '', type: 'string', }, prompt: undefined, }; static gossipPrivateKey = { constName: 'gossipPrivateKey', name: 'gossip-private-key', definition: { describe: 'path and file name of the private key for signing gossip in PEM key format to be used', defaultValue: '', type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: undefined, }; static tlsPublicKey = { constName: 'tlsPublicKey', name: 'tls-public-key', definition: { describe: 'path and file name of the public TLS key to be used', defaultValue: '', type: 'string', }, prompt: undefined, }; static tlsPrivateKey = { constName: 'tlsPrivateKey', name: 'tls-private-key', definition: { describe: 'path and file name of the private TLS key to be used', defaultValue: '', type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: undefined, }; static log4j2Xml = { constName: 'log4j2Xml', name: 'log4j2-xml', definition: { describe: 'log4j2.xml file for node', defaultValue: path.join(constants.SOLO_CACHE_DIR, 'templates', 'log4j2.xml'), type: 'string', }, prompt: undefined, }; static updateAccountKeys = { constName: 'updateAccountKeys', name: 'update-account-keys', definition: { describe: 'Updates the special account keys to new keys and stores their keys in a corresponding Kubernetes secret', defaultValue: true, type: 'boolean', }, prompt: async function promptUpdateAccountKeys(task, input) { return await Flags.promptToggle(task, input, Flags.updateAccountKeys.definition.defaultValue, 'Would you like to updates the special account keys to new keys and stores their keys in a corresponding Kubernetes secret? ', null, Flags.updateAccountKeys.name); }, }; static ed25519PrivateKey = { constName: 'ed25519PrivateKey', name: 'ed25519-private-key', definition: { describe: 'ED25519 private key for the Hedera account', defaultValue: '', type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: async function promptPrivateKey(task, input) { return await Flags.promptText(task, input, Flags.ed25519PrivateKey.definition.defaultValue, 'Enter the private key: ', null, Flags.ed25519PrivateKey.name); }, }; static generateEcdsaKey = { constName: 'generateEcdsaKey', name: 'generate-ecdsa-key', definition: { describe: 'Generate ECDSA private key for the Hedera account', defaultValue: false, type: 'boolean', }, prompt: undefined, }; static ecdsaPrivateKey = { constName: 'ecdsaPrivateKey', name: 'ecdsa-private-key', definition: { describe: 'ECDSA private key for the Hedera account', defaultValue: '', type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: async function promptPrivateKey(task, input) { return await Flags.promptText(task, input, Flags.ed25519PrivateKey.definition.defaultValue, 'Enter the private key: ', null, Flags.ed25519PrivateKey.name); }, }; static setAlias = { constName: 'setAlias', name: 'set-alias', definition: { describe: 'Sets the alias for the Hedera account when it is created, requires --ecdsa-private-key', defaultValue: false, type: 'boolean', }, prompt: undefined, }; static accountId = { constName: 'accountId', name: 'account-id', definition: { describe: 'The Hedera account id, e.g.: 0.0.1001', defaultValue: '', type: 'string', }, prompt: async function promptAccountId(task, input) { return await Flags.promptText(task, input, Flags.accountId.definition.defaultValue, 'Enter the account id: ', null, Flags.accountId.name); }, }; static amount = { constName: 'amount', name: 'hbar-amount', definition: { describe: 'Amount of HBAR to add', defaultValue: 100, type: 'number', }, prompt: async function promptAmount(task, input) { return await Flags.prompt('number', task, input, Flags.amount.definition.defaultValue, 'How much HBAR do you want to add? ', null, Flags.amount.name); }, }; static createAmount = { constName: 'createAmount', name: 'create-amount', definition: { describe: 'Amount of new account to create', defaultValue: 1, type: 'number', }, prompt: async function promptCreateAmount(task, input) { return await Flags.prompt('number', task, input, Flags.createAmount.definition.defaultValue, 'How many account to create? ', null, Flags.createAmount.name); }, }; static nodeAlias = { constName: 'nodeAlias', name: 'node-alias', definition: { describe: 'Node alias (e.g. node99)', type: 'string', }, prompt: async function promptNewNodeAlias(task, input) { return await Flags.promptText(task, input, Flags.nodeAlias.definition.defaultValue, 'Enter the new node id: ', null, Flags.nodeAlias.name); }, }; static gossipEndpoints = { constName: 'gossipEndpoints', name: 'gossip-endpoints', definition: { describe: 'Comma separated gossip endpoints of the node(e.g. first one is internal, second one is external)', type: 'string', }, prompt: async function promptGossipEndpoints(task, input) { return await Flags.promptText(task, input, Flags.gossipEndpoints.definition.defaultValue, 'Enter the gossip endpoints(comma separated): ', null, Flags.gossipEndpoints.name); }, }; static grpcEndpoints = { constName: 'grpcEndpoints', name: 'grpc-endpoints', definition: { describe: 'Comma separated gRPC endpoints of the node (at most 8)', type: 'string', }, prompt: async function promptGrpcEndpoints(task, input) { return await Flags.promptText(task, input, Flags.grpcEndpoints.definition.defaultValue, 'Enter the gRPC endpoints(comma separated): ', null, Flags.grpcEndpoints.name); }, }; static endpointType = { constName: 'endpointType', name: 'endpoint-type', definition: { describe: 'Endpoint type (IP or FQDN)', defaultValue: constants.ENDPOINT_TYPE_FQDN, type: 'string', }, prompt: async function promptEndpointType(task, input) { return await Flags.promptText(task, input, Flags.endpointType.definition.defaultValue, 'Enter the endpoint type(IP or FQDN): ', null, Flags.endpointType.name); }, }; static persistentVolumeClaims = { constName: 'persistentVolumeClaims', name: 'pvcs', definition: { describe: 'Enable persistent volume claims to store data outside the pod, required for node add', defaultValue: false, type: 'boolean', }, prompt: async function promptPersistentVolumeClaims(task, input) { return await Flags.promptToggle(task, input, Flags.persistentVolumeClaims.definition.defaultValue, 'Would you like to enable persistent volume claims to store data outside the pod? ', null, Flags.persistentVolumeClaims.name); }, }; static debugNodeAlias = { constName: 'debugNodeAlias', name: 'debug-node-alias', definition: { describe: 'Enable default jvm debug port (5005) for the given node id', defaultValue: '', type: 'string', }, prompt: undefined, }; static outputDir = { constName: 'outputDir', name: 'output-dir', definition: { describe: 'Path to the directory where the command context will be saved to', defaultValue: '', type: 'string', }, prompt: async function promptOutputDir(task, input) { return await Flags.promptToggle(task, input, Flags.outputDir.definition.defaultValue, 'Enter path to directory to store the temporary context file', null, Flags.outputDir.name); }, }; static inputDir = { constName: 'inputDir', name: 'input-dir', definition: { describe: 'Path to the directory where the command context will be loaded from', defaultValue: '', type: 'string', }, prompt: async function promptInputDir(task, input) { return await Flags.promptToggle(task, input, Flags.inputDir.definition.defaultValue, 'Enter path to directory containing the temporary context file', null, Flags.inputDir.name); }, }; static adminKey = { constName: 'adminKey', name: 'admin-key', definition: { describe: 'Admin key', defaultValue: constants.GENESIS_KEY, type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: undefined, }; static adminPublicKeys = { constName: 'adminPublicKeys', name: 'admin-public-keys', definition: { describe: 'Comma separated list of DER encoded ED25519 public keys and must match the order of the node aliases', defaultValue: constants.GENESIS_KEY, type: 'string', }, prompt: undefined, }; static quiet = { constName: 'quiet', name: 'quiet-mode', definition: { describe: 'Quiet mode, do not prompt for confirmation', defaultValue: false, alias: 'q', type: 'boolean', disablePrompt: true, }, prompt: undefined, }; static mirrorNodeVersion = { constName: 'mirrorNodeVersion', name: 'mirror-node-version', definition: { describe: 'Mirror node chart version', defaultValue: version.MIRROR_NODE_VERSION, type: 'string', }, prompt: async function promptMirrorNodeVersion(task, input) { return await Flags.promptToggle(task, input, Flags.mirrorNodeVersion.definition.defaultValue, 'Would you like to choose mirror node version? ', null, Flags.mirrorNodeVersion.name); }, }; static enableIngress = { constName: 'enableIngress', name: 'enable-ingress', definition: { describe: 'enable ingress on the component/pod', defaultValue: false, type: 'boolean', }, prompt: undefined, }; static mirrorStaticIp = { constName: 'mirrorStaticIp', name: 'mirror-static-ip', definition: { describe: 'static IP address for the mirror node', defaultValue: '', type: 'string', }, prompt: undefined, }; static hederaExplorerVersion = { constName: 'hederaExplorerVersion', name: 'hedera-explorer-version', definition: { describe: 'Hedera explorer chart version', defaultValue: version.HEDERA_EXPLORER_VERSION, type: 'string', }, prompt: async function promptHederaExplorerVersion(task, input) { return await Flags.promptToggle(task, input, Flags.hederaExplorerVersion.definition.defaultValue, 'Would you like to choose hedera explorer version? ', null, Flags.hederaExplorerVersion.name); }, }; static userEmailAddress = { constName: 'userEmailAddress', name: 'email', definition: { describe: 'User email address used for local configuration', type: 'string', }, prompt: async function promptUserEmailAddress(task, input) { if (input?.length) { return input; } const promptForInput = async () => { return await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'text', message: 'Please enter your email address:', }); }; input = await promptForInput(); while (!validator.isEmail(input)) { input = await promptForInput(); } return input; }, }; static context = { constName: 'contextName', name: 'context', definition: { describe: 'The Kubernetes context name to be used. Multiple contexts can be separated by a comma', defaultValue: '', type: 'string', }, prompt: async function promptContext(task, input, cluster) { return await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'select', name: 'context', message: 'Select kubectl context' + (cluster ? ` to be associated with cluster: ${cluster}` : ''), choices: input, }); }, }; static deployment = { constName: 'deployment', name: 'deployment', definition: { describe: 'The name the user will reference locally to link to a deployment', defaultValue: '', type: 'string', }, prompt: async function promptDeployment(task, input) { return await Flags.promptText(task, input, Flags.deployment.definition.defaultValue, 'Enter the name of the deployment:', null, Flags.deployment.name); }, }; static deploymentClusters = { constName: 'deploymentClusters', name: 'deployment-clusters', definition: { describe: 'Solo deployment cluster list (comma separated)', type: 'string', }, prompt: async function promptDeploymentClusters(task, input) { return await Flags.promptText(task, input, Flags.deploymentClusters.definition.defaultValue, 'Enter the Solo deployment cluster names (comma separated): ', null, Flags.deploymentClusters.name); }, }; static pinger = { constName: 'pinger', name: 'pinger', definition: { describe: 'Enable Pinger service in the Mirror node monitor', defaultValue: false, type: 'boolean', }, prompt: undefined, }; //* ------------- Node Proxy Certificates ------------- !// static grpcTlsCertificatePath = { constName: 'grpcTlsCertificatePath', name: 'grpc-tls-cert', definition: { describe: 'TLS Certificate path for the gRPC ' + '(e.g. "node1=/Users/username/node1-grpc.cert" ' + 'with multiple nodes comma seperated)', defaultValue: '', type: 'string', }, prompt: async function promptGrpcTlsCertificatePath(task, input) { return await Flags.promptText(task, input, Flags.grpcTlsCertificatePath.definition.defaultValue, 'Enter node alias and path to TLS certificate for gRPC (ex. nodeAlias=path )', null, Flags.grpcTlsCertificatePath.name); }, }; static grpcWebTlsCertificatePath = { constName: 'grpcWebTlsCertificatePath', name: 'grpc-web-tls-cert', definition: { describe: 'TLS Certificate path for gRPC Web ' + '(e.g. "node1=/Users/username/node1-grpc-web.cert" ' + 'with multiple nodes comma seperated)', defaultValue: '', type: 'string', }, prompt: async function promptGrpcWebTlsCertificatePath(task, input) { return await Flags.promptText(task, input, Flags.grpcWebTlsCertificatePath.definition.defaultValue, 'Enter node alias and path to TLS certificate for gGRPC web (ex. nodeAlias=path )', null, Flags.grpcWebTlsCertificatePath.name); }, }; static useExternalDatabase = { constName: 'useExternalDatabase', name: 'use-external-database', definition: { describe: 'Set to true if you have an external database to use instead of the database that the Mirror Node Helm chart supplies', defaultValue: false, type: 'boolean', }, prompt: undefined, }; //* ----------------- External Mirror Node PostgreSQL Database Related Flags ------------------ *// static externalDatabaseHost = { constName: 'externalDatabaseHost', name: 'external-database-host', definition: { describe: `Use to provide the external database host if the '--${Flags.useExternalDatabase.name}' is passed`, defaultValue: '', type: 'string', }, prompt: async function promptGrpcWebTlsKeyPath(task, input) { return await Flags.promptText(task, input, Flags.externalDatabaseHost.definition.defaultValue, 'Enter host of the external database', null, Flags.externalDatabaseHost.name); }, }; static externalDatabaseOwnerUsername = { constName: 'externalDatabaseOwnerUsername', name: 'external-database-owner-username', definition: { describe: `Use to provide the external database owner's username if the '--${Flags.useExternalDatabase.name}' is passed`, defaultValue: '', type: 'string', }, prompt: async function promptGrpcWebTlsKeyPath(task, input) { return await Flags.promptText(task, input, Flags.externalDatabaseOwnerUsername.definition.defaultValue, 'Enter username of the external database owner', null, Flags.externalDatabaseOwnerUsername.name); }, }; static externalDatabaseOwnerPassword = { constName: 'externalDatabaseOwnerPassword', name: 'external-database-owner-password', definition: { describe: `Use to provide the external database owner's password if the '--${Flags.useExternalDatabase.name}' is passed`, defaultValue: '', type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: async function promptGrpcWebTlsKeyPath(task, input) { return await Flags.promptText(task, input, Flags.externalDatabaseOwnerPassword.definition.defaultValue, 'Enter password of the external database owner', null, Flags.externalDatabaseOwnerPassword.name); }, }; static externalDatabaseReadonlyUsername = { constName: 'externalDatabaseReadonlyUsername', name: 'external-database-read-username', definition: { describe: `Use to provide the external database readonly user's username if the '--${Flags.useExternalDatabase.name}' is passed`, defaultValue: '', type: 'string', }, prompt: async function promptGrpcWebTlsKeyPath(task, input) { return await Flags.promptText(task, input, Flags.externalDatabaseReadonlyUsername.definition.defaultValue, 'Enter username of the external database readonly user', null, Flags.externalDatabaseReadonlyUsername.name); }, }; static externalDatabaseReadonlyPassword = { constName: 'externalDatabaseReadonlyPassword', name: 'external-database-read-password', definition: { describe: `Use to provide the external database readonly user's password if the '--${Flags.useExternalDatabase.name}' is passed`, defaultValue: '', type: 'string', dataMask: constants.STANDARD_DATAMASK, }, prompt: async function promptGrpcWebTlsKeyPath(task, input) { return await Flags.promptText(task, inpu