UNPKG

@hashgraph/solo

Version:

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

280 lines 17.6 kB
// SPDX-License-Identifier: Apache-2.0 import sinon from 'sinon'; import { before, beforeEach, describe, it } from 'mocha'; import { expect } from 'chai'; import { getTestCluster, HEDERA_PLATFORM_VERSION_TAG } from '../../test-utility.js'; import { Flags as flags } from '../../../src/commands/flags.js'; import * as version from '../../../version.js'; import * as constants from '../../../src/core/constants.js'; import { ROOT_DIR } from '../../../src/core/constants.js'; import { NetworkCommand } from '../../../src/commands/network.js'; import { ListrLock } from '../../../src/core/lock/listr-lock.js'; import { GenesisNetworkDataConstructor } from '../../../src/core/genesis-network-models/genesis-network-data-constructor.js'; import { container } from 'tsyringe-neo'; import { resetForTest } from '../../test-container.js'; import { InjectTokens } from '../../../src/core/dependency-injection/inject-tokens.js'; import { K8Client } from '../../../src/integration/kube/k8-client/k8-client.js'; import { ConsensusNode } from '../../../src/core/model/consensus-node.js'; import { NamespaceName } from '../../../src/types/namespace/namespace-name.js'; import { Argv } from '../../helpers/argv-wrapper.js'; import { PathEx } from '../../../src/business/utils/path-ex.js'; import fs from 'node:fs'; import { ValueContainer } from '../../../src/core/dependency-injection/value-container.js'; import { StringFacade } from '../../../src/business/runtime-state/facade/string-facade.js'; import { SemanticVersion } from '../../../src/business/utils/semantic-version.js'; const testName = 'network-cmd-unit'; const namespace = NamespaceName.of(testName); const argv = Argv.getDefaultArgv(namespace); const realK8Factory = container.resolve(InjectTokens.K8Factory); argv.setArg(flags.releaseTag, HEDERA_PLATFORM_VERSION_TAG); argv.setArg(flags.nodeAliasesUnparsed, 'node1'); argv.setArg(flags.deployment, 'deployment'); argv.setArg(flags.generateGossipKeys, true); argv.setArg(flags.generateTlsKeys, true); argv.setArg(flags.clusterRef, getTestCluster()); argv.setArg(flags.soloChartVersion, version.SOLO_CHART_VERSION); argv.setArg(flags.force, true); argv.setArg(flags.clusterSetupNamespace, constants.SOLO_SETUP_NAMESPACE.name); argv.setArg(flags.chartDirectory, undefined); if (new SemanticVersion(version.HEDERA_PLATFORM_VERSION).lessThan('v0.61.0')) { argv.setArg(flags.releaseTag, 'v0.61.0'); } describe('NetworkCommand unit tests', () => { before(async () => { const sourceDirectory = PathEx.joinWithRealPath('test', 'data'); const destinationDirectory = PathEx.join(sourceDirectory, 'tmp', 'templates'); if (!fs.existsSync(destinationDirectory)) { fs.mkdirSync(destinationDirectory, { recursive: true }); } fs.copyFileSync(PathEx.joinWithRealPath(sourceDirectory, constants.APPLICATION_PROPERTIES), PathEx.join(destinationDirectory, constants.APPLICATION_PROPERTIES)); }); describe('Chart Install Function is called correctly', () => { let options; const k8SFactoryStub = sinon.stub(); const clusterChecksStub = sinon.stub(); const remoteConfigStub = sinon.stub(); const chartManagerStub = sinon.stub(); const certificateManagerStub = sinon.stub(); const profileManagerStub = sinon.stub(); const platformInstallerStub = sinon.stub(); const keyManagerStub = sinon.stub(); const depManagerStub = sinon.stub(); const helmStub = sinon.stub(); let containerOverrides; beforeEach(async () => { containerOverrides = new Map([ [InjectTokens.K8Factory, new ValueContainer(InjectTokens.K8Factory, k8SFactoryStub)], [InjectTokens.ClusterChecks, new ValueContainer(InjectTokens.ClusterChecks, clusterChecksStub)], [ InjectTokens.RemoteConfigRuntimeState, new ValueContainer(InjectTokens.RemoteConfigRuntimeState, remoteConfigStub), ], [InjectTokens.ChartManager, new ValueContainer(InjectTokens.ChartManager, chartManagerStub)], [InjectTokens.CertificateManager, new ValueContainer(InjectTokens.CertificateManager, certificateManagerStub)], [InjectTokens.ProfileManager, new ValueContainer(InjectTokens.ProfileManager, profileManagerStub)], [InjectTokens.PlatformInstaller, new ValueContainer(InjectTokens.PlatformInstaller, platformInstallerStub)], [InjectTokens.KeyManager, new ValueContainer(InjectTokens.KeyManager, keyManagerStub)], [InjectTokens.DependencyManager, new ValueContainer(InjectTokens.DependencyManager, depManagerStub)], [InjectTokens.Helm, new ValueContainer(InjectTokens.Helm, helmStub)], ]); resetForTest(undefined, undefined, true, containerOverrides); options = { logger: container.resolve(InjectTokens.SoloLogger), configManager: container.resolve(InjectTokens.ConfigManager), }; options.configManager.update(argv.build()); options.k8Factory = k8SFactoryStub; const k8Stub = sinon.stub(); options.k8Factory.default = sinon.stub().returns(k8Stub); options.k8Factory.default().namespaces = sinon.stub().returns({ has: sinon.stub().returns(true), }); options.k8Factory.default().contexts = sinon.stub().returns({ readCurrent: sinon .stub() .returns(new K8Client(undefined, realK8Factory.default().getKubectlExecutablePath()).contexts().readCurrent()), }); options.k8Factory.default().configMaps = sinon.stub(); options.k8Factory.default().configMaps.read = sinon.stub(); options.k8Factory.default().pods = sinon.stub().returns({ waitForRunningPhase: sinon.stub(), waitForReadyStatus: sinon.stub(), }); options.k8Factory.default().leases = sinon.stub().returns({ read: sinon.stub(), }); options.k8Factory.default().logger = options.logger; options.k8Factory.getK8 = sinon.stub().returns(k8Stub); options.k8Factory.getK8().namespaces = sinon.stub().returns({ has: sinon.stub().returns(true), }); options.k8Factory.getK8().configMaps = sinon.stub(); options.k8Factory.getK8().configMaps.read = sinon.stub(); options.k8Factory.getK8().pods = sinon.stub().returns({ waitForRunningPhase: sinon.stub(), waitForReadyStatus: sinon.stub(), }); options.k8Factory.getK8().leases = sinon.stub().returns({ read: sinon.stub(), }); options.k8Factory.getK8().manifests = sinon.stub().returns({ applyManifest: sinon.stub().resolves(), patchObject: sinon.stub().resolves(), }); options.k8Factory.getK8().logger = options.logger; options.k8Factory.default().clusters = sinon.stub().returns({ list: sinon.stub().returns([{ name: 'solo-e2e' }]), }); options.k8Factory.default().clusters().readCurrent = sinon.stub().returns('solo-e2e'); clusterChecksStub.isMinioInstalled = sinon.stub(); clusterChecksStub.isCertManagerInstalled = sinon.stub(); container.registerInstance(InjectTokens.ClusterChecks, clusterChecksStub); container.registerInstance(InjectTokens.K8Factory, options.k8Factory); options.depManager = sinon.stub(); container.registerInstance(InjectTokens.DependencyManager, options.depManager); options.localConfig = container.resolve(InjectTokens.LocalConfigRuntimeState); options.helm = container.resolve(InjectTokens.Helm); options.helm.dependency = sinon.stub(); ListrLock.newAcquireLockTask = sinon.stub().returns({ run: sinon.stub().returns({}), }); options.keyManager = container.resolve(InjectTokens.KeyManager); options.keyManager.prepareTlsKeyFilePaths = sinon.stub(); options.keyManager.copyGossipKeysToStaging = sinon.stub(); options.keyManager.copyNodeKeysToStaging = sinon.stub(); options.platformInstaller = platformInstallerStub; options.platformInstaller.copyNodeKeys = sinon.stub(); container.registerInstance(InjectTokens.PlatformInstaller, options.platformInstaller); options.profileManager = container.resolve(InjectTokens.ProfileManager); options.profileManager.prepareValuesForSoloChart = sinon.stub(); options.certificateManager = certificateManagerStub; container.registerInstance(InjectTokens.CertificateManager, options.certificateManager); options.chartManager = container.resolve(InjectTokens.ChartManager); options.chartManager.isChartInstalled = sinon.stub().returns(true); options.chartManager.isChartInstalled.onSecondCall().returns(false); options.chartManager.upgrade = sinon.stub().returns(true); options.chartManager.uninstall = sinon.stub().returns(true); options.remoteConfig = container.resolve(InjectTokens.RemoteConfigRuntimeState); options.remoteConfig.isLoaded = sinon.stub().returns(true); options.remoteConfig.getConfigMap = sinon.stub().returns(null); options.remoteConfig.persist = sinon.stub(); options.remoteConfig.loadAndValidate = sinon.stub(); options.remoteConfig.getNamespace = sinon.stub(); options.remoteConfig.configuration = { components: { changeNodePhase: sinon.stub(), getNewComponentId: sinon.stub(), addNewComponent: sinon.stub() }, versions: { consensusNode: '0.0.0' }, }; await options.localConfig.load(); options.localConfig.configuration.clusterRefs.set('solo-e2e', new StringFacade('context-1')); options.leaseManager = container.resolve(InjectTokens.LockManager); options.leaseManager.currentNamespace = sinon.stub().returns(testName); GenesisNetworkDataConstructor.initialize = sinon.stub().returns(null); }); afterEach(() => { sinon.restore(); }); it('Install function is called with expected parameters', async () => { try { const networkCommand = container.resolve(NetworkCommand); options.remoteConfig.getConsensusNodes = sinon.stub().returns([{ name: 'node1' }]); options.remoteConfig.getContexts = sinon.stub().returns(['context1']); const stubbedClusterReferences = new Map([['solo-e2e', 'context1']]); options.remoteConfig.getClusterRefs = sinon.stub().returns(stubbedClusterReferences); options.remoteConfig.updateComponentVersion = sinon.stub(); options.remoteConfig.configuration.state = {}; // @ts-expect-error - TS2341: to mock networkCommand.getBlockNodes = sinon.stub().returns([]); // @ts-expect-error - TS2341: to mock networkCommand.ensurePodLogsCrd = sinon.stub().returns(true); // @ts-expect-error - TS2341: to mock networkCommand.ensurePrometheusOperatorCrds = sinon.stub().returns(true); // @ts-expect-error - TS2341: to mock networkCommand.componentFactory = { createNewEnvoyProxyComponent: sinon.stub(), createNewHaProxyComponent: sinon.stub(), }; await networkCommand.deploy(argv.build()); expect(options.chartManager.upgrade.args[0][0].name).to.equal('solo-e2e'); expect(options.chartManager.upgrade.args[0][1]).to.equal(constants.SOLO_DEPLOYMENT_CHART); expect(options.chartManager.upgrade.args[0][2]).to.equal(constants.SOLO_DEPLOYMENT_CHART); expect(options.chartManager.upgrade.args[0][3]).to.equal(constants.SOLO_TESTING_CHART_URL); } finally { sinon.restore(); } }); it('Should use local chart directory', async () => { try { argv.setArg(flags.chartDirectory, 'test-directory'); argv.setArg(flags.force, true); const networkCommand = container.resolve(NetworkCommand); options.remoteConfig.getConsensusNodes = sinon.stub().returns([{ name: 'node1' }]); options.remoteConfig.getContexts = sinon.stub().returns(['context1']); options.remoteConfig.updateComponentVersion = sinon.stub(); const stubbedClusterReferences = new Map([['solo-e2e', 'context1']]); options.remoteConfig.getClusterRefs = sinon.stub().returns(stubbedClusterReferences); options.remoteConfig.configuration.state = {}; // @ts-expect-error - TS2341: to mock networkCommand.ensurePodLogsCrd = sinon.stub().returns(true); // @ts-expect-error - TS2341: to mock networkCommand.ensurePrometheusOperatorCrds = sinon.stub().returns(true); // @ts-expect-error - TS2341: to mock networkCommand.getBlockNodes = sinon.stub().returns([]); // @ts-expect-error - TS2341: to mock networkCommand.componentFactory = { createNewEnvoyProxyComponent: sinon.stub(), createNewHaProxyComponent: sinon.stub(), }; await networkCommand.deploy(argv.build()); expect(options.chartManager.upgrade.args[0][0].name).to.equal('solo-e2e'); expect(options.chartManager.upgrade.args[0][1]).to.equal(constants.SOLO_DEPLOYMENT_CHART); expect(options.chartManager.upgrade.args[0][2]).to.equal(constants.SOLO_DEPLOYMENT_CHART); expect(options.chartManager.upgrade.args[0][3]).to.equal(PathEx.join(ROOT_DIR, 'test-directory')); } finally { sinon.restore(); } }); it('Should use prepare config correctly for all clusters', async () => { try { const common = PathEx.join('test', 'data', 'test-values.yaml'); const values1 = PathEx.join('test', 'data', 'test-values1.yaml'); const values2 = PathEx.join('test', 'data', 'test-values2.yaml'); argv.setArg(flags.networkDeploymentValuesFile, `${common},cluster=${values1},cluster=${values2}`); argv.setArg(flags.chartDirectory, 'test-directory'); argv.setArg(flags.force, true); const task = sinon.stub(); options.remoteConfig.getConsensusNodes = sinon .stub() .returns([ new ConsensusNode('node1', 0, 'solo-e2e', 'cluster', 'context-1', 'base', 'pattern', 'fqdn', [], []), ]); options.remoteConfig.getContexts = sinon.stub().returns(['context-1']); const stubbedClusterReferences = new Map([['cluster', 'context1']]); options.remoteConfig.getClusterRefs = sinon.stub().returns(stubbedClusterReferences); const networkCommand = container.resolve(NetworkCommand); // @ts-expect-error - to mock networkCommand.getBlockNodes = sinon.stub().returns([]); networkCommand.configManager.update(argv.build()); // @ts-expect-error - to access private method const config = await networkCommand.prepareConfig(task, argv.build()); expect(config.valuesArgMap).to.not.empty; expect(config.valuesArgMap['cluster']).to.not.empty; expect(config.valuesArgMap['cluster'].indexOf(PathEx.join('solo-deployment', 'values.yaml'))).to.not.equal(-1); expect(config.valuesArgMap['cluster'].indexOf('values.yaml')).to.not.equal(-1); expect(config.valuesArgMap['cluster'].indexOf('test-values1.yaml')).to.not.equal(-1); expect(config.valuesArgMap['cluster'].indexOf('test-values2.yaml')).to.not.equal(-1); // chart values file should precede the values file passed in the command expect(config.valuesArgMap['cluster'].indexOf('solo-deployment/values.yaml')).to.be.lt(config.valuesArgMap['cluster'].indexOf('test-values1.yaml')); expect(config.valuesArgMap['cluster'].indexOf('solo-deployment/values.yaml')).to.be.lt(config.valuesArgMap['cluster'].indexOf('test-values2.yaml')); expect(config.valuesArgMap['cluster'].indexOf('values.yaml')).to.be.lt(config.valuesArgMap['cluster'].indexOf('test-values1.yaml')); expect(config.valuesArgMap['cluster'].indexOf('test-values1.yaml')).to.be.lt(config.valuesArgMap['cluster'].indexOf('test-values2.yaml')); expect(config.valuesArgMap['cluster'].indexOf('values.yaml')).to.be.lt(config.valuesArgMap['cluster'].indexOf('test-values2.yaml')); } finally { sinon.restore(); } }); }); }); //# sourceMappingURL=network.test.js.map