Pass -- before every unit, and give a unit back only when it is exactly as the mesh held it
Third review of mesh-catalog #147. A unit's name can now come from a setting (a template's instance), so every systemctl call that names a unit passes `--` before it, and a name can never be read as an option. On removal, a unit is started or enabled again only when it is exactly the stopped and disabled the mesh held it to. A masked unit, or one somebody started or enabled since, is left as it is, and the report says why. A restore that fails (the start refused, or the unit not running after it) puts back what this attempt changed and is reported as waiting, so no apply is made fatal by it and the next one tries again. The tests' fake service managers read systemctl's arguments in the shape they were written for (oldShape); internal/system's own test holds the `--`.
This commit is contained in:
@@ -187,9 +187,9 @@ func describeAge(when, now time.Time) string {
|
||||
// so LoadState is what is read — and it is the thing an interface spanning systemd and OpenRC
|
||||
// would have had to drop.
|
||||
func (arch) ServiceState(ctx context.Context, run Runner, unit string) (string, error) {
|
||||
out, asked := run(ctx, "systemctl", "show", unit,
|
||||
out, asked := run(ctx, "systemctl", "show",
|
||||
"--property=LoadState", "--property=ActiveState", "--property=Type",
|
||||
"--property=RemainAfterExit", "--property=ExecMainStatus")
|
||||
"--property=RemainAfterExit", "--property=ExecMainStatus", "--", unit)
|
||||
|
||||
var load, active, kind, remains, exited string
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
@@ -266,7 +266,7 @@ func (arch) SetServiceState(ctx context.Context, run Runner, unit, state string)
|
||||
if state == "stopped" {
|
||||
verb = "stop"
|
||||
}
|
||||
_, err := run(ctx, "systemctl", verb, unit)
|
||||
_, err := run(ctx, "systemctl", verb, "--", unit)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ func (arch) SetServiceState(ctx context.Context, run Runner, unit, state string)
|
||||
// install section and CANNOT be enabled. Reading it as "disabled" would have the host try, fail,
|
||||
// and blame the wrong thing — the same shape as reading a missing unit as "stopped".
|
||||
func (arch) ServiceBoot(ctx context.Context, run Runner, unit string) (string, error) {
|
||||
out, _ := run(ctx, "systemctl", "is-enabled", unit)
|
||||
out, _ := run(ctx, "systemctl", "is-enabled", "--", unit)
|
||||
switch state := strings.TrimSpace(out); state {
|
||||
case "enabled", "enabled-runtime", "alias":
|
||||
return "enabled", nil
|
||||
@@ -302,7 +302,7 @@ func (arch) SetServiceBoot(ctx context.Context, run Runner, unit, boot string) e
|
||||
if boot == "disabled" {
|
||||
verb = "disable"
|
||||
}
|
||||
_, err := run(ctx, "systemctl", verb, unit)
|
||||
_, err := run(ctx, "systemctl", verb, "--", unit)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ func (arch) RemoveUserFromGroup(ctx context.Context, run Runner, name, group str
|
||||
// is how the host tells a unit an administrator installed, under /etc or /run, from one a package
|
||||
// ships under /usr (novox/hq ADR 0103). Empty, with no error, for a unit that loads from nowhere.
|
||||
func (arch) ServiceUnitFile(ctx context.Context, run Runner, unit string) (string, error) {
|
||||
out, err := run(ctx, "systemctl", "show", unit, "--property=FragmentPath")
|
||||
out, err := run(ctx, "systemctl", "show", "--property=FragmentPath", "--", unit)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("the service manager did not say where %s comes from: %w", unit, err)
|
||||
}
|
||||
@@ -376,7 +376,7 @@ func (arch) ReloadUnits(ctx context.Context, run Runner) error {
|
||||
|
||||
// ReloadService tells a running unit to read its configuration again, without stopping it.
|
||||
func (arch) ReloadService(ctx context.Context, run Runner, unit string) error {
|
||||
_, err := run(ctx, "systemctl", "reload", unit)
|
||||
_, err := run(ctx, "systemctl", "reload", "--", unit)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Third review of mesh-catalog #147: a unit's name reaches systemctl as an argument, and since a setting
|
||||
// may now name a template's instance, a name beginning with '-' must never be read as an option. Every
|
||||
// call that names a unit passes `--` before it.
|
||||
func TestEverySystemctlCallPassesDashDashBeforeTheUnit(t *testing.T) {
|
||||
var asked [][]string
|
||||
run := func(_ context.Context, name string, args ...string) (string, error) {
|
||||
if name == "systemctl" {
|
||||
asked = append(asked, args)
|
||||
}
|
||||
return "LoadState=loaded\nActiveState=active\nType=simple\nFragmentPath=/x\n", nil
|
||||
}
|
||||
ctx := context.Background()
|
||||
a := arch{}
|
||||
unit := "-x@y.timer"
|
||||
a.ServiceState(ctx, run, unit)
|
||||
a.SetServiceState(ctx, run, unit, "running")
|
||||
a.SetServiceState(ctx, run, unit, "stopped")
|
||||
a.ServiceBoot(ctx, run, unit)
|
||||
a.SetServiceBoot(ctx, run, unit, "enabled")
|
||||
a.SetServiceBoot(ctx, run, unit, "disabled")
|
||||
if len(asked) != 6 {
|
||||
t.Fatalf("asked %d times: %v", len(asked), asked)
|
||||
}
|
||||
for _, args := range asked {
|
||||
if len(args) < 2 || args[len(args)-1] != unit || args[len(args)-2] != "--" {
|
||||
t.Errorf("systemctl %s", strings.Join(args, " "))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user