Files
mesh-controller/internal/catalogue/needs_test.go
T
jschoubben a18c3b9d13 needs is now own-secrets, named for whose it is
It sat beside `secrets` — where a *provision's* credential lands on a consumer.
Both were name-to-path, both held something secret, and the names
distinguished them not at all. Reaching for the wrong one parsed cleanly and
failed somewhere else entirely, which is the shape of fault this whole design
exists to prevent, sitting in the manifest format.

The axis that separates them is not how secret they are — both are — but
whose. `secrets` is keyed by the provision it is for and belongs to a
relationship with another machine. `own-secrets` is keyed by a name the module
chose and belongs to nobody else.

A manifest using the old name is told the new one rather than refused with
"unknown field": whoever wrote it knew what they meant, and the mesh knows what
it is called now. An invented key is still refused as one rather than guessed
at.

Found by auditing the 19 manifest fields for whether any could be mistaken for
another. This was the only pair that could — and while checking it, a second
instance of the same collision turned up one layer down: `Manifest.Needs` and
`Resolution.Needs` were different concepts sharing a name in Go. The rename
separates those too.
2026-08-31 13:47:21 +02:00

125 lines
4.2 KiB
Go

package catalogue
import (
"strings"
"testing"
)
// A secret a module needs in order to be itself.
//
// A database has a superuser password, a broker an administrator, a registry an account. None is
// *for* anybody — it is not the credential a consumer is given, and the mechanism that hands
// those out has a consumer in the middle of it.
func needy() Manifest {
return Manifest{
Module: "postgres", Version: "1",
OwnSecrets: map[string]string{"superuser": "/var/lib/mesh/postgres/superuser"},
Resources: []map[string]any{
{"id": "store", "type": "container", "name": "mesh-postgres", "image": "postgres@sha256:x"},
},
}
}
func TestAModulesOwnSecretLandsSealed(t *testing.T) {
got, err := Resolve(shelf(needy()), []string{"postgres"}, reachable(), World{})
if err != nil {
t.Fatal(err)
}
out, err := got.Declaration(Rendering{
Needed: map[string]map[string]string{"postgres": {"superuser": "c2VhbGVk"}},
})
if err != nil {
t.Fatal(err)
}
for _, r := range out {
if r["path"] != "/var/lib/mesh/postgres/superuser" {
continue
}
if r["sealed"] != "c2VhbGVk" {
t.Fatalf("got %v", r)
}
if r["content"] != nil {
t.Fatal("a module's own secret was written in the clear")
}
return
}
t.Fatalf("no secret was written: %v", out)
}
func TestADeclaredNeedThatWasNotMadeIsRefused(t *testing.T) {
// Skipping it would start a database with no password it knows, which fails to authenticate
// three layers from the machine reporting it.
got, _ := Resolve(shelf(needy()), []string{"postgres"}, reachable(), World{})
_, err := got.Declaration(Rendering{})
if err == nil {
t.Fatal("a module needing a secret was declared without one")
}
if !strings.Contains(err.Error(), "superuser") {
t.Fatalf("the refusal does not name what is missing: %v", err)
}
}
func TestANeedIsAnAbsolutePath(t *testing.T) {
_, err := ParseManifest([]byte(`{"module":"postgres","version":"1",
"own-secrets":{"superuser":"superuser.txt"}}`))
if err == nil {
t.Fatal("a relative path was accepted")
}
if !strings.Contains(err.Error(), "absolute path") {
t.Fatalf("unhelpful refusal: %v", err)
}
}
func TestAModuleMayNeedSeveralThings(t *testing.T) {
// A password and a token, say. Telling them apart is the module's business, not the mesh's.
m := needy()
m.OwnSecrets["replication"] = "/var/lib/mesh/postgres/replication"
got, _ := Resolve(shelf(m), []string{"postgres"}, reachable(), World{})
out, err := got.Declaration(Rendering{Needed: map[string]map[string]string{
"postgres": {"superuser": "b25l", "replication": "dHdv"},
}})
if err != nil {
t.Fatal(err)
}
seen := map[string]string{}
for _, r := range out {
if path, ok := r["path"].(string); ok && strings.Contains(path, "/var/lib/mesh/postgres/") {
seen[path], _ = r["sealed"].(string)
}
}
if len(seen) != 2 || seen["/var/lib/mesh/postgres/superuser"] == seen["/var/lib/mesh/postgres/replication"] {
t.Fatalf("two needs did not land as two secrets: %v", seen)
}
}
// A manifest written against the old name is told what the field became.
//
// `needs` and `secrets` were both name-to-path and differed only in whose secret it was, so
// reaching for the wrong one parsed cleanly and failed somewhere else entirely. Refusing the old
// name with "unknown field" would be correct and unhelpful: whoever wrote it knew what they meant,
// and the mesh knows what it is called now.
func TestAManifestUsingTheOldNameIsToldTheNewOne(t *testing.T) {
_, err := ParseManifest([]byte(
`{"module":"postgres","version":"1","needs":{"superuser":"/var/lib/superuser"}}`))
if err == nil {
t.Fatal("a manifest using the old name was accepted, so two fields now mean one thing")
}
for _, want := range []string{"needs", "own-secrets"} {
if !strings.Contains(err.Error(), want) {
t.Fatalf("the refusal does not mention %q: %v", want, err)
}
}
}
// And an ordinary unknown key is still refused as one, rather than being guessed at.
func TestAnInventedKeyIsNotTreatedAsARename(t *testing.T) {
_, err := ParseManifest([]byte(`{"module":"postgres","version":"1","nonsense":{}}`))
if err == nil {
t.Fatal("an invented key was accepted")
}
if strings.Contains(err.Error(), "is now called") {
t.Fatalf("an invented key was reported as a rename: %v", err)
}
}