@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
154 lines • 8.05 kB
JavaScript
// 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); }
};
var RemoteConfigValidator_1;
import { inject, injectable } from 'tsyringe-neo';
import { patchInject } from '../../dependency-injection/container-helper.js';
import { InjectTokens } from '../../dependency-injection/inject-tokens.js';
import { SoloError } from '../../errors/solo-error.js';
import { Templates } from '../../templates.js';
import { DeploymentPhase } from '../../../data/schema/model/remote/deployment-phase.js';
import * as constants from '../../constants.js';
/**
* Static class is used to validate that components in the remote config
* are present in the kubernetes cluster, and throw errors if there is mismatch.
*/
let RemoteConfigValidator = class RemoteConfigValidator {
static { RemoteConfigValidator_1 = this; }
k8Factory;
localConfig;
chartManager;
constructor(k8Factory, localConfig, chartManager) {
this.k8Factory = k8Factory;
this.localConfig = localConfig;
this.chartManager = chartManager;
this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
this.localConfig = patchInject(localConfig, InjectTokens.LocalConfigRuntimeState, this.constructor.name);
this.chartManager = patchInject(chartManager, InjectTokens.ChartManager, this.constructor.name);
}
static consensusNodeSkipConditionCallback(nodeComponent) {
return (nodeComponent.metadata.phase === DeploymentPhase.REQUESTED ||
nodeComponent.metadata.phase === DeploymentPhase.STOPPED);
}
// This skips components that are requested, because they are not yet deployed.
// This is needed to avoid errors during the deployment of the requested components.
// Especially during one-shot deployments.
static componentSkipConditionCallback(component) {
return component.metadata.phase === DeploymentPhase.REQUESTED;
}
static componentValidationsMapping = {
relayNodes: {
displayName: 'Relay Nodes',
getLabelsCallback: Templates.renderRelayLabels,
legacyReleaseName: constants.JSON_RPC_RELAY_RELEASE_NAME,
skipCondition: RemoteConfigValidator_1.componentSkipConditionCallback,
},
haProxies: {
displayName: 'HaProxy',
getLabelsCallback: Templates.renderHaProxyLabels,
},
mirrorNodes: {
displayName: 'Mirror Node',
getLabelsCallback: Templates.renderMirrorNodeLabels,
legacyReleaseName: constants.MIRROR_NODE_RELEASE_NAME,
skipCondition: RemoteConfigValidator_1.componentSkipConditionCallback,
},
envoyProxies: {
displayName: 'Envoy Proxy',
getLabelsCallback: Templates.renderEnvoyProxyLabels,
},
explorers: {
displayName: 'Explorer',
getLabelsCallback: Templates.renderExplorerLabels,
legacyReleaseName: 'hiero-explorer',
skipCondition: RemoteConfigValidator_1.componentSkipConditionCallback,
},
consensusNodes: {
displayName: 'Consensus Node',
getLabelsCallback: Templates.renderConsensusNodeLabels,
skipCondition: RemoteConfigValidator_1.consensusNodeSkipConditionCallback,
},
blockNodes: {
displayName: 'Block Node',
getLabelsCallback: Templates.renderBlockNodeLabels,
legacyReleaseName: `${constants.BLOCK_NODE_RELEASE_NAME}-0`,
skipCondition: RemoteConfigValidator_1.componentSkipConditionCallback,
},
};
async validateComponents(namespace, skipConsensusNodes, state) {
const validationPromises = Object.entries(RemoteConfigValidator_1.componentValidationsMapping)
.filter(([key]) => key !== 'consensusNodes' || !skipConsensusNodes)
.flatMap(([key, { getLabelsCallback, displayName, skipCondition, legacyReleaseName }]) => this.validateComponentGroup(key, namespace, state[key], getLabelsCallback, displayName, skipCondition, legacyReleaseName));
await Promise.all(validationPromises);
}
validateComponentGroup(key, namespace, components, getLabelsCallback, displayName, skipCondition, legacyReleaseName) {
return components.map(async (component) => {
if (skipCondition?.(component)) {
return;
}
const context = this.localConfig.configuration.clusterRefs.get(component.metadata.cluster)?.toString();
let useLegacyReleaseName = false;
if (legacyReleaseName && component.metadata.id <= 1) {
if (key === 'relayNodes') {
const nodeAliases = component?.consensusNodeIds.map((nodeId) => Templates.renderNodeAliasFromNumber(nodeId + 1));
legacyReleaseName = `${legacyReleaseName}-${nodeAliases.join('-')}`;
}
const isLegacyChartInstalled = await this.chartManager.isChartInstalled(namespace, legacyReleaseName, context);
if (isLegacyChartInstalled) {
useLegacyReleaseName = true;
}
}
const labels = useLegacyReleaseName
? getLabelsCallback(component.metadata.id, legacyReleaseName)
: getLabelsCallback(component.metadata.id);
try {
const pods = await this.k8Factory.getK8(context).pods().list(namespace, labels);
if (pods.length === 0) {
throw new Error('Pod not found'); // to return the generic error message
}
}
catch (error) {
throw RemoteConfigValidator_1.buildValidationError(displayName, component, error, labels);
}
});
}
/**
* Generic handler that throws errors.
*
* @param displayName - name to display in error message
* @param component - component which is not found in the cluster
* @param error - original error for the kube client
* @param labels - labels used to find the component
*/
static buildValidationError(displayName, component, error, labels) {
return new SoloError(RemoteConfigValidator_1.buildValidationErrorMessage(displayName, component, labels), error, component);
}
static buildValidationErrorMessage(displayName, component, labels = []) {
let message = `${displayName} in remote config with id ${component.metadata.id} was not found in ` +
`namespace: ${component.metadata.namespace}, ` +
`cluster: ${component.metadata.cluster}`;
if (labels?.length !== 0) {
message += `, labels: ${labels}`;
}
return message;
}
};
RemoteConfigValidator = RemoteConfigValidator_1 = __decorate([
injectable(),
__param(0, inject(InjectTokens.K8Factory)),
__param(1, inject(InjectTokens.LocalConfigRuntimeState)),
__param(2, inject(InjectTokens.ChartManager)),
__metadata("design:paramtypes", [Object, Function, Function])
], RemoteConfigValidator);
export { RemoteConfigValidator };
//# sourceMappingURL=remote-config-validator.js.map