Files
mesh-host/internal/compose/resolution/machine_into_files_test.go
T
jschoubben d68e241724 Take the catalogue's composition rules into the node-engine
ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk
carries no composition rule. The catalogue functions only the
node-engine's composition reaches (Resolve, Resolution's Compose,
Declaration, DataItems, LeftOut, Rules and the rest, the settings,
placeholder and template refusals, the rendering helpers) are copied
with their tests into internal/compose/resolution; a method of a
mesh-sdk shape becomes a function taking it first. The composition
calls its own copies, and the captured catalogue its tests read lives
here now, shared by mesh-compose's tests.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 19:15:55 +01:00

123 lines
4.9 KiB
Go

package resolution
import (
"strings"
"testing"
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
)
// anchored is a machine on the private network, which is where a name worth certifying comes from.
func anchored() mcat.Node {
n := workstation()
n.At = "workstation.internal"
return n
}
// authority is a module that must put its own machine's name into a file it writes.
func authority() mcat.Manifest {
return mcat.Manifest{
Module: "authority", Version: "1",
Provides: mcat.FromAnywhere("acme-ca"),
Serves: map[string]map[string]any{"acme-ca": {}},
Resources: []map[string]any{{
"id": "init", "type": "file", "path": "/var/lib/authority/init.env",
"content": "NAMES=${machine:at},${machine:name},localhost\n",
}},
}
}
func TestAModuleNamesTheMachineItWasAssignedTo(t *testing.T) {
got, err := Resolve(shelf(authority()), []string{"authority"}, anchored(), mcat.World{})
if err != nil {
t.Fatal(err)
}
content := ""
for _, r := range mustDeclare(t, got) {
if strings.HasSuffix(plainly(r["id"]), "init") {
content = plainly(r["content"])
}
}
if content == "" {
t.Fatal("the authority's file was not declared")
}
if strings.Contains(content, "${machine:") {
t.Fatalf("the placeholder was written into the file as a value: %q", content)
}
// The machine's own name on the private network — the one a consumer is handed as `at`, and so
// the one a certificate has to carry.
if !strings.Contains(content, "workstation.internal") {
t.Errorf("the file does not name the address consumers reach it at: %q", content)
}
}
// A machine not on the private network has no such address, and a module asking to be reached at
// one is named here rather than left to fail later as a certificate nobody can verify.
func TestAnAddressAMachineDoesNotHaveIsRefused(t *testing.T) {
got, err := Resolve(shelf(authority()), []string{"authority"}, workstation(), mcat.World{})
if err != nil {
t.Fatal(err)
}
if _, err := Declaration(got, mcat.Rendering{}); err == nil {
t.Fatal("an address the machine does not have was substituted rather than refused")
} else if !strings.Contains(err.Error(), "at") {
t.Errorf("the refusal does not name what was asked for: %v", err)
}
}
// A module that must give software the machine's ADDRESS rather than its name — a resolver pointing
// the container runtime at itself, whose list of resolvers cannot be a name — says
// ${machine:address}, and gets what the machine's name resolves to on the private network: the same
// address every other machine's hosts file carries for it.
func TestAModuleNamesTheAddressBehindItsMachinesName(t *testing.T) {
pointing := mcat.Manifest{Module: "pointing", Version: "1", Resources: []map[string]any{{
"id": "runtime", "type": "file", "path": "/etc/runtime.json",
"content": `{"dns":["${machine:address}"]}`,
}}}
got, err := Resolve(shelf(pointing), []string{"pointing"}, anchored(), mcat.World{})
if err != nil {
t.Fatal(err)
}
out, err := Declaration(got, mcat.Rendering{Names: map[string]string{
"workstation.internal": "10.42.0.7", "anchor.internal": "10.42.0.1"}})
if err != nil {
t.Fatal(err)
}
if content := plainly(out[0]["content"]); content != `{"dns":["10.42.0.7"]}` {
t.Fatalf("the machine's address was not the one its name resolves to: %q", content)
}
// Off the network there is no such address, and the refusal says what the machine does have —
// rather than a placeholder written into the runtime's file and read as an address.
got, err = Resolve(shelf(pointing), []string{"pointing"}, workstation(), mcat.World{})
if err != nil {
t.Fatal(err)
}
if _, err := Declaration(got, mcat.Rendering{}); err == nil || !strings.Contains(err.Error(), "${machine:address}") {
t.Fatalf("a machine off the network was given an address, or refused for another reason: %v", err)
}
}
// The mesh's private range is offered as ${machine:mesh-range}, so a module names it rather than
// hardcoding a value it cannot know (novox/hq ADR 0112) — the fail2ban ignoreip is the case.
func TestAModuleNamesTheMeshRange(t *testing.T) {
facts := machineFacts(mcat.Resolution{Node: "anchor", At: "anchor.internal"}, map[string]string{"anchor.internal": "10.10.0.1"}, "10.10.0.0/24")
if facts["mesh-range"] != "10.10.0.0/24" {
t.Fatalf("the mesh range is not a machine fact: %v", facts)
}
res := map[string]any{"type": "file", "id": "jail", "content": "ignoreip = 127.0.0.1/8 ${machine:mesh-range}\n"}
if err := machineInto(res, facts, "fail2ban"); err != nil {
t.Fatal(err)
}
if got := res["content"].(string); !strings.Contains(got, "10.10.0.0/24") || strings.Contains(got, "${machine:") {
t.Fatalf("the mesh range was not written in: %q", got)
}
// A mesh with no range gives no such fact, and a file that names it is refused rather than
// left with a literal placeholder in it.
none := machineFacts(mcat.Resolution{Node: "anchor"}, nil, "")
if _, has := none["mesh-range"]; has {
t.Fatal("a mesh with no range still offered one")
}
}