From a330d7b0da161721a095e6999812420be8823a95 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 20:59:46 +0100 Subject: [PATCH] Hold the uplinks' resolver file here and the resolver's own configuration in its module (issue 1966) The literal server=1.1.1.1 broke on every pull request when the upstreams became settings; the dnsmasq configuration is checked in the module that owns it. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- go/catalogue/resolver_manifests_test.go | 85 +++---------------------- 1 file changed, 9 insertions(+), 76 deletions(-) diff --git a/go/catalogue/resolver_manifests_test.go b/go/catalogue/resolver_manifests_test.go index 92c0836..7174045 100644 --- a/go/catalogue/resolver_manifests_test.go +++ b/go/catalogue/resolver_manifests_test.go @@ -5,83 +5,16 @@ import ( "testing" ) -// The catalogue's resolver modules as they are, parsed by the real parser and composed as a -// machine would receive them (hal dnsmasq-app conversion, novox/hq 08-connectivity). -// -// The predecessor's resolver answered every name on a machine: the mesh's own itself, the rest -// forwarded to two fixed upstreams, with the machine's resolv.conf naming it alone and the -// container runtime pointed at its private-network address. These hold the mesh's modules to the -// same arrangement, and to the two things a resolver here must never do — read resolv.conf for -// its upstreams, or take an address systemd-resolved holds. +// The resolver module's own configuration is held by the module's tests in the catalogue (novox/hq issue 1966): a +// file moves with its settings, and a literal here broke on every pull request when the upstreams became settings. +// What stays is what the uplink modules write, which names the mesh's resolvers and is the catalogue's across +// modules. -// Its configuration forwards to the upstreams the predecessor's module shipped, and gets them from -// nowhere else: `no-resolv` is what makes the documented loop — the resolver finding its own -// address in resolv.conf and becoming its own upstream — impossible. -func TestTheResolverForwardsToFixedUpstreamsAndNeverReadsResolvConf(t *testing.T) { - m := catalogueManifest(t, "dnsmasq") - var config string - for _, r := range m.Resources { - if r["id"] == "config" { - config, _ = r["content"].(string) - } - } - if config == "" { - t.Fatal("the resolver has no configuration file") - } - for _, want := range []string{ - "\nno-resolv\n", "\nserver=1.1.1.1\n", "\nserver=8.8.8.8\n", - // The private address and loopback, never a LAN's (novox/hq ADR 0194): a device that is not a - // member cannot reach what the mesh's names point at. - "\nlisten-address=127.0.0.1\n", "\nlisten-address=${machine:address}\n", - - // No hosts file and no operator's files: the mesh's resolver answers every node (ADR 0199). - "\nno-hosts\n", - "\nconf-file=" + m.Facts["zones"].Path + "\n", - "\ndomain-needed\n", "\nbogus-priv\n", - "\nconf-file=" + m.Facts["node-zones"].Path + "\n", - } { - if !strings.Contains(config, want) { - t.Errorf("the resolver's configuration lacks %q:\n%s", strings.TrimSpace(want), config) - } - } - // Bound to each address once, at start, and never by bind-dynamic (novox/hq issue 348): bind-dynamic - // re-reads the machine's addresses on every change of any link and closes each listener that reading - // did not find, and a reading made while the container runtime tears down a bridge's virtual links - // fails, so the private address was left unbound for twenty minutes, the daemon running and silent. - // The catalogue's main binds once since mesh-catalog PR 161 (105cc7c0). - if !strings.Contains(config, "\nbind-interfaces\n") { - t.Errorf("the resolver's configuration does not bind its addresses once (bind-interfaces):\n%s", config) - } - if strings.Contains(config, "\nbind-dynamic\n") { - t.Errorf("the resolver's configuration says bind-dynamic, which closes its listeners when a bridge is "+ - "torn down (novox/hq issue 348):\n%s", config) - } - // By address and never by interface: dnsmasq admits a query by the interface it arrives on - // when told one, and a container's query to the private address arrives on the runtime's - // bridge — `interface=mesh0` dropped every such query, silently (novox/hq issue 110). - for _, line := range strings.Split(config, "\n") { - if strings.HasPrefix(line, "interface=") { - t.Errorf("the resolver answers by interface, so a container's query on a bridge is dropped: %s", line) - } - } - // Not .53 or .54, which systemd-resolved holds; and not .55 any more, which was a convention - // beside the one every machine already followed — the predecessor's resolv.conf says .1. - for _, taken := range []string{"127.0.0.53", "127.0.0.54", "127.0.0.55"} { - if strings.Contains(config, "listen-address="+taken) { - t.Errorf("the resolver listens on %s", taken) - } - } - // Never a directory or a file the operator keeps: a line written for one machine's programs would - // become an answer for every node (ADR 0199). - for _, never := range []string{"conf-dir=", "addn-hosts=", "listen-address=${setting:"} { - if strings.Contains(config, never) { - t.Errorf("the mesh's resolver still reads or listens on %q", never) - } - } - // And the file that decides what the machine asks names every one of the mesh's resolvers, by - // address, from the seat's holders, and no public one (ADR 0223): a resolver library that asks - // every listed server at once takes the first reply, and a public "no such name" for a mesh name - // won it. Written by every uplink module, since the uplink's holder owns the file. +// The file that decides what the machine asks names every one of the mesh's resolvers, by +// address, from the seat's holders, and no public one (ADR 0223): a resolver library that asks +// every listed server at once takes the first reply, and a public "no such name" for a mesh name +// won it. Written by every uplink module, since the uplink's holder owns the file. +func TestEveryUplinkNamesTheMeshsResolversAndNoOther(t *testing.T) { for _, uplink := range uplinks { fact, ok := catalogueManifest(t, uplink).Facts["resolvers"] if !ok || fact.Path != "/etc/resolv.conf" { -- 2.54.0