Files
mesh-controller/internal/catalogue/handed_consumers_test.go
T
mesh-tools 97a2d841c7 Land feature 57's controller work on its release branch
The controller plans config, templates and moved providers' provisions,
hands bus accounts to the vault and judges the new manifest words, on the
feature's SDK and node-engine validator (feature 57, config and provisioning).
2026-10-15 21:27:33 +00:00

135 lines
6.3 KiB
Go

package catalogue
import (
"os"
"path/filepath"
"strings"
"testing"
)
// A provider's consumers' credentials reach it in a file its machine's provisioner client fills (novox/hq ADR 0373,
// issue 679): `${consumers:<provision>}`, which only a provider's template may hold, for a provision it provides, in a
// file of mode 0600 owned at runtime; filled by the client alone, so a module not handed to it on a machine is
// refused there rather than written the placeholder's text, or nothing.
const (
handedProvider = `{"module":"db","version":"1","moved":true,
"provides":[{"name":"postgres-database","scope":"mesh","identity":{"max":63,"in":"a PostgreSQL role"}}],
"build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]},
"resources":[
{"id":"state","type":"directory","mode":"0700","place":"."},
{"id":"consumers","type":"file","path":"${dir:state}/consumers.json","mode":"0600","template":"files/consumers.json"}]}`
consumersText = "${consumers:postgres-database}"
)
func builtProvider(text string) []Built {
return []Built{{Name: "files", Kind: ArtifactArchive,
Reference: ArtifactStoreScheme + "db/files/blobs/sha256:" + strings.Repeat("f", 64),
Digest: "sha256:" + strings.Repeat("f", 64), Texts: map[string]string{"consumers.json": text}}}
}
func TestAHandedProvidersConsumersFileIsHandedToTheClient(t *testing.T) {
r := notifierOn(t, held(t, handedProvider, builtProvider(consumersText)))
c, got := composeWith(t, r, handingTo("store:5000"))
if strings.Contains(got, "${consumers:") {
t.Fatalf("the declaration carries the placeholder: %s", got)
}
plan := c.Plans["db"]
if len(plan.Files) != 1 || plan.Files[0].Template != "consumers.json" || plan.Files[0].OwnerAtRuntime != RuntimeOwned {
t.Fatalf("the plan fills %+v, want the consumers' file owned at runtime", plan.Files)
}
}
func TestWhatAConsumersPlaceholderMayNotBeIsRefused(t *testing.T) {
for why, c := range map[string]struct{ raw, text, want string }{
"a provision the module does not provide": {handedProvider, "${consumers:cache}", "does not provide"},
"a module providing nothing": {strings.Replace(handedProvider, `"provides":[{"name":"postgres-database","scope":"mesh","identity":{"max":63,"in":"a PostgreSQL role"}}],`,
"", 1), consumersText, "does not provide"},
"a file others may read": {strings.Replace(handedProvider, `"mode":"0600","template"`,
`"mode":"0640","template"`, 1), consumersText, "0600"},
"a file the program keeps": {strings.Replace(handedProvider, `"mode":"0600","template"`,
`"mode":"0600","create-once":true,"template"`, 1), consumersText, "owned"},
} {
m, err := ParseManifest([]byte(c.raw))
if err != nil {
t.Fatalf("%s: %v", why, err)
}
resolved, err := m.Resolve(builtProvider(c.text))
if err == nil {
r := notifierOn(t, resolved)
_, err = r.Compose(handingTo("store:5000"))
}
if err == nil || !strings.Contains(err.Error(), c.want) {
t.Errorf("%s: %v, want a refusal saying %q", why, err, c.want)
}
}
}
// Where the provider is not handed, nothing fills the placeholder, and it is never written as its text or as an empty
// list the provider would read as no consumers: the file is left out, said once, and the module composes exactly as
// it would without the file, so a provider below the handing contract keeps working from its grant files (issue 780).
func TestAProvidersConsumersFileIsLeftOutWhereItIsNotHanded(t *testing.T) {
withFile := notifierOn(t, held(t, handedProvider, builtProvider(consumersText)))
without := strings.Replace(handedProvider, `,
{"id":"consumers","type":"file","path":"${dir:state}/consumers.json","mode":"0600","template":"files/consumers.json"}`, "", 1)
if without == handedProvider {
t.Fatal("the manifest without the file was not made")
}
bare := notifierOn(t, held(t, strings.Replace(strings.Replace(without, `"moved":true,`, "", 1),
`"build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]},`, "", 1), nil))
for name, with := range map[string]Rendering{
"a node-engine that fills no file": {},
"a node-engine that fills, no client": {Hands: true, ArtifactStore: "store:5000"},
"a client, a node-engine that does not": {Provisioner: "mesh-provisioner-client", ArtifactStore: "store:5000"},
} {
c, got := composeWith(t, withFile, with)
_, want := composeWith(t, bare, with)
if got != want {
t.Errorf("%s: composes\n%s\nwithout its file, it composed\n%s", name, got, want)
}
if strings.Contains(got, "consumers") {
t.Errorf("%s: the declaration names the consumers' file: %s", name, got)
}
if len(c.HandedOnly) != 1 || !strings.Contains(c.HandedOnly[0], "left out, handed only") {
t.Errorf("%s: said %q, want the one line saying it is left out", name, c.HandedOnly)
}
if len(c.Unplaced) != 0 || len(c.Filled) != 0 || len(c.Plans) != 0 {
t.Errorf("%s: unplaced %v, filled %v, plans %v", name, c.Unplaced, c.Filled, c.Plans)
}
}
// Where it is handed, the file is there and nothing is said.
c, _ := composeWith(t, withFile, handingTo("store:5000"))
if len(c.HandedOnly) != 0 || len(c.Filled) != 1 {
t.Fatalf("handed: said %q, filled %v", c.HandedOnly, c.Filled)
}
}
// What `module check` and the merge gate judge: the provider read from its tree, its text inline. Judged by the same
// rules, a provider naming a provision it does not provide is refused there too, and one that does composes.
func TestAProviderReadFromItsTreeIsJudgedByTheSameRules(t *testing.T) {
read := func(text string) (Manifest, error) {
dir := t.TempDir()
if err := os.MkdirAll(filepath.Join(dir, "files"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "files", "consumers.json"), []byte(text), 0o644); err != nil {
t.Fatal(err)
}
m, err := ParseManifest([]byte(handedProvider))
if err != nil {
t.Fatal(err)
}
return WithTemplatesFrom(m, dir)
}
filled, err := read(consumersText)
if err != nil {
t.Fatalf("a provider read from its tree is refused: %v", err)
}
if _, err := notifierOn(t, filled).Compose(handingTo("store:5000")); err != nil {
t.Fatalf("a provider read from its tree does not compose where it would be handed: %v", err)
}
if _, err := read("${consumers:cache}"); err == nil || !strings.Contains(err.Error(), "does not provide") {
t.Fatalf("a provider read from its tree naming a provision it does not provide: %v", err)
}
}