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