/** * 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 = { 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 /root/q8/drive.log 2>&1; echo finished > /root/q8/drive.done' >/dev/null 2>&1 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:/); });