Files
mesh-lab/test/integration/signed-logins.test.ts
T

109 lines
5.3 KiB
TypeScript

/**
* Research 053, question 8: the bus on signed logins (NATS operator mode), over a real network.
*
* Three machines on one segment: `a` is today's style server (static users, an account with
* JetStream), `b` and `c` are a two-server cluster in operator mode with a full resolver, and `a` is
* joined to `b` as a leaf node, the system account bridged. The measurements run in
* test/bed-programs/signed-logins (one Go program; it embeds nats-server 2.11.17, the mesh's
* version, so nothing is downloaded into the machines). It runs on `a` and reaches `b` and `c`
* across the segment:
*
* part 2 revocation: time for b and c to refuse a revoked user and close its open connection
* part 4 moving a stream and a KV bucket from a to b over the leaf link, with a writer running
* part 3 c cut off from b by a packet filter on c while a user is revoked, then restored:
* how long c still accepts the user with the periodic sync alone, and with a re-push
* triggered by the first system-account event that shows c is back
*
* The test asserts only that the measurements ran to the end; the numbers are the result and are
* printed. It proves nothing about the mesh's own software, which it does not use.
*/
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { loadScenario } from "../../src/declaration/parse.ts";
import { raise } from "../../src/lifecycle/raise.ts";
import { destroy, exec, push } from "../../src/lifecycle/operate.ts";
import { labIsUsable } from "./harness.ts";
const capability = await labIsUsable();
const skip = capability.usable ? false : `lab not usable: ${capability.why}`;
const ADDR = { a: "192.0.2.11", b: "192.0.2.12", c: "192.0.2.13" };
let instanceId = "";
before(async () => {
if (skip) return;
instanceId = (await raise(loadScenario("scenarios/signed-logins.yml"), {})).instanceId;
});
after(async () => {
if (instanceId && !process.env["MESH_LAB_KEEP"]) await destroy(instanceId);
});
const sh = (machine: string, script: string, timeout = 120_000) =>
exec(instanceId, machine, ["sh", "-c", script], timeout);
test("research 053 q8: signed-login bus over a real network", { skip, timeout: 3_000_000 }, async () => {
const work = mkdtempSync(join(tmpdir(), "q8lab-"));
const bin = join(work, "q8lab");
execFileSync("go", ["build", "-o", bin, "."], {
cwd: "test/bed-programs/signed-logins",
env: { ...process.env, CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64", GOFLAGS: "-mod=readonly" },
stdio: "inherit",
});
const conf = join(work, "conf");
mkdirSync(conf);
execFileSync(bin, ["gen", "-out", conf, "-a", ADDR.a, "-b", ADDR.b, "-c", ADDR.c], { stdio: "inherit" });
for (const m of ["a", "b", "c"]) {
await sh(m, "mkdir -p /root/q8 /var/lib/q8");
await push(instanceId, m, bin, "/root/q8/q8lab", "0755");
}
const files: Record<string, string[]> = {
a: ["A.conf", "leaf-mesh.creds", "leaf-sys.creds", "keys.json"],
b: ["B.conf", "op.jwt"],
c: ["C.conf", "op.jwt"],
};
for (const [m, list] of Object.entries(files)) {
for (const f of list) await push(instanceId, m, join(conf, f), `/root/q8/${f}`, "0600");
}
const start = (m: string, cmd: string, out: string) =>
sh(m, `setsid nohup ${cmd} >/root/q8/${out} 2>&1 </dev/null & sleep 1; echo started`);
await start("b", "/root/q8/q8lab server -conf /root/q8/B.conf", "B.out");
await start("c", "/root/q8/q8lab server -conf /root/q8/C.conf", "C.out");
for (const m of ["b", "c"]) {
const r = await sh(m, "/root/q8/q8lab wait -url http://127.0.0.1:8222/healthz -s 90");
assert.match(r.stdout, /up/, `${m} server did not come up`);
}
await start("a", "/root/q8/q8lab server -conf /root/q8/A.conf", "A.out");
assert.match((await sh("a", "/root/q8/q8lab wait -url http://127.0.0.1:8222/healthz -s 60")).stdout, /up/);
await start("c", `/root/q8/q8lab agent -peer ${ADDR.b}`, "agent.out");
console.log("servers up:", (await sh("a", "cat /root/q8/A.out")).stdout.trim());
await sh("a", "setsid nohup sh -c '/root/q8/q8lab drive -keys /root/q8/keys.json > /root/q8/drive.log 2>&1; echo finished > /root/q8/drive.done' >/dev/null 2>&1 </dev/null & sleep 1; echo started");
let offset = 0;
let finished = false;
let all = "";
for (let i = 0; i < 400 && !finished; i++) {
await new Promise((r) => setTimeout(r, 5000));
const chunk = (await sh("a", `tail -c +${offset + 1} /root/q8/drive.log`)).stdout;
offset += Buffer.byteLength(chunk);
finished = (await sh("a", "test -f /root/q8/drive.done && echo yes; true")).stdout.includes("yes");
if (chunk.trim()) { console.log(chunk.trimEnd()); all += chunk; }
}
for (const m of ["a", "b", "c"]) {
const up = m.toUpperCase();
const r = await sh(m, `grep -c '\\[ERR\\]' /var/log/q8-${up}.log; grep '\\[ERR\\]' /var/log/q8-${up}.log | cut -c1-220 | sort | uniq -c | sort -rn | head -8; true`);
console.log(`--- server ${up} log, error lines:\n${r.stdout.trim()}`);
}
writeFileSync(join(work, "drive.log"), all);
assert.ok(finished, "the measurements did not finish");
assert.match(all, /DRIVE-DONE/);
assert.doesNotMatch(all, /panic:/);
});