Keep the node's own as it runs from an engine's first start, and refuse the node's own apply removing a module no want removed (issue 774, review round 1)

The reviewer showed how 76e214a still lets the 07:54 outage happen at the re-landed engine's first start on novox.
applied/ is empty there, so loadApplied loads nothing. The kept declaration retires, because every placed module has a
have. Nothing then carries the node's own composition. The guard misses nats, which has no want there, so its
nats.conf is guessed as nats's and removed.

- At the start, when the store has no node's own composition, the node's own runs as the kept declaration has it, and
  the store keeps it now. The kept declaration retires only once the store holds `node`.
- What this engine applied runs until loadHad reads the composition a differing have names. The bucket no longer keeps
  the one novox's have names, so the stored one stays.
- In the node's own apply, a resource of another module goes only when that module's want says removed
  (Scope.RemovedByWant). A module the node's own composition carried has no want, so Placed never named it.
  Cost: the node's own apply cannot drop one of its own dotted resources without such a want. It is refused whole and
  said, never removed silently.

Tests, with no new test functions:
- A first-start case in the agent's Everything test (empty store, then a start with the declaration retired). It fails
  on 76e214a.
- Two rows in the guard's table: no want refuses; a want saying removed removes.
- A case for the store in the authority test.
This commit is contained in:
2026-10-29 21:25:51 +01:00
parent 76e214a8b9
commit 81672b183d
7 changed files with 184 additions and 12 deletions
+12
View File
@@ -131,10 +131,16 @@ func TestAResourceThatMovedToItsModulesCompositionIsLeftByTheNodesOwnApply(t *te
placed bool // a want still places its module here
refused bool // the node's own apply is refused whole
stays bool // the resource is still on the machine and recorded
// wants names the modules whose want says removed (Scope.RemovedByWant); nil leaves the guard to Placed alone.
wants []string
}{
{name: "moved to its module's own composition", id: "systemd-resolved.fact-suffix", moved: true, placed: true, stays: true},
{name: "a held folded module, in no composition", id: "nats.server-conf", placed: true, refused: true, stays: true},
{name: "its module's want removed", id: "nats.server-conf"},
// Issue 774, review of the re-land, round 1: a module folded in the node's own has no want, so Placed does not name
// it; in the node's own apply only a module a want removed loses its resources.
{name: "a folded module with no want", id: "nats.server-conf", wants: []string{}, refused: true, stays: true},
{name: "a folded module a want removed", id: "nats.server-conf", wants: []string{"nats", "old"}},
} {
t.Run(tc.name, func(t *testing.T) {
dir := t.TempDir()
@@ -156,6 +162,12 @@ func TestAResourceThatMovedToItsModulesCompositionIsLeftByTheNodesOwnApply(t *te
if tc.placed {
scope.Placed = map[string]bool{module: true}
}
if tc.wants != nil {
scope.RemovedByWant = map[string]bool{}
for _, m := range tc.wants {
scope.RemovedByWant[m] = true
}
}
report, after, err := ApplyModule(context.Background(), archHost(t), &declaration.Declaration{}, scope, state,
store.OriginDeclared, noServices, nil, nil, nil, nil)
if tc.refused != (err != nil) {