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 `--`.
69 lines
2.6 KiB
Go
69 lines
2.6 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// Below a person's or an agent's home, a directory is that account's (novox/hq issue 339): root's, or another
|
|
// account's, there is one the account does not control in a tree whose every parent it does — and a container
|
|
// mounting one the engine refused is refused with it.
|
|
func TestBelowAHomeOnlyThatAccountsDirectoryIsPlaced(t *testing.T) {
|
|
dir := t.TempDir()
|
|
home := filepath.Join(dir, "home", "alice")
|
|
if err := os.MkdirAll(home, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// A uid of its own, never the test's: the build seat runs tests as root, and root's home is no person's.
|
|
const alice = 4242
|
|
passwd := filepath.Join(dir, "passwd")
|
|
if err := os.WriteFile(passwd, fmt.Appendf(nil, "root:x:0:0::/root:/bin/sh\nalice:x:%d:%d::%s:/bin/sh\n",
|
|
alice, alice, home), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := passwdFile
|
|
passwdFile = passwd
|
|
t.Cleanup(func() { passwdFile = before })
|
|
|
|
_, _, err := applyOnce(t, `{"id":"notes.data","type":"directory","path":"`+home+`/.config/autostart"}`, nil, store.State{})
|
|
refusedFor339(t, "root's directory below alice's home", err)
|
|
_, _, err = applyOnce(t, `{"id":"notes.data","type":"directory","path":"`+home+`/.config/autostart",`+
|
|
`"owner":"`+strconv.Itoa(alice+1)+`"}`, nil, store.State{})
|
|
refusedFor339(t, "another account's directory below alice's home", err)
|
|
if _, err := os.Stat(filepath.Join(home, ".config")); err == nil {
|
|
t.Fatal("a refused directory's parent was made")
|
|
}
|
|
// Alice's own is placed (judged, not applied: a chown to her needs root).
|
|
if err := refusePath(home+"/.config/notes", placingDirectory, "notes", strconv.Itoa(alice)+":"+strconv.Itoa(alice)); err != nil {
|
|
t.Fatalf("alice's own directory: %v", err)
|
|
}
|
|
// A container mounting a refused directory is refused with it, and not run.
|
|
ran := false
|
|
run := func(ctx context.Context, name string, args ...string) (string, error) {
|
|
args = oldShape(name, args)
|
|
switch args[0] {
|
|
case "info":
|
|
return "27.0\n", nil
|
|
case "container":
|
|
return "", os.ErrNotExist
|
|
case "run":
|
|
ran = true
|
|
}
|
|
return "", nil
|
|
}
|
|
_, _, err = Apply(context.Background(), archHost(t), parseTrusted(t, `{"declaration":1,"resources":[
|
|
{"id":"notes.data","type":"directory","path":"`+home+`/.ssh"},
|
|
{"id":"notes.server","type":"container","name":"notes","image":"`+pinned+`","volumes":["`+home+`/.ssh:/keys"]}
|
|
]}`), store.State{}, store.OriginCarried, run, nil, nil)
|
|
refusedFor339(t, "a container mounting a refused directory", err)
|
|
if ran {
|
|
t.Fatal("a container mounting a refused directory was run")
|
|
}
|
|
}
|