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") } }