UNPKG

@hashgraph/solo

Version:

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

164 lines 9.77 kB
// SPDX-License-Identifier: Apache-2.0 import { beforeEach, describe, it } from 'mocha'; import { expect } from 'chai'; import { RemoteConfigValidator } from '../../../../src/core/config/remote/remote-config-validator.js'; import { ComponentsDataWrapper } from '../../../../src/core/config/remote/components-data-wrapper.js'; import { SoloError } from '../../../../src/core/errors/solo-error.js'; import { container } from 'tsyringe-neo'; import { NamespaceName } from '../../../../src/types/namespace/namespace-name.js'; import { PodReference } from '../../../../src/integration/kube/resources/pod/pod-reference.js'; import { PodName } from '../../../../src/integration/kube/resources/pod/pod-name.js'; import { ContainerName } from '../../../../src/integration/kube/resources/container/container-name.js'; import { InjectTokens } from '../../../../src/core/dependency-injection/inject-tokens.js'; import { getTestCacheDirectory } from '../../../test-utility.js'; import { Duration } from '../../../../src/core/time/duration.js'; import { DeploymentPhase } from '../../../../src/data/schema/model/remote/deployment-phase.js'; import { Templates } from '../../../../src/core/templates.js'; import { ComponentTypes } from '../../../../src/core/config/remote/enumerations/component-types.js'; import { ComponentFactory } from '../../../../src/core/config/remote/component-factory.js'; import { DeploymentStateSchema } from '../../../../src/data/schema/model/remote/deployment-state-schema.js'; import { RemoteConfigSchema } from '../../../../src/data/schema/model/remote/remote-config-schema.js'; import { resetForTest } from '../../../test-container.js'; function prepareComponentsData(namespace) { const remoteConfigMock = { configuration: { components: { getNewComponentId: () => 1 } } }; const clusterReference = 'cluster'; const nodeState = DeploymentPhase.STARTED; const id = 1; const componentFactory = new ComponentFactory(remoteConfigMock); const components = { explorers: componentFactory.createNewExplorerComponent(clusterReference, namespace), mirrorNodes: componentFactory.createNewMirrorNodeComponent(clusterReference, namespace), relayNodes: componentFactory.createNewRelayComponent(clusterReference, namespace, [0]), consensusNodes: componentFactory.createNewConsensusNodeComponent(id, clusterReference, namespace, nodeState), haProxies: componentFactory.createNewHaProxyComponent(clusterReference, namespace), envoyProxies: componentFactory.createNewEnvoyProxyComponent(clusterReference, namespace), blockNodes: componentFactory.createNewBlockNodeComponent(clusterReference, namespace), }; const labelRecord = { relayNodes: Templates.renderRelayLabels(components.relayNodes.metadata.id), haProxies: Templates.renderHaProxyLabels(components.haProxies.metadata.id), mirrorNodes: Templates.renderMirrorNodeLabels(components.mirrorNodes.metadata.id), envoyProxies: Templates.renderEnvoyProxyLabels(components.envoyProxies.metadata.id), explorers: Templates.renderExplorerLabels(components.explorers.metadata.id), consensusNodes: Templates.renderConsensusNodeLabels(components.consensusNodes.metadata.id), blockNodes: Templates.renderBlockNodeLabels(components.blockNodes.metadata.id), }; const podNames = { explorers: `hedera-explorer-${components.explorers.metadata.id}`, mirrorNodes: `mirror-importer-${components.mirrorNodes.metadata.id}`, relayNodes: `relay-${components.relayNodes.metadata.id}`, consensusNodes: Templates.renderNetworkPodName(Templates.renderNodeAliasFromNumber(components.consensusNodes.metadata.id)).name, haProxies: `haproxy-node1-${Templates.renderNodeAliasFromNumber(components.haProxies.metadata.id)}`, envoyProxies: `envoy-proxy-${Templates.renderNodeAliasFromNumber(components.envoyProxies.metadata.id)}`, }; const state = new DeploymentStateSchema(); const remoteConfig = new RemoteConfigSchema(undefined, undefined, undefined, undefined, state); const componentsDataWrapper = new ComponentsDataWrapper(remoteConfig.state); return { namespace, components, labelRecord, componentsDataWrapper, podNames, componentFactory }; } describe('RemoteConfigValidator', () => { const namespace = NamespaceName.of('remote-config-validator'); let k8Factory; let localConfig; let components; let labelRecord; let componentsDataWrapper; let podNames; let componentFactory; let state; let remoteConfigValidator; before(async () => { resetForTest(namespace.name, `${getTestCacheDirectory('LocalConfig')}`, false); k8Factory = container.resolve(InjectTokens.K8Factory); localConfig = container.resolve(InjectTokens.LocalConfigRuntimeState); await localConfig.load(); await k8Factory.default().namespaces().create(namespace); remoteConfigValidator = new RemoteConfigValidator(k8Factory, localConfig); }); beforeEach(() => { const testData = prepareComponentsData(namespace); podNames = testData.podNames; components = testData.components; labelRecord = testData.labelRecord; componentsDataWrapper = testData.componentsDataWrapper; componentFactory = testData.componentFactory; state = componentsDataWrapper.state; }); after(async function () { this.timeout(Duration.ofMinutes(5).toMillis()); await k8Factory.default().namespaces().delete(namespace); }); async function createPod(name, labelsRaw) { const labels = {}; for (const rawLabel of labelsRaw) { const [key, value] = rawLabel.split('='); labels[key] = value; } await k8Factory .default() .pods() .create(PodReference.of(namespace, PodName.of(name)), labels, ContainerName.of(name), 'alpine:latest', ['/bin/sh', '-c', 'apk update && apk upgrade && apk add --update bash && sleep 7200'], ['bash', '-c', 'exit 0']); } const testCasesForIndividualComponents = [ { componentKey: 'relayNodes', displayName: 'Relay Nodes', type: ComponentTypes.RelayNodes }, { componentKey: 'haProxies', displayName: 'HaProxy', type: ComponentTypes.HaProxy }, { componentKey: 'mirrorNodes', displayName: 'Mirror Node', type: ComponentTypes.MirrorNode }, { componentKey: 'envoyProxies', displayName: 'Envoy Proxy', type: ComponentTypes.EnvoyProxy }, { componentKey: 'consensusNodes', displayName: 'Consensus Node', type: ComponentTypes.ConsensusNode }, { componentKey: 'explorers', displayName: 'Explorer', type: ComponentTypes.Explorer }, ]; for (const { componentKey, displayName, type } of testCasesForIndividualComponents) { describe(`${displayName} validation`, () => { it('should fail if component is not present', async () => { const component = components[componentKey]; componentsDataWrapper.addNewComponent(component, type); try { await remoteConfigValidator.validateComponents(namespace, true, state); if (type !== ComponentTypes.ConsensusNode) { expect.fail(); } } catch (error) { expect(error).to.be.instanceOf(SoloError); expect(error.message).to.include(RemoteConfigValidator.buildValidationErrorMessage(displayName, component)); } }); it('should succeed if component is present', async () => { await createPod(podNames[componentKey], labelRecord[componentKey]); await remoteConfigValidator.validateComponents(namespace, false, state); }); }); } describe('Additional test cases', () => { it('Should not validate consensus nodes if skipConsensusNodes is enabled', async () => { const skipConsensusNodes = true; const nodeIds = [0, 1, 2]; const consensusNodeComponents = componentFactory.createConsensusNodeComponentsFromNodeIds(nodeIds, 'cluster-ref', namespace); // @ts-expect-error - to mock const componentsDataWrapper = new ComponentsDataWrapper({ consensusNodes: consensusNodeComponents, }); for (const nodeId of nodeIds) { // Make sure the status is STARTED componentsDataWrapper.changeNodePhase(Templates.renderComponentIdFromNodeId(nodeId), DeploymentPhase.STARTED); } await remoteConfigValidator.validateComponents(namespace, skipConsensusNodes, state); }); const nodeStates = [DeploymentPhase.REQUESTED, DeploymentPhase.STOPPED]; for (const nodeState of nodeStates) { it(`Should not validate consensus nodes if status is ${nodeState} `, async () => { const nodeIds = [0, 1, 2]; const consensusNodeComponents = componentFactory.createConsensusNodeComponentsFromNodeIds(nodeIds, 'cluster-ref', namespace); // @ts-expect-error - to mock const componentsDataWrapper = new ComponentsDataWrapper({ consensusNodes: consensusNodeComponents, }); for (const nodeId of nodeIds) { componentsDataWrapper.changeNodePhase(Templates.renderComponentIdFromNodeId(nodeId), nodeState); } await remoteConfigValidator.validateComponents(namespace, false, state); }); } }); }); //# sourceMappingURL=remote-config-validator.test.js.map