Undeclaring gives a unit back the state it was found in, and removes a process the mesh made (hq ADR 0118, issue 130)

A service undeclared used to be stopped: unassigning the private network stopped the container
runtime, unassigning sshd would stop ssh, an uplink module would take the machine offline. The
host now records the unit's state when it first applies it and restores that on undeclare —
found running stays running; started by the mesh (the converge filter) is stopped again; nothing
is started on the way out; a pre-existing record leaves the unit alone.

An undeclared process had no removal at all and failed every apply on its node; its unit, timer
and bundle are now removed.
This commit is contained in:
jochen
2026-09-27 00:21:25 +02:00
parent 06aaac0820
commit 3112c881e4
7 changed files with 322 additions and 47 deletions
+96 -18
View File
@@ -348,33 +348,111 @@ func TestAnUnknownServiceStateIsRefusedNotGuessed(t *testing.T) {
}
}
func TestADroppedServiceIsStoppedNotDeleted(t *testing.T) {
// The host did not install the unit and does not own the unit file — only the state it put
// the unit into.
var commands []string
func TestADroppedServiceIsGivenBackTheStateItWasFoundIn(t *testing.T) {
// novox/hq ADR 0118: undeclaring removes what the mesh made, gives back what it changed, and
// leaves what was the machine's. A service resource never installs a unit — so undeclaring it
// undoes what the mesh did to the unit, and nothing more. Stopping every undeclared unit is
// how unassigning the private network stopped the container runtime (novox/hq issue 130).
cases := []struct {
name string
found *store.FoundUnit
action string
stop bool
disable bool
}{
{"recorded before the host kept what it found", nil, "forgotten", false, false},
{"running before the mesh", &store.FoundUnit{State: "running"}, "forgotten", false, false},
{"running and enabled before the mesh", &store.FoundUnit{State: "running", Boot: "enabled"}, "forgotten", false, false},
{"started by the mesh", &store.FoundUnit{State: "stopped"}, "restored", true, false},
{"started and enabled by the mesh", &store.FoundUnit{State: "stopped", Boot: "disabled"}, "restored", true, true},
{"enabled by the mesh, running before it", &store.FoundUnit{State: "running", Boot: "disabled"}, "restored", false, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var commands []string
run := func(ctx context.Context, name string, args ...string) (string, error) {
commands = append(commands, strings.Join(args, " "))
if args[0] == "show" {
return "LoadState=loaded\nActiveState=active\n", nil
}
return "", nil
}
state := store.State{Resources: []store.Applied{
{ID: "s", Type: "service", Target: "unit.service", Found: c.found},
}}
d := parse(t, `{"declaration":1,"resources":[
{"id":"other","type":"file","path":"`+filepath.Join(t.TempDir(), "a")+`","content":"a\n"}
]}`)
report, after, err := Apply(context.Background(), archHost(t), d, state, store.OriginCarried, run, nil, nil)
if err != nil {
t.Fatal(err)
}
joined := strings.Join(commands, "; ")
if stopped := strings.Contains(joined, "stop unit.service"); stopped != c.stop {
t.Errorf("stopped %v, want %v: %s", stopped, c.stop, joined)
}
if disabled := strings.Contains(joined, "disable unit.service"); disabled != c.disable {
t.Errorf("disabled %v, want %v: %s", disabled, c.disable, joined)
}
if strings.Contains(joined, "start unit.service") || strings.Contains(joined, "mask") {
t.Errorf("the host started or masked a unit on its way out: %s", joined)
}
if o := outcomeOf(report, "s"); o.Action != c.action {
t.Errorf("outcome %+v, want %s", o, c.action)
}
if _, still := after.Find("s"); still {
t.Error("the host still believes it owns the undeclared service")
}
})
}
}
func TestAServiceRecordsTheStateItWasFoundInOnceAndCarriesIt(t *testing.T) {
// Read the first time the host applies the unit — before it starts or enables anything —
// and never again: by the next apply, the unit's state is the mesh's doing.
active := "inactive"
enabled := "disabled"
run := func(ctx context.Context, name string, args ...string) (string, error) {
commands = append(commands, strings.Join(args, " "))
if args[0] == "show" {
return "LoadState=loaded\nActiveState=active\n", nil
switch args[0] {
case "show":
return "LoadState=loaded\nActiveState=" + active + "\n", nil
case "is-enabled":
return enabled, nil
case "start":
active = "active"
case "enable":
enabled = "enabled"
}
return "", nil
}
state := store.State{Resources: []store.Applied{
{ID: "s", Type: "service", Target: "gone.service"},
}}
d := parse(t, `{"declaration":1,"resources":[
{"id":"other","type":"file","path":"`+filepath.Join(t.TempDir(), "a")+`","content":"a\n"}
{"id":"s","type":"service","unit":"filter.service","state":"running","boot":"enabled"}
]}`)
if _, _, err := Apply(context.Background(), archHost(t), d, state, store.OriginCarried, run, nil, nil); err != nil {
_, first, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
if err != nil {
t.Fatal(err)
}
joined := strings.Join(commands, "; ")
if !strings.Contains(joined, "stop gone.service") {
t.Errorf("the dropped service was not stopped: %s", joined)
rec, _ := first.Find("s")
if rec.Found == nil || rec.Found.State != "stopped" || rec.Found.Boot != "disabled" {
t.Fatalf("first apply recorded %+v, want stopped and disabled", rec.Found)
}
if strings.Contains(joined, "disable") || strings.Contains(joined, "mask") {
t.Errorf("the host did more than stop a unit it does not own: %s", joined)
_, second, err := Apply(context.Background(), archHost(t), d, first, store.OriginCarried, run, nil, nil)
if err != nil {
t.Fatal(err)
}
rec, _ = second.Find("s")
if rec.Found == nil || rec.Found.State != "stopped" {
t.Errorf("a later apply replaced what was found with what the mesh made: %+v", rec.Found)
}
// A record from before the host kept what it found is not given one later.
old := store.State{Resources: []store.Applied{{ID: "s", Type: "service", Target: "filter.service"}}}
_, third, err := Apply(context.Background(), archHost(t), d, old, store.OriginCarried, run, nil, nil)
if err != nil {
t.Fatal(err)
}
if rec, _ = third.Find("s"); rec.Found != nil {
t.Errorf("a record that predates the field was given one after the mesh had acted: %+v", rec.Found)
}
}