Retire the networking bundle and what the control plane stops shipping (hq ADR 0226)

networking required mesh-wireguard and nothing else; machines are assigned the network directly.
module forget refuses a provided module, so a retired one is removed at start once no machine has it.
Guard route-proxy's public account directory against a reissue.
This commit is contained in:
jochen
2026-10-06 02:21:18 +02:00
parent e096b4595a
commit 64bc138692
10 changed files with 381 additions and 52 deletions
+86
View File
@@ -284,6 +284,92 @@ func (i *Inventory) Provide(ctx context.Context, m catalogue.Manifest) error {
return err
}
// RetireUnshipped removes every module the control plane recorded as its own and no longer ships.
//
// **`module forget` refuses a provided module**, rightly: the next start would put it back. So a
// module the control plane stops shipping — `networking`, retired by novox/hq ADR 0226 — could be
// removed by nothing at all, and would sit in the catalogue for ever, assignable and pointing at a
// manifest no release carries. This is the other half of Provide: what this release ships is
// recorded, and what it does not is taken away.
//
// **Never from under a machine.** A retired module still assigned somewhere is kept, and named in
// `kept` with the machines it is on, so the caller can say "unassign it, and it goes at the next
// start". Taking it while assigned would drop whatever it pulled in — for `networking`, the
// private network itself — at the next push, with nobody having asked for that. Its settings,
// secrets and ports are kept the same way: a provided module that holds any is kept and named,
// because discarding them is a person's decision (novox/hq 04-ISSUES/017).
func (i *Inventory) RetireUnshipped(ctx context.Context, shipped []string) (retired []string, kept map[string]string, err error) {
keep := map[string]bool{}
for _, s := range shipped {
keep[s] = true
}
rows, err := i.store.Pool().Query(ctx,
`select name from module where source = 'the control plane' order by name`)
if err != nil {
return nil, nil, err
}
var gone []string
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
rows.Close()
return nil, nil, err
}
if !keep[name] {
gone = append(gone, name)
}
}
rows.Close()
if err := rows.Err(); err != nil {
return nil, nil, err
}
kept = map[string]string{}
for _, name := range gone {
on, err := i.assignedOn(ctx, name)
if err != nil {
return nil, nil, err
}
if len(on) > 0 {
kept[name] = "still assigned on " + strings.Join(on, ", ") + "; unassign it and it goes at the next start"
continue
}
held, err := i.HeldFor(ctx, name)
if err != nil {
return nil, nil, err
}
if held.Any() {
kept[name] = "the mesh still holds things for it:\n" + strings.Join(held.Lines(), "\n")
continue
}
if err := i.discard(ctx, name); err != nil {
return nil, nil, err
}
retired = append(retired, name)
}
return retired, kept, nil
}
// assignedOn is the machines a module is assigned to, sorted.
func (i *Inventory) assignedOn(ctx context.Context, name string) ([]string, error) {
rows, err := i.store.Pool().Query(ctx,
`select n.name from assignment a join node n on n.id = a.node where a.module = $1
order by n.name`, name)
if err != nil {
return nil, err
}
defer rows.Close()
var on []string
for rows.Next() {
var node string
if err := rows.Scan(&node); err != nil {
return nil, err
}
on = append(on, node)
}
return on, rows.Err()
}
// Provided reports whether a module came with the control plane rather than from a repository.
func (i *Inventory) Provided(ctx context.Context, name string) (bool, error) {
var source *string
+91
View File
@@ -0,0 +1,91 @@
package inventory
import (
"errors"
"strings"
"testing"
"github.com/jackc/pgx/v5"
)
// What a release stops shipping is retired at the next start, and never from under a machine
// (novox/hq ADR 0226). `module forget` refuses a provided module, so without this a module the
// control plane no longer carries could be removed by nothing.
func TestAModuleTheControlPlaneNoLongerShipsIsRetired(t *testing.T) {
inv := fresh(t)
ctx := t.Context()
for _, m := range []string{"mesh-wireguard", "networking"} {
if err := inv.Provide(ctx, manifest(m, nil, nil)); err != nil {
t.Fatal(err)
}
}
retired, kept, err := inv.RetireUnshipped(ctx, []string{"mesh-wireguard"})
if err != nil {
t.Fatal(err)
}
if strings.Join(retired, ",") != "networking" || len(kept) != 0 {
t.Fatalf("retired %v, kept %v", retired, kept)
}
if _, err := inv.Provided(ctx, "networking"); !errors.Is(err, pgx.ErrNoRows) {
t.Fatalf("networking is still in the catalogue: %v", err)
}
if provided, err := inv.Provided(ctx, "mesh-wireguard"); err != nil || !provided {
t.Fatalf("what this release ships went too: %v %v", provided, err)
}
}
func TestARetiredModuleStillAssignedIsKeptAndSaidSo(t *testing.T) {
inv := fresh(t)
ctx := t.Context()
if _, err := inv.AddNode(ctx, "anchor"); err != nil {
t.Fatal(err)
}
if err := inv.Provide(ctx, manifest("networking", nil, nil)); err != nil {
t.Fatal(err)
}
if _, err := inv.Assign(ctx, "anchor", "networking"); err != nil {
t.Fatal(err)
}
retired, kept, err := inv.RetireUnshipped(ctx, nil)
if err != nil {
t.Fatal(err)
}
if len(retired) != 0 {
// Taking it now would drop whatever it pulled in at the next push — for the bundle, the
// private network.
t.Fatalf("a module assigned on a machine was retired: %v", retired)
}
if !strings.Contains(kept["networking"], "anchor") {
t.Fatalf("keeping it does not say where it is still assigned: %v", kept)
}
// Unassigned, the next start takes it.
if err := inv.Unassign(ctx, "anchor", "networking"); err != nil {
t.Fatal(err)
}
retired, _, err = inv.RetireUnshipped(ctx, nil)
if err != nil {
t.Fatal(err)
}
if strings.Join(retired, ",") != "networking" {
t.Fatalf("unassigned, it was still not retired: %v", retired)
}
}
func TestAModuleFromARepositoryIsNeverRetiredByThis(t *testing.T) {
// Only what the control plane recorded as its own. A module somebody registered is theirs to
// forget.
inv := fresh(t)
ctx := t.Context()
if err := inv.RegisterModule(ctx, manifest("public-acme", nil, nil), Source{}); err != nil {
t.Fatal(err)
}
retired, kept, err := inv.RetireUnshipped(ctx, nil)
if err != nil {
t.Fatal(err)
}
if len(retired) != 0 || len(kept) != 0 {
t.Fatalf("a registered module was considered: retired %v, kept %v", retired, kept)
}
}