diff --git a/test/integration/mesh.test.ts b/test/integration/mesh.test.ts index 13538cb..b2f4cf5 100644 --- a/test/integration/mesh.test.ts +++ b/test/integration/mesh.test.ts @@ -816,17 +816,14 @@ 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}`); - + // **By name, from inside the container.** This used to resolve the address on the machine and + // pass it in, because a container does not inherit its host's /etc/hosts and the name failed as + // "could not translate host name" — which reads like a mesh that never wrote the name. The mesh + // now gives every container the names it knows, so the workaround is gone and its absence is + // the assertion. const login = async (password: string) => await on("laptop", `docker run --rm -e PGPASSWORD=${quote(password)} ` + - `${pinned("postgres")} psql -h ${where} -p 5433 -U mesh_laptop ` + + `${pinned("postgres")} psql -h anchor.internal -p 5433 -U mesh_laptop ` + `-d realapp -qAt -c "select 1"`, 120_000); const diagnostics = async () => @@ -1257,3 +1254,44 @@ test("the hub can be filtered without severing the mesh", { await mesh("unassign laptop stillworks"); await mesh("push"); }); + +test("a container reaches another machine by the name the mesh gave it", { + skip, timeout: 900_000, +}, async () => { + // Internal names are written to the machine's hosts file, which serves the machine and not what + // the machine runs: a container gets its own hosts file holding only its own hostname. So every + // name the mesh wrote was invisible to the majority of things that need one. + // + // Checked from inside a container rather than on the machine, because on the machine it has + // always worked — and that is exactly what made this easy to miss. + await must("anchor", `printf %s '{"module":"resolves","version":"1",` + + `"capabilities":["container-runtime"],` + + `"resources":[{"id":"idle","type":"container","name":"resolves",` + + `"image":"${pinned("registry")}"}]}' > /tmp/resolves.json`); + await must("anchor", `docker cp /tmp/resolves.json mesh-control:/resolves.json`); + await mesh("module add /resolves.json"); + await mesh("assign laptop resolves"); + await mesh("push laptop"); + await new Promise((r) => setTimeout(r, 15_000)); + + // The names are in the container's own hosts file, written by the runtime. + const inside = await must("laptop", `docker exec resolves cat /etc/hosts`); + assert.match(inside, /anchor\.internal/, + `the container cannot see the other machine's name:\n${inside}`); + assert.match(inside, /laptop\.internal/, + `the container cannot see its own machine's name:\n${inside}`); + + // And the name actually reaches the machine, which is the part that matters: a hosts entry + // pointing at the wrong address resolves perfectly and connects to nothing. + const reached = await on("laptop", + `docker exec resolves sh -c 'getent hosts anchor.internal'`); + assert.ok(reached.ok, `the name does not resolve inside the container:\n${reached.out}`); + const address = reached.out.trim().split(/\s+/)[0]; + const onTheMachine = (await must("laptop", + `getent hosts anchor.internal | head -1 | cut -d' ' -f1`)).trim(); + assert.equal(address, onTheMachine, + "the container and its machine disagree about where the other machine is"); + + await mesh("unassign laptop resolves"); + await mesh("push laptop"); +});