@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
148 lines • 9.1 kB
JavaScript
// SPDX-License-Identifier: Apache-2.0
import { main } from '../../../../src/index.js';
import { Flags } from '../../../../src/commands/flags.js';
import { Duration } from '../../../../src/core/time/duration.js';
import { BaseCommandTest } from './base-command-test.js';
import { BlockCommandDefinition } from '../../../../src/commands/command-definitions/block-command-definition.js';
import * as constants from '../../../../src/core/constants.js';
import { expect } from 'chai';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
import { HEDERA_HAPI_PATH } from '../../../../src/core/constants.js';
import { K8Helper } from '../../../../src/business/utils/k8-helper.js';
import { sleep } from '../../../../src/core/helpers.js';
import { OperatingSystem } from '../../../../src/business/utils/operating-system.js';
export class BlockNodeTest extends BaseCommandTest {
static soloBlockNodeDeployArgv(testName, deployment, clusterReference, enableLocalBuildPathTesting, localBuildReleaseTag, nodeAliases) {
const { newArgv, argvPushGlobalFlags, optionFromFlag } = BlockNodeTest;
const argv = newArgv();
argv.push(BlockCommandDefinition.COMMAND_NAME, BlockCommandDefinition.NODE_SUBCOMMAND_NAME, BlockCommandDefinition.NODE_ADD, optionFromFlag(Flags.deployment), deployment, optionFromFlag(Flags.clusterRef), clusterReference);
if (enableLocalBuildPathTesting) {
argv.push(optionFromFlag(Flags.releaseTag), localBuildReleaseTag);
}
if (nodeAliases !== undefined && nodeAliases.length > 0) {
const stringBuilder = [];
for (const nodeAlias of nodeAliases) {
stringBuilder.push(`${nodeAlias}=1`);
}
argv.push(optionFromFlag(Flags.priorityMapping), stringBuilder.join(','));
}
argvPushGlobalFlags(argv, testName, false, true);
return argv;
}
static soloBlockNodeAddExternalArgv(testName, deployment, clusterReference, address, nodeAliases) {
const { newArgv, argvPushGlobalFlags, optionFromFlag } = BlockNodeTest;
const argv = newArgv();
argv.push(BlockCommandDefinition.COMMAND_NAME, BlockCommandDefinition.NODE_SUBCOMMAND_NAME, BlockCommandDefinition.NODE_ADD_EXTERNAL, optionFromFlag(Flags.deployment), deployment, optionFromFlag(Flags.clusterRef), clusterReference, optionFromFlag(Flags.externalBlockNodeAddress), address);
if (nodeAliases !== undefined && nodeAliases.length > 0) {
const stringBuilder = [];
for (const nodeAlias of nodeAliases) {
stringBuilder.push(`${nodeAlias}=1`);
}
argv.push(optionFromFlag(Flags.priorityMapping), stringBuilder.join(','));
}
argvPushGlobalFlags(argv, testName, false, true);
return argv;
}
static soloBlockNodeDestroyArgv(testName, deployment, clusterReference) {
const { newArgv, argvPushGlobalFlags, optionFromFlag } = BlockNodeTest;
const argv = newArgv();
argv.push(BlockCommandDefinition.COMMAND_NAME, BlockCommandDefinition.NODE_SUBCOMMAND_NAME, BlockCommandDefinition.NODE_DESTROY, optionFromFlag(Flags.deployment), deployment, optionFromFlag(Flags.clusterRef), clusterReference, optionFromFlag(Flags.force), optionFromFlag(Flags.quiet), optionFromFlag(Flags.devMode));
argvPushGlobalFlags(argv, testName, false, true);
return argv;
}
static soloBlockNodeDeleteExternalArgv(testName, deployment, clusterReference, id) {
const { newArgv, argvPushGlobalFlags, optionFromFlag } = BlockNodeTest;
const argv = newArgv();
argv.push(BlockCommandDefinition.COMMAND_NAME, BlockCommandDefinition.NODE_SUBCOMMAND_NAME, BlockCommandDefinition.NODE_DELETE_EXTERNAL, optionFromFlag(Flags.deployment), deployment, optionFromFlag(Flags.clusterRef), clusterReference, optionFromFlag(Flags.force), optionFromFlag(Flags.quiet), optionFromFlag(Flags.devMode));
if (id !== undefined) {
argv.push(optionFromFlag(Flags.id), id.toString());
}
argvPushGlobalFlags(argv, testName);
return argv;
}
static add(options, nodeAliases) {
const { testName, deployment, clusterReferenceNameArray, localBuildReleaseTag, enableLocalBuildPathTesting } = options;
const { soloBlockNodeDeployArgv } = BlockNodeTest;
it(`${testName}: block node add`, async () => {
await main(soloBlockNodeDeployArgv(testName, deployment, clusterReferenceNameArray[0], enableLocalBuildPathTesting, localBuildReleaseTag, nodeAliases));
// Block node add can exceed 5 minutes on CI when image/chart pulls are slow.
}).timeout(Duration.ofMinutes(10).toMillis());
}
static addExternal(options, address, nodeAliases) {
const { testName, deployment, clusterReferenceNameArray } = options;
const { soloBlockNodeAddExternalArgv } = BlockNodeTest;
it(`${testName}: block node add-external`, async () => {
await main(soloBlockNodeAddExternalArgv(testName, deployment, clusterReferenceNameArray[0], address, nodeAliases));
}).timeout(Duration.ofMinutes(5).toMillis());
}
static deleteExternal(options, id) {
const { testName, deployment, clusterReferenceNameArray } = options;
const { soloBlockNodeDeleteExternalArgv } = BlockNodeTest;
it(`${testName}: block node delete-external`, async () => {
await main(soloBlockNodeDeleteExternalArgv(testName, deployment, clusterReferenceNameArray[1], id));
}).timeout(Duration.ofMinutes(5).toMillis());
}
static destroy(options) {
const { testName, deployment, clusterReferenceNameArray } = options;
const { soloBlockNodeDestroyArgv } = BlockNodeTest;
it(`${testName}: block node destroy`, async () => {
await main(soloBlockNodeDestroyArgv(testName, deployment, clusterReferenceNameArray[1]));
}).timeout(Duration.ofMinutes(5).toMillis());
}
static testBlockNode(options, blockNodeId = 1) {
const { namespace, contexts, testName } = options;
const execAsync = promisify(exec);
it(`${testName}: test block node connection for block node ${blockNodeId}`, async () => {
const pod = await new K8Helper(contexts[0]).getBlockNodePod(namespace, blockNodeId);
const srv = await pod.portForward(constants.BLOCK_NODE_PORT, constants.BLOCK_NODE_PORT);
// Sleep to allow the port-forward to be established before attempting to connect
await sleep(Duration.ofSeconds(5));
const commandOptions = {
cwd: './test/data',
maxBuffer: 50 * 1024 * 1024,
encoding: 'utf8',
};
// Make script executable (no-op on Windows; chmod is not available)
if (!OperatingSystem.isWin32()) {
await execAsync('chmod +x ./get-block.sh', commandOptions);
}
// Execute script (use bash explicitly on Windows since .sh files have no default handler)
const scriptCommand = OperatingSystem.isWin32() ? 'bash ./get-block.sh 1' : './get-block.sh 1';
const scriptStd = await execAsync(scriptCommand, commandOptions);
expect(scriptStd.stderr).to.equal('');
expect(scriptStd.stdout).to.include('"status": "SUCCESS"');
await pod.stopPortForward(srv);
}).timeout(Duration.ofMinutes(2).toMillis());
}
static verifyBlockNodesJson(options, nodeAlias, blockNodeIds, excludedBlockNodeIds = [], { expectedExternalAddress, expectedExternalPort, unexpectedExternalAddress, unexpectedExternalPort, }) {
const { namespace, contexts, testName } = options;
it(`${testName}: verify block-nodes.json for ${nodeAlias}`, async () => {
const root = await new K8Helper(contexts[0]).getConsensusNodeRootContainer(namespace, nodeAlias);
const output = await root.execContainer([
'bash',
'-c',
`cat ${HEDERA_HAPI_PATH}/data/config/block-nodes.json`,
]);
for (const blockNodeId of blockNodeIds) {
expect(output).to.include(`block-node-${blockNodeId}`);
}
for (const excludedBlockNodeId of excludedBlockNodeIds) {
expect(output).to.not.include(`block-node-${excludedBlockNodeId}`);
}
if (expectedExternalAddress !== undefined) {
expect(output).to.include(expectedExternalAddress);
}
if (expectedExternalPort !== undefined) {
expect(output).to.include(expectedExternalPort.toString());
}
if (unexpectedExternalAddress !== undefined) {
expect(output).not.to.include(unexpectedExternalAddress);
}
if (unexpectedExternalPort !== undefined) {
expect(output).not.to.include(unexpectedExternalPort.toString());
}
});
}
}
//# sourceMappingURL=block-node-test.js.map