Carry the grant-file and keycloak-shape moved-path tests into harness (issue 849)

This commit is contained in:
fix-sdk-pas
2026-10-16 19:37:02 +02:00
parent 377fd6d67d
commit d7cac79585
2 changed files with 245 additions and 9 deletions
+123
View File
@@ -0,0 +1,123 @@
package harness
// postgres handed to the provisioner still serves the consumers the controller's grant file gives (issue 937): a
// consumer not moved has no plan naming postgres, only its grant, until R1 (issue 682).
import (
"encoding/json"
"os"
"path/filepath"
"reflect"
"testing"
"time"
)
// granting gives the moved world a grant file, as the controller writes it for the consumers not moved.
func (w *movedWorld) granting(given ...map[string]any) {
if w.h.Receives == "" {
w.h.Receives = filepath.Join(w.t.TempDir(), "mesh.json")
}
dir := filepath.Dir(w.h.Receives)
for _, g := range given {
secret := filepath.Join(dir, g["as"].(string)+".secret")
if err := os.WriteFile(secret, []byte("pw-"+g["as"].(string)+"\n"), 0o600); err != nil {
w.t.Fatal(err)
}
g["secret"] = secret
}
if given == nil {
given = []map[string]any{}
}
raw, _ := json.Marshal(map[string]any{"requirement": ProvisionName, "given": given})
if err := os.WriteFile(w.h.Receives, raw, 0o600); err != nil {
w.t.Fatal(err)
}
}
// settled is enough passes and time for a retirement to be stable.
func (w *movedWorld) settled() {
for i := 0; i < 8; i++ {
w.pass()
}
w.now = w.now.Add(15 * time.Minute)
w.pass()
w.pass()
}
// The logins the grant file gives are asked for: made as the grant path makes them, never offered for retirement,
// beside a moved consumer the helper makes from its plan.
func TestAConsumerOnlyTheGrantFileGivesIsAskedForAndMade(t *testing.T) {
bound, logins := bindings(1)
w := newMovedWorld(t, bound)
w.a.inv.Active = []string{"mesh_ace_baserow", "mesh_ace_letta", logins[0]}
w.bus.bind("ace", "app1", nil)
w.granting(
map[string]any{"as": "mesh_ace_baserow", "node": "ace", "values": map[string]any{"name": "baserow"}},
map[string]any{"as": "mesh_ace_letta", "node": "ace",
"values": map[string]any{"name": "letta", "extensions": []any{"vector"}}},
)
w.settled()
if len(w.a.removed) != 0 || w.h.r.waiting != nil {
t.Fatalf("retired %v, waiting %+v: the grant file's logins were counted unasked", w.a.removed, w.h.r.waiting)
}
made := map[string]Provision{}
for _, c := range w.a.created {
made[c.As] = c
}
letta := made["mesh_ace_letta"]
if letta.Password != "pw-mesh_ace_letta" || letta.Consumer != "ace" ||
!reflect.DeepEqual(letta.Values, map[string]any{"name": "letta", "extensions": []any{"vector"}}) {
t.Fatalf("made %+v for letta, as its grant gives it", letta)
}
if _, ok := made["mesh_ace_baserow"]; !ok {
t.Fatalf("baserow not made: %+v", w.a.created)
}
if _, ok := made[logins[0]]; !ok {
t.Fatalf("the moved consumer not made: %+v", w.a.created)
}
}
// A login both a plan and the grant file name is the plan's: made once, with the plan's credential.
func TestALoginAPlanNamesIsNotMadeFromTheGrantFile(t *testing.T) {
bound, logins := bindings(1)
w := newMovedWorld(t, bound)
w.bus.bind("ace", "app1", nil)
w.granting(map[string]any{"as": logins[0], "node": "ace", "values": map[string]any{"name": "app1"}})
w.pass()
w.pass()
for _, c := range w.a.created {
if c.As == logins[0] && c.Password != "pw-app1" {
t.Fatalf("made %s with the grant's password %q, not the plan's", c.As, c.Password)
}
}
}
// An unreadable grant file is not nobody asking (issue 241): nothing is retired while it cannot be read.
func TestAnUnreadableGrantFileRetiresNothing(t *testing.T) {
bound, logins := bindings(1)
w := newMovedWorld(t, bound)
w.a.inv.Active = []string{"mesh_ace_baserow", logins[0]}
w.bus.bind("ace", "app1", nil)
w.h.Receives = filepath.Join(t.TempDir(), "absent.json")
w.settled()
if len(w.a.removed) != 0 || w.h.r.waiting != nil {
t.Fatalf("retired %v, waiting %+v with the grant file unreadable", w.a.removed, w.h.r.waiting)
}
}
// A login the grant file stops giving is retired by the usual rules: stable, then retired, never dropped.
func TestALoginTheGrantFileStopsGivingIsRetired(t *testing.T) {
bound, _ := bindings(1)
w := newMovedWorld(t, bound)
w.bus.bind("ace", "app1", nil)
w.granting(
map[string]any{"as": "mesh_ace_baserow", "node": "ace", "values": map[string]any{"name": "baserow"}},
map[string]any{"as": "mesh_ace_letta", "node": "ace", "values": map[string]any{"name": "letta"}},
)
w.pass()
w.granting(map[string]any{"as": "mesh_ace_baserow", "node": "ace", "values": map[string]any{"name": "baserow"}})
w.settled()
if !reflect.DeepEqual(w.a.removed, []string{"mesh_ace_letta"}) || len(w.a.deleted) != 0 {
t.Fatalf("retired %v, deleted %v", w.a.removed, w.a.deleted)
}
}
+122 -9
View File
@@ -60,9 +60,14 @@ func (b *bucketBus) Delete(_ context.Context, bucket, key string) error {
// bind is one consumer's assignment, whose plan names this machine's postgres as the provider and carries what the
// consumer contributes (issue 776).
func (b *bucketBus) bind(node, module string, contributes map[string]any) {
b.bindNamed("postgres", node, module, contributes, nil)
}
// bindNamed is bind to a provider module, with the names the controller composed for the consumer (issue 792).
func (b *bucketBus) bindNamed(provides, node, module string, contributes map[string]any, names map[string]string) {
plan, _ := json.Marshal(map[string]any{"node": node, "module": module, "provisioning": map[string]any{
"provisions": []any{map[string]any{"provision": ProvisionName, "provider": "novox", "provider-module": "postgres",
"contributes": contributes}}}})
"provisions": []any{map[string]any{"provision": ProvisionName, "provider": "novox", "provider-module": provides,
"contributes": contributes, "names": names}}}})
if err := b.Put(context.Background(), provider.AssignmentsBucket, node+"."+module, plan); err != nil {
panic(err)
}
@@ -89,6 +94,7 @@ type movedWorld struct {
a *recorder
bus *bucketBus
h *Harness
spec MovedSpec
run *Moved
p *provider.Provider
now time.Time
@@ -96,13 +102,17 @@ type movedWorld struct {
}
func newMovedWorld(t *testing.T, bound map[[2]string]string) *movedWorld {
w := &movedWorld{t: t, a: &recorder{}, bus: &bucketBus{buckets: map[string]map[string][]byte{}},
return newMovedWorldOf(t, bound, postgresSpec)
}
func newMovedWorldOf(t *testing.T, bound map[[2]string]string, spec MovedSpec) *movedWorld {
w := &movedWorld{t: t, spec: spec, a: &recorder{}, bus: &bucketBus{buckets: map[string]map[string][]byte{}},
now: time.Date(2026, 10, 15, 12, 0, 0, 0, time.UTC)}
w.file = consumersFile(t, bound)
w.h = &Harness{Resource: ProvisionName, Adapter: w.a, Node: "novox", Now: func() time.Time { return w.now },
Log: func(string, ...any) {}}
var bus provider.Bus
w.run, w.p, bus, _ = NewMoved(w.file, w.h, postgresSpec)
w.run, w.p, bus, _ = NewMoved(w.file, w.h, spec)
_ = bus
if err := w.run.watch(ctx, w.bus); err != nil {
t.Fatal(err)
@@ -119,11 +129,10 @@ func (w *movedWorld) pass() []provider.Outcome {
w.run.assignments[k] = v
}
// the helper reads its buckets from the in-memory bus
w.p = provider.New(provider.Options{Node: "novox", Module: "postgres", Bus: w.bus,
Vault: provider.ConsumersFile{Paths: []string{w.file}},
Offers: map[string]provider.Offer{ProvisionName: {Credential: provider.PerConsumer,
Serves: map[string]any{"port": servedPort}}},
Make: w.run.Make, Gone: w.run.Gone, Logf: func(string, ...any) {}})
w.p = provider.New(provider.Options{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) {}})
out, err := w.p.Pass(ctx)
if err != nil {
w.t.Fatal(err)
@@ -365,3 +374,107 @@ func TestASpecWithoutGrantsMakesUnderThePlansAsAndIgnoresTheGrantFile(t *testing
t.Fatalf("made %+v", a.created)
}
}
const testIssuer = "https://id.example.org/realms/Novox"
var (
grafanaContributes = map[string]any{"label": "grafana", "endpoint": "web", "callback": "/login/generic_oauth"}
grafanaNames = map[string]string{"name": "grafana.example.org", "internal-name": "grafana.home.internal"}
)
// 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,
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 },
Values: func(e provider.Entry) map[string]any {
values := map[string]any{}
for k, v := range e.Contributes {
values[k] = v
}
for k, name := range e.Names {
values[k] = name
}
return values
},
Noun: "client id", Made: "client", Backend: "Keycloak"}
// The consumers' file makes the same call on the adapter as the equivalent grant file does (the harness), for a
// provider of the keycloak shape: the same client id, secret and values; and the entry tells the consumer what the
// provider serves.
func TestTheConsumersFileMakesWhatTheGrantFileMakesForTheKeycloakShape(t *testing.T) {
grafana := provider.ConsumerIdentity("home", "grafana")
values := map[string]any{"name": grafanaNames["name"], "internal-name": grafanaNames["internal-name"]}
for k, v := range grafanaContributes {
values[k] = v
}
g := newWorld(t)
g.give(map[string]any{"as": grafana, "node": "home", "values": values})
g.h.Reconcile(ctx)
w := newMovedWorldOf(t, map[[2]string]string{{"home", "grafana"}: "pw-" + grafana}, keycloakSpec)
w.bus.bindNamed("keycloak", "home", "grafana", grafanaContributes, grafanaNames)
for _, o := range w.pass() {
if o.Status != provider.Answered {
t.Fatalf("%+v", o)
}
}
if len(w.a.created) != 1 || len(g.a.created) != 1 {
t.Fatalf("created %d through the helper, %d through the harness, want 1 each", len(w.a.created), len(g.a.created))
}
c, want := w.a.created[0], g.a.created[0]
if c.As != want.As || c.Password != want.Password || c.Consumer != want.Consumer ||
!reflect.DeepEqual(orEmpty(c.Values), orEmpty(want.Values)) {
t.Fatalf("made %+v, the grant file makes %+v", c, want)
}
var entry provider.Entry
if err := json.Unmarshal(w.bus.buckets[provider.BucketOf("keycloak")]["home.grafana."+ProvisionName], &entry); err != nil {
t.Fatal(err)
}
if entry.Values["issuer"] != testIssuer || entry.Values["token-path"] != "/protocol/openid-connect/token" {
t.Fatalf("served %v", entry.Values)
}
w.a.held = true
w.pass()
w.now = w.now.Add(2 * time.Minute)
w.pass()
if len(w.a.created) != 1 {
t.Fatalf("an unchanged pass created again: %d", len(w.a.created))
}
w.a.held = false
w.now = w.now.Add(2 * time.Minute)
w.pass()
if len(w.a.created) != 2 {
t.Fatalf("not made again: %d", len(w.a.created))
}
}
// A consumer whose plan names its client id (a slugged module) keeps it, and the retirement counts it as asked for.
func TestAConsumerNamedByItsPlanKeepsItsClientId(t *testing.T) {
agent := provider.ConsumerIdentity("home", "dashboard")
w := newMovedWorldOf(t, map[[2]string]string{{"home", "grafana-2"}: "pw"}, keycloakSpec)
plan, _ := json.Marshal(map[string]any{"node": "home", "module": "grafana-2", "provisioning": map[string]any{
"provisions": []any{map[string]any{"provision": ProvisionName, "provider": "novox", "provider-module": "keycloak",
"as": agent, "contributes": grafanaContributes, "names": grafanaNames}}}})
if err := w.bus.Put(ctx, provider.AssignmentsBucket, "home.grafana-2", plan); err != nil {
t.Fatal(err)
}
for _, o := range w.pass() {
if o.Status != provider.Answered {
t.Fatalf("%+v", o)
}
}
if len(w.a.created) != 1 || w.a.created[0].As != agent {
t.Fatalf("made %+v, want one under %s", w.a.created, agent)
}
for i := 0; i < 8; i++ {
w.pass()
}
w.now = w.now.Add(15 * time.Minute)
w.pass()
w.pass()
if len(w.a.removed) != 0 {
t.Fatalf("retired %v: the plan's client id was counted unasked", w.a.removed)
}
}