Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env node
|
||||
// Safe runner for the k6 load-test suite.
|
||||
//
|
||||
// SKIPs cleanly (exit 0) when the k6 binary is missing or the target URL is
|
||||
// unreachable, so this script is safe to wire into CI lanes that may run on
|
||||
// machines without k6 installed. Only fails (non-zero exit) when k6 itself
|
||||
// reports a threshold or runtime failure.
|
||||
|
||||
import path from "node:path";
|
||||
import url from "node:url";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const DEFAULT_TARGET_URL = "http://localhost:3000";
|
||||
const DEFAULT_SCENARIO_PATH = "tests/load/scenarios.js";
|
||||
const REACHABILITY_TIMEOUT_SECONDS = 3;
|
||||
|
||||
/**
|
||||
* Test whether the k6 binary is available on PATH.
|
||||
* Returns the trimmed version string on success, null on failure.
|
||||
*/
|
||||
export function detectK6Binary({ spawn = spawnSync } = {}) {
|
||||
const result = spawn("k6", ["version"], { encoding: "utf8" });
|
||||
if (result.error || result.status !== 0) {
|
||||
return null;
|
||||
}
|
||||
return (result.stdout || "").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the configured target URL responds to an HTTP request.
|
||||
* Uses curl with a short timeout so the runner does not hang when the
|
||||
* target environment is offline. Returns true on reachable, false otherwise.
|
||||
*/
|
||||
export function isTargetReachable(targetUrl, { spawn = spawnSync, timeoutSeconds = REACHABILITY_TIMEOUT_SECONDS } = {}) {
|
||||
const result = spawn(
|
||||
"curl",
|
||||
[
|
||||
"-fsS",
|
||||
"--max-time",
|
||||
String(timeoutSeconds),
|
||||
"-o",
|
||||
"/dev/null",
|
||||
"-w",
|
||||
"%{http_code}",
|
||||
targetUrl,
|
||||
],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
if (result.error) {
|
||||
return false;
|
||||
}
|
||||
// curl -fsS exits non-zero on HTTP >= 400, but for reachability we accept
|
||||
// any successful TCP+HTTP roundtrip. Re-check by looking at the captured
|
||||
// status code in stdout when curl exited non-zero.
|
||||
if (result.status === 0) {
|
||||
return true;
|
||||
}
|
||||
const code = (result.stdout || "").trim();
|
||||
return /^[1-5]\d\d$/.test(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse argv into a configuration object.
|
||||
* --target <url> override K6_TARGET_URL
|
||||
* --scenario <path> override the default composite scenario script
|
||||
* --skip-reachability skip the curl probe (useful when the target is
|
||||
* only reachable from within a Docker network)
|
||||
*/
|
||||
export function parseArgs(argv) {
|
||||
const config = {
|
||||
targetUrl: null,
|
||||
scenarioPath: null,
|
||||
skipReachability: false,
|
||||
};
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--target" || arg === "--target-url") {
|
||||
config.targetUrl = argv[index + 1] || null;
|
||||
index += 1;
|
||||
} else if (arg === "--scenario") {
|
||||
config.scenarioPath = argv[index + 1] || null;
|
||||
index += 1;
|
||||
} else if (arg === "--skip-reachability") {
|
||||
config.skipReachability = true;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the final runtime configuration from argv + env, applying defaults.
|
||||
*/
|
||||
export function resolveConfig({ argv = [], env = process.env } = {}) {
|
||||
const parsed = parseArgs(argv);
|
||||
const targetUrl = parsed.targetUrl || env.K6_TARGET_URL || DEFAULT_TARGET_URL;
|
||||
const scenarioPath = parsed.scenarioPath || DEFAULT_SCENARIO_PATH;
|
||||
return {
|
||||
targetUrl,
|
||||
scenarioPath,
|
||||
skipReachability: parsed.skipReachability,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the load-test suite. Returns an object describing the outcome so the
|
||||
* CLI wrapper can map it to an exit code.
|
||||
*/
|
||||
export function runLoadTests({
|
||||
argv = [],
|
||||
env = process.env,
|
||||
spawn = spawnSync,
|
||||
stdout = (line) => process.stdout.write(`${line}\n`),
|
||||
stderr = (line) => process.stderr.write(`${line}\n`),
|
||||
} = {}) {
|
||||
const config = resolveConfig({ argv, env });
|
||||
|
||||
const version = detectK6Binary({ spawn });
|
||||
if (!version) {
|
||||
stdout(
|
||||
"SKIP k6 binary not installed; download from https://k6.io/docs/getting-started/installation/",
|
||||
);
|
||||
return { status: "skip", reason: "binary-missing", exitCode: 0 };
|
||||
}
|
||||
|
||||
if (!config.skipReachability && !isTargetReachable(config.targetUrl, { spawn })) {
|
||||
stdout(`SKIP target ${config.targetUrl} not reachable`);
|
||||
return { status: "skip", reason: "target-unreachable", exitCode: 0 };
|
||||
}
|
||||
|
||||
stdout(`PASS k6 detected (${version}); running ${config.scenarioPath} against ${config.targetUrl}`);
|
||||
const result = spawn("k6", ["run", config.scenarioPath], {
|
||||
stdio: "inherit",
|
||||
env: { ...env, K6_TARGET_URL: config.targetUrl },
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
stderr(`FAIL k6 run failed to start: ${result.error.message}`);
|
||||
return { status: "fail", reason: "spawn-error", exitCode: 1 };
|
||||
}
|
||||
if (typeof result.status === "number" && result.status !== 0) {
|
||||
stderr(`FAIL k6 run exited with status ${result.status}`);
|
||||
return { status: "fail", reason: "non-zero-exit", exitCode: result.status };
|
||||
}
|
||||
return { status: "pass", reason: "ok", exitCode: 0 };
|
||||
}
|
||||
|
||||
const isCli = process.argv[1] && url.fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
|
||||
|
||||
if (isCli) {
|
||||
const outcome = runLoadTests({ argv: process.argv.slice(2) });
|
||||
process.exit(outcome.exitCode);
|
||||
}
|
||||
Reference in new issue
Block a user