A one-shot service that finished is not stopped, and a container is what it reads
Two faults that both reported success while being wrong, found while proving the firewall module actually delivers. A unit whose job is to apply something and exit — load a rule set, set a sysctl — is inactive the instant it succeeds. Reading that as stopped made it permanently unsatisfiable: the host started it, it worked, the host read back stopped and reported the machine as not doing what it was told, on every apply, for ever, with the rules correctly in place the whole time. That is what the firewall has been doing on every machine it was assigned to, and why the four-machine bed was red. And a container took its identity from its own fields, not from the files it reads. A file written in an earlier apply — or before the container declared it as a dependency — left a process holding a credential the mesh had already replaced, with everything reporting success (novox/hq 04-ISSUES/045). What a container reads is now part of what it is, so the comparison is a standing one rather than a tripwire that fires during one apply and never again.
This commit is contained in:
+29
-2
@@ -99,9 +99,10 @@ func staleIndex(out string) bool {
|
||||
// would have had to drop.
|
||||
func (arch) ServiceState(ctx context.Context, run Runner, unit string) (string, error) {
|
||||
out, _ := run(ctx, "systemctl", "show", unit,
|
||||
"--property=LoadState", "--property=ActiveState")
|
||||
"--property=LoadState", "--property=ActiveState", "--property=Type",
|
||||
"--property=RemainAfterExit", "--property=ExecMainStatus")
|
||||
|
||||
var load, active string
|
||||
var load, active, kind, remains, exited string
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
key, value, found := strings.Cut(strings.TrimSpace(line), "=")
|
||||
if !found {
|
||||
@@ -112,6 +113,12 @@ func (arch) ServiceState(ctx context.Context, run Runner, unit string) (string,
|
||||
load = value
|
||||
case "ActiveState":
|
||||
active = value
|
||||
case "Type":
|
||||
kind = value
|
||||
case "RemainAfterExit":
|
||||
remains = value
|
||||
case "ExecMainStatus":
|
||||
exited = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +136,26 @@ func (arch) ServiceState(ctx context.Context, run Runner, unit string) (string,
|
||||
return "", fmt.Errorf("%s is installed but its unit file cannot be loaded (%s)", unit, load)
|
||||
}
|
||||
|
||||
// **A one-shot that finished is not stopped.** A unit whose whole job is to apply something
|
||||
// and exit — load a rule set, set a sysctl — is reported inactive the moment it succeeds, and
|
||||
// unless it is told to linger there is no state in which it is ever "active". Reading that as
|
||||
// "stopped" makes such a unit permanently unsatisfiable: the host starts it, it does its work,
|
||||
// it exits, the host reads back "stopped" and reports failure — for ever, on every apply,
|
||||
// while the thing it configured is in place and working.
|
||||
//
|
||||
// That is not hypothetical. It is what the firewall did on every machine it was ever assigned
|
||||
// to: rules loaded, service reported failed, the mesh reported a machine not doing what it was
|
||||
// told, and the only visible symptom was a red line about a unit nobody could see anything
|
||||
// wrong with.
|
||||
//
|
||||
// So for that shape, what "running" means is "it ran, and it worked".
|
||||
if kind == "oneshot" && remains != "yes" && active == "inactive" {
|
||||
if exited == "0" || exited == "" {
|
||||
return "running", nil
|
||||
}
|
||||
return "stopped", nil
|
||||
}
|
||||
|
||||
switch active {
|
||||
case "active", "activating", "reloading":
|
||||
return "running", nil
|
||||
|
||||
Reference in New Issue
Block a user