Files
mesh-controller/internal/catalogue/handed_providers_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

245 lines
12 KiB
Go

package catalogue
import (
"encoding/json"
"reflect"
"strings"
"testing"
)
// A handed module's plan names the provider serving each provision it requires (design 57 V5a, issue 760): the
// module on the machine resolution chose for this consumer, and the local name it keeps the credential under where
// it keeps several, so the vault, which reads the plan in the assignments mirror, sees a consumer bind and unbind
// (novox/hq ADR 0349 clause 8). One entry per provision and local name, the same entry that names a credential
// the vault keeps for it alone (issue 757).
// notifierConsumer is the notifier requiring a mesh-scoped database, marked moved when moved, with its credential
// kept under two local names (ADR 0094) when locals is set.
func notifierConsumer(t *testing.T, moved, locals bool) Manifest {
t.Helper()
raw := handedNotifier
if moved {
raw = notifierMoved()
}
add := `"requires":["postgres-database"],`
if locals {
add += `"secrets":{"postgres-database":{"hot":"/var/lib/notify/hot","cold":"/var/lib/notify/cold"}},`
}
raw = strings.Replace(raw, `"build":`, add+`"build":`, 1)
return held(t, raw, builtNotifier())
}
// twoStores is a mesh where two machines each run a store providing the database, the consumer pinned to one.
func twoStores(pin string) World {
return World{Offered: map[string][]Provider{"postgres-database": {
{Node: "anchor", At: "anchor.internal", Module: "store", Serves: map[string]any{"port": 5432}},
{Node: "home", At: "home.internal", Module: "store-two", Serves: map[string]any{"port": 5434}},
}}, Pinned: map[string]Chosen{"postgres-database": {Node: pin}}}
}
func consumerOn(t *testing.T, m Manifest, w World) Resolution {
t.Helper()
node := workstation()
node.At = "workstation.internal"
stores := shelf(m,
Manifest{Module: "store", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh}},
Serves: map[string]map[string]any{"postgres-database": {"port": 5432}}},
Manifest{Module: "store-two", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh}},
Serves: map[string]map[string]any{"postgres-database": {"port": 5434}}})
r, err := Resolve(stores, []string{m.Module}, node, w)
if err != nil {
t.Fatal(err)
}
r.Account = "op"
return r
}
func TestAHandedModulesPlanNamesTheProviderServingEachProvision(t *testing.T) {
c, _ := composeWith(t, consumerOn(t, notifierConsumer(t, true, false), twoStores("anchor")), handingTo("store:5000"))
want := []PlanProvision{{Provision: "postgres-database", Provider: "anchor", ProviderModule: "store",
CredentialForm: CredentialFormPair, At: "anchor.internal", From: "anchor", As: ConsumerIdentity("workstation", "notify")}}
if got := c.Plans["notify"].Provisions; !reflect.DeepEqual(got, want) {
t.Fatalf("the plan names %+v, want %+v", got, want)
}
// **A changed binding changes the plan**: pinned to the other store, the plan names it and not the first.
c, _ = composeWith(t, consumerOn(t, notifierConsumer(t, true, false), twoStores("home")), handingTo("store:5000"))
want = []PlanProvision{{Provision: "postgres-database", Provider: "home", ProviderModule: "store-two",
CredentialForm: CredentialFormPair, At: "home.internal", From: "home", As: ConsumerIdentity("workstation", "notify")}}
if got := c.Plans["notify"].Provisions; !reflect.DeepEqual(got, want) {
t.Fatalf("rebound, the plan names %+v, want %+v", got, want)
}
}
// A consumer keeping one provider's credential under several local names is bound once per local name.
func TestAHandedModulesPlanNamesEachLocalName(t *testing.T) {
c, _ := composeWith(t, consumerOn(t, notifierConsumer(t, true, true), twoStores("anchor")), handingTo("store:5000"))
want := []PlanProvision{
{Provision: "postgres-database", Local: "cold", Provider: "anchor", ProviderModule: "store",
CredentialForm: CredentialFormPair, At: "anchor.internal", From: "anchor", As: ConsumerIdentity("workstation", "notify")},
{Provision: "postgres-database", Local: "hot", Provider: "anchor", ProviderModule: "store",
CredentialForm: CredentialFormPair, At: "anchor.internal", From: "anchor", As: ConsumerIdentity("workstation", "notify")},
}
if got := c.Plans["notify"].Provisions; !reflect.DeepEqual(got, want) {
t.Fatalf("the plan names %+v, want %+v", got, want)
}
}
// A module not moved, or moved where it is not handed, composes as before and has no plan.
func TestAConsumerNotHandedComposesAsBefore(t *testing.T) {
_, before := composeWith(t, consumerOn(t, notifierConsumer(t, false, false), twoStores("anchor")),
handingTo("store:5000"))
c, got := composeWith(t, consumerOn(t, notifierConsumer(t, true, false), twoStores("anchor")),
Rendering{Provisioner: "mesh-provisioner-client", ArtifactStore: "store:5000"})
if got != before {
t.Fatalf("the moved consumer composes differently where it is not handed:\nbefore: %s\nnow: %s", before, got)
}
if c.Plans != nil {
t.Fatalf("a consumer not handed has plans %+v", c.Plans)
}
}
// A credential the vault keeps for the holder alone is its provision's one entry, with its provider's module too.
func TestAHandedModulesIssuedCredentialIsItsProvisionsOneEntry(t *testing.T) {
r := forgeReader(t, true, true)
r.Needs = append(r.Needs, Needed{Name: "s3-bucket", From: "ace", For: "bot", Module: "minio"},
Needed{Name: "openai", From: "personal", For: "bot", ByRecord: true})
c, _ := composeWith(t, r, handingTo("store:5000"))
got := c.Plans["bot"].Provisions
if len(got) != 2 || got[0].Provision != "forge-read" || got[0].Name != "forge-read" ||
got[0].ProviderModule != "forge" || got[0].Path == "" ||
!reflect.DeepEqual(got[1], PlanProvision{Provision: "s3-bucket", Provider: "ace", ProviderModule: "minio",
From: "ace", As: ConsumerIdentity("workstation", "bot")}) {
t.Fatalf("the plan names %+v: one entry per provision, the issued credential's with its name and file, "+
"none for a licence", got)
}
}
// Each entry names the credential form the provider's offer declares (issue 768), from the provider's manifest, so
// the vault makes a first value in that form for a provider it holds no row of: one of its own secrets shared by
// every consumer, a login per consumer when the offer says no credential, issued by its software, or given by a
// person (the offer's own secret issued outside).
func TestAHandedModulesPlanNamesTheCredentialFormOfItsProvidersOffer(t *testing.T) {
storeOffering := func(credential string) Manifest {
raw := `{"module":"store","version":"1","provides":[{"name":"postgres-database","scope":"mesh"` +
credential + `}],"serves":{"postgres-database":{"port":5432}}`
if strings.Contains(credential, `"own"`) {
raw += `,"own-secrets":{"admin":{"path":"/var/lib/store/admin","taken":"at-start"},` +
`"token":{"path":"/var/lib/store/token","taken":"at-start","issued-by":"outside"}}`
}
return mustParse(t, raw+`}`)
}
for credential, want := range map[string]string{
``: CredentialFormPair,
`,"credential":{"own":"admin"}`: CredentialFormShared,
`,"credential":{"own":"token"}`: CredentialFormGiven,
`,"credential":{"issued-by":"outside"}`: CredentialFormGiven,
`,"credential":{"issued-by":"software","tool":"store_issued_token"}`: CredentialFormIssued,
} {
node := workstation()
node.At = "workstation.internal"
consumer := notifierConsumer(t, true, false)
r, err := Resolve(shelf(consumer, storeOffering(credential)), []string{consumer.Module}, node, twoStores("anchor"))
if err != nil {
t.Fatal(err)
}
r.Account = "op"
c, _ := composeWith(t, r, handingTo("store:5000"))
got := c.Plans["notify"].Provisions
if len(got) != 1 || got[0].CredentialForm != want {
t.Errorf("an offer with %q: the plan names %+v, want the form %q", credential, got, want)
}
}
if got := (Manifest{Module: "x"}).CredentialFormOf("postgres-database"); got != "" {
t.Errorf("a module offering nothing has the credential form %q", got)
}
}
// A consumer's plan entry carries what its manifest contributes to that provision (issue 776), so the provider's
// helper learns a database's name from the bus; a consumer contributing nothing has the entry it had.
func TestAHandedModulesPlanEntryCarriesWhatItContributesToItsProvider(t *testing.T) {
m := notifierConsumer(t, true, false)
m.Contributes = map[string]map[string]any{"postgres-database": {"name": "nextcloud"}}
c, _ := composeWith(t, consumerOn(t, m, twoStores("anchor")), handingTo("store:5000"))
want := []PlanProvision{{Provision: "postgres-database", Provider: "anchor", ProviderModule: "store",
CredentialForm: CredentialFormPair, Contributes: map[string]any{"name": "nextcloud"}, At: "anchor.internal", From: "anchor", As: ConsumerIdentity("workstation", "notify"),
// What the binding file says as `${bound:postgres-database:name}` (issue 792).
Names: map[string]string{"name": "nextcloud"}}}
if got := c.Plans["notify"].Provisions; !reflect.DeepEqual(got, want) {
t.Fatalf("the plan names %+v, want %+v", got, want)
}
m = notifierConsumer(t, true, false)
c, _ = composeWith(t, consumerOn(t, m, twoStores("anchor")), handingTo("store:5000"))
raw, err := json.Marshal(c.Plans["notify"].Provisions)
if err != nil {
t.Fatal(err)
}
const before = `[{"provision":"postgres-database","provider":"anchor","provider-module":"store",` +
`"credential-form":"pair","at":"anchor.internal","from":"anchor","as":"mesh_workstation_notify"}]`
if string(raw) != before {
t.Fatalf("a consumer contributing nothing has the entry %s, want %s", raw, before)
}
}
// A consumer's plan entry carries the names the mesh composed for it through that provision (issue 792): what its
// binding file names as `${bound:<provision>:name|internal-name|name-<local>|internal-name-<local>}`, so a moved
// provider's helper copies them into the consumer's entry and the client fills the same text. A consumer
// contributing no label has the entry it had.
func TestAHandedModulesPlanEntryCarriesTheNamesComposedForIt(t *testing.T) {
m := notifierConsumer(t, true, false)
m.ContributesMany = map[string]map[string]map[string]any{
"postgres-database": {"web": {"label": "git"}, "api": {"label": "git-api"}}}
r := consumerOn(t, m, twoStores("anchor"))
r.PublicDomain = "example.org"
c, _ := composeWith(t, r, handingTo("store:5000"))
got := c.Plans["notify"].Provisions
if len(got) != 1 {
t.Fatalf("the plan names %+v", got)
}
want := map[string]string{"name-web": "git.example.org", "name-api": "git-api.example.org"}
own, err := r.ownNames(m, "postgres-database", nil)
if err != nil {
t.Fatal(err)
}
file := map[string]string{}
withOwnNames(file, own)
for k, v := range file {
if strings.HasPrefix(k, "internal-") {
want[k] = v
}
}
if !reflect.DeepEqual(got[0].Names, want) {
t.Fatalf("the entry carries %v, the binding file names %v", got[0].Names, want)
}
}
// The flag a provides entry carries, `"answered-by": "helper"`, is read and written back, and refused with any other
// word (issue 792). It is no part of moving the module: the flag alone hands none of the module's files, and the
// module composes byte for byte as it did without it.
func TestAnOfferAnsweredByTheHelperIsReadAndHandsNoFile(t *testing.T) {
with := strings.Replace(handedNotifier, `"version":"1",`,
`"version":"1","provides":[{"name":"route","scope":"mesh","answered-by":"helper"}],`, 1)
without := strings.Replace(handedNotifier, `"version":"1",`,
`"version":"1","provides":[{"name":"route","scope":"mesh"}],`, 1)
flagged := held(t, with, builtNotifier())
if flagged.Moved || len(flagged.Provides) != 1 || flagged.Provides[0].AnsweredBy != AnsweredByHelper {
t.Fatalf("the flag was read as %+v (moved %v)", flagged.Provides, flagged.Moved)
}
if flagged.Handed != nil {
t.Fatalf("the flag alone kept what a moved module's build keeps: %+v", flagged.Handed)
}
raw, err := json.Marshal(flagged.Provides[0])
if err != nil || !strings.Contains(string(raw), `"answered-by":"helper"`) {
t.Fatalf("written back as %s: %v", raw, err)
}
if _, err := ParseManifest([]byte(strings.Replace(with, `"helper"`, `"harness"`, 1))); err == nil {
t.Fatal("an unknown answerer was accepted")
}
c1, before := composeWith(t, notifierOn(t, held(t, without, builtNotifier())), handingTo("store:5000"))
c2, got := composeWith(t, notifierOn(t, flagged), handingTo("store:5000"))
if got != before || len(c2.Filled) > 0 || c2.Plans != nil || c1.Plans != nil {
t.Fatalf("the flag changed what is composed or handed:\nbefore: %s\nnow: %s\n%v %v", before, got, c2.Filled, c2.Plans)
}
}