Files
mesh-controller/internal/catalogue/shared_account_test.go
T
jochen c43277f9c6
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
Compose an account given only groups where its module wrote it (hq ADR 0252, issue 247)
Composed first, an account a module puts in its daemon's group was put in a group the package
had not made yet, and only the next apply healed it. An account that sets a shell or home still
goes first (issue 213).
2026-10-08 12:04:08 +02:00

74 lines
3.2 KiB
Go

package catalogue
import (
"strings"
"testing"
)
// One account, several modules: the shell's module sets its shell, the container runtime's adds it
// to a group. Groups are only ever added by the host, so they are contributed; a shell or a home is
// one value, owned by one module per node.
func userResource(fields map[string]any) map[string]any {
r := map[string]any{"id": "operator", "type": "user", "name": "op"}
for k, v := range fields {
r[k] = v
}
return r
}
func TestAShellAndAGroupOnOneAccountFromTwoModulesResolve(t *testing.T) {
zsh := Manifest{Module: "zsh", Resources: []map[string]any{userResource(map[string]any{"shell": "/usr/bin/zsh"})}}
docker := Manifest{Module: "docker", Resources: []map[string]any{userResource(map[string]any{"groups": []any{"docker"}})}}
other := Manifest{Module: "media", Resources: []map[string]any{userResource(map[string]any{"groups": []any{"video"}})}}
if problems := checkResources([]Manifest{zsh, docker, other}); len(problems) != 0 {
t.Fatalf("a shell and two modules' groups on one account were refused: %v", problems)
}
if _, err := Resolve(shelf(zsh, docker, other), []string{"zsh", "docker", "media"}, workstation(), World{}); err != nil {
t.Fatalf("the three did not resolve together: %v", err)
}
}
func TestTwoModulesSettingOneAccountsShellOrHomeAreRefused(t *testing.T) {
for _, field := range []string{"shell", "home"} {
a := Manifest{Module: "zsh", Resources: []map[string]any{userResource(map[string]any{field: "/one"})}}
b := Manifest{Module: "fish", Resources: []map[string]any{userResource(map[string]any{field: "/two", "groups": []any{"x"}})}}
problems := checkResources([]Manifest{a, b})
want := `zsh and fish both set the ` + field + ` of the user "op"`
if len(problems) != 1 || !strings.Contains(problems[0], want) {
t.Errorf("two modules setting %s gave %v, want %q", field, problems, want)
}
}
}
// An account a module declares only to put in a group is applied where it is written, after the package
// that makes the group (novox/hq ADR 0252); one that says how it logs in still goes first (issue 213).
func TestAnAccountOnlyGivenGroupsKeepsItsWrittenPlace(t *testing.T) {
compose := func(raw string) []map[string]any {
t.Helper()
m, err := ParseManifest([]byte(raw))
if err != nil {
t.Fatal(err)
}
out, err := Resolution{Node: "workstation", Modules: []Manifest{m}}.Declaration(Rendering{})
if err != nil {
t.Fatal(err)
}
return out
}
out := compose(`{"module": "lights", "version": "1", "resources": [
{"id": "daemon", "type": "package", "package": "lights-daemon"},
{"id": "account", "type": "user", "name": "op", "groups": ["lights"]}
]}`)
if pkg, acct := indexOf(out, "lights.daemon"), indexOf(out, "lights.account"); pkg < 0 || acct < pkg {
t.Fatalf("the account (%d) is not after the package that makes its group (%d): %v", acct, pkg, out)
}
out = compose(`{"module": "shell", "version": "1", "resources": [
{"id": "package", "type": "package", "package": "zsh"},
{"id": "login", "type": "user", "name": "op", "shell": "/usr/bin/zsh", "groups": ["wheel"]}
]}`)
if pkg, acct := indexOf(out, "shell.package"), indexOf(out, "shell.login"); acct < 0 || acct > pkg {
t.Fatalf("an account with a shell is no longer first (%d, package %d): %v", acct, pkg, out)
}
}