Reach one scenario from the workstation, by name
A scenario is a closed address space: two raised from the same declaration hold the same addresses and never meet, which is what lets two run at once and why the lab talks to machines through the hypervisor rather than over IP. Reaching in from outside breaks that, so it is opt-in, one scenario at a time, and reversible. `connect` takes an address on the scenario's public link and writes a resolver rule answering everything under each machine's name. `disconnect` gives both back. `connected` says what is true right now, for somebody who cannot remember. It refuses rather than guessing when more than one scenario is standing — the failure being avoided is not an error but one scenario's traffic arriving in another. It also refuses when a machine's name is already answered here for something real, because connecting would point that name at the lab, and the damage would land on the real thing. Names answer with the segment address rather than the overlay one. Inside the mesh a name gives a machine's private address; from here that would need this workstation on the overlay, which is a much larger door. The segment address reaches the same machine and the same ports, which is what opening a board in a browser actually needs. Proven against a live two-node scenario: registry.internal:5000/v2/ answered 200 from this workstation, and so did a wildcard name under the same machine. Disconnect put the address back, stopped answering, and left the real mesh's own names alone. One thing measured rather than assumed: it restarts dnsmasq instead of reloading it. A reload is SIGHUP, which re-reads the hosts file and clears the cache but not the configuration — the rule was written, the reload reported success, and nothing resolved. The daemon's start time was nine days old afterwards.
This commit is contained in:
+40
@@ -35,6 +35,14 @@ const USAGE = `mesh-lab — raise a disposable mesh on one machine
|
||||
suite [paths...] [--no-build] rebuild the artifacts, run the end-to-end tests, leave a receipt
|
||||
last-run whether the last run still counts; non-zero when it does not
|
||||
|
||||
connect [instance] reach the standing scenario from this workstation, by name
|
||||
disconnect give the address back and stop answering those names
|
||||
connected what is reachable right now
|
||||
|
||||
A scenario is a closed address space, so only one can be reachable at a time: connect refuses
|
||||
rather than guessing which you meant. It needs root for an address and a resolver rule, and
|
||||
disconnect puts both back.
|
||||
|
||||
Set MESH_LAB_INCUS if the daemon needs a different invocation, e.g. "sudo -n incus".
|
||||
`;
|
||||
|
||||
@@ -182,6 +190,38 @@ async function main(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
case "connect": {
|
||||
const { connect } = await import("./lifecycle/connect.ts");
|
||||
const reached = await connect(rest[0]);
|
||||
console.log(`connected to ${reached.instanceId} as ${reached.address} on ${reached.bridge}\n`);
|
||||
console.log("these answer here now:");
|
||||
for (const [machine, address] of Object.entries(reached.machines).sort()) {
|
||||
console.log(` anything.${machine}.internal → ${address}`);
|
||||
}
|
||||
console.log(`\ntry: curl -sI http://${Object.keys(reached.machines)[0]}.internal`);
|
||||
console.log("run `mesh-lab disconnect` when finished — these names are only true while");
|
||||
console.log("that scenario is standing.");
|
||||
return;
|
||||
}
|
||||
|
||||
case "disconnect": {
|
||||
const { disconnect } = await import("./lifecycle/connect.ts");
|
||||
for (const line of await disconnect()) console.log(line);
|
||||
return;
|
||||
}
|
||||
|
||||
case "connected": {
|
||||
const { connection } = await import("./lifecycle/connect.ts");
|
||||
const now = await connection();
|
||||
if (now.length === 0) {
|
||||
console.log("nothing is connected");
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
for (const line of now) console.log(` ${line}`);
|
||||
return;
|
||||
}
|
||||
|
||||
case "raise": {
|
||||
const path = rest[0] ?? fail("raise needs a scenario file");
|
||||
const scenario = loadScenario(path);
|
||||
|
||||
Reference in New Issue
Block a user