@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
219 lines • 9.22 kB
JavaScript
/**
* 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