Files
mesh-controller/internal/inventory/public_domain_test.go
T
jschoubben 232862315c catalogue: compose a route's name from a label and its node's domain, and resolve it in-mesh
A public route used to carry its whole hostname as a literal in the module
manifest, so running the same catalogue against a different domain meant
overriding that literal on every routed module, per node. The mesh was, in
effect, holding a map of names to services: the one thing it should never hold,
because the subdomain is the operator's choice and the domain is the node's.

Compose instead. A route contribution carries a `label` (the subdomain); a node
carries its `public_domain` as node-level configuration; the mesh joins
`<label>.<public-domain>` and grants exactly that, interpreting neither half.
Held as a node property beside the node's other node-level facts (endpoint,
site, overlay address), not in a module's settings — the ADR calls it
node-level, and the settings table is keyed per module.

Additive, so an unmigrated catalogue keeps working: a contribution that still
carries a full `name` and no `label` passes through unchanged, and the catalogue
can migrate module by module. A labelled contribution on a node with no public
domain composes nothing, reading downstream as a route that named no host.

And propagate: each granted route name is published into internal resolution
mesh-wide, mapped to the node that serves it, alongside the `<node>.internal`
names every container already gets. So a container — and an internal ACME
validator, which cannot complete a challenge for a name it cannot reach —
resolves a routed name to the proxy that serves it. Name-agnostic throughout:
the mesh propagates whatever names it was told to serve and knows nothing about
what they mean.

novox/hq 02-DECISIONS/0056

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-09 23:27:35 +02:00

73 lines
2.2 KiB
Go

package inventory
import (
"testing"
)
// novox/hq ADR 0056 — a node's public domain is a node-level fact, held here beside its other
// node-level configuration rather than in a module's settings. These check it round-trips, that a
// node with none reports empty rather than failing, and that clearing it works — the setting the
// ADR names as the whole of moving a catalogue between a lab and production.
func TestAPublicDomainRoundTrips(t *testing.T) {
inv := fresh(t)
if _, err := inv.AddNode(t.Context(), "anchor"); err != nil {
t.Fatal(err)
}
if err := inv.SetPublicDomain(t.Context(), "anchor", "example.tld"); err != nil {
t.Fatal(err)
}
domain, err := inv.PublicDomainOf(t.Context(), "anchor")
if err != nil {
t.Fatal(err)
}
if domain != "example.tld" {
t.Fatalf("set example.tld and read %q", domain)
}
}
func TestANodeWithNoPublicDomainReportsEmpty(t *testing.T) {
// Empty, not an error: most machines serve nothing to the outside, and a routed module on such
// a node simply composes no name.
inv := fresh(t)
if _, err := inv.AddNode(t.Context(), "workstation"); err != nil {
t.Fatal(err)
}
domain, err := inv.PublicDomainOf(t.Context(), "workstation")
if err != nil {
t.Fatal(err)
}
if domain != "" {
t.Fatalf("a node that was never given a domain reported %q", domain)
}
}
func TestAPublicDomainCanBeCleared(t *testing.T) {
// A node that stops facing the outside composes no names. Clearing with an empty value is how
// that is said, and it must actually clear rather than store the empty string.
inv := fresh(t)
if _, err := inv.AddNode(t.Context(), "anchor"); err != nil {
t.Fatal(err)
}
if err := inv.SetPublicDomain(t.Context(), "anchor", "example.tld"); err != nil {
t.Fatal(err)
}
if err := inv.SetPublicDomain(t.Context(), "anchor", ""); err != nil {
t.Fatal(err)
}
domain, err := inv.PublicDomainOf(t.Context(), "anchor")
if err != nil {
t.Fatal(err)
}
if domain != "" {
t.Fatalf("cleared the domain and read %q", domain)
}
}
func TestPublicDomainOfAnUnknownNodeFails(t *testing.T) {
inv := fresh(t)
if _, err := inv.PublicDomainOf(t.Context(), "never-seen"); err == nil {
t.Fatal("reading the domain of a node that does not exist was not refused")
}
}