Review round 1 of the bus want: a removal keeps the bus server, the have says a server made anew runs the want's fingerprint, the old build is read before the first write, and an unanswered inspect fails the apply rather than replacing the server (ADR 0594 §2, §4)
mesh/delivery-group group feat/774-bus-want rejected: composed together: the checker's verdict at the members' heads (a0ab6ce2e316): novox
mesh/delivery closed: the pull request closed unmerged
mesh/check failed: novox: not composed on novox: nats has not been built for this mesh: server is its "server" artifact… | 2c5f818d1522 on 798d73eb11e9

This commit is contained in:
2026-11-03 09:18:31 +01:00
parent a096abac92
commit 2c5f818d15
7 changed files with 279 additions and 55 deletions
+85 -17
View File
@@ -2,9 +2,10 @@ package apply
import (
"context"
"fmt"
"errors"
"os"
"path/filepath"
"slices"
"strings"
"testing"
@@ -225,8 +226,24 @@ func TestAResourceThatMovedToItsModulesCompositionIsLeftByTheNodesOwnApply(t *te
// while the bus was folded. Kept (a want without the warrant's mark), the server's container and its program archive
// are left as they run, whatever the build changed in them, and become nats's; its files and the tools' account file
// are written. Not kept, the same apply replaces the container.
for _, keep := range []bool{true, false} {
t.Run(fmt.Sprintf("the bus server kept %v", keep), func(t *testing.T) {
// Review of 774, round 1: the keep is decided once before anything moves, by the runtime's answer for the server. A
// server not there is made and its archives applied with it, none said kept; a runtime that does not answer fails the
// apply, nothing written and nothing replaced; a removal of the bus removes the rest and leaves the server.
for _, tc := range []struct {
name string
keep bool
gone bool // the server is not on the machine
mute bool // the runtime does not answer the server's inspect
remove bool // the bus module's want is removed
}{
{name: "kept", keep: true},
{name: "not kept"},
{name: "kept, the server not there", keep: true, gone: true},
{name: "kept, the runtime does not answer", keep: true, mute: true},
{name: "kept, the bus removed", keep: true, remove: true},
} {
t.Run("the bus server "+tc.name, func(t *testing.T) {
keep := tc.keep && !tc.gone
dir := t.TempDir()
conf, account, program := filepath.Join(dir, "nats.conf"), filepath.Join(dir, "tools", "account"), filepath.Join(dir, "program")
bus := func(env, confBody, programBody, module string) *declaration.Declaration {
@@ -250,29 +267,54 @@ func TestAResourceThatMovedToItsModulesCompositionIsLeftByTheNodesOwnApply(t *te
if made == nil {
t.Fatalf("the node's own made no server: %v", m.asked)
}
if tc.gone {
delete(m.containers, "mesh-broker-nats")
}
m.asked = nil
run := m.run
if tc.mute {
run = func(ctx context.Context, name string, args ...string) (string, error) {
if name == "docker" && len(args) > 1 && args[0] == "container" && args[1] == "inspect" {
m.asked = append(m.asked, "docker "+strings.Join(args, " "))
return "", errors.New("Cannot connect to the Docker daemon at unix:///var/run/docker.sock")
}
return m.run(ctx, name, args...)
}
}
next := bus("2", "b", "two", "nats")
next.Resources = append(next.Resources, declare(t, `{"id":"nats.tools-account","type":"file","path":"`+account+
`","content":"tools","module":"nats"}`).Resources...)
scope := Scope{Module: "nats", Placed: map[string]bool{"nats": true}}
if keep {
if tc.keep {
scope.Keep = map[string]bool{"nats.server": true, "nats.program-snapshot": true}
}
moved := func() []string {
var out []string
for _, a := range m.asked {
for _, verb := range []string{"docker rm", "docker run", "docker create", "docker stop", "docker restart", "docker rename"} {
if strings.HasPrefix(a, verb+" ") {
out = append(out, a)
}
}
}
return out
}
report, after, err := ApplyModule(context.Background(), archHost(t), next, scope, state,
store.OriginDeclared, m.run, nil, nil, nil, nil)
store.OriginDeclared, run, nil, nil, nil, nil)
if tc.mute {
if err == nil || !strings.Contains(err.Error(), "did not say whether the bus server") {
t.Fatalf("an unanswered inspect did not fail the apply: %v", err)
}
if got, _ := os.ReadFile(conf); string(got) != "a" || len(moved()) > 0 {
t.Errorf("an apply that could not tell whether the server runs wrote %q and moved %v", got, moved())
}
return
}
if err != nil {
t.Fatal(err)
}
var moved []string
for _, a := range m.asked {
for _, verb := range []string{"docker rm", "docker run", "docker create", "docker stop", "docker restart"} {
if strings.HasPrefix(a, verb+" ") {
moved = append(moved, a)
}
}
}
if keep == (len(moved) > 0) {
t.Fatalf("kept %v, and the server was moved by %v", keep, moved)
if keep == (len(moved()) > 0) {
t.Fatalf("kept %v, and the server was moved by %v", keep, moved())
}
if got, _ := os.ReadFile(conf); string(got) != "b" {
t.Errorf("the server's file holds %q", got)
@@ -283,7 +325,8 @@ func TestAResourceThatMovedToItsModulesCompositionIsLeftByTheNodesOwnApply(t *te
if got, _ := os.ReadFile(filepath.Join(program, "snapshot")); keep != (string(got) == "one") {
t.Errorf("kept %v, and the server's program is %q", keep, got)
}
for _, id := range []string{"nats.server", "nats.program-snapshot"} {
var kept []string
for _, id := range []string{"nats.program-snapshot", "nats.server"} {
rec, ok := after.Find(id)
if !ok || rec.Module != "nats" {
t.Errorf("%s is recorded %+v, not as nats's", id, rec)
@@ -291,10 +334,35 @@ func TestAResourceThatMovedToItsModulesCompositionIsLeftByTheNodesOwnApply(t *te
if o := outcomeOf(report, id); keep != (o.Action == keptAsItRuns) {
t.Errorf("kept %v, and %s was %s: %s", keep, id, o.Action, o.Detail)
}
if keep {
kept = append(kept, id)
}
}
if keep && report.Changed() == false {
if !slices.Equal(report.Kept, kept) && !(len(kept) == 0 && len(report.Kept) == 0) {
t.Errorf("the report says it kept %v, want %v", report.Kept, kept)
}
if tc.keep && report.Changed() == false {
t.Error("an apply that wrote the server's files says nothing changed")
}
if !tc.remove {
return
}
m.asked = nil
gone, last, err := ApplyModule(context.Background(), archHost(t), &declaration.Declaration{}, scope, after,
store.OriginDeclared, run, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
if len(moved()) > 0 || m.containers["mesh-broker-nats"] == nil || !exists(filepath.Join(program, "snapshot")) {
t.Errorf("the bus's removal removed its server: %v", moved())
}
if exists(conf) || exists(account) {
t.Errorf("the bus's removal left the rest of it: conf %v, account %v", exists(conf), exists(account))
}
if _, ok := last.Find("nats.server"); !ok || !slices.Equal(gone.Kept, []string{"nats.program-snapshot", "nats.server"}) &&
!slices.Equal(gone.Kept, []string{"nats.server", "nats.program-snapshot"}) {
t.Errorf("the removal kept %v, the server recorded %v", gone.Kept, ok)
}
})
}
}