Facts carry the format as a template, so the control plane holds none (ADR 0120)
A roster fact used to be a name from a closed list, each formatted in Go here — node-names as a hosts file, node-zones as a resolver's zones. Every new consumer (ssh's known_hosts, an authorized_keys) meant another formatter in the control plane, in the consumer's own configuration language. Now a fact is a path and a Go template over the roster view (this node, the suffix, and every served name vs the machines). The mesh owns the data; the module owns the format. /etc/hosts is a template on the network module; dnsmasq's zones move to dnsmasq. The controller renders and reads neither. WireGuard stays a computed generator: the overlay is the substrate delivery rides on, and its config is topology, not a roster projection. Output is byte-for-byte unchanged, pinned by the hosts golden tests and the resolver tests that compose the real dnsmasq manifest.
This commit is contained in:
@@ -161,6 +161,23 @@ func (g *Generator) Nodes() []Node { return g.nodes }
|
||||
// Graph is the peer list per node, for showing.
|
||||
func (g *Generator) Graph() Graph { return g.graph }
|
||||
|
||||
// hostsTemplate is the `/etc/hosts` the network module asks the mesh to write — every machine's
|
||||
// mesh name at its private address, so a service binding the name it was given stays reachable from
|
||||
// everywhere else. It is a roster template like any module's (catalogue.RosterFile): the mesh owns
|
||||
// the data, this owns the format.
|
||||
//
|
||||
// - The loopback floor stays, or things with nothing to do with the mesh break.
|
||||
// - `127.0.1.1 <node>` only when there is a node, the ordinary Debian self-name line.
|
||||
// - A machine's own line is marked, and its mesh name resolves to its mesh address, not loopback.
|
||||
// - `.Names` is every name the mesh serves (issue 111), so a container reaching a routed name
|
||||
// finds the machine serving it; machines with no address yet are already left out of the set.
|
||||
const hostsTemplate = "# Generated by the mesh. Do not edit — this file is replaced whenever a machine\n" +
|
||||
"# joins or leaves, and an edit would survive until then and vanish.\n\n" +
|
||||
"127.0.0.1\tlocalhost\n" +
|
||||
"::1\t\tlocalhost ip6-localhost ip6-loopback\n" +
|
||||
"{{if .Node}}127.0.1.1\t{{.Node}}\n{{end}}\n" +
|
||||
"{{range .Names}}{{.Address}}\t{{.FQDN}}\t{{.Name}}{{if eq .Name $.Node}}\t# this machine{{end}}\n{{end}}"
|
||||
|
||||
// Manifest is the module the mesh provides for itself.
|
||||
//
|
||||
// It ships with the control plane rather than coming from a repository, because the thing that
|
||||
@@ -175,8 +192,12 @@ func Manifest() map[string]any {
|
||||
// Being on the private network is what gives a machine a name, so the module that puts it
|
||||
// there is what writes them. Asked for rather than generated by a module of its own: the
|
||||
// mesh knows which machines exist and where; writing that into a hosts file is not a thing
|
||||
// that needs a module to run nowhere.
|
||||
"facts": map[string]string{"node-names": "/etc/hosts"},
|
||||
// that needs a module to run nowhere. The format is a template like any other roster fact —
|
||||
// the mesh's own module owns the `/etc/hosts` layout the way dnsmasq owns its zones, and the
|
||||
// control plane holds no formatter (see catalogue.RosterFile).
|
||||
"facts": map[string]any{
|
||||
"node-names": map[string]any{"path": "/etc/hosts", "template": hostsTemplate},
|
||||
},
|
||||
"claims": []map[string]any{{"name": TheNetwork, "scope": "node"}},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user