@zombienet/orchestrator
Version:
ZombieNet aim to be a testing framework for substrate based blockchains, providing a simple cli tool that allow users to spawn and test ephemeral Substrate based networks
367 lines (366 loc) • 16 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.genCumulusCollatorCmd = genCumulusCollatorCmd;
exports.genCmd = genCmd;
const utils_1 = require("@zombienet/utils");
const constants_1 = require("./constants");
const sharedTypes_1 = require("./sharedTypes");
const debug = require("debug")("zombie::cmdGenerator");
/**
* Parses exclusion flags from args array and returns both exclusions and filtered args
* Exclusion syntax: -:<flag> (e.g., -:--insecure-validator-i-know-what-i-do or -:insecure-validator-i-know-what-i-do)
* @param args - Array of command line arguments
* @returns Object containing exclusions set and filtered args array
*/
function parseExclusionFlags(args) {
const exclusions = new Set();
const filteredArgs = [];
for (const arg of args) {
if (arg.startsWith("-:")) {
// Extract the flag to exclude (remove -: prefix)
let flagToExclude = arg.substring(2);
// Normalize flag format - ensure it starts with --
if (!flagToExclude.startsWith("--")) {
flagToExclude = `--${flagToExclude}`;
}
exclusions.add(flagToExclude);
}
else {
filteredArgs.push(arg);
}
}
return { exclusions, filteredArgs };
}
/**
* Filters out excluded flags from a command arguments array
* @param cmdArgs - Array of command arguments to filter
* @param exclusions - Set of flags to exclude
* @returns Filtered array with excluded flags removed
*/
function filterExcludedFlags(cmdArgs, exclusions) {
if (exclusions.size === 0)
return cmdArgs;
const filtered = [];
let skipNext = false;
for (let i = 0; i < cmdArgs.length; i++) {
if (skipNext) {
skipNext = false;
continue;
}
const arg = cmdArgs[i];
let shouldExclude = false;
for (const excludedFlag of exclusions) {
if (arg === excludedFlag || arg.startsWith(`${excludedFlag}=`)) {
shouldExclude = true;
// If the flag doesn't contain '=' and has a separate value, skip the next argument too
if (!arg.includes("=") &&
i + 1 < cmdArgs.length &&
!cmdArgs[i + 1].startsWith("-")) {
skipNext = true;
}
break;
}
}
if (!shouldExclude) {
filtered.push(arg);
}
}
return filtered;
}
function parseCmdWithArguments(commandWithArgs, useWrapper = true) {
const parts = commandWithArgs.split(" ");
let finalCommand = [];
if (["bash", "ash"].includes(parts[0])) {
finalCommand.push(parts[0]);
let partIndex;
if (parts[1] === "-c") {
finalCommand.push(parts[1]);
partIndex = 2;
}
else {
finalCommand.push("-c");
partIndex = 1;
}
finalCommand = [...finalCommand, ...[parts.slice(partIndex).join(" ")]];
}
else {
finalCommand = [commandWithArgs];
if (useWrapper)
finalCommand.unshift("/cfg/zombie-wrapper.sh");
}
return finalCommand;
}
function genCumulusCollatorCmd(nodeSetup_1) {
return __awaiter(this, arguments, void 0, function* (nodeSetup, cfgPath = "/cfg", dataPath = "/data", relayDataPath = "/relay-data", useWrapper = true) {
const { name, chain, parachainId, key, validator, commandWithArgs } = nodeSetup;
// command with args
if (commandWithArgs) {
return parseCmdWithArguments(commandWithArgs, useWrapper);
}
const parachainAddedArgs = {
"--name": true,
"--collator": true,
"--base-path": true,
"--port": true,
"--ws-port": true,
"--rpc-port": true,
"--chain": true,
"--prometheus-port": true,
};
// bind localhost only in native provider
const ip_to_bind = useWrapper ? "0.0.0.0" : "127.0.0.1";
let fullCmd = [
nodeSetup.command || constants_1.DEFAULT_COMMAND,
"--name",
name,
"--node-key",
key,
"--chain",
`${cfgPath}/${chain}-${parachainId}.json`,
"--base-path",
dataPath,
"--listen-addr",
`/ip4/${ip_to_bind}/tcp/${nodeSetup.p2pPort ? nodeSetup.p2pPort : constants_1.P2P_PORT}/ws`,
"--prometheus-external",
"--rpc-cors all",
// Do not add `--unsafe-rpc-external` in native provider
useWrapper ? "--unsafe-rpc-external" : "",
"--rpc-methods unsafe",
];
if (nodeSetup.substrateCliArgsVersion === sharedTypes_1.SubstrateCliArgsVersion.V0)
fullCmd.push("--unsafe-ws-external");
const portFlags = getPortFlagsByCliArgsVersion(nodeSetup);
for (const [k, v] of Object.entries(portFlags)) {
fullCmd.push(...[k, v.toString()]);
}
const chainParts = chain.split("_");
const relayChain = chainParts.length > 1 ? chainParts[chainParts.length - 1] : chainParts[0];
if (validator)
fullCmd.push(...["--collator"]);
// ports passed to the relaychain part of the collator binary
const collatorPorts = {
"--port": 0,
"--rpc-port": 0,
"--prometheus-port": 0,
};
if (nodeSetup.args.length > 0) {
let argsFullNode = null;
let argsParachain = null;
const splitIndex = nodeSetup.args.indexOf("--");
if (splitIndex < 0) {
argsParachain = nodeSetup.args;
}
else {
argsParachain = nodeSetup.args.slice(0, splitIndex);
argsFullNode = nodeSetup.args.slice(splitIndex + 1);
}
// Parse exclusion flags from parachain args
const { exclusions: parachainExclusions, filteredArgs: filteredParachainArgs, } = parseExclusionFlags(argsParachain || []);
argsParachain = filteredParachainArgs;
// Parse exclusion flags from full node args if they exist
let fullNodeExclusions = new Set();
if (argsFullNode) {
const { exclusions, filteredArgs } = parseExclusionFlags(argsFullNode);
fullNodeExclusions = exclusions;
argsFullNode = filteredArgs;
}
if (argsParachain) {
for (const arg of argsParachain) {
if (parachainAddedArgs[arg])
continue;
fullCmd.push(arg);
}
}
// Apply parachain exclusions to the current fullCmd
fullCmd = filterExcludedFlags(fullCmd, parachainExclusions);
// Arguments for the relay chain node part of the collator binary.
fullCmd.push(...[
"--",
"--base-path",
relayDataPath,
"--chain",
`${cfgPath}/${relayChain}.json`,
"--execution wasm",
]);
if (argsFullNode) {
// Add any additional flags to the CLI
for (const [index, arg] of argsFullNode.entries()) {
if (collatorPorts[arg] >= 0) {
// port passed as argument, we need to ensure is not a default one because it will be
// use by the parachain part.
const selectedPort = parseInt(argsFullNode[index + 1], 10);
if ([
constants_1.P2P_PORT,
constants_1.RPC_HTTP_PORT,
constants_1.RPC_WS_PORT,
nodeSetup.p2pPort,
nodeSetup.rpcPort,
nodeSetup.wsPort,
].includes(selectedPort)) {
console.log(utils_1.decorators.yellow(`WARN: default port configured, changing to use a random free port`));
const randomPort = yield (0, utils_1.getRandomPort)();
collatorPorts[arg] = randomPort;
argsFullNode[index + 1] = randomPort.toString();
}
}
}
// check ports
for (const portArg of Object.keys(collatorPorts)) {
if (collatorPorts[portArg] === 0) {
const randomPort = yield (0, utils_1.getRandomPort)();
argsFullNode.push(portArg);
argsFullNode.push(randomPort.toString());
debug(`Added ${portArg} with value ${randomPort}`);
}
}
fullCmd = fullCmd.concat(argsFullNode);
debug(`Added ${argsFullNode} to collator`);
fullCmd = filterExcludedFlags(fullCmd, fullNodeExclusions);
}
else {
// ensure ports
for (const portArg of Object.keys(collatorPorts)) {
if (collatorPorts[portArg] === 0) {
const randomPort = yield (0, utils_1.getRandomPort)();
fullCmd.push(portArg);
fullCmd.push(randomPort.toString());
debug(`Added ${portArg} with value ${randomPort}`);
}
}
}
}
else {
// no args
// Arguments for the relay chain node part of the collator binary.
fullCmd.push(...["--", "--chain", `${cfgPath}/${relayChain}.json`, "--execution wasm"]);
// ensure ports
for (const portArg of Object.keys(collatorPorts)) {
if (collatorPorts[portArg] === 0) {
const randomPort = yield (0, utils_1.getRandomPort)();
fullCmd.push(portArg);
fullCmd.push(randomPort.toString());
debug(`Added ${portArg} with value ${randomPort}`);
}
}
}
const resolvedCmd = [fullCmd.join(" ")];
if (useWrapper)
resolvedCmd.unshift("/cfg/zombie-wrapper.sh");
return resolvedCmd;
});
}
function genCmd(nodeSetup_1) {
return __awaiter(this, arguments, void 0, function* (nodeSetup, cfgPath = "/cfg", dataPath = "/data", useWrapper = true) {
const { name, key, chain, commandWithArgs, fullCommand, telemetry, telemetryUrl, prometheus, validator, bootnodes, zombieRole, jaegerUrl, parachainId, } = nodeSetup;
let { command, args } = nodeSetup;
// fullCommand is NOT decorated by the `zombie` wrapper
// and is used internally in init containers.
if (fullCommand)
return ["bash", "-c", fullCommand];
// command with args
if (commandWithArgs) {
return parseCmdWithArguments(commandWithArgs, useWrapper);
}
if (!command)
command = constants_1.DEFAULT_COMMAND;
const { exclusions, filteredArgs } = parseExclusionFlags(args);
args = [...filteredArgs];
args.push("--no-mdns");
if (key)
args.push(...["--node-key", key]);
if (!telemetry)
args.push("--no-telemetry");
else
args.push(...["--telemetry-url", telemetryUrl]);
if (prometheus && !args.includes("--prometheus-external"))
args.push("--prometheus-external");
if (jaegerUrl && zombieRole === sharedTypes_1.ZombieRole.Node)
args.push(...["--jaeger-agent", jaegerUrl]);
if (validator) {
if (!args.includes("--validator"))
args.push("--validator");
if (nodeSetup.substrateCliArgsVersion &&
nodeSetup.substrateCliArgsVersion >= sharedTypes_1.SubstrateCliArgsVersion.V2) {
args.push("--insecure-validator-i-know-what-i-do");
}
}
if (zombieRole === sharedTypes_1.ZombieRole.Collator && parachainId) {
const parachainIdArgIndex = args.findIndex((arg) => arg.includes("--parachain-id"));
if (parachainIdArgIndex >= 0)
args.splice(parachainIdArgIndex, 1);
args.push(`--parachain-id ${parachainId}`);
}
if (bootnodes && bootnodes.length)
args.push("--bootnodes", bootnodes.join(" "));
const portFlags = getPortFlagsByCliArgsVersion(nodeSetup);
for (const [k, v] of Object.entries(portFlags)) {
args.push(...[k, v.toString()]);
}
const listenIndex = args.findIndex((arg) => arg === "--listen-addr");
if (listenIndex >= 0) {
const listenAddrParts = args[listenIndex + 1].split("/");
listenAddrParts[4] = `${nodeSetup.p2pPort}`;
const listenAddr = listenAddrParts.join("/");
args[listenIndex + 1] = listenAddr;
}
else {
// no --listen-add args
// bind localhost only in native provider
const ip_to_bind = useWrapper ? "0.0.0.0" : "127.0.0.1";
args.push(...["--listen-addr", `/ip4/${ip_to_bind}/tcp/${nodeSetup.p2pPort}/ws`]);
}
// set our base path - only if user hasn't provided one
const hasUserBasePath = args.some((arg) => arg === "--base-path" || arg.startsWith("--base-path="));
if (!hasUserBasePath) {
args.push(...["--base-path", dataPath]);
}
if (nodeSetup.substrateCliArgsVersion === sharedTypes_1.SubstrateCliArgsVersion.V0)
args.push("--unsafe-ws-external");
const finalArgs = [
command,
"--chain",
`${cfgPath}/${chain}.json`,
"--name",
name,
"--rpc-cors",
"all",
// Do not add `--unsafe-rpc-external` in native provider
useWrapper ? "--unsafe-rpc-external" : "",
"--rpc-methods",
"unsafe",
...args,
];
const filteredFinalArgs = filterExcludedFlags(finalArgs, exclusions);
const resolvedCmd = [filteredFinalArgs.join(" ")];
if (useWrapper)
resolvedCmd.unshift("/cfg/zombie-wrapper.sh");
return resolvedCmd;
});
}
const getPortFlagsByCliArgsVersion = (nodeSetup) => {
// port flags logic
const portFlags = {
"--prometheus-port": (nodeSetup.prometheusPort || constants_1.PROMETHEUS_PORT).toString(),
};
if (nodeSetup.substrateCliArgsVersion === sharedTypes_1.SubstrateCliArgsVersion.V0) {
portFlags["--rpc-port"] = (nodeSetup.rpcPort || constants_1.RPC_HTTP_PORT).toString();
portFlags["--ws-port"] = (nodeSetup.wsPort || constants_1.RPC_WS_PORT).toString();
}
else {
// use rpc_port as default (since ws_port was deprecated in https://github.com/paritytech/substrate/pull/13384)
const portToUse = nodeSetup.rpcPort
? nodeSetup.rpcPort
: nodeSetup.wsPort || constants_1.RPC_HTTP_PORT;
portFlags["--rpc-port"] = portToUse.toString();
}
return portFlags;
};