Hold out a module whose own composition waits to be applied, so the node's own apply never removes the resources it moved there (issue 1944, issue 1945)
This commit is contained in:
+44
-2
@@ -389,6 +389,48 @@ func (a *Agent) others(except string) []delivery.Composed {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// heldOut is what an apply or a removal holds out: others, and every module whose own composition the node's bucket holds
|
||||||
|
// and the agent does not run yet. A module that left the node's own composition for one of its own keeps its resources
|
||||||
|
// until that one is applied, so the node's own apply never removes them in between (issue 1945, live on 2026-10-25:
|
||||||
|
// shanks lost systemd-resolved's guard while its composition waited for a credential). The composition its want names,
|
||||||
|
// else its newest. Not part of Everything: what has not been applied is not what runs.
|
||||||
|
func (a *Agent) heldOut(except string) []delivery.Composed {
|
||||||
|
out := a.others(except)
|
||||||
|
held := map[string]bool{}
|
||||||
|
for _, c := range out {
|
||||||
|
held[c.Module] = true
|
||||||
|
}
|
||||||
|
waiting := map[string]compNow{}
|
||||||
|
for key, c := range a.comps {
|
||||||
|
_, m, fp, isNode, err := delivery.ParseComposedKey(key)
|
||||||
|
if err != nil || isNode || c.Err != nil || m == except || held[m] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if h, ok := a.running[m]; ok && h.Phase != delivery.PhaseRemoved {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if w := a.wants[m]; w.Present && !w.Want.Removed && w.Want.FP == fp {
|
||||||
|
waiting[m] = c
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if w := a.wants[m]; w.Present && !w.Want.Removed && waiting[m].Rev != 0 && waiting[m].C.FP == w.Want.FP {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if prev, ok := waiting[m]; !ok || c.Rev > prev.Rev {
|
||||||
|
waiting[m] = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(waiting))
|
||||||
|
for m := range waiting {
|
||||||
|
names = append(names, m)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, m := range names {
|
||||||
|
out = append(out, waiting[m].C)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// Everything is what the node runs: the compositions the agent runs, and the kept declaration's resources for each module
|
// Everything is what the node runs: the compositions the agent runs, and the kept declaration's resources for each module
|
||||||
// the agent does not hold yet. What the node judges its health by, and what the filling, the take act and the judgement
|
// the agent does not hold yet. What the node judges its health by, and what the filling, the take act and the judgement
|
||||||
// of directories read, is built from it.
|
// of directories read, is built from it.
|
||||||
@@ -457,7 +499,7 @@ func (a *Agent) settleOne(ctx context.Context, module string) {
|
|||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
return // held for a failed backup, and no answer since
|
return // held for a failed backup, and no answer since
|
||||||
}
|
}
|
||||||
others := a.others(module)
|
others := a.heldOut(module)
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
a.apply(ctx, module, w, comp, others)
|
a.apply(ctx, module, w, comp, others)
|
||||||
}
|
}
|
||||||
@@ -559,7 +601,7 @@ func (a *Agent) remove(ctx context.Context, module string, w wantNow) {
|
|||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
others := a.others(module)
|
others := a.heldOut(module)
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
res := a.env.Remove(ctx, module, others)
|
res := a.env.Remove(ctx, module, others)
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
|||||||
@@ -640,3 +640,39 @@ func TestAHaveWithNoCompositionDefersToTheKeptDeclaration(t *testing.T) {
|
|||||||
t.Fatalf("applied %v", got)
|
t.Fatalf("applied %v", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A module that leaves the node's own composition for a composition of its own keeps its resources until that one is
|
||||||
|
// applied: the node's own apply holds out every module whose composition the node's bucket holds and the agent does not
|
||||||
|
// run yet (issue 1945, live on 2026-10-25 at 02:32: shanks' node-engine applied a node's own composition that no longer
|
||||||
|
// carried systemd-resolved, whose own composition waited for its credential, and removed its guard, so the machine lost
|
||||||
|
// its resolver).
|
||||||
|
func TestTheNodesOwnApplyHoldsOutAModuleThatMovedToItsOwnComposition(t *testing.T) {
|
||||||
|
b := newBed(t)
|
||||||
|
fp := fpOf("b")
|
||||||
|
// systemd-resolved's own composition is written; no want names it yet.
|
||||||
|
rk, rraw, _ := composition(t, "systemd-resolved", fp, false, file("systemd-resolved", "guard", "g"))
|
||||||
|
b.comps.before(rk, rraw)
|
||||||
|
nk, nraw, ndig := composition(t, NodeModule, "", false)
|
||||||
|
nwk, err := delivery.WantNodeKey(node)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
nwraw, err := delivery.EncodeNodeWant(delivery.NodeWant{Composed: ndig})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b.comps.before(nk, nraw)
|
||||||
|
b.wants.before(nwk, nwraw)
|
||||||
|
stop := b.run()
|
||||||
|
defer stop()
|
||||||
|
eventually(t, "the node's own not applied", func() bool { return len(b.appliedList()) == 1 })
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
var names []string
|
||||||
|
for _, c := range b.others[0] {
|
||||||
|
names = append(names, c.Module)
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.Join(names, ","), "systemd-resolved") {
|
||||||
|
t.Fatalf("the others of the node's own apply were %v: systemd-resolved's resources would be removed", names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user