A workstation's per-user daemons — a window manager's reload watcher, an
audio mask, a memory guard — are units in the operator account's own service
manager, and until now had no form the mesh could send (to-be 29). The
`service` shape gains `scope` ("system", the default, or "user") and `user`
(the account, named ${machine:account} by a module); a user-scoped unit
without an account, or a system unit naming one, is refused at parse.
The host reaches the account's manager as `systemctl --user --machine=<account>@`
from its own process: no environment to forge, no user to switch to. Done on
the runner rather than per system, since every system's reading of a unit
already goes through systemctl. Apply, reflect-only and removal all go through
the same manager, and the applied record carries scope and user so removal
gives the unit back to the manager it came from. It answers only while that
manager runs — a login, or lingering enabled for the account; declaring
lingering is a follow-up.
Tests: a user-scoped unit is started and enabled in the account's manager and
recorded with its scope; a system unit never sees --user; the validation of
scope and user.
31 lines
1017 B
Go
31 lines
1017 B
Go
package declaration
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// novox/hq ADR 0177: a unit is in the system manager or an account's own; a user-scoped one names
|
|
// the account, a system one may not, and any other word is refused.
|
|
func TestAUserScopedUnitNamesItsAccountAndASystemOneMayNot(t *testing.T) {
|
|
cases := []struct{ scope, user, wants string }{
|
|
{"user", "ops", ""},
|
|
{"", "", ""},
|
|
{"system", "", ""},
|
|
{"user", "", "names the account"},
|
|
{"", "ops", "names no user"},
|
|
{"session", "ops", "scope \"session\""},
|
|
}
|
|
for _, c := range cases {
|
|
s := &Service{ID: "m.u", Type: TypeService, Unit: "u.service", State: "running", Scope: c.scope, User: c.user}
|
|
problems := s.validate("m.u", false)
|
|
got := strings.Join(problems, "; ")
|
|
if c.wants == "" && len(problems) != 0 {
|
|
t.Fatalf("scope %q user %q refused: %s", c.scope, c.user, got)
|
|
}
|
|
if c.wants != "" && !strings.Contains(got, c.wants) {
|
|
t.Fatalf("scope %q user %q: wanted a refusal saying %q, got %q", c.scope, c.user, c.wants, got)
|
|
}
|
|
}
|
|
}
|