Keep what an adopted node was found holding until its module is taken, and report it held (hq ADR 0100)

This commit is contained in:
2026-09-22 17:14:04 +02:00
parent fcc447c216
commit 3a613113be
5 changed files with 714 additions and 3 deletions
+58 -1
View File
@@ -61,7 +61,8 @@ type Report struct {
// nothing is the ordinary steady state, and saying so is not the same as saying it failed.
func (r Report) Changed() bool {
for _, o := range r.Outcomes {
if o.Action != "unchanged" {
// Holding is keeping the machine as it was found, which is not moving it.
if o.Action != "unchanged" && o.Action != "held" {
return true
}
}
@@ -121,6 +122,23 @@ func Apply(
run Runner,
log func(string),
unseal Unseal,
) (Report, store.State, error) {
return ApplyKeeping(ctx, sys, d, known, origin, run, log, unseal, nil)
}
// ApplyKeeping is Apply on a node that may be adopted: keep is where the original of a file found
// there is recorded before anything else happens to it (novox/hq ADR 0100). Nil is a caller that
// can never be handed an adopted declaration — the carried bundle, which may not say it.
func ApplyKeeping(
ctx context.Context,
sys system.System,
d *declaration.Declaration,
known store.State,
origin string,
run Runner,
log func(string),
unseal Unseal,
keep Keep,
) (Report, store.State, error) {
if log == nil {
log = func(string) {}
@@ -182,6 +200,40 @@ func Apply(
// a declaration", and they are fixed in different places.
var failures []*Error
for _, resource := range d.Resources {
// **On an adopted node, what is found is kept until its module is taken** (novox/hq ADR
// 0100). Before anything is applied: a file present with no record of this host writing
// it, or a container present under that name that no host made, is held as it is and
// reported. Once held it stays held — changed or gone — until its module is taken, and
// it is never recorded as applied, so it is never removed as an orphan either.
if d.Adoption != nil && holdable(resource) {
if module, untaken := d.Adoption.UntakenModuleOf(resource.Identity()); untaken {
was, already := known.HeldAt(resource.Identity())
isFound := false
if !already {
var err error
if isFound, err = found(ctx, resource, run, known); err != nil {
failures = append(failures, &Error{Resource: resource.Identity(), Err: err, Done: report})
log(fmt.Sprintf(" failed %s (%s): %v", resource.Identity(), resource.Target(), err))
continue
}
}
if already || isFound {
outcome, held, err := hold(ctx, resource, module, was, already, run, keep, time.Now().UTC())
if err != nil {
failures = append(failures, &Error{Resource: resource.Identity(), Err: err, Done: report})
log(fmt.Sprintf(" failed %s (%s): %v", resource.Identity(), outcome.Target, err))
continue
}
known.RecordHeld(held)
report.Outcomes = append(report.Outcomes, outcome)
if !already || held.Changed != was.Changed {
log(fmt.Sprintf(" held %s (%s): %s", outcome.ID, outcome.Target, outcome.Detail))
}
continue
}
}
}
was, _ := known.Find(resource.Identity())
outcome, err := applyOne(ctx, sys, resource, run, changed, declares, was, unseal)
if err != nil {
@@ -229,6 +281,11 @@ func Apply(
Wrote: outcome.wrote,
Holds: holds(resource),
})
// Its module has been taken, and what was held for it is now the mesh's.
if held, wasHeld := known.HeldAt(resource.Identity()); wasHeld {
known.Release(held.ID)
outcome.Detail = takenDetail(held)
}
report.Outcomes = append(report.Outcomes, outcome)
if outcome.Action != "unchanged" {
changed[resource.Identity()] = true