Refuse an apply that would remove a resource of a module a want still places here, so a composition that lost what runs can never remove the bus server or a held module again (issue 774)
This commit is contained in:
@@ -312,6 +312,27 @@ type Scope struct {
|
||||
Module string
|
||||
// Others are the resources of every other module the node has a composition for.
|
||||
Others []declaration.Resource
|
||||
// Placed are the modules a want still places on the node (present, not removed), held ones included: no apply but
|
||||
// the module's own removes a resource of one of them (issue 774: on 2026-10-29 the node's own apply on the bus node,
|
||||
// composed without what its folded modules ran, removed the bus server and four held modules). Nil guards nothing.
|
||||
Placed map[string]bool
|
||||
}
|
||||
|
||||
// placedOwner is the module a want still places on the node that the record belongs to, other than the scope's own:
|
||||
// the module recorded with it, else the one its id names (a resource the node's own composition carried for a folded
|
||||
// module is recorded as the node's, its id naming the module). Empty when there is none.
|
||||
func (sc *Scope) placedOwner(a store.Applied) string {
|
||||
if sc == nil || len(sc.Placed) == 0 {
|
||||
return ""
|
||||
}
|
||||
m := a.Module
|
||||
if m == "" || m == store.NodeModule {
|
||||
m, _ = declaration.ModuleGuess(a.ID)
|
||||
}
|
||||
if m == "" || m == sc.Module || !sc.Placed[m] {
|
||||
return ""
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// owns reports whether a record belongs to the scope's module.
|
||||
@@ -441,6 +462,31 @@ func applyScoped(
|
||||
}
|
||||
}
|
||||
|
||||
// **A module a want still places here is never removed by another's apply** (issue 774): its resources go only with
|
||||
// its own apply or its want's removal. One that would is refused whole, before anything is changed, naming them: a
|
||||
// composition that leaves them out lost what runs, and removing what runs is the worst a node can do with that.
|
||||
if scope != nil {
|
||||
var refused []string
|
||||
for _, orphan := range known.Orphans(declared, origin) {
|
||||
if !scope.owns(orphan) {
|
||||
continue
|
||||
}
|
||||
if _, kept := heldByOthers[orphan.ID]; kept {
|
||||
continue
|
||||
}
|
||||
if m := scope.placedOwner(orphan); m != "" {
|
||||
refused = append(refused, orphan.ID+" of "+m)
|
||||
}
|
||||
}
|
||||
if len(refused) > 0 {
|
||||
sort.Strings(refused)
|
||||
err := fmt.Errorf("the apply of %s would remove %s, of modules a want still places here; a module's resources "+
|
||||
"go only with its own apply or its want's removal, so nothing was applied", scope.Module, strings.Join(refused, ", "))
|
||||
log(" refused: " + err.Error())
|
||||
return report, known, &Error{Resource: "the resources of modules placed here", Err: err, Done: report}
|
||||
}
|
||||
}
|
||||
|
||||
removeOrphan := func(orphan store.Applied) error {
|
||||
var action, detail string
|
||||
var err error
|
||||
|
||||
Reference in New Issue
Block a user