package system import ( "context" "testing" ) // How a one-shot unit's state is read: what it did, not whether a process is up. // // **A one-shot that did its work and exited is satisfied, not stopped.** Its unit loads a rule set // and exits, so it is inactive the instant it succeeds; reading that as stopped made the firewall // permanently unsatisfiable, on every apply, with the rules correctly in place the whole time. // // **One that failed is still stopped**, or the host would report success for work that did not // happen — the opposite mistake and the worse one. // // **One that never ran is stopped** (novox/hq issue 1126, the kernel's boot-time cleanup on ace): // systemd says ExecMainStatus=0 for a unit that never ran exactly as for one that succeeded, and // only its start timestamp separates them. Read as "running", a package's one-shot declared // running was never started, and one declared stopped could never be. // // **At rest** is whether a one-shot that does not linger has nothing running now: a declaration // that it be stopped is then satisfied, whatever it last did, because there is nothing to stop. func TestAOneShotIsReadByWhatItDid(t *testing.T) { for _, c := range []struct { name, show, want string atRest bool }{ {"finished", "ActiveState=inactive\nType=oneshot\nRemainAfterExit=no\nExecMainStatus=0\n" + "ExecMainStartTimestampMonotonic=5123456\n", "running", true}, {"finished, no timestamp read", "ActiveState=inactive\nType=oneshot\nRemainAfterExit=no\n" + "ExecMainStatus=0\n", "running", true}, {"failed", "ActiveState=inactive\nType=oneshot\nRemainAfterExit=no\nExecMainStatus=1\n" + "ExecMainStartTimestampMonotonic=5123456\n", "stopped", true}, {"never ran", "ActiveState=inactive\nType=oneshot\nRemainAfterExit=no\nExecMainStatus=0\n" + "ExecMainStartTimestampMonotonic=0\n", "stopped", true}, {"running now", "ActiveState=activating\nType=oneshot\nRemainAfterExit=no\nExecMainStatus=0\n" + "ExecMainStartTimestampMonotonic=5123456\n", "running", false}, {"lingers", "ActiveState=active\nType=oneshot\nRemainAfterExit=yes\nExecMainStatus=0\n", "running", false}, {"a daemon, stopped", "ActiveState=inactive\nType=simple\nRemainAfterExit=no\nExecMainStatus=0\n", "stopped", false}, } { t.Run(c.name, func(t *testing.T) { run := func(_ context.Context, _ string, _ ...string) (string, error) { return "LoadState=loaded\n" + c.show, nil } state, err := arch{}.ServiceState(context.Background(), run, "thing.service") if err != nil { t.Fatalf("read as an error: %v", err) } if state != c.want { t.Errorf("reads as %q, want %q", state, c.want) } rest, err := arch{}.ServiceAtRest(context.Background(), run, "thing.service") if err != nil { t.Fatalf("at rest read as an error: %v", err) } if rest != c.atRest { t.Errorf("at rest is %v, want %v", rest, c.atRest) } }) } }