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
129 lines
4.5 KiB
Go
129 lines
4.5 KiB
Go
package resolution
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
func threeConsumers() map[string]mcat.Manifest {
|
|
return shelf(mcat.Manifest{Module: "postgres", Version: "1", Provides: mcat.FromAnywhere("postgres-database")}, mcat.Manifest{Module: "gitea", Version: "1", Requires: []string{"postgres-database"},
|
|
Contributes: map[string]map[string]any{"postgres-database": {"name": "gitea"}},
|
|
Secrets: map[string]string{"postgres-database": "/var/lib/gitea/db.secret"}}, mcat.Manifest{Module: "keycloak", Version: "1", Requires: []string{"postgres-database"},
|
|
Contributes: map[string]map[string]any{"postgres-database": {"name": "keycloak"}},
|
|
Secrets: map[string]string{"postgres-database": "/var/lib/keycloak/db.secret"}}, mcat.Manifest{Module: "umami", Version: "1", Requires: []string{"postgres-database"},
|
|
Contributes: map[string]map[string]any{"postgres-database": {"name": "umami"}},
|
|
Secrets: map[string]string{"postgres-database": "/var/lib/umami/db.secret"}},
|
|
)
|
|
}
|
|
|
|
func TestEveryConsumerOnANodeGetsItsOwnCredential(t *testing.T) {
|
|
got, err := Resolve(threeConsumers(), []string{"gitea", "keycloak", "umami"},
|
|
reachable(), mcat.World{Offered: onNetwork("anchor")})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(got.Needs) != 3 {
|
|
t.Fatalf("three modules want a database and the node has %d need(s): %v",
|
|
len(got.Needs), got.Needs)
|
|
}
|
|
for _, want := range []string{"gitea", "keycloak", "umami"} {
|
|
var found bool
|
|
for _, n := range got.Needs {
|
|
if n.For == want {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("%s wants a database and no credential is made for it", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Each consumer's own credential reaches its own file. Matching on the provision alone, every
|
|
// consumer took whichever need was last — a module handed somebody else's password, which is a
|
|
// valid credential and therefore fails in a way that looks like a configuration error.
|
|
func TestEachConsumerGetsItsOwnCredentialAndNotAnothersFile(t *testing.T) {
|
|
got, err := Resolve(threeConsumers(), []string{"gitea", "keycloak", "umami"},
|
|
reachable(), mcat.World{Offered: onNetwork("anchor")})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i := range got.Needs {
|
|
got.Needs[i].Sealed = "sealed-for-" + got.Needs[i].For
|
|
}
|
|
out, err := Declaration(got, mcat.Rendering{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, want := range []string{"gitea", "keycloak", "umami"} {
|
|
var seen bool
|
|
for _, r := range out {
|
|
if r["path"] != "/var/lib/"+want+"/db.secret" {
|
|
continue
|
|
}
|
|
seen = true
|
|
if r["sealed"] != "sealed-for-"+want {
|
|
t.Errorf("%s was given %v, which belongs to something else", want, r["sealed"])
|
|
}
|
|
}
|
|
if !seen {
|
|
t.Errorf("%s resolved and its credential file was never written", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The provider is told about all three, separately, and names each grant after the module.
|
|
func TestAProviderIsToldAboutEveryConsumerOnOneMachine(t *testing.T) {
|
|
provider, err := Resolve(shelf(mcat.Manifest{
|
|
Module: "postgres", Version: "1", Provides: mcat.FromAnywhere("postgres-database"),
|
|
Grants: map[string]string{"postgres-database": "/var/lib/postgres/grants"},
|
|
Receives: map[string]string{"postgres-database": "/var/lib/postgres/grants/mesh.json"},
|
|
}), []string{"postgres"}, reachable(), mcat.World{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var grants []mcat.Grant
|
|
for _, who := range []string{"gitea", "keycloak", "umami"} {
|
|
grants = append(grants, mcat.Grant{
|
|
Provision: "postgres-database", Consumer: "anchor", From: who,
|
|
Values: map[string]any{"name": who}, Sealed: "sealed-for-" + who})
|
|
}
|
|
out, err := Declaration(provider, mcat.Rendering{Grants: grants})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, who := range []string{"gitea", "keycloak", "umami"} {
|
|
path := "/var/lib/postgres/grants/anchor." + who + ".secret"
|
|
var found bool
|
|
for _, r := range out {
|
|
if r["path"] == path {
|
|
found = true
|
|
if r["sealed"] != "sealed-for-"+who {
|
|
t.Errorf("%s's grant holds %v", who, r["sealed"])
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("the provider was never told to create a login for %s", who)
|
|
}
|
|
}
|
|
|
|
// And all three appear in the readable manifest, so the provisioner sees three consumers
|
|
// where there are three. Named after one machine, they were one path and the last won.
|
|
for _, r := range out {
|
|
if r["path"] != "/var/lib/postgres/grants/mesh.json" {
|
|
continue
|
|
}
|
|
body := r["content"].(string)
|
|
for _, who := range []string{"gitea", "keycloak", "umami"} {
|
|
if !strings.Contains(body, `"`+who+`"`) {
|
|
t.Errorf("the provider's manifest never mentions %s: %s", who, body)
|
|
}
|
|
}
|
|
}
|
|
}
|