The mesh's one resolver: its seat, a provider's address, zones, and a node's hosts file (hq ADR 0194, 0196, 0199)
- mesh-dns-resolver: a mesh seat delivering wildcard-resolution, so every node's resolver
configuration resolves to its one holder; node-dns-resolver kept until nothing claims it.
- ${bound:<provision>:address}: the providing machine's private address, for the one consumer
that cannot use a name — a machine's resolver configuration.
- zone: a module declares the zone it answers and the listen that answers it; the controller
settles it per node, refuses duplicates and shadowing, and hands the resolver .Zones to forward.
- node-hosts-file: a node seat whose holder owns /etc/hosts, with entries/add/remove.
The resolver tests follow the catalogue: no runtime dns (containers copy the machine's resolvers),
live-restore held by resolv-conf, resolv.conf naming the resolver by address then a public one.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package catalogue
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A zone names the listen that answers it, or there is nothing to forward to (novox/hq ADR 0199).
|
||||
func TestAZoneMustNameOneOfTheModulesListens(t *testing.T) {
|
||||
_, err := ParseManifest([]byte(`{"module":"lab","version":"1",
|
||||
"listens":[{"name":"dns","port":5353,"protocol":"udp","from":"mesh","why":"the lab's names"}],
|
||||
"zone":{"name":"${setting:zone}","listen":"web"}}`))
|
||||
if err == nil || !strings.Contains(err.Error(), `has no listen of that name`) {
|
||||
t.Fatalf("a zone answered by a listen the module does not have was accepted: %v", err)
|
||||
}
|
||||
if _, err := ParseManifest([]byte(`{"module":"lab","version":"1",
|
||||
"listens":[{"name":"dns","port":5353,"protocol":"udp","from":"mesh","why":"the lab's names"}],
|
||||
"zone":{"name":"${setting:zone}","listen":"dns"}}`)); err != nil {
|
||||
t.Fatalf("a well-formed zone was refused: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The zone is the operator's (a setting) and the port is where this machine publishes the listen —
|
||||
// neither is the definition's to state.
|
||||
func TestAZoneIsPlacedFromTheNodesSettingAndPublishedPort(t *testing.T) {
|
||||
m := Manifest{Module: "lab", Zone: &Zone{Name: "${setting:zone}", Listen: "dns"},
|
||||
Listens: []Listening{{Name: "dns", Port: 5353, From: FromMesh}}}
|
||||
z, err := ZoneOn(m, []Layer{{From: "node", Values: map[string]any{"zone": "Incus."}}},
|
||||
map[int]int{5353: 15353}, "workstation", "10.77.0.3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if z.Zone != "incus" || z.Address != "10.77.0.3" || z.Port != 15353 || z.Node != "workstation" {
|
||||
t.Fatalf("the zone was placed as %+v", *z)
|
||||
}
|
||||
if _, err := ZoneOn(m, nil, nil, "workstation", "10.77.0.3"); err == nil {
|
||||
t.Fatal("a zone nobody named was placed")
|
||||
}
|
||||
}
|
||||
|
||||
// One module answers a zone, and none may shadow the mesh's names or a public domain.
|
||||
func TestTheMeshRefusesAZoneTwiceOrOneThatShadows(t *testing.T) {
|
||||
one := ZoneAt{Zone: "incus", Node: "workstation", Module: "lab", Address: "10.77.0.3", Port: 53}
|
||||
if p := ZonesProblems([]ZoneAt{one}, "internal", []string{"example.tld"}); len(p) != 0 {
|
||||
t.Fatalf("one ordinary zone was refused: %v", p)
|
||||
}
|
||||
twice := one
|
||||
twice.Node, twice.Module = "laptop", "other"
|
||||
cases := map[string][]ZoneAt{
|
||||
"declared by": {one, twice},
|
||||
"mesh's own suffix": {{Zone: "lab.internal", Node: "a", Module: "m"}},
|
||||
"public domain": {{Zone: "dev.example.tld", Node: "a", Module: "m"}},
|
||||
}
|
||||
for want, zones := range cases {
|
||||
p := strings.Join(ZonesProblems(zones, "internal", []string{"example.tld"}), "\n")
|
||||
if !strings.Contains(p, want) {
|
||||
t.Errorf("not refused for %q: %q", want, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The resolver's template sees every zone with where it is answered, in zone order.
|
||||
func TestTheResolversTemplateRangesTheZones(t *testing.T) {
|
||||
m := Manifest{Module: "dnsmasq", Facts: map[string]RosterFile{"zones": {
|
||||
Path: "/etc/mesh-resolver/zones.conf",
|
||||
Template: "{{range .Zones}}server=/{{.Zone}}/{{.Address}}#{{.Port}}\n{{end}}",
|
||||
}}}
|
||||
out, err := FactsWithZonesInto(m, Resolution{Node: "anchor"}, nil, nil, nil, "", []ZoneAt{
|
||||
{Zone: "zeta", Address: "10.77.0.2", Port: 53},
|
||||
{Zone: "incus", Address: "10.77.0.3", Port: 15353},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := out[0]["content"]; got != "server=/incus/10.77.0.3#15353\nserver=/zeta/10.77.0.2#53\n" {
|
||||
t.Fatalf("the resolver was told %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user