The hourly release of ADR 0229's brake still ended in the mesh acting alone on a mistake. A consumer now stays active until the same unasked set holds for five passes, waits for a person past three or half of those held, is disabled and marked rather than withdrawn, comes back as it was when asked again, and is deleted only through the provider's delete tool. The backend keeps the mark, so a restart forgets nothing and finds what was withdrawn before.
108 lines
4.4 KiB
Go
108 lines
4.4 KiB
Go
package main
|
|
|
|
// What retired means in postgres, held against the statements sent (retire_pg.go); the server's side
|
|
// of it — the login refused, the data kept, a deletion dropping only its own — is live_test.go's.
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRetiringLocksMarksAndDropsNothing(t *testing.T) {
|
|
f, c := newFake(admin)
|
|
f.answer(`shobj_description\(oid`, []string{"c"}, []string{`{"mesh":"consumer","node":"ace"}`})
|
|
f.answer(`SELECT 1 FROM pg_roles`, []string{"?column?"}, []string{"1"})
|
|
at := time.Date(2026, 10, 6, 12, 0, 0, 0, time.UTC)
|
|
if err := c.RetireRole(ctx, "mesh_ace_letta", "the mesh stopped asking for it", at); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sql := f.statements()
|
|
if !has(sql, `ALTER ROLE "mesh_ace_letta" NOLOGIN`) || !has(sql, `pg_terminate_backend.*usename = 'mesh_ace_letta'`) ||
|
|
!has(sql, `COMMENT ON ROLE "mesh_ace_letta" IS '\{"mesh":"consumer","node":"ace","retired":"2026-10-06T12:00:00Z","why":"the mesh stopped asking for it"\}'`) {
|
|
t.Fatal(strings.Join(sql, "\n"))
|
|
}
|
|
noDrop(t, sql)
|
|
if has(sql, `REVOKE|ALLOW_CONNECTIONS|RENAME`) {
|
|
t.Fatal("retiring touched the database:", strings.Join(sql, "\n"))
|
|
}
|
|
|
|
// No such role: nothing to retire, nothing done.
|
|
f, c = newFake(admin)
|
|
if err := c.RetireRole(ctx, "gone", "x", at); err != nil || has(f.statements(), `ALTER|COMMENT`) {
|
|
t.Fatal(f.statements(), err)
|
|
}
|
|
}
|
|
|
|
func TestTheInventoryIsWhatTheMeshMadeByMarkOrShape(t *testing.T) {
|
|
f, c := newFake(admin)
|
|
f.answer(`FROM pg_roles r`, []string{"rolname", "rolcanlogin", "mark", "mesh_shaped", "size"},
|
|
[]string{"active_marked", "t", `{"mesh":"consumer","node":"ace"}`, "f", "100"},
|
|
[]string{"active_shaped", "t", "", "t", "200"},
|
|
[]string{"retired_marked", "f", `{"mesh":"consumer","node":"ace","retired":"2026-10-06T12:00:00Z","why":"gone"}`, "t", "300"},
|
|
[]string{"locked_before", "f", "", "t", "400"},
|
|
[]string{"hal_registry", "t", "", "f", "500"},
|
|
[]string{"jochens", "t", "", "t", ""},
|
|
)
|
|
f.answer(`_deleted_`, []string{"datname", "size"}, []string{"mesh_novox_gitea_deleted_20261005", "7771827"})
|
|
inv, err := c.Inventory(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Join(inv.Active, ",") != "active_marked,active_shaped" {
|
|
t.Fatalf("active: %v", inv.Active)
|
|
}
|
|
if len(inv.Retired) != 3 {
|
|
t.Fatalf("%+v", inv.Retired)
|
|
}
|
|
r := inv.Retired[0]
|
|
if r.Consumer != "retired_marked" || r.Node != "ace" || r.Why != "gone" || r.SizeBytes != 300 ||
|
|
r.RetiredAt.Format(time.RFC3339) != "2026-10-06T12:00:00Z" {
|
|
t.Fatalf("%+v", r)
|
|
}
|
|
if r := inv.Retired[1]; r.Consumer != "locked_before" || !r.RetiredAt.IsZero() || r.Kind != KindConsumer {
|
|
t.Fatalf("found locked without a mark: %+v", r)
|
|
}
|
|
if r := inv.Retired[2]; r.Kind != KindSetAside || r.RetiredAt.Format("20060102") != "20261005" || r.SizeBytes != 7771827 {
|
|
t.Fatalf("set aside: %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestDeletingRefusesALoginThatCanConnect(t *testing.T) {
|
|
f, c := newFake(admin)
|
|
f.answer(`SELECT rolcanlogin`, []string{"rolcanlogin"}, []string{"t"})
|
|
if _, err := c.DeleteRetired(ctx, Retired{Consumer: "mesh_ace_baserow", Kind: KindConsumer}); err == nil {
|
|
t.Fatal("deleted an active consumer")
|
|
}
|
|
noDrop(t, f.statements())
|
|
|
|
f, c = newFake(admin)
|
|
if _, err := c.DeleteRetired(ctx, Retired{Consumer: "mesh_ace_baserow", Kind: KindSetAside}); err == nil {
|
|
t.Fatal("dropped a database not set aside")
|
|
}
|
|
noDrop(t, f.statements())
|
|
}
|
|
|
|
func TestDeletingDropsTheDatabaseThenTheRole(t *testing.T) {
|
|
f, c := newFake(admin)
|
|
f.answer(`SELECT rolcanlogin`, []string{"rolcanlogin"}, []string{"f"})
|
|
f.answer(`SELECT pg_database_size`, []string{"size"}, []string{"9000"})
|
|
freed, err := c.DeleteRetired(ctx, Retired{Consumer: "mesh_ace_letta", Kind: KindConsumer})
|
|
if err != nil || freed != 9000 {
|
|
t.Fatal(freed, err)
|
|
}
|
|
sql := strings.Join(f.statements(), "\n")
|
|
db, role := strings.Index(sql, `DROP DATABASE "mesh_ace_letta"`), strings.Index(sql, `DROP ROLE "mesh_ace_letta"`)
|
|
if db < 0 || role < db {
|
|
t.Fatal(sql)
|
|
}
|
|
// A role still owning a set-aside database is kept, and said.
|
|
f, c = newFake(admin)
|
|
f.answer(`SELECT rolcanlogin`, []string{"rolcanlogin"}, []string{"f"})
|
|
f.answer(`SELECT datname FROM pg_database WHERE datdba`, []string{"datname"}, []string{"mesh_ace_letta_deleted_20261005"})
|
|
if _, err := c.DeleteRetired(ctx, Retired{Consumer: "mesh_ace_letta", Kind: KindConsumer}); err == nil ||
|
|
!strings.Contains(err.Error(), "still owns") || has(f.statements(), `DROP ROLE`) {
|
|
t.Fatal(err, f.statements())
|
|
}
|
|
}
|