UNPKG

@hashgraph/solo

Version:

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

219 lines 9.22 kB
/** * SPDX-License-Identifier: Apache-2.0 */ import * as x509 from '@peculiar/x509'; import os from 'os'; import path from 'path'; import { DataValidationError, IllegalArgumentError, MissingArgumentError, SoloError } from './errors.js'; import * as constants from './constants.js'; import { PodName } from './kube/resources/pod/pod_name.js'; import { GrpcProxyTlsEnums } from './enumerations.js'; import { HEDERA_PLATFORM_VERSION } from '../../version.js'; export class Templates { static renderNetworkPodName(nodeAlias) { return PodName.of(`network-${nodeAlias}-0`); } static renderNetworkSvcName(nodeAlias) { return `network-${nodeAlias}-svc`; } static nodeAliasFromNetworkSvcName(svcName) { return svcName.split('-').slice(1, -1).join('-'); } static renderNetworkHeadlessSvcName(nodeAlias) { return `network-${nodeAlias}`; } static renderGossipPemPrivateKeyFile(nodeAlias) { return `${constants.SIGNING_KEY_PREFIX}-private-${nodeAlias}.pem`; } static renderGossipPemPublicKeyFile(nodeAlias) { return `${constants.SIGNING_KEY_PREFIX}-public-${nodeAlias}.pem`; } static renderTLSPemPrivateKeyFile(nodeAlias) { return `hedera-${nodeAlias}.key`; } static renderTLSPemPublicKeyFile(nodeAlias) { return `hedera-${nodeAlias}.crt`; } static renderNodeAdminKeyName(nodeAlias) { return `${nodeAlias}-admin`; } static renderNodeFriendlyName(prefix, nodeAlias, suffix = '') { const parts = [prefix, nodeAlias]; if (suffix) parts.push(suffix); return parts.join('-'); } static extractNodeAliasFromPodName(podName) { const parts = podName.name.split('-'); if (parts.length !== 3) throw new DataValidationError(`pod name is malformed : ${podName.name}`, 3, parts.length); return parts[1].trim(); } static prepareReleasePrefix(tag) { if (!tag) throw new MissingArgumentError('tag cannot be empty'); const parsed = tag.split('.'); if (parsed.length < 3) throw new Error(`tag (${tag}) must include major, minor and patch fields (e.g. v0.40.4)`); return `${parsed[0]}.${parsed[1]}`; } /** * renders the name to be used to store the new account key as a Kubernetes secret * @param accountId * @returns the name of the Kubernetes secret to store the account key */ static renderAccountKeySecretName(accountId) { return `account-key-${accountId.toString()}`; } /** * renders the label selector to be used to fetch the new account key from the Kubernetes secret * @param accountId * @returns the label selector of the Kubernetes secret to retrieve the account key */ static renderAccountKeySecretLabelSelector(accountId) { return `solo.hedera.com/account-id=${accountId.toString()}`; } /** * renders the label object to be used to store the new account key in the Kubernetes secret * @param accountId * @returns the label object to be used to store the new account key in the Kubernetes secret */ static renderAccountKeySecretLabelObject(accountId) { return { 'solo.hedera.com/account-id': accountId.toString(), }; } static renderDistinguishedName(nodeAlias, state = 'TX', locality = 'Richardson', org = 'Hedera', orgUnit = 'Hedera', country = 'US') { return new x509.Name(`CN=${nodeAlias},ST=${state},L=${locality},O=${org},OU=${orgUnit},C=${country}`); } static renderStagingDir(cacheDir, releaseTagOverride) { let releaseTag = releaseTagOverride; if (!cacheDir) { throw new IllegalArgumentError('cacheDir cannot be empty'); } if (!releaseTag) { releaseTag = HEDERA_PLATFORM_VERSION; } const releasePrefix = this.prepareReleasePrefix(releaseTag); if (!releasePrefix) { throw new IllegalArgumentError('releasePrefix cannot be empty'); } return path.resolve(path.join(cacheDir, releasePrefix, 'staging', releaseTag)); } static installationPath(dep, osPlatform = os.platform(), installationDir = path.join(constants.SOLO_HOME_DIR, 'bin')) { switch (dep) { case constants.HELM: if (osPlatform === constants.OS_WINDOWS) { return path.join(installationDir, `${dep}.exe`); } return path.join(installationDir, dep); default: throw new SoloError(`unknown dep: ${dep}`); } } static renderFullyQualifiedNetworkPodName(namespace, nodeAlias) { return `${Templates.renderNetworkPodName(nodeAlias)}.${Templates.renderNetworkHeadlessSvcName(nodeAlias)}.${namespace.name}.svc.cluster.local`; } static renderFullyQualifiedNetworkSvcName(namespace, nodeAlias) { return `${Templates.renderNetworkSvcName(nodeAlias)}.${namespace.name}.svc.cluster.local`; } static nodeAliasFromFullyQualifiedNetworkSvcName(svcName) { const parts = svcName.split('.'); return this.nodeAliasFromNetworkSvcName(parts[0]); } static nodeIdFromNodeAlias(nodeAlias) { for (let i = nodeAlias.length - 1; i > 0; i--) { // @ts-ignore if (isNaN(nodeAlias[i])) { return parseInt(nodeAlias.substring(i + 1, nodeAlias.length)) - 1; } } throw new SoloError(`Can't get node id from node ${nodeAlias}`); } static renderGossipKeySecretName(nodeAlias) { return `network-${nodeAlias}-keys-secrets`; } static renderGossipKeySecretLabelObject(nodeAlias) { return { 'solo.hedera.com/node-name': nodeAlias }; } /** * Creates the secret name based on the node alias type * * @param nodeAlias - node alias * @param type - whether is for gRPC or gRPC Web ( Haproxy or Envoy ) * * @returns the appropriate secret name */ static renderGrpcTlsCertificatesSecretName(nodeAlias, type) { switch (type) { //? HAProxy Proxy case GrpcProxyTlsEnums.GRPC: return `haproxy-proxy-secret-${nodeAlias}`; //? Envoy Proxy case GrpcProxyTlsEnums.GRPC_WEB: return `envoy-proxy-secret-${nodeAlias}`; } } /** * Creates the secret labels based on the node alias type * * @param nodeAlias - node alias * @param type - whether is for gRPC or gRPC Web ( Haproxy or Envoy ) * * @returns the appropriate secret labels */ static renderGrpcTlsCertificatesSecretLabelObject(nodeAlias, type) { switch (type) { //? HAProxy Proxy case GrpcProxyTlsEnums.GRPC: return { 'haproxy-proxy-secret': nodeAlias }; //? Envoy Proxy case GrpcProxyTlsEnums.GRPC_WEB: return { 'envoy-proxy-secret': nodeAlias }; } } static renderEnvoyProxyName(nodeAlias) { return `envoy-proxy-${nodeAlias}`; } static renderHaProxyName(nodeAlias) { return `haproxy-${nodeAlias}`; } static renderFullyQualifiedHaProxyName(nodeAlias, namespace) { return `${Templates.renderHaProxyName(nodeAlias)}-svc.${namespace}.svc.cluster.local`; } static parseNodeAliasToIpMapping(unparsed) { const mapping = {}; unparsed.split(',').forEach(data => { const [nodeAlias, ip] = data.split('='); mapping[nodeAlias] = ip; }); return mapping; } /** * Renders the fully qualified domain name for a consensus node. We support the following variables for templating * in the dnsConsensusNodePattern: ${nodeAlias}, ${nodeId}, ${namespace}, ${cluster} * * The end result will be `${dnsConsensusNodePattern}.${dnsBaseDomain}`. * For example, if the dnsConsensusNodePattern is `network-${nodeAlias}-svc.${namespace}.svc` and the dnsBaseDomain is `cluster.local`, * the fully qualified domain name will be `network-${nodeAlias}-svc.${namespace}.svc.cluster.local`. * @param nodeAlias - the alias of the consensus node * @param nodeId - the id of the consensus node * @param namespace - the namespace of the consensus node * @param cluster - the cluster of the consensus node * @param dnsBaseDomain - the base domain of the cluster * @param dnsConsensusNodePattern - the pattern to use for the consensus node */ // TODO @Lenin, needs testing static renderConsensusNodeFullyQualifiedDomainName(nodeAlias, nodeId, namespace, cluster, dnsBaseDomain, dnsConsensusNodePattern) { const searchReplace = { '${nodeAlias}': nodeAlias, '${nodeId}': nodeId.toString(), '${namespace}': namespace, '${cluster}': cluster, }; Object.entries(searchReplace).forEach(([search, replace]) => { dnsConsensusNodePattern = dnsConsensusNodePattern.replace(search, replace); }); return `${dnsConsensusNodePattern}.${dnsBaseDomain}`; } } //# sourceMappingURL=templates.js.map