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
76 lines
4.0 KiB
Go
76 lines
4.0 KiB
Go
package resolution
|
|
|
|
import (
|
|
"encoding/json"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// issue 900, ADR 0394 §4: the holder is told the secret places and the paths modules own on its node.
|
|
func TestTheHolderOfNodeFilesIsToldSecretPlacesAndOwnedPaths(t *testing.T) {
|
|
files := mcat.Manifest{Module: "files", Version: "1", Claims: []mcat.Claim{{Name: mcat.FilesSeat}},
|
|
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/files/broker"}}}
|
|
host := mcat.Manifest{Module: "hostnames", Version: "1", Claims: []mcat.Claim{{Name: "node-hostname"}},
|
|
Resources: []map[string]any{{"id": "hosts", "type": "file", "path": "/etc/hosts"}}}
|
|
app := mcat.Manifest{Module: "app", Version: "1",
|
|
Resources: []map[string]any{{"id": "conf", "type": "file", "path": "/etc/app/app.conf"}}}
|
|
r := mcat.Resolution{Node: "anchor", Modules: []mcat.Manifest{files, host, app}}
|
|
words, err := resolutionFilesWords(r, files, nil, mcat.Rendering{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var places []string
|
|
if err := json.Unmarshal([]byte(words[mcat.ReadsSecretPlaces]), &places); err != nil || len(places) == 0 {
|
|
t.Errorf("no secret places: %v %v", err, words)
|
|
}
|
|
var owned map[string]string
|
|
if err := json.Unmarshal([]byte(words[mcat.FilesOwnedPaths]), &owned); err != nil {
|
|
t.Fatalf("no owned paths: %v (%v)", err, words)
|
|
}
|
|
if !strings.HasPrefix(owned["/etc/hosts"], "hostnames") {
|
|
t.Errorf("/etc/hosts is owned by %q, want hostnames", owned["/etc/hosts"])
|
|
}
|
|
if !strings.HasPrefix(owned["/etc/app/app.conf"], "app") {
|
|
t.Errorf("a placed file is owned by %q, want app", owned["/etc/app/app.conf"])
|
|
}
|
|
// Issue 1126, C3: a seat path that steps back for a seat held here is that holder's, whatever the order of the seat
|
|
// table (the controller reads it sorted by name, a node in the order compiled).
|
|
resolver := mcat.Manifest{Module: "systemd-resolved", Version: "1", Claims: []mcat.Claim{{Name: mcat.ResolverSeat}},
|
|
Resources: []map[string]any{{"id": "stub", "type": "file", "path": mcat.ResolverFile}}}
|
|
uplink := mcat.Manifest{Module: "networkmanager", Version: "1", Claims: []mcat.Claim{{Name: "node-uplink"}},
|
|
Resources: []map[string]any{{"id": "resolv", "type": "file", "path": mcat.ResolverFile}}}
|
|
compiled := seats
|
|
t.Cleanup(func() { seats = compiled })
|
|
for _, order := range []string{"compiled", "reversed"} {
|
|
if order == "reversed" {
|
|
seats = append([]mcat.Seat(nil), compiled...)
|
|
slices.Reverse(seats)
|
|
}
|
|
both := mcat.Resolution{Node: "anchor", Modules: []mcat.Manifest{files, uplink, resolver}}
|
|
w, err := resolutionFilesWords(both, files, nil, mcat.Rendering{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var o map[string]string
|
|
if err := json.Unmarshal([]byte(w[mcat.FilesOwnedPaths]), &o); err != nil || o[mcat.ResolverFile] != "systemd-resolved ("+mcat.ResolverSeat+")" {
|
|
t.Errorf("seat table %s: %s is owned by %q, want systemd-resolved (%s)", order, mcat.ResolverFile, o[mcat.ResolverFile], mcat.ResolverSeat)
|
|
}
|
|
}
|
|
seats = compiled
|
|
// Issue 1126, C5 and seed-8: a module carried as it runs (compose.Carried) owns its manifest's paths (OwnsPaths), as
|
|
// the controller tells them, and not the paths its running resources are at (a directory the mesh placed).
|
|
carried := mcat.Manifest{Module: "de-spiegel", Version: "1", OwnsPaths: []string{"/var/lib/de-spiegel/server.env"},
|
|
Resources: []map[string]any{{"id": "state", "type": "directory", "path": "/var/lib/de-spiegel"}}}
|
|
w, err := resolutionFilesWords(mcat.Resolution{Node: "anchor", Modules: []mcat.Manifest{files, carried}}, files, nil, mcat.Rendering{})
|
|
if err != nil || !strings.Contains(w[mcat.FilesOwnedPaths], `"/var/lib/de-spiegel/server.env":"de-spiegel (its own tools)"`) ||
|
|
strings.Contains(w[mcat.FilesOwnedPaths], `"/var/lib/de-spiegel":`) {
|
|
t.Errorf("a carried module does not own its manifest's paths alone: %v %v", w[mcat.FilesOwnedPaths], err)
|
|
}
|
|
if w, _ := resolutionFilesWords(r, app, nil, mcat.Rendering{}); w[mcat.FilesOwnedPaths] != "" {
|
|
t.Error("a module not holding node-files is told the owned paths")
|
|
}
|
|
}
|