Files
mesh-catalog/modules/systemd/cmd/systemd-tools/client_test.go
T
jochen c42f1ce45b systemd: port to Go, and read a system unit's journal as root
The journal verb ran journalctl as the operator account, which outside the
journal's group sees only its own entries: every system service read
'-- No entries --', and a person reached for a shell. The read now
escalates with sudo -n like the acts; ported to Go with every test. hq
issue 255.
2026-10-05 18:09:42 +02:00

295 lines
11 KiB
Go

package main
// The service manager's verbs over a fake runner (novox/hq ADR 0177, to-be 41 WP4), ported with the
// TypeScript module's tests: which manager a call reaches and how, acts on the system manager escalated,
// failures named rather than read as empty answers, whether the mesh declares a unit — and the system
// journal read escalated (novox/hq issue 255).
import (
"encoding/json"
"errors"
"os"
"reflect"
"strings"
"testing"
)
type call struct {
cmd string
args []string
env []string
}
func fake(answer func(c call) Ran, calls *[]call) Runner {
return func(cmd string, args []string, env []string) Ran {
c := call{cmd, args, env}
if calls != nil {
*calls = append(*calls, c)
}
return answer(c)
}
}
const (
list = "sshd.service loaded active running OpenSSH Daemon\nbroken.service loaded failed failed A broken thing\n"
showMesh = "LoadState=loaded\nActiveState=active\nSubState=running\nUnitFileState=enabled\nMainPID=42\nExecMainStatus=0\nDescription=showcase, a mesh daemon\nFragmentPath=/etc/systemd/system/showcase.service\n"
showPackage = "LoadState=loaded\nActiveState=active\nSubState=running\nUnitFileState=enabled\nMainPID=7\nExecMainStatus=0\nDescription=OpenSSH Daemon\nFragmentPath=/usr/lib/systemd/system/sshd.service\n"
)
var files = map[string]string{
"/etc/systemd/system/showcase.service": MeshUnitHeader + " Do not edit — this file is replaced whenever the\n[Unit]\n",
"/usr/lib/systemd/system/sshd.service": "[Unit]\nDescription=OpenSSH Daemon\n",
}
func read(p string) (string, error) {
if s, ok := files[p]; ok {
return s, nil
}
return "", errors.New("ENOENT")
}
func manager(run Runner, uid int, name string) *Manager {
return &Manager{Account: "operator", UID: uid, User: name, Run: run, Read: read, Env: []string{"HOME=/h"}}
}
func operator(run Runner) *Manager { return manager(run, 1000, "operator") }
func TestAnActAndASystemJournalReadGoThroughSudoUnlessRoot(t *testing.T) {
for _, verb := range []string{"start", "stop", "restart", "enable", "disable"} {
if p, _ := escalated("systemctl", []string{verb, "x.service"}, System, 1000); p != "sudo" {
t.Errorf("%s was not escalated", verb)
}
}
if p, a := escalated("systemctl", []string{"restart", "sshd.service"}, System, 1000); p != "sudo" || !reflect.DeepEqual(a, []string{"-n", "systemctl", "restart", "sshd.service"}) {
t.Errorf("%s %v", p, a)
}
if p, _ := escalated("systemctl", []string{"restart", "sshd.service"}, System, 0); p != "systemctl" {
t.Error("root escalated")
}
for _, args := range [][]string{{"show", "sshd.service"}, {"list-units", "restart"}} {
if p, _ := escalated("systemctl", args, System, 1000); p != "systemctl" {
t.Errorf("a read was escalated: %v", args)
}
}
if p, _ := escalated("systemctl", []string{"--user", "restart", "x.service"}, User, 1000); p != "systemctl" {
t.Error("the user scope was escalated")
}
// The system journal is read as root: unescalated, an account outside the journal's group sees only
// its own entries and every service's journal reads empty (issue 255).
if p, a := escalated("journalctl", []string{"-u", "x"}, System, 1000); p != "sudo" || a[1] != "journalctl" {
t.Errorf("the system journal read was not escalated: %s %v", p, a)
}
if p, _ := escalated("journalctl", []string{"--user", "-u", "x"}, User, 1000); p != "journalctl" {
t.Error("the account's own journal was escalated")
}
}
func TestTheUserScopeIsPlainUserWithTheAccountsRuntimeDirectoryAndBus(t *testing.T) {
var calls []call
m := manager(fake(func(call) Ran { return Ran{Stdout: list} }, &calls), 1234, "operator")
if _, err := m.Units(User, ""); err != nil {
t.Fatal(err)
}
if calls[0].cmd != "systemctl" || calls[0].args[0] != "--user" {
t.Fatalf("%+v", calls[0])
}
env := strings.Join(calls[0].env, "\n")
if !strings.Contains(env, "XDG_RUNTIME_DIR=/run/user/1234") || !strings.Contains(env, "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1234/bus") || !strings.Contains(env, "HOME=/h") {
t.Fatalf("%v", calls[0].env)
}
if _, err := m.Journal(User, "watcher.service", 10); err != nil {
t.Fatal(err)
}
if calls[1].cmd != "journalctl" || calls[1].args[0] != "--user" {
t.Fatalf("%+v", calls[1])
}
}
func TestTheSystemScopeIsGivenNoSessionWords(t *testing.T) {
var calls []call
if _, err := operator(fake(func(call) Ran { return Ran{Stdout: list} }, &calls)).Units(System, ""); err != nil {
t.Fatal(err)
}
if calls[0].env != nil {
t.Fatalf("%v", calls[0].env)
}
for _, a := range calls[0].args {
if a == "--user" {
t.Fatal("--user in a system call")
}
}
}
func TestTheUserScopeFromAnotherAccountIsRefused(t *testing.T) {
m := manager(fake(func(call) Ran { return Ran{Stdout: list} }, nil), 0, "root")
if _, err := m.Units(User, ""); err == nil || !strings.Contains(err.Error(), "operator's service manager, and this runs as root") {
t.Fatalf("%v", err)
}
}
func TestASystemActEscalatesAndAnswersTheStateAfter(t *testing.T) {
var calls []call
m := operator(fake(func(c call) Ran {
if contains(c.args, "show") {
return Ran{Stdout: showPackage}
}
return Ran{}
}, &calls))
r, err := m.Act(System, "restart", "sshd.service")
if err != nil {
t.Fatal(err)
}
if got := append([]string{calls[0].cmd}, calls[0].args...); !reflect.DeepEqual(got, []string{"sudo", "-n", "systemctl", "restart", "sshd.service"}) {
t.Fatalf("%v", got)
}
if r["ok"] != true || r["active"] != "active" || r["mesh_declared"] != false || r["note"] != nil {
t.Fatalf("%v", r)
}
}
func TestTheRestoreNoteIsOnlyOnAUnitTheMeshDeclares(t *testing.T) {
m := operator(fake(func(c call) Ran {
if contains(c.args, "show") {
return Ran{Stdout: showMesh}
}
return Ran{}
}, nil))
r, _ := m.Act(System, "stop", "showcase.service")
if r["mesh_declared"] != true || !strings.Contains(r["note"].(string), "host restores its declared state") {
t.Fatalf("%v", r)
}
}
func TestStatusSaysWhetherTheMeshDeclaresTheUnit(t *testing.T) {
answer := func(s string) Runner { return fake(func(call) Ran { return Ran{Stdout: s} }, nil) }
mesh, _ := operator(answer(showMesh)).Status(System, "showcase.service")
pkg, _ := operator(answer(showPackage)).Status(System, "sshd.service")
none, _ := operator(answer("LoadState=not-found\nFragmentPath=\n")).Status(System, "nope.service")
if mesh["mesh_declared"] != true || mesh["MainPID"] != "42" || pkg["mesh_declared"] != false || none["mesh_declared"] != false {
t.Fatalf("%v %v %v", mesh, pkg, none)
}
if MeshUnitHeader != "# Generated by the mesh." {
t.Fatal("the header is not the one the host writes")
}
}
func TestRefusalsAreNamed(t *testing.T) {
refused := operator(fake(func(call) Ran { return Ran{Status: 1, Stderr: "sudo: a password is required\n"} }, nil))
if _, err := refused.Act(System, "start", "x.service"); err == nil || !strings.Contains(err.Error(), "may not run it without a prompt: sudo: a password is required") {
t.Fatalf("%v", err)
}
missing := operator(fake(func(call) Ran { return Ran{Status: 127, Error: "ENOENT"} }, nil))
if _, err := missing.Act(System, "start", "x.service"); err == nil || !strings.Contains(err.Error(), "sudo is not installed here") {
t.Fatalf("%v", err)
}
polkitRefused := manager(fake(func(call) Ran {
return Ran{Status: 1, Stderr: "Failed to stop x.service: Access denied as the requested operation requires interactive authentication.\n"}
}, nil), 0, "root")
if _, err := polkitRefused.Act(System, "stop", "x.service"); err == nil || !strings.Contains(err.Error(), "the service manager refused the runtime's account") {
t.Fatalf("%v", err)
}
failing := operator(fake(func(call) Ran { return Ran{Status: 1, Stderr: "Failed to list units: Connection timed out\n"} }, nil))
if _, err := failing.Units(System, ""); err == nil || !strings.Contains(err.Error(), "systemctl failed (1): Failed to list units: Connection timed out") {
t.Fatalf("%v", err)
}
}
func TestAnUnreachableUserManagerIsSaidEvenWhenSystemctlExitsZero(t *testing.T) {
said := "Failed to connect to user scope bus via local transport: No such file or directory\n"
m := operator(fake(func(call) Ran { return Ran{Stderr: said} }, nil))
if _, err := m.Units(User, ""); err == nil || !strings.Contains(err.Error(), "operator's own service manager does not answer at /run/user/1000") {
t.Fatalf("%v", err)
}
nonzero := operator(fake(func(call) Ran { return Ran{Status: 1, Stderr: said} }, nil))
if _, err := nonzero.Status(User, "x.service"); err == nil || !strings.Contains(err.Error(), "does not answer") {
t.Fatalf("%v", err)
}
}
func TestFailedReportsEachManagerAndOneThatDoesNotAnswerByItsError(t *testing.T) {
m := operator(fake(func(c call) Ran {
if c.args[0] == "--user" {
return Ran{Status: 1, Stderr: "Failed to connect to user scope bus via local transport: No such file or directory\n"}
}
return Ran{Stdout: list}
}, nil))
r := m.Failed()
system, _ := json.Marshal(r["system"])
if string(system) != `[{"unit":"broken.service","load":"loaded","active":"failed","sub":"failed","description":"A broken thing"}]` {
t.Fatalf("%s", system)
}
if e, ok := r["user"].(map[string]string); !ok || !strings.Contains(e["error"], "does not answer") {
t.Fatalf("%v", r["user"])
}
}
func TestAUnitsNameIsNeverAnOption(t *testing.T) {
for _, bad := range []string{"--host=elsewhere", "a b", ""} {
if unitArg(bad) == nil {
t.Errorf("%q accepted", bad)
}
}
var calls []call
m := operator(fake(func(call) Ran { return Ran{Stdout: list} }, &calls))
if _, err := m.Act(System, "stop", "-H"); err == nil || len(calls) != 0 {
t.Fatalf("an option ran as a unit: %v %d", err, len(calls))
}
if _, err := m.Units(System, "-x*"); err != nil {
t.Fatal(err)
}
if a := calls[0].args; a[len(a)-2] != "--" || a[len(a)-1] != "-x*" {
t.Fatalf("%v", a)
}
}
// The manifest owns the systemd package, claims the seat's eight verbs, and lists exactly the tools served.
func TestTheManifestOwnsThePackageAndListsWhatIsServed(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Capabilities []string `json:"capabilities"`
Tools []string `json:"tools"`
Claims []struct {
Name string `json:"name"`
Serves []string `json:"serves"`
} `json:"claims"`
Resources []struct {
Type string `json:"type"`
Package string `json:"package"`
} `json:"resources"`
}
_ = json.Unmarshal(raw, &m)
owns := false
for _, r := range m.Resources {
owns = owns || (r.Type == "package" && r.Package == "systemd")
}
if !owns || !contains(m.Capabilities, "package-manager") {
t.Fatal("the manifest does not own the systemd package")
}
served := map[string]bool{}
for _, tool := range tools(operator(nil)) {
served[tool.Name] = true
}
want := map[string]bool{}
for _, v := range m.Claims[0].Serves {
want[seat+"."+v] = true
}
for _, n := range m.Tools {
want[n] = true
}
if !reflect.DeepEqual(served, want) {
t.Fatalf("served %v, the manifest says %v", served, want)
}
}
func contains(list []string, s string) bool {
for _, x := range list {
if x == s {
return true
}
}
return false
}