A service may omit its state, so unassigning an uplink module never stops the machine's network manager (hq ADR 0117)
This commit is contained in:
+89
-9
@@ -54,6 +54,8 @@ type Outcome struct {
|
||||
into *store.Into
|
||||
// kept is where this apply kept the original of a file it wrote over (novox/hq ADR 0100).
|
||||
kept string
|
||||
// stateless is a service whose unit's lifecycle is the machine's (novox/hq ADR 0117).
|
||||
stateless bool
|
||||
// reads is, for a container, the digest of each file it was created reading, by path — so
|
||||
// the next apply can say which one changed (novox/hq 04-ISSUES/103).
|
||||
reads map[string]string
|
||||
@@ -458,19 +460,21 @@ func ApplyKeeping(
|
||||
Origin: origin,
|
||||
ID: resource.Identity(), Type: string(resource.Kind()),
|
||||
Target: outcome.Target, AppliedAt: time.Now().UTC(),
|
||||
Wrote: outcome.wrote,
|
||||
Into: outcome.into,
|
||||
Kept: kept,
|
||||
Reads: outcome.reads,
|
||||
Holds: holds(resource),
|
||||
Wrote: outcome.wrote,
|
||||
Into: outcome.into,
|
||||
Kept: kept,
|
||||
Reads: outcome.reads,
|
||||
Stateless: outcome.stateless,
|
||||
Holds: holds(resource),
|
||||
})
|
||||
// Its module has been taken, and what was held for it is now the mesh's. A file written
|
||||
// into replaced nothing that was found, so its outcome says what the write did, not that
|
||||
// a cutover happened; its hold from when it was declared whole goes all the same — here,
|
||||
// after the write worked, so a failed one keeps the hold and where its original is.
|
||||
// into, or a service whose lifecycle is the machine's, replaced nothing that was found, so
|
||||
// its outcome says what the apply did, not that a cutover happened; a hold from when it was
|
||||
// declared otherwise goes all the same — here, after the apply worked, so a failed one
|
||||
// keeps the hold and where its original is.
|
||||
if wasHeld {
|
||||
known.Release(held.ID)
|
||||
if f, isFile := resource.(*declaration.File); !isFile || f.Into == "" {
|
||||
if !replacesNothing(resource) {
|
||||
outcome.Detail = takenDetail(held)
|
||||
}
|
||||
}
|
||||
@@ -919,6 +923,9 @@ type unitReloader interface {
|
||||
|
||||
func applyService(ctx context.Context, sys system.System, r *declaration.Service, run Runner,
|
||||
changed map[string]bool) (Outcome, error) {
|
||||
if r.Stateless() {
|
||||
return reflectOnly(ctx, sys, r, run, changed)
|
||||
}
|
||||
out := begin(r)
|
||||
var changes []string
|
||||
|
||||
@@ -1029,6 +1036,73 @@ func applyService(ctx context.Context, sys system.System, r *declaration.Service
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// reflectOnly is a service whose unit's lifecycle is the machine's (novox/hq ADR 0117): nothing is
|
||||
// started, stopped, enabled or disabled, and a changed trigger is acted on only where the unit is
|
||||
// already running. An inactive unit is left so — started, it would be a second network manager on
|
||||
// a machine that uses another — and it reads the change when whatever starts it does.
|
||||
func reflectOnly(ctx context.Context, sys system.System, r *declaration.Service, run Runner,
|
||||
changed map[string]bool) (Outcome, error) {
|
||||
out := begin(r)
|
||||
out.stateless = true
|
||||
restart := reflected(r, changed)
|
||||
reload := restartedBy(r.ReloadOn, changed)
|
||||
if len(restart) == 0 && len(reload) == 0 {
|
||||
out.Action = "unchanged"
|
||||
out.Detail = "its lifecycle is the machine's; nothing it reflects changed"
|
||||
return out, nil
|
||||
}
|
||||
state, err := sys.ServiceState(ctx, run, r.Unit)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
if state != "running" {
|
||||
out.Action = "unchanged"
|
||||
out.Detail = "not running; the change applies at its next start"
|
||||
return out, nil
|
||||
}
|
||||
if len(restart) > 0 {
|
||||
// The same as a stated service: the unit's own file may be what changed, and the manager
|
||||
// reads that again only when told to.
|
||||
if u, ok := sys.(unitReloader); ok {
|
||||
if err := u.ReloadUnits(ctx, run); err != nil {
|
||||
return out, fmt.Errorf("reloading the service manager's units for %s: %w", r.Unit, err)
|
||||
}
|
||||
}
|
||||
if err := sys.SetServiceState(ctx, run, r.Unit, "stopped"); err != nil {
|
||||
return out, fmt.Errorf("restarting %s: stopping it: %w", r.Unit, err)
|
||||
}
|
||||
if err := sys.SetServiceState(ctx, run, r.Unit, "running"); err != nil {
|
||||
return out, fmt.Errorf("restarting %s: starting it again: %w", r.Unit, err)
|
||||
}
|
||||
} else {
|
||||
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(reload, ", "))
|
||||
}
|
||||
if err := reloader.ReloadService(ctx, run, r.Unit); err != nil {
|
||||
return out, fmt.Errorf("reloading %s: %w", r.Unit, err)
|
||||
}
|
||||
}
|
||||
// Read back: it was running, and a restart or reload that left it otherwise is a failure —
|
||||
// the machine's network manager down is not a change to report and move past.
|
||||
after, err := sys.ServiceState(ctx, run, r.Unit)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Action = "updated"
|
||||
if len(restart) > 0 {
|
||||
out.Detail = "restarted for " + strings.Join(restart, ", ")
|
||||
} else {
|
||||
out.Detail = "reloaded for " + strings.Join(reload, ", ")
|
||||
}
|
||||
if after != "running" {
|
||||
return out, fmt.Errorf("%s was %s to pick up a change and is %s", r.Unit,
|
||||
strings.Fields(out.Detail)[0], after)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// remove undoes one resource the host applied and the declaration no longer names, and reports
|
||||
// what it actually did.
|
||||
//
|
||||
@@ -1086,6 +1160,12 @@ func remove(ctx context.Context, sys system.System, a store.Applied, run Runner)
|
||||
return "removed", "no longer declared", nil
|
||||
|
||||
case declaration.TypeService:
|
||||
// A unit whose lifecycle was the machine's is left exactly as it is (novox/hq ADR 0117):
|
||||
// stopping it here is how unassigning an uplink module would take down the machine's
|
||||
// network manager, and with it the channel the mesh reaches the machine on.
|
||||
if a.Stateless {
|
||||
return "forgotten", "its state was never the mesh's", nil
|
||||
}
|
||||
// A unit that is no longer declared is stopped, not deleted. The host did not install
|
||||
// it and does not own the unit file — only the state it put the unit into.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user