No client names the holder of the seat mesh-provisioner: each reader keeps its own constant, held to its manifest (ADR 0625 section 4, issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 19:36:21 +01:00
parent bc7763a9c0
commit f0ed2d9b12
8 changed files with 95 additions and 60 deletions
@@ -16,19 +16,26 @@ import (
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// The seat, its holder and its two states, and the names a reader declares in its `reads`.
// The seat and its two states, by their local names. The module that holds the seat, and so the addresses and buckets of the
// states (`<module>.<state>`, `<module>_<state>`), is each reader's own, taken from its manifest's `reads` (novox/hq ADR 0625 §4).
const (
Seat = "mesh-provisioner"
Module = "mesh-provisioner"
ProvisionsState = "provisions"
PlansState = "plans"
ProvisionsRead = Module + "." + ProvisionsState
PlansRead = Module + "." + PlansState
// ProvisionsBucket and PlansBucket are the states' buckets.
ProvisionsBucket = Module + "_" + ProvisionsState
PlansBucket = Module + "_" + PlansState
)
// The states' buckets and `reads` addresses for the module holding the seat, which a reader names from its own manifest.
func ProvisionsBucketOf(holder string) string { return holder + "_" + ProvisionsState }
// PlansBucketOf is the bucket of the state plans in the holder's name.
func PlansBucketOf(holder string) string { return holder + "_" + PlansState }
// ProvisionsReadOf is the address a manifest's `reads` names the state provisions by.
func ProvisionsReadOf(holder string) string { return holder + "." + ProvisionsState }
// PlansReadOf is the address a manifest's `reads` names the state plans by.
func PlansReadOf(holder string) string { return holder + "." + PlansState }
// The states of a provision (clause 1).
const (
// Asked is a provision its consumer needs that its provider has not answered yet, or whose dependencies are not
@@ -118,7 +118,10 @@ func TestAProvisionIsAReferenceUnderItsConsumersKey(t *testing.T) {
t.Errorf("the secret provided by %s depends on %v", holder, got)
}
}
if ProvisionsBucket != "mesh-provisioner_provisions" || PlansRead != "mesh-provisioner.plans" {
t.Errorf("the states are %s and %s", ProvisionsBucket, PlansRead)
if ProvisionsBucketOf(testProvisioner) != "mesh-provisioner_provisions" || PlansReadOf(testProvisioner) != "mesh-provisioner.plans" {
t.Errorf("the states are %s and %s", ProvisionsBucketOf(testProvisioner), PlansReadOf(testProvisioner))
}
}
// testProvisioner is the module holding the seat in these tests: a reader names it from its own manifest's `reads`.
const testProvisioner = "mesh-provisioner"
@@ -32,6 +32,8 @@ import (
type MovedSpec struct {
// Module and Provision are the provider module's name and what it provides, as its manifest says.
Module, Provision string
// Provisioner is the module that holds the seat mesh-provisioner, as the provider's manifest's `reads` name it.
Provisioner string
// Offer is what it serves: its credential form and served values.
Offer provider.Offer
// Login is the identity a consumer is made under at the provider's end (postgres: the entry's User; keycloak: its
@@ -234,7 +236,7 @@ func (m *Moved) retire(ctx context.Context) {
// watch keeps the plans as the bus holds them, from the first read on.
func (m *Moved) watch(ctx context.Context, bus provider.Bus) error {
err := bus.Watch(ctx, provisions.PlansBucket, func(key string, value []byte) {
err := bus.Watch(ctx, provisions.PlansBucketOf(m.spec.Provisioner), func(key string, value []byte) {
m.mu.Lock()
defer m.mu.Unlock()
if value == nil {
@@ -297,10 +299,10 @@ func NewMoved(consumersFile string, h *Harness, spec MovedSpec) (*Moved, *provid
return nil, nil, nil, errors.New("no consumers' file")
}
run := newMoved(h, spec)
bus := provider.StateBus(spec.Module)
bus := provider.StateBus(spec.Module, spec.Provisioner)
return run, provider.New(provider.Options{
Node: h.Node,
Module: spec.Module,
Module: spec.Module, Provisioner: spec.Provisioner,
Offers: map[string]provider.Offer{spec.Provision: spec.Offer},
Bus: bus,
Vault: provider.ConsumersFile{Paths: []string{consumersFile}},
@@ -22,7 +22,7 @@ const (
)
// postgresSpec is what a postgres-like provider says: a login per consumer, and the grant file's logins.
var postgresSpec = MovedSpec{Module: "postgres", Provision: ProvisionName,
var postgresSpec = MovedSpec{Module: "postgres", Provisioner: testProvisioner, Provision: ProvisionName,
Offer: provider.Offer{Credential: provider.PerConsumer, Serves: map[string]any{"port": servedPort}},
Login: func(e provider.Entry) string { return e.User },
Noun: "login", Made: "login", Backend: "the backend", WithGrants: true}
@@ -75,13 +75,13 @@ func (b *bucketBus) putPlan(node, module string, entries ...map[string]any) {
if err != nil {
panic(err)
}
_ = b.Put(context.Background(), provisions.ProvisionsBucket, key, value)
_ = b.Put(context.Background(), provisions.ProvisionsBucketOf(testProvisioner), key, value)
}
plan, err := provisions.EncodePlan(provisions.Plan{Node: node, Module: module, Plan: catalogue.Plan{Provisions: ps}})
if err != nil {
panic(err)
}
_ = b.Put(context.Background(), provisions.PlansBucket, node+"."+module, plan)
_ = b.Put(context.Background(), provisions.PlansBucketOf(testProvisioner), node+"."+module, plan)
}
// retireOn is a consumer's provision retiring with the operator's yes (ADR 0570 clause 5), its plan gone: the one way
@@ -92,8 +92,8 @@ func (b *bucketBus) retireOn(node, module, provides string) {
Warrant: &provisions.Warrant{Ask: "a-1", Option: "yes", By: "the operator"}}
key, _ := pr.Key()
value, _ := provisions.Encode(pr)
_ = b.Delete(context.Background(), provisions.PlansBucket, node+"."+module)
_ = b.Put(context.Background(), provisions.ProvisionsBucket, key, value)
_ = b.Delete(context.Background(), provisions.PlansBucketOf(testProvisioner), node+"."+module)
_ = b.Put(context.Background(), provisions.ProvisionsBucketOf(testProvisioner), key, value)
}
// bind is one consumer's assignment, whose plan names this machine's postgres as the provider and carries what the
@@ -160,11 +160,11 @@ func (w *movedWorld) pass() []provider.Outcome {
w.now = w.now.Add(5 * time.Second)
// the plans as the bus holds them now, as the watch would hand them
w.run.plans = map[string][]byte{}
for k, v := range w.bus.buckets[provisions.PlansBucket] {
for k, v := range w.bus.buckets[provisions.PlansBucketOf(testProvisioner)] {
w.run.plans[k] = v
}
// the helper reads its buckets from the in-memory bus
w.p = provider.New(provider.Options{Node: "novox", Module: w.spec.Module, Bus: w.bus,
w.p = provider.New(provider.Options{Provisioner: testProvisioner, Node: "novox", Module: w.spec.Module, Bus: w.bus,
Vault: provider.ConsumersFile{Paths: []string{w.file}},
Offers: map[string]provider.Offer{ProvisionName: w.spec.Offer},
Make: w.run.Make, Gone: w.run.Gone, Logf: func(string, ...any) {}})
@@ -248,7 +248,7 @@ func testAConsumerMissingFromOneReadRetiresNothing(t *testing.T) {
w.bus.bind("ace", "app1", nil)
w.bus.bind("ace", "app2", nil)
w.pass()
_ = w.bus.Delete(ctx, provisions.PlansBucket, "ace.app2")
_ = w.bus.Delete(ctx, provisions.PlansBucketOf(testProvisioner), "ace.app2")
for i := 0; i < 8; i++ {
w.pass()
}
@@ -276,7 +276,7 @@ func testNoPlansReadRetiresNothing(t *testing.T) {
w := newMovedWorld(t, bound)
w.bus.bind("ace", "app1", nil)
w.pass()
w.bus.Delete(ctx, provisions.PlansBucket, "ace.app1")
w.bus.Delete(ctx, provisions.PlansBucketOf(testProvisioner), "ace.app1")
for i := 0; i < 8; i++ {
w.pass()
}
@@ -334,7 +334,7 @@ func testASpecWithoutGrantsMakesUnderThePlansAsAndIgnoresTheGrantFile(t *testing
now := time.Date(2026, 10, 15, 12, 0, 0, 0, time.UTC)
h := &Harness{Resource: ProvisionName, Adapter: a, Node: "novox", Now: func() time.Time { return now },
Receives: filepath.Join(t.TempDir(), "unreadable-or-missing.json"), Log: func(string, ...any) {}}
spec := MovedSpec{Module: "keycloak", Provision: ProvisionName,
spec := MovedSpec{Module: "keycloak", Provisioner: testProvisioner, Provision: ProvisionName,
Offer: provider.Offer{Credential: provider.PerConsumer},
Login: func(e provider.Entry) string { return e.As }, Noun: "client id", Made: "client", Backend: "Keycloak",
Values: func(e provider.Entry) map[string]any { return map[string]any{"label": e.Contributes["label"]} }}
@@ -348,7 +348,7 @@ func testASpecWithoutGrantsMakesUnderThePlansAsAndIgnoresTheGrantFile(t *testing
if err := run.watch(ctx, bus); err != nil {
t.Fatal(err)
}
p := provider.New(provider.Options{Node: "novox", Module: "keycloak", Bus: bus,
p := provider.New(provider.Options{Provisioner: testProvisioner, Node: "novox", Module: "keycloak", Bus: bus,
Vault: provider.ConsumersFile{Paths: []string{file}},
Offers: map[string]provider.Offer{ProvisionName: spec.Offer}, Make: run.Make, Gone: run.Gone,
Logf: func(string, ...any) {}})
@@ -372,7 +372,7 @@ var (
// keycloakSpec is what a keycloak-like provider says: the client id is the plan's `as`, the values are what the
// consumer contributes with the controller's names over them, and no grant file is consulted.
var keycloakSpec = MovedSpec{Module: "keycloak", Provision: ProvisionName,
var keycloakSpec = MovedSpec{Module: "keycloak", Provisioner: testProvisioner, Provision: ProvisionName,
Offer: provider.Offer{Credential: provider.PerConsumer, Serves: map[string]any{"issuer": testIssuer,
"token-path": "/protocol/openid-connect/token"}},
Login: func(e provider.Entry) string { return e.As },
@@ -462,3 +462,6 @@ func testAConsumerNamedByItsPlanKeepsItsClientId(t *testing.T) {
t.Fatalf("retired %v: the plan's client id was counted unasked", w.a.removed)
}
}
// testProvisioner is the module holding the seat mesh-provisioner in these tests.
const testProvisioner = "mesh-provisioner"
@@ -25,7 +25,7 @@ func TestHarnessRuns(t *testing.T) {
w.run.FirstRetry = time.Millisecond
w.run.loaded = false
bus := &earlyBus{bucketBus: w.bus, refuse: 3}
p := provider.New(provider.Options{Node: "novox", Module: "postgres", Bus: w.bus,
p := provider.New(provider.Options{Provisioner: testProvisioner, Node: "novox", Module: "postgres", Bus: w.bus,
Vault: provider.ConsumersFile{Paths: []string{w.file}}, Make: w.run.Make, Gone: w.run.Gone,
Every: time.Hour, Logf: func(string, ...any) {}})
ctx, cancel := context.WithCancel(context.Background())
@@ -162,6 +162,9 @@ type Consumer struct {
type Options struct {
// Node and Module are this provider: the machine it runs on and the module serving.
Node, Module string
// Provisioner is the module that holds the seat mesh-provisioner, whose states plans and provisions the provider reads: its
// own manifest's `reads` name it (`<module>.plans`); the client names no holder (ADR 0625 §4).
Provisioner string
// Offers are the provisions it serves, by name.
Offers map[string]Offer
Bus Bus
@@ -238,12 +241,15 @@ type Outcome struct {
// Run watches the binds and the provider's own entries, and passes once at start, on every change and every
// Every, until ctx ends.
func (p *Provider) Run(ctx context.Context) error {
if p.opts.Provisioner == "" {
return errors.New("the provider names no module holding the seat mesh-provisioner (Options.Provisioner), so it reads no plans")
}
if err := p.watch(ctx, BucketOf(p.opts.Module), p.entries); err != nil {
return fmt.Errorf("watching %s: %w", BucketOf(p.opts.Module), err)
}
for _, bucket := range []string{provisions.PlansBucket, provisions.ProvisionsBucket} {
for _, bucket := range []string{provisions.PlansBucketOf(p.opts.Provisioner), provisions.ProvisionsBucketOf(p.opts.Provisioner)} {
into := p.plans
if bucket == provisions.ProvisionsBucket {
if bucket == provisions.ProvisionsBucketOf(p.opts.Provisioner) {
into = p.provisions
}
if err := p.watch(ctx, bucket, into); err != nil {
@@ -317,13 +323,13 @@ func (p *Provider) Pass(ctx context.Context) ([]Outcome, error) {
if err != nil {
return nil, fmt.Errorf("reading %s: %w", BucketOf(p.opts.Module), err)
}
plans, err := read(provisions.PlansBucket)
plans, err := read(provisions.PlansBucketOf(p.opts.Provisioner))
if err != nil {
return nil, fmt.Errorf("reading %s: %w", provisions.PlansBucket, err)
return nil, fmt.Errorf("reading %s: %w", provisions.PlansBucketOf(p.opts.Provisioner), err)
}
provs, err := read(provisions.ProvisionsBucket)
provs, err := read(provisions.ProvisionsBucketOf(p.opts.Provisioner))
if err != nil {
return nil, fmt.Errorf("reading %s: %w", provisions.ProvisionsBucket, err)
return nil, fmt.Errorf("reading %s: %w", provisions.ProvisionsBucketOf(p.opts.Provisioner), err)
}
return p.passIn(ctx, Inputs{Entries: entries, Plans: plans, Provisions: provs})
}
@@ -705,26 +711,28 @@ func sortedKeys[V any](m map[string]V) []string {
}
// StateBus is the Bus of a provider that runs as a module bundle over the runtime's stdio: the provider's own bucket,
// BucketOf(module), is the module's state `bound`, and provisions.PlansBucket and provisions.ProvisionsBucket are the
// states it reads, `mesh-provisioner.plans` and `mesh-provisioner.provisions` (each declared in the module's manifest).
// BucketOf(module), is the module's state `bound`, and the provisioner holder's plans and provisions are the
// states it reads, `<holder>.plans` and `<holder>.provisions` (each declared in the module's manifest).
// Any other bucket is refused.
//
// A stdio state watch lasts as long as the process and cannot be ended (stdio.KeptState.Watch), so Watch honours the
// Bus contract by silencing instead: once ctx has ended the handler is never called again, though the runtime keeps
// sending changes that are dropped. A Pass, which watches twice per call, is therefore correct, only the dropped
// changes of its two spent watches remain.
func StateBus(module string) Bus { return stateBus{module: module} }
func StateBus(module, provisioner string) Bus {
return stateBus{module: module, provisioner: provisioner}
}
type stateBus struct{ module string }
type stateBus struct{ module, provisioner string }
func (b stateBus) state(bucket string) (stdio.KeptState, error) {
switch bucket {
case BucketOf(b.module):
return stdio.State("bound"), nil
case provisions.PlansBucket:
return stdio.State(provisions.PlansRead), nil
case provisions.ProvisionsBucket:
return stdio.State(provisions.ProvisionsRead), nil
case provisions.PlansBucketOf(b.provisioner):
return stdio.State(provisions.PlansReadOf(b.provisioner)), nil
case provisions.ProvisionsBucketOf(b.provisioner):
return stdio.State(provisions.ProvisionsReadOf(b.provisioner)), nil
}
return stdio.KeptState{}, fmt.Errorf("%s is not a bucket this provider keeps or reads", bucket)
}
@@ -194,13 +194,13 @@ func planned(t *testing.T, bus *standIn, node, module string, ps ...catalogue.Pl
if err != nil {
t.Fatal(err)
}
_ = bus.Put(context.Background(), provisions.ProvisionsBucket, key, value)
_ = bus.Put(context.Background(), provisions.ProvisionsBucketOf(testProvisioner), key, value)
}
raw, err := provisions.EncodePlan(provisions.Plan{Node: node, Module: module, Plan: catalogue.Plan{Provisions: ps}})
if err != nil {
t.Fatal(err)
}
if err := bus.Put(context.Background(), provisions.PlansBucket, node+"."+module, raw); err != nil {
if err := bus.Put(context.Background(), provisions.PlansBucketOf(testProvisioner), node+"."+module, raw); err != nil {
t.Fatal(err)
}
}
@@ -217,8 +217,8 @@ func retire(t *testing.T, bus *standIn, node, module, provision, provider, provi
if err != nil {
t.Fatal(err)
}
_ = bus.Delete(context.Background(), provisions.PlansBucket, node+"."+module)
_ = bus.Put(context.Background(), provisions.ProvisionsBucket, key, raw)
_ = bus.Delete(context.Background(), provisions.PlansBucketOf(testProvisioner), node+"."+module)
_ = bus.Put(context.Background(), provisions.ProvisionsBucketOf(testProvisioner), key, raw)
}
func bind(provision, local, provider, providerModule string) map[string]string {
@@ -245,7 +245,7 @@ func entryOf(t *testing.T, bus *standIn, key string) Entry {
// postgres is a provider of postgres-database on novox with a login per consumer (ADR 0048, ADR 0158 §11), its
// served values derived from each consumer's identity (ADR 0202 §1).
func postgres(bus *standIn, v *vault, m *maker) *Provider {
return New(Options{
return New(Options{Provisioner: testProvisioner,
Node: "novox", Module: "postgres", Bus: bus, Vault: v, Make: m.Make, Gone: m.Gone,
Offers: map[string]Offer{
"postgres-database": {Credential: PerConsumer, Serves: map[string]any{
@@ -292,3 +292,15 @@ func keysOf(v any, into *[]string) {
}
}
}
// testProvisioner is the module holding the seat mesh-provisioner in these tests (a reader names it from its manifest).
const testProvisioner = "mesh-provisioner"
// A provider that does not say which module holds the seat mesh-provisioner reads no plans, and says so at its start: the
// client names no holder, so a reader that forgot to give it is loud (ADR 0625 §4).
func TestAProviderWithoutTheHoldersNameDoesNotRun(t *testing.T) {
p := New(Options{Node: "novox", Module: "postgres", Bus: &standIn{}, Logf: func(string, ...any) {}})
if err := p.Run(context.Background()); err == nil || !strings.Contains(err.Error(), "mesh-provisioner") {
t.Errorf("Run = %v", err)
}
}
@@ -73,7 +73,7 @@ func TestEntries(t *testing.T) {
v := &vault{values: map[string]string{"provision/novox/sonarr-api": "one-value"}}
assign(t, bus, "ace", "overseerr", bind("sonarr-api", "", "novox", "sonarr"))
assign(t, bus, "g14", "homepage", bind("sonarr-api", "", "novox", "sonarr"))
p := New(Options{Node: "novox", Module: "sonarr", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
p := New(Options{Provisioner: testProvisioner, Node: "novox", Module: "sonarr", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
Offers: map[string]Offer{"sonarr-api": {Credential: Shared, User: "sonarr",
Serves: map[string]any{"url": "http://sonarr.novox.mesh:8989"}}}})
if _, err := p.Pass(context.Background()); err != nil {
@@ -129,7 +129,7 @@ func TestEntries(t *testing.T) {
bus, m := newStandIn(), &maker{}
v := &vault{values: map[string]string{"pair/novox/redis-cache/ace/letta/queue": "q-secret"}}
assign(t, bus, "ace", "letta", bind("redis-cache", "queue", "novox", "redis"))
p := New(Options{Node: "novox", Module: "redis", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
p := New(Options{Provisioner: testProvisioner, Node: "novox", Module: "redis", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
Offers: map[string]Offer{"redis-cache": {Credential: PerConsumer, Serves: map[string]any{"port": 6379}}}})
if _, err := p.Pass(context.Background()); err != nil {
t.Fatal(err)
@@ -255,7 +255,7 @@ func TestEntries(t *testing.T) {
b[k] = val
}
assign(t, bus, "ace", "letta", b)
p := New(Options{Node: "novox", Module: "postgres", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
p := New(Options{Provisioner: testProvisioner, Node: "novox", Module: "postgres", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
Offers: map[string]Offer{"postgres-database": {Credential: offer, User: "shared-user"}}})
out, err := p.Pass(context.Background())
if err != nil || len(out) != 1 || out[0].Status != Answered {
@@ -288,7 +288,7 @@ func TestEntries(t *testing.T) {
b := bind("route", "", "novox", "route-proxy")
b["credential-form"] = "pair"
assign(t, bus, "ace", "letta", b)
p := New(Options{Node: "novox", Module: "route-proxy", Bus: bus, Logf: func(string, ...any) {},
p := New(Options{Provisioner: testProvisioner, Node: "novox", Module: "route-proxy", Bus: bus, Logf: func(string, ...any) {},
Offers: map[string]Offer{"route": {Credential: NoCredential}}})
out, err := p.Pass(context.Background())
if err != nil || len(out) != 1 || out[0].Status != Answered {
@@ -318,12 +318,12 @@ func TestEntries(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_ = bus.Put(context.Background(), provisions.ProvisionsBucket, "ace.letta.postgres-database", raw)
_ = bus.Put(context.Background(), provisions.ProvisionsBucketOf(testProvisioner), "ace.letta.postgres-database", raw)
// And umami's key carries none: asked, but no plan names it.
bare := provisions.Provision{Provision: "postgres-database", Consumer: provisions.End{Module: "umami", Node: "g14"},
Provider: provisions.End{Module: "postgres", Node: "novox"}, State: provisions.Asked}
raw, _ = provisions.Encode(bare)
_ = bus.Put(context.Background(), provisions.ProvisionsBucket, "g14.umami.postgres-database", raw)
_ = bus.Put(context.Background(), provisions.ProvisionsBucketOf(testProvisioner), "g14.umami.postgres-database", raw)
out, err := postgres(bus, v, m).Pass(context.Background())
if err != nil {
@@ -416,7 +416,7 @@ func TestBinds(t *testing.T) {
if _, err := p.Pass(context.Background()); err != nil {
t.Fatal(err)
}
_ = bus.Put(context.Background(), provisions.PlansBucket, "ace.letta", []byte(bad))
_ = bus.Put(context.Background(), provisions.PlansBucketOf(testProvisioner), "ace.letta", []byte(bad))
assign(t, bus, "g14", "umami", bind("postgres-database", "", "novox", "postgres"))
var logged []string
p.opts.Logf = func(f string, a ...any) { logged = append(logged, fmt.Sprintf(f, a...)) }
@@ -628,7 +628,7 @@ func TestCredentialsAndKeys(t *testing.T) {
v := &vault{values: map[string]string{"pair/novox/postgres-database/ace/letta": "s1",
"provision/novox/postgres-database": "s2"}}
assign(t, bus, "ace", "letta", bind("postgres-database", "", "novox", "postgres"))
p := New(Options{Node: "novox", Module: "postgres", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
p := New(Options{Provisioner: testProvisioner, Node: "novox", Module: "postgres", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
Offers: map[string]Offer{"postgres-database": {Credential: form, User: "shared-user"}}})
if _, err := p.Pass(context.Background()); err != nil {
t.Fatal(err)
@@ -664,7 +664,7 @@ func TestCredentialsAndKeys(t *testing.T) {
assign(t, bus, "ace", "letta", bind("postgres-database", "", "shanks", "postgres"))
plan, _ := provisions.EncodePlan(provisions.Plan{Node: "ace", Module: "letta", Plan: catalogue.Plan{
Provisions: []catalogue.PlanProvision{{Provision: "postgres-database", Provider: "novox", ProviderModule: "postgres"}}}})
_ = bus.Put(ctx, provisions.PlansBucket, "ace.letta", plan)
_ = bus.Put(ctx, provisions.PlansBucketOf(testProvisioner), "ace.letta", plan)
key := "ace.letta.postgres-database"
put := func(k string, state string, depends []string, warrant *provisions.Warrant) {
pr := provisions.Provision{Provision: "postgres-database", Consumer: provisions.End{Module: "letta", Node: "ace"},
@@ -680,7 +680,7 @@ func TestCredentialsAndKeys(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_ = bus.Put(ctx, provisions.ProvisionsBucket, k, raw)
_ = bus.Put(ctx, provisions.ProvisionsBucketOf(testProvisioner), k, raw)
}
p := postgres(bus, v, m)
steps := []struct {
@@ -697,7 +697,7 @@ func TestCredentialsAndKeys(t *testing.T) {
}, status: Waiting, says: "depends on ace.letta.secret, which is asked"},
{name: "the dependency provided", before: func() { put("ace.letta.secret", provisions.Provided, nil, nil) },
status: Answered, entry: true},
{name: "its plan gone: nothing let go by itself", before: func() { _ = bus.Delete(ctx, provisions.PlansBucket, "ace.letta") },
{name: "its plan gone: nothing let go by itself", before: func() { _ = bus.Delete(ctx, provisions.PlansBucketOf(testProvisioner), "ace.letta") },
entry: true},
{name: "retiring, not yet answered", before: func() { put(key, provisions.Retiring, nil, nil) }, entry: true},
{name: "retiring, the operator's yes", before: func() {
@@ -769,7 +769,7 @@ func TestConsumersFileAndStateBus(t *testing.T) {
bus := newStandIn()
assign(t, bus, "ace", "letta", bind("postgres-database", "", "novox", "postgres"))
var made []string
p := New(Options{Node: "novox", Module: "postgres", Bus: bus, Vault: v,
p := New(Options{Provisioner: testProvisioner, Node: "novox", Module: "postgres", Bus: bus, Vault: v,
Offers: map[string]Offer{"postgres-database": {Serves: map[string]any{"port": 5432}}},
Make: func(_ context.Context, c Consumer) error {
made = append(made, c.Entry.User+"="+string(c.Credential))
@@ -814,7 +814,7 @@ func TestConsumersFileAndStateBus(t *testing.T) {
// `mesh-provisioner.plans` (novox/hq ADR 0570 clause 2), writes only the own bucket's state, and refuses any other
// bucket, the controller's assignments among them.
t.Run("StateBusMapsTheProvidersBucketsToTheRuntimesStates", func(t *testing.T) {
bus := StateBus("postgres")
bus := StateBus("postgres", testProvisioner)
rt := serve(t, []stdio.Tool{
{Name: "watch", Run: func(a map[string]any) (any, error) {
return nil, bus.Watch(context.Background(), a["bucket"].(string), func(string, []byte) {})
@@ -828,7 +828,7 @@ func TestConsumersFileAndStateBus(t *testing.T) {
})
for i, c := range []struct{ bucket, state string }{
{BucketOf("postgres"), "bound"},
{provisions.PlansBucket, "mesh-provisioner.plans"},
{provisions.PlansBucketOf(testProvisioner), "mesh-provisioner.plans"},
} {
rt.call(i+1, "watch", map[string]any{"bucket": c.bucket})
w := rt.next()
@@ -869,12 +869,12 @@ func TestConsumersFileAndStateBus(t *testing.T) {
// A watch whose ctx has ended calls its handler no more, though the runtime keeps sending changes: the Bus contract
// says a watch lasts until ctx ends, and a stdio state watch lasts as long as the process.
t.Run("StateBusWatchCallsItsHandlerNoMoreOnceItsContextEnded", func(t *testing.T) {
bus := StateBus("postgres")
bus := StateBus("postgres", testProvisioner)
ctx, cancel := context.WithCancel(context.Background())
handled := make(chan string, 8)
rt := serve(t, []stdio.Tool{
{Name: "start", Run: func(map[string]any) (any, error) {
return "started", bus.Watch(ctx, provisions.PlansBucket, func(key string, value []byte) {
return "started", bus.Watch(ctx, provisions.PlansBucketOf(testProvisioner), func(key string, value []byte) {
handled <- key + "=" + string(value)
})
}},