Name every held kind of each module the flip takes in the converge preview and its digest (hq ADR 0103)

This commit is contained in:
2026-09-22 18:27:19 +02:00
parent 689c2c6d33
commit bfe4991dd7
2 changed files with 59 additions and 8 deletions
+43 -1
View File
@@ -168,7 +168,7 @@ func TestConvergingPreviewsThenChangesAndAdoptingKeepsWhatWasTaken(t *testing.T)
// The anchor faces inward and is on the private network: the derived filter admits ssh
// from the mesh only.
"WILL CLOSE to everything outside the private network — ssh stays open from the mesh",
"notes\n replacing the found file /etc/notes.conf (original kept at",
"notes\n replacing the found file /etc/notes.conf (notes.conf), original kept at",
"assigns nftables",
"the found firewall (ufw) is disabled, never flushed",
// What it routes is not a listener: said not to be previewed, and to be dropped.
@@ -431,3 +431,45 @@ func TestTheFlipHoldsTheNodeWhileItSends(t *testing.T) {
}
release()
}
// novox/hq ADR 0103: the preview names every kind of thing a module the flip takes holds as found,
// not only its files, and the digest changes when any of them does.
func TestThePreviewNamesEveryHeldKind(t *testing.T) {
open, _ := anAdoptedAnchor(t)
ctx := t.Context()
if _, err := take(ctx, open, "anchor", "hello-web"); err != nil {
t.Fatal(err)
}
since := time.Now()
held := []link.Held{heldFile,
{ID: "notes.data", Module: "notes", Kind: "directory", Target: "/var/lib/notes", Since: since},
{ID: "notes.daemon", Module: "notes", Kind: "service", Target: "notes.service", Since: since},
{ID: "notes.seed", Module: "notes", Kind: "archive", Target: "/srv/notes", Since: since},
{ID: "notes.worker", Module: "notes", Kind: "process", Target: "notes-worker", Since: since},
{ID: "notes.account", Module: "notes", Kind: "user", Target: "notes", Since: since},
}
reportsHolding(t, open, held...)
preview, err := converge(ctx, open, "anchor", false, "", "")
if err != nil {
t.Fatal(err)
}
for _, want := range []string{
"replacing the found directory /var/lib/notes (notes.data)",
"replacing the found service notes.service (notes.daemon)",
"replacing the found archive /srv/notes (notes.seed)",
"replacing the found process notes-worker (notes.worker)",
"replacing the found user notes (notes.account)",
} {
if !strings.Contains(preview, want) {
t.Errorf("the preview does not say %q:\n%s", want, preview)
}
}
reportsHolding(t, open, held[:len(held)-1]...)
fewer, err := converge(ctx, open, "anchor", false, "", "")
if err != nil {
t.Fatal(err)
}
if digestIn(t, fewer) == digestIn(t, preview) {
t.Fatal("the digest does not change with what is held")
}
}
+16 -7
View File
@@ -119,7 +119,7 @@ func take(ctx context.Context, open *stores, node, module string) (string, error
var replaces []string
for _, h := range reported.Held {
if h.Module == module {
replaces = append(replaces, fmt.Sprintf(" %s %s (%s)", h.Kind, h.Target, h.ID))
replaces = append(replaces, " "+heldLine(h))
}
}
if len(replaces) > 0 {
@@ -347,14 +347,18 @@ func previewOf(node string, reported inventory.Adoption, derived derivedFilter,
for _, m := range takes {
fmt.Fprintf(&b, " %s\n", m)
said = append(said, "take "+m)
// Every kind it holds — a directory, a service, an archive, a process, a user as well as a
// file (novox/hq ADR 0103) — each said, and each part of what the flip is asked to act on.
for _, h := range reported.Held {
if h.Module == m && h.Kind == "file" {
fmt.Fprintf(&b, " replacing the found file %s", h.Target)
if h.Kept != "" {
fmt.Fprintf(&b, " (original kept at %s)", h.Kept)
}
b.WriteString("\n")
if h.Module != m {
continue
}
fmt.Fprintf(&b, " replacing the found %s", heldLine(h))
if h.Kept != "" {
fmt.Fprintf(&b, ", original kept at %s", h.Kept)
}
b.WriteString("\n")
said = append(said, "replace "+m+" "+heldLine(h)+" "+h.Kept)
}
}
if !filterAssigned {
@@ -427,6 +431,11 @@ func (d derivedFilter) fate(r inventory.Reach) string {
return "WILL CLOSE — no module assigned here declares it"
}
// heldLine is one thing a node holds as found, as take and the converge preview both say it.
func heldLine(h inventory.Held) string {
return fmt.Sprintf("%s %s (%s)", h.Kind, h.Target, h.ID)
}
// loopback is an address nothing off the machine reaches.
func loopback(address string) bool {
a := strings.Trim(address, "[]")