Write into a file the machine shares instead of over it, and reload a service that re-reads its configuration instead of restarting it (hq ADR 0102)

This commit is contained in:
2026-09-22 17:48:57 +02:00
parent 6eabed63eb
commit 0f126d137c
8 changed files with 575 additions and 2 deletions
+33
View File
@@ -50,6 +50,8 @@ type Outcome struct {
// wrote is a digest of what this apply put there, kept so the next one can tell a machine
// that drifted from one the mesh changed its mind about. Not reported: it is bookkeeping.
wrote string
// into is what a file written into held before the mesh's keys (novox/hq ADR 0102).
into *store.Into
}
// Report is what an apply did, in the order it did it.
@@ -298,6 +300,7 @@ func ApplyKeeping(
ID: resource.Identity(), Type: string(resource.Kind()),
Target: outcome.Target, AppliedAt: time.Now().UTC(),
Wrote: outcome.wrote,
Into: outcome.into,
Holds: holds(resource),
})
// Its module has been taken, and what was held for it is now the mesh's.
@@ -489,6 +492,9 @@ func applyAccess(r *declaration.Access) (Outcome, error) {
}
func applyFile(r *declaration.File, previous store.Applied, unseal Unseal) (Outcome, error) {
if r.Into != "" {
return applyInto(r, previous)
}
out := begin(r)
// What actually goes on disk. For a sealed file the mesh never had this, and neither did
@@ -688,6 +694,11 @@ func reflected(r *declaration.Service, changed map[string]bool) []string {
return restartedBy(r.RestartOn, changed)
}
// serviceReloader is a service manager that can tell a running unit to read its configuration again.
type serviceReloader interface {
ReloadService(ctx context.Context, run system.Runner, unit string) error
}
// unitReloader is a service manager that caches unit files and must be told to read them again.
type unitReloader interface {
ReloadUnits(ctx context.Context, run system.Runner) error
@@ -774,6 +785,25 @@ func applyService(ctx context.Context, sys system.System, r *declaration.Service
"%s was restarted to pick up a change and is %s", r.Unit, after)
}
changes = append(changes, "restarted for "+strings.Join(reflected(r, changed), ", "))
} else if r.State == "running" && len(restartedBy(r.ReloadOn, changed)) > 0 {
// Told to read its configuration again, not stopped: for a service whose restart would
// stop what it runs — every container, for the container runtime (novox/hq ADR 0102).
reloader, ok := sys.(serviceReloader)
if !ok {
return out, fmt.Errorf("%s must be reloaded for %s and this machine's service manager "+
"cannot reload a unit", r.Unit, strings.Join(restartedBy(r.ReloadOn, changed), ", "))
}
if err := reloader.ReloadService(ctx, run, r.Unit); err != nil {
return out, fmt.Errorf("reloading %s: %w", r.Unit, err)
}
after, err := sys.ServiceState(ctx, run, r.Unit)
if err != nil {
return out, err
}
if after != "running" {
return out, fmt.Errorf("%s was reloaded to pick up a change and is %s", r.Unit, after)
}
changes = append(changes, "reloaded for "+strings.Join(restartedBy(r.ReloadOn, changed), ", "))
}
if len(changes) == 0 {
@@ -828,6 +858,9 @@ func remove(ctx context.Context, sys system.System, a store.Applied, run Runner)
return "removed", "no longer declared, and empty", nil
case declaration.TypeFile:
if a.Into != nil {
return removeInto(a)
}
if err := os.RemoveAll(a.Target); err != nil {
return "", "", err
}