Files
mesh-controller/internal/inventory/provisioning_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

355 lines
18 KiB
Go

package inventory
import (
"bytes"
"encoding/json"
"reflect"
"strings"
"testing"
"time"
"github.com/novox/mesh-controller/internal/catalogue"
)
// APlan is a whole plan, as design 57 P6 composes one for a module handed to the provisioner.
func aPlan() Provisioning {
return Provisioning{
Templates: TemplatesArchive{Source: "artifacts/dunst/files.tar.gz",
Digest: "sha256:" + strings.Repeat("ab", 32)},
Files: []ProvisionedFile{{ID: "dunstrc", Path: "/home/nox/.config/dunst/dunstrc",
Template: "dunstrc", OwnerAtRuntime: "owned", Takes: "reload-on"}},
Placement: map[string]string{"machine:account-home": "/home/nox", "dir:state": "/var/lib/dunst"},
}
}
// **An assignment with no plan is the bytes it was** (design 57 P5, issue 671): the plan joins the value of a
// module handed to the provisioner alone, so no other assignment's key is written again.
func TestAnAssignmentWithoutAPlanIsMirroredAsBefore(t *testing.T) {
at := time.Date(2026, 10, 14, 20, 0, 0, 0, time.UTC)
values, err := AssignmentValues([]AssignmentMirrored{{Node: "g14", Module: "zsh", Assigned: at}}, nil)
if err != nil {
t.Fatal(err)
}
if got, want := string(values["g14.zsh"]), `{"node":"g14","module":"zsh","assigned":"2026-10-14T20:00:00Z"}`; got != want {
t.Errorf("an assignment without a plan is mirrored as %s, want %s", got, want)
}
}
// **A handed module's assignment carries its plan**, whole, under `provisioning`, as the client reads it.
func TestAHandedModulesAssignmentCarriesItsPlan(t *testing.T) {
at := time.Date(2026, 10, 14, 20, 0, 0, 0, time.UTC)
values, err := AssignmentValues([]AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: at},
{Node: "g14", Module: "zsh", Assigned: at}}, map[string]Provisioning{"g14.dunst": aPlan()})
if err != nil {
t.Fatal(err)
}
var got AssignmentMirrored
if err := json.Unmarshal(values["g14.dunst"], &got); err != nil {
t.Fatal(err)
}
if got.Provisioning == nil || got.Provisioning.Files[0].Path != "/home/nox/.config/dunst/dunstrc" ||
got.Provisioning.Placement["dir:state"] != "/var/lib/dunst" {
t.Fatalf("the plan is mirrored as %s", values["g14.dunst"])
}
for _, field := range []string{`"templates":{"source":`, `"digest":"sha256:`, `"owner-at-runtime":"owned"`,
`"template":"dunstrc"`, `"takes":"reload-on"`} {
if !strings.Contains(string(values["g14.dunst"]), field) {
t.Errorf("the plan's value lacks %s: %s", field, values["g14.dunst"])
}
}
if strings.Contains(string(values["g14.zsh"]), "provisioning") {
t.Errorf("an assignment with no plan carries one: %s", values["g14.zsh"])
}
}
// **A plan the client would have to guess at is refused, by name**, and so is one for a module not assigned
// there: nothing is written rather than something wrong.
func TestAPlanWithAProblemIsRefusedByName(t *testing.T) {
at := time.Now().UTC()
assigned := []AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: at}}
cases := map[string]func(*Provisioning){
"templates.digest": func(p *Provisioning) { p.Templates.Digest = "sha256:short" },
"templates.source": func(p *Provisioning) { p.Templates.Source = "" },
"files and items are empty": func(p *Provisioning) { p.Files = nil },
`collection "Shell"`: func(p *Provisioning) { p.Items = []ProvisionedItem{{Collection: "Shell", Template: "x"}} },
"collection a is contributed to twice": func(p *Provisioning) {
p.Items = []ProvisionedItem{{Collection: "a", Template: "x"}, {Collection: "a", Template: "y"}}
},
`items[0]: template "/x"`: func(p *Provisioning) { p.Items = []ProvisionedItem{{Collection: "a", Template: "/x"}} },
"collections[1]: a is named twice": func(p *Provisioning) {
p.Collections = []ProvisionedCollection{{Name: "a"}, {Name: "a"}}
},
"the comment of a holds a line break": func(p *Provisioning) {
p.Collections = []ProvisionedCollection{{Name: "a", Comment: "#\n"}}
},
"not a clean": func(p *Provisioning) { p.Files[0].Path = "relative/dunstrc" },
"inside the templates": func(p *Provisioning) { p.Files[0].Template = "../dunstrc" },
"owner-at-runtime": func(p *Provisioning) { p.Files[0].OwnerAtRuntime = "mine" },
"takes": func(p *Provisioning) { p.Files[0].Takes = "now" },
`placement "secret`: func(p *Provisioning) { p.Placement["secret:token"] = "x" },
`placement "dir:"`: func(p *Provisioning) { p.Placement["dir:"] = "x" },
"is named twice": func(p *Provisioning) { p.Files = append(p.Files, p.Files[0]) },
"not a resource id": func(p *Provisioning) { p.Files[0].ID = "Dunst RC" },
`secret "Token"`: func(p *Provisioning) { p.Secrets = []ProvisionedSecret{{Name: "Token"}} },
"secret token is named twice": func(p *Provisioning) {
p.Secrets = []ProvisionedSecret{{Name: "token"}, {Name: "token"}}
},
`secret token: path "rel/token"`: func(p *Provisioning) {
p.Secrets = []ProvisionedSecret{{Name: "token", Path: "rel/token"}}
},
"secret token: path /home/nox/.config/dunst/dunstrc is named twice": func(p *Provisioning) {
p.Secrets = []ProvisionedSecret{{Name: "token", Path: "/home/nox/.config/dunst/dunstrc"}}
},
}
for want, spoil := range cases {
p := aPlan()
spoil(&p)
_, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": p})
if err == nil || !strings.Contains(err.Error(), want) || !strings.Contains(err.Error(), "g14.dunst") {
t.Errorf("%s: refused with %v", want, err)
}
}
if _, err := AssignmentValues(assigned, map[string]Provisioning{"shanks.dunst": aPlan()}); err == nil ||
!strings.Contains(err.Error(), "shanks.dunst: a plan for a module not assigned there") {
t.Errorf("a plan for a module not assigned there: %v", err)
}
}
// **A plan names the module's own secrets its declaration names there** (design 57 V4, issue 647): each by name, and
// a secret kept in a file of its own with that file's path; the vault hands a node's client a secret only when the
// plan names it. Names, never a value.
func TestAPlanNamesTheModulesSecretsAndNoValue(t *testing.T) {
p := aPlan()
p.Secrets = []ProvisionedSecret{{Name: "token"}, {Name: "db-password", Path: "/var/lib/dunst/db"}}
values, err := AssignmentValues([]AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: time.Now().UTC()}},
map[string]Provisioning{"g14.dunst": p})
if err != nil {
t.Fatal(err)
}
if want := `"secrets":[{"name":"token"},{"name":"db-password","path":"/var/lib/dunst/db"}]`; !strings.Contains(
string(values["g14.dunst"]), want) {
t.Errorf("the plan's value is %s, want it to carry %s", values["g14.dunst"], want)
}
plain := aPlan()
values, err = AssignmentValues([]AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: time.Now().UTC()}},
map[string]Provisioning{"g14.dunst": plain})
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(values["g14.dunst"]), "secrets") {
t.Errorf("a plan naming no secret says secrets: %s", values["g14.dunst"])
}
}
// **A contributor's plan may fill no file** (design 57 L1, issue 684): its item is its part on the node; and a
// collector's names the collections its files read, the old namespaces' by their own names.
func TestAPlanMayContributeAnItemAndNameItsCollections(t *testing.T) {
p := aPlan()
p.Files = nil
p.Items = []ProvisionedItem{{Collection: "shell:zsh:normal", Template: "zsh"}}
p.Collections = []ProvisionedCollection{{Name: "widgets", Comment: "#"}, {Name: "contribution:node-hotkeys:trigger"}}
values, err := AssignmentValues([]AssignmentMirrored{{Node: "g14", Module: "prompt", Assigned: time.Now()}},
map[string]Provisioning{"g14.prompt": p})
if err != nil {
t.Fatal(err)
}
var back AssignmentMirrored
if err := json.Unmarshal(values["g14.prompt"], &back); err != nil || back.Provisioning == nil ||
len(back.Provisioning.Items) != 1 || len(back.Provisioning.Collections) != 2 {
t.Fatalf("mirrored %s (%v)", values["g14.prompt"], err)
}
}
// **A plan names the credentials of what the module requires that the vault keeps for it alone** (issue 757): each
// by the name its files call it, its provision, its provider and its file; never a value. Read as strictly as the
// rest of the plan.
func TestAPlanNamesTheCredentialsIssuedForTheModuleAndNoValue(t *testing.T) {
assigned := []AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: time.Now().UTC()}}
good := ProvisionedCredential{Name: "gitea-read", Provision: "gitea-read", Provider: "novox",
ProviderModule: "gitea", Path: "/var/lib/dunst/gitea-read.secret"}
p := aPlan()
p.Provisions = []ProvisionedCredential{good}
values, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": p})
if err != nil {
t.Fatal(err)
}
if want := `"provisions":[{"name":"gitea-read","provision":"gitea-read","provider":"novox",` +
`"provider-module":"gitea","path":"/var/lib/dunst/gitea-read.secret"}]`; !strings.Contains(string(values["g14.dunst"]), want) {
t.Errorf("the plan's value is %s, want it to carry %s", values["g14.dunst"], want)
}
plain, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": aPlan()})
if err != nil || strings.Contains(string(plain["g14.dunst"]), "provisions") {
t.Errorf("a plan naming no credential says provisions: %s (%v)", plain["g14.dunst"], err)
}
for want, spoil := range map[string]func(*ProvisionedCredential){
`provision credential "Gitea Read"`: func(c *ProvisionedCredential) { c.Name = "Gitea Read" },
`provision ""`: func(c *ProvisionedCredential) { c.Provision = "" },
`provider "../ace"`: func(c *ProvisionedCredential) { c.Provider = "../ace" },
`path "rel"`: func(c *ProvisionedCredential) { c.Path = "rel" },
"is named twice": func(c *ProvisionedCredential) { c.Path = "/home/nox/.config/dunst/dunstrc" },
} {
c := good
spoil(&c)
bad := aPlan()
bad.Provisions = []ProvisionedCredential{c}
if _, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": bad}); err == nil ||
!strings.Contains(err.Error(), want) {
t.Errorf("%s: refused with %v", want, err)
}
}
twice := aPlan()
twice.Secrets = []ProvisionedSecret{{Name: "gitea-read"}}
twice.Provisions = []ProvisionedCredential{good}
if _, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": twice}); err == nil ||
!strings.Contains(err.Error(), "secret gitea-read is named twice") {
t.Errorf("a credential named as an own secret too: %v", err)
}
}
// **A module only pulled in by another has a value of its own** (issue 765): where the machine runs a handed module
// that nothing assigned there, its plan is mirrored under `<node>.<module>` like an assigned one's, saying which
// module requires it and no time it was assigned. A plan for a module neither assigned nor required there is still
// refused, and a module assigned there is mirrored as an assignment whatever else requires it.
func TestAModulePulledInHasAValueOfItsOwnWithItsPlan(t *testing.T) {
at := time.Date(2026, 10, 14, 20, 0, 0, 0, time.UTC)
assigned := []AssignmentMirrored{{Node: "g14", Module: "zsh", Assigned: at}}
pulled := aPlan()
pulled.RequiredBy = "desk"
both := aPlan()
both.RequiredBy = "desk"
values, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": pulled, "g14.zsh": both})
if err != nil {
t.Fatal(err)
}
if got := string(values["g14.dunst"]); !strings.HasPrefix(got, `{"node":"g14","module":"dunst","required-by":"desk",`+
`"provisioning":{"templates":{"source":"artifacts/dunst/files.tar.gz",`) || strings.Contains(got, "assigned") {
t.Errorf("a pulled-in module is mirrored as %s", got)
}
var got AssignmentMirrored
if err := json.Unmarshal(values["g14.dunst"], &got); err != nil || got.Provisioning == nil ||
got.Provisioning.Files[0].Path != "/home/nox/.config/dunst/dunstrc" {
t.Fatalf("the pulled-in module's plan is mirrored as %s (%v)", values["g14.dunst"], err)
}
if zsh := string(values["g14.zsh"]); !strings.HasPrefix(zsh, `{"node":"g14","module":"zsh","assigned":"2026-10-14T20:00:00Z",`+
`"provisioning":`) || strings.Contains(zsh, "required-by") {
t.Errorf("an assigned module also required is mirrored as %s", zsh)
}
if len(values) != 2 {
t.Errorf("the mirror holds %d values, want 2", len(values))
}
spoilt := pulled
spoilt.Templates.Digest = "sha256:short"
if _, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": spoilt}); err == nil ||
!strings.Contains(err.Error(), "g14.dunst: templates.digest") {
t.Errorf("a pulled-in module's spoilt plan: %v", err)
}
if _, err := AssignmentValues(assigned, map[string]Provisioning{"shanks.dunst": aPlan()}); err == nil ||
!strings.Contains(err.Error(), "shanks.dunst: a plan for a module not assigned there") {
t.Errorf("a plan for a module neither assigned nor required there: %v", err)
}
}
// **A plan names the provider serving each provision its module requires** (design 57 V5a, issue 760): machine,
// module and local name, so the vault reading the mirror sees a bind and an unbind; an entry with no credential
// to fill has no name and no file. Read as strictly as the rest of the plan.
func TestAPlanNamesTheProviderOfEachProvision(t *testing.T) {
assigned := []AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: time.Now().UTC()}}
p := aPlan()
p.Provisions = []ProvisionedCredential{
{Provision: "postgres-database", Provider: "novox", ProviderModule: "postgres"},
{Provision: "s3-bucket", Local: "hot", Provider: "ace", ProviderModule: "minio"}}
values, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": p})
if err != nil {
t.Fatal(err)
}
if want := `"provisions":[{"provision":"postgres-database","provider":"novox","provider-module":"postgres"},` +
`{"provision":"s3-bucket","local":"hot","provider":"ace","provider-module":"minio"}]`; !strings.Contains(
string(values["g14.dunst"]), want) {
t.Errorf("the plan's value is %s, want it to carry %s", values["g14.dunst"], want)
}
for want, provisions := range map[string][]ProvisionedCredential{
`postgres-database: provider-module ""`: {{Provision: "postgres-database", Provider: "novox"}},
`postgres-database: provider ""`: {{Provision: "postgres-database", ProviderModule: "postgres"}},
"s3-bucket (hot): its provider is named twice": {
{Provision: "s3-bucket", Local: "hot", Provider: "ace", ProviderModule: "minio"},
{Provision: "s3-bucket", Local: "hot", Provider: "novox", ProviderModule: "minio"}},
`postgres-database: path "/var/lib/dunst/db" names a file and no credential`: {{Provision: "postgres-database",
Provider: "novox", ProviderModule: "postgres", Path: "/var/lib/dunst/db"}},
} {
bad := aPlan()
bad.Provisions = provisions
if _, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": bad}); err == nil ||
!strings.Contains(err.Error(), want) {
t.Errorf("%s: refused with %v", want, err)
}
}
}
// The plan a composition makes (catalogue.Plan) is the plan the mirror carries, its provisions whole: kept with the
// send as the one and read back as the other, nothing is dropped and nothing is unknown.
func TestAComposedPlansProvisionsAreTheMirroredPlans(t *testing.T) {
composed := catalogue.Plan{Templates: catalogue.PlanTemplates{Source: "s", Digest: "d"},
Provisions: []catalogue.PlanProvision{{Name: "db-hot", Provision: "postgres-database", Local: "hot",
Provider: "novox", ProviderModule: "postgres", CredentialForm: catalogue.CredentialFormPair,
Path: "/var/lib/x/hot"}}}
raw, err := json.Marshal(composed)
if err != nil {
t.Fatal(err)
}
var mirrored Provisioning
dec := json.NewDecoder(bytes.NewReader(raw))
dec.DisallowUnknownFields()
if err := dec.Decode(&mirrored); err != nil {
t.Fatal(err)
}
want := []ProvisionedCredential{{Name: "db-hot", Provision: "postgres-database", Local: "hot", Provider: "novox",
ProviderModule: "postgres", CredentialForm: "pair", Path: "/var/lib/x/hot"}}
if !reflect.DeepEqual(mirrored.Provisions, want) {
t.Fatalf("the mirrored plan's provisions are %+v, want %+v", mirrored.Provisions, want)
}
}
// **A plan names the credential form of each provider's offer** (issue 768), so the vault makes a first value in
// that form for a provider it holds no row of: carried as written, and refused when it is not a form.
func TestAPlanNamesTheCredentialFormOfEachProvidersOffer(t *testing.T) {
assigned := []AssignmentMirrored{{Node: "g14", Module: "dunst", Assigned: time.Now().UTC()}}
p := aPlan()
p.Provisions = []ProvisionedCredential{
{Provision: "postgres-database", Provider: "novox", ProviderModule: "postgres", CredentialForm: "pair"},
{Provision: "sonarr-api", Provider: "ace", ProviderModule: "sonarr", CredentialForm: "shared"}}
values, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": p})
if err != nil {
t.Fatal(err)
}
if want := `{"provision":"postgres-database","provider":"novox","provider-module":"postgres",` +
`"credential-form":"pair"},{"provision":"sonarr-api","provider":"ace","provider-module":"sonarr",` +
`"credential-form":"shared"}`; !strings.Contains(string(values["g14.dunst"]), want) {
t.Errorf("the plan's value is %s, want it to carry %s", values["g14.dunst"], want)
}
bad := aPlan()
bad.Provisions = []ProvisionedCredential{{Provision: "postgres-database", Provider: "novox",
ProviderModule: "postgres", CredentialForm: "one-for-all"}}
if _, err := AssignmentValues(assigned, map[string]Provisioning{"g14.dunst": bad}); err == nil ||
!strings.Contains(err.Error(), `credential-form "one-for-all"`) {
t.Errorf("a credential form that is not one was refused with %v", err)
}
}
func TestAPlanMayHoldSeveralNamedItemsOfOneCollectionAndRefusesABadOne(t *testing.T) {
p := aPlan()
p.Items = []ProvisionedItem{
{Collection: "blocks", Name: "a", Attributes: map[string]string{"bar": "top"}, Template: "a"},
{Collection: "blocks", Name: "b", Template: "b"}}
if problems := p.Problems(); len(problems) != 0 {
t.Fatalf("a plan with two named items is refused: %v", problems)
}
p.Items = append(p.Items, ProvisionedItem{Collection: "blocks", Name: "b", Template: "c"},
ProvisionedItem{Collection: "blocks", Name: "c", Attributes: map[string]string{"Bar": "x y"}, Template: "d"})
got := strings.Join(p.Problems(), "; ")
for _, want := range []string{"item blocks/b is contributed to twice", "attribute Bar"} {
if !strings.Contains(got, want) {
t.Errorf("the problems %q lack %q", got, want)
}
}
}