Retire a consumer the mesh stops asking for, and delete only on a person's word (hq ADR 0230)

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.
This commit is contained in:
jochen
2026-10-06 13:54:10 +02:00
parent 4554e18279
commit e68ef88333
26 changed files with 3105 additions and 494 deletions
@@ -6,13 +6,16 @@ package main
// MESH_POSTGRES_LIVE=postgres://postgres:admin@127.0.0.1:55432/postgres?sslmode=disable go test ./...
//
// It proves what the fakes cannot: an untrusted extension is installed by the superuser in a fresh
// consumer database and a second pass is a no-op; the consumer then holds; a withdrawn login cannot
// log in and its data is still there; the reader cannot write, even past a COMMIT.
// consumer database and a second pass is a no-op; the consumer then holds; a retired login cannot
// log in, its data is still there and the backend lists it retired with when and why; asked for again
// it is enabled as it was; only a deletion drops it, and only it; the reader cannot write, even past a
// COMMIT (novox/hq ADR 0230).
import (
"net/url"
"os"
"testing"
"time"
)
func TestLive(t *testing.T) {
@@ -64,21 +67,97 @@ func TestLive(t *testing.T) {
t.Fatal(r, err)
}
if err := a.Remove(ctx, p.As, nil); err != nil {
// A neighbour that must survive everything below untouched.
other := Provision{As: "mesh_test_other", Password: "other-pw"}
if err := a.Create(ctx, other); err != nil {
t.Fatal(err)
}
defer c.DeleteRetired(ctx, Retired{Consumer: other.As}) //nolint — best effort, after a retire below
at := time.Date(2026, 10, 6, 12, 0, 0, 0, time.UTC)
if err := a.Retire(ctx, p.As, nil, "the mesh stopped asking for it", at); err != nil {
t.Fatal(err)
}
if ok, err := a.Holds(ctx, p); err != nil || ok {
t.Fatal("a withdrawn login still logs in:", ok, err)
t.Fatal("a retired login still logs in:", ok, err)
}
r, err = c.Query(ctx, p.As, "SELECT count(*) FROM kept")
if err != nil || len(r.Rows) != 1 {
t.Fatal("withdrawing lost the data:", err)
if err != nil || len(r.Rows) != 1 || cell(r.Rows[0], 0) != "1" {
t.Fatal("retiring lost the data:", r, err)
}
// Coming back is given the same database.
inv, err := a.Inventory(ctx)
if err != nil {
t.Fatal(err)
}
var found *Retired
for i := range inv.Retired {
if inv.Retired[i].Consumer == p.As {
found = &inv.Retired[i]
}
}
if found == nil || !found.RetiredAt.Equal(at) || found.Why != "the mesh stopped asking for it" || found.SizeBytes <= 0 {
t.Fatalf("not listed retired with when, why and size: %+v", inv)
}
if !listHas(inv.Active, other.As) || listHas(inv.Active, p.As) {
t.Fatalf("%+v", inv)
}
// An active consumer is never deleted, whatever is asked.
if _, err := c.DeleteRetired(ctx, Retired{Consumer: other.As}); err == nil {
t.Fatal("deleted an active consumer")
}
// Asked for again: the same database, the same rows, the mark active.
if err := a.Create(ctx, p); err != nil {
t.Fatal(err)
}
if ok, _ := a.Holds(ctx, p); !ok {
t.Fatal("not held after coming back")
}
if r, err := c.as(ctx, Login{Database: p.As, User: p.As, Password: p.Password}, "SELECT count(*) FROM kept"); err != nil ||
cell(r.Rows[0], 0) != "1" {
t.Fatal("re-enabled without its data:", err)
}
if inv, _ := a.Inventory(ctx); !listHas(inv.Active, p.As) {
t.Fatalf("not active again: %+v", inv)
}
// Retired again and deleted: that database and role go; the neighbour stays.
if err := a.Retire(ctx, p.As, nil, "again", at); err != nil {
t.Fatal(err)
}
freed, err := a.Delete(ctx, Retired{Consumer: p.As, Kind: KindConsumer})
if err != nil || freed <= 0 {
t.Fatal(freed, err)
}
if has, _ := c.exists(ctx, "SELECT 1 FROM pg_database WHERE datname = "+Literal(p.As)); has {
t.Fatal("the database is still there")
}
if has, _ := c.exists(ctx, "SELECT 1 FROM pg_roles WHERE rolname = "+Literal(p.As)); has {
t.Fatal("the role is still there")
}
if ok, err := a.Holds(ctx, other); err != nil || !ok {
t.Fatal("the neighbour was touched:", ok, err)
}
// A database set aside by hand is listed since its date and can be deleted, alone.
aside, err := c.RetireDatabase(ctx, other.As, at)
if err != nil {
t.Fatal(err)
}
inv, _ = a.Inventory(ctx)
var setAside *Retired
for i := range inv.Retired {
if inv.Retired[i].Consumer == aside {
setAside = &inv.Retired[i]
}
}
if setAside == nil || setAside.Kind != KindSetAside || setAside.RetiredAt.Format("20060102") != "20261006" {
t.Fatalf("%+v", inv.Retired)
}
if _, err := a.Delete(ctx, *setAside); err != nil {
t.Fatal(err)
}
if has, _ := c.exists(ctx, "SELECT 1 FROM pg_database WHERE datname = "+Literal(aside)); has {
t.Fatal("the set-aside database is still there")
}
}