From 56de22767322d1458bf3b4f5feb2568a04287b08 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 31 Aug 2026 03:22:24 +0200 Subject: [PATCH] Connect by address, and ask grep whether rather than how many MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A container does not inherit its host's /etc/hosts, so a name the mesh wrote there resolves for the machine and not for anything it runs. It fails as "could not translate host name", which reads like a mesh that never wrote the name — so the address is resolved on the machine and the container is given that. And `grep -c` prints 0 and exits non-zero when it finds nothing, so the obvious `|| echo 0` prints a second one and the count is never what it looks like. The question was always whether, not how many. The rotation check now also reports what psql said, not only what the provisioner said: the failure was on the client side and the diagnostics were all from the server. --- test/integration/mesh.test.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/integration/mesh.test.ts b/test/integration/mesh.test.ts index 42c5dc4..1e47d38 100644 --- a/test/integration/mesh.test.ts +++ b/test/integration/mesh.test.ts @@ -816,9 +816,17 @@ test("rotating a credential moves both ends, and the old one stops working", { // As the role the provisioner made, into the database it made. The provisioner names a role // after the machine and a database after what the module asked for — which is the contract, and // getting it wrong here made the test fail against a provisioner that had done its job. + // By address, resolved on the machine itself. A container does not inherit its host's + // /etc/hosts, so a name the mesh wrote there resolves for the machine and not for anything it + // runs — which fails as "could not translate host name" and reads like a mesh that never wrote + // the name. + const where = (await must("laptop", + `getent hosts anchor.internal | head -1 | cut -d' ' -f1`)).trim(); + assert.match(where, /^[0-9.]+$/, `the mesh's name for anchor does not resolve here: ${where}`); + const login = async (password: string) => await on("laptop", `docker run --rm -e PGPASSWORD=${quote(password)} ` + - `${pinned("postgres")} psql -h anchor.internal -p 5433 -U mesh_laptop ` + + `${pinned("postgres")} psql -h ${where} -p 5433 -U mesh_laptop ` + `-d realapp -qAt -c "select 1"`, 120_000); const diagnostics = async () => @@ -832,7 +840,8 @@ test("rotating a credential moves both ends, and the old one stops working", { works = (await login(first)).ok; if (!works) await new Promise((r) => setTimeout(r, 5000)); } - assert.ok(works, `the delivered credential does not authenticate:\n${await diagnostics()}`); + assert.ok(works, `the delivered credential does not authenticate:\n` + + `${(await login(first)).out}\n${await diagnostics()}`); // Now rotate. One command: the record changes AND both ends are sent, because leaving the // sending to a later command is the fault above, exactly. @@ -1020,8 +1029,11 @@ test("model access is answered by a record, and the key the mesh took is one it // Nor is it anywhere it could have been read on the way. for (const where of ["/var/lib/mesh-host/declared.json", "/var/lib/mesh-host/state.json"]) { - const held = await on("laptop", `grep -c ${quote(secret)} ${where} 2>/dev/null || echo 0`); - assert.equal(held.out.trim(), "0", `the key is in the open in ${where}`); + // Whether grep found it, not how many times. `grep -c` prints 0 and exits non-zero when it + // finds nothing, so the obvious `|| echo 0` prints a second one and the count is never what + // it looks like. + const found = await on("laptop", `grep -q ${quote(secret)} ${where}`); + assert.ok(!found.ok, `the key is in the open in ${where}`); } });