mesh/merge-gate head d12bba01090d merged into trunk dd9ab7927d96 — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head d12bba01090d merged into trunk dd9ab7927d96 — pass: its merge-check.sh passed
mesh/delivery delivered
The build seat runs merge-check as root, so every folder a test made was root's data, which is never kept (§12), and 28 tests failed for that alone. A test's folders now stand for an operator's: run as root, they are given to nobody before they are judged. The root rule is unchanged.
137 lines
5.0 KiB
Go
137 lines
5.0 KiB
Go
//go:build linux
|
|
|
|
package apply
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/novox/mesh-host/internal/declaration"
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// The engine delivers before the controller half (novox/hq ADR 0325): a controller that does not speak
|
|
// link.IdentityContract sends no identities, no users and no taken, and every access of its declarations is applied
|
|
// exactly as before this engine — mounted when present, refused when absent (ADR 0051 §7), nothing kept, nothing
|
|
// made, no container held, nothing judged. These are origin/main's outcomes, word for word.
|
|
func TestAnOlderControllersAccessesAreAppliedExactlyAsBefore(t *testing.T) {
|
|
keeping(t, nil, nil)
|
|
dir := userDir(t)
|
|
unwritable := filepath.Join(dir, "library") // its group may not write it
|
|
empty := filepath.Join(dir, "spool")
|
|
absent := filepath.Join(dir, "media", "movies")
|
|
for _, d := range []string{unwritable, empty} {
|
|
if err := os.Mkdir(d, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chmod(d, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
// A root-owned empty folder, read only.
|
|
rootEmpty := ""
|
|
for _, c := range []string{"/var/empty", "/srv/http", "/srv/ftp"} {
|
|
if entries, err := os.ReadDir(c); err == nil && len(entries) == 0 {
|
|
if uid, _, _ := statOwner(mustLstat(t, c)); uid == 0 {
|
|
rootEmpty = c
|
|
break
|
|
}
|
|
}
|
|
}
|
|
resources := []string{
|
|
`{"id":"radarr.library","type":"access","path":"` + unwritable + `","mode":"read-write"}`,
|
|
`{"id":"sab.spool","type":"access","path":"` + empty + `","mode":"read-write"}`,
|
|
`{"id":"plex.movies","type":"access","path":"` + absent + `"}`,
|
|
`{"id":"plex.app","type":"container","name":"plex","image":"` + pinned + `","volumes":["` + absent + `:/movies"]}`,
|
|
}
|
|
if rootEmpty != "" {
|
|
resources = append(resources, `{"id":"web.root","type":"access","path":"`+rootEmpty+`","mode":"read-write"}`)
|
|
}
|
|
var started bool
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
if len(args) > 0 && args[0] == "run" {
|
|
started = true
|
|
}
|
|
if len(args) > 0 && args[0] == "container" {
|
|
if started {
|
|
return "true\t" + containerSpec(declare(t, resources[3]).Resources[0].(*declaration.Container), inputs{}), nil
|
|
}
|
|
return "", errors.New("no such container")
|
|
}
|
|
return "", nil
|
|
}
|
|
toUser(t)
|
|
report, _, err := Apply(context.Background(), archHost(t), declare(t, strings.Join(resources, ",")), store.State{},
|
|
store.OriginDeclared, run, nil, nil)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), absent+" is not there, and the mesh does not own it — the operator "+
|
|
"must provide it") {
|
|
t.Fatalf("an absent access is not refused as before: %v", err)
|
|
}
|
|
for id, mode := range map[string]string{"radarr.library": "read-write", "sab.spool": "read-write"} {
|
|
o := outcomeOf(report, id)
|
|
if o.Action != "unchanged" || o.Detail != "operator-owned; present, "+mode+", nothing managed" {
|
|
t.Errorf("%s: %+v, and before it was unchanged, %q", id, o, "operator-owned; present, "+mode+", nothing managed")
|
|
}
|
|
}
|
|
if rootEmpty != "" {
|
|
if o := outcomeOf(report, "web.root"); o.Action != "unchanged" {
|
|
t.Errorf("a root-owned folder was judged: %+v", o)
|
|
}
|
|
}
|
|
if !started {
|
|
t.Error("a container mounting an absent access was held, and before it was created")
|
|
}
|
|
if _, err := os.Stat(absent); !errors.Is(err, os.ErrNotExist) {
|
|
t.Error("an absent access was made")
|
|
}
|
|
for _, d := range []string{unwritable, empty} {
|
|
if n, _ := unix.Getxattr(d, aclAccessXattr, nil); n > 0 {
|
|
t.Errorf("an ACL was set on %s", d)
|
|
}
|
|
if info := mustLstat(t, d); info.Mode().Perm() != 0o755 {
|
|
t.Errorf("%s changed mode: %o", d, info.Mode().Perm())
|
|
}
|
|
}
|
|
if all, _ := KeptFolders.All(); len(all) != 0 {
|
|
t.Errorf("folders were recorded as kept: %v", all)
|
|
}
|
|
if len(Leftovers()) != 0 || len(WaitingOnAccess(declare(t, strings.Join(resources, ",")))) != 0 {
|
|
t.Error("something was said or held for an older controller's access")
|
|
}
|
|
}
|
|
|
|
func mustLstat(t *testing.T, path string) os.FileInfo {
|
|
t.Helper()
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return info
|
|
}
|
|
|
|
// Review (low): a folder no access names, whose filesystem could not be named this look, is not said as its disk
|
|
// being away.
|
|
func TestALeftoverSaysWhyItsWalkCouldNotRun(t *testing.T) {
|
|
uuid, fsid, source := "6f1c0a4e-1b2d-4c3e-8f00-aa11bb22cc33", "0000080100000000", "/dev/sdb1"
|
|
_, lib, access := poolNamed(t, &uuid, &fsid, &source)
|
|
takeIt(t, lib, access)
|
|
other := `{"id":"x.dir","type":"directory","path":"` + filepath.Join(filepath.Dir(filepath.Dir(lib)), "x") + `"}`
|
|
if _, _, err := applyAccesses(t, store.State{}, other); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
uuid, fsid, source = "", "0000000000000000", "" // nothing names it now
|
|
toUser(t)
|
|
ReconcileKept(context.Background())
|
|
left := Leftovers()
|
|
if len(left) != 1 || strings.Contains(left[0].Why, "disk") || !strings.Contains(left[0].Why, "could not be named") {
|
|
t.Fatalf("the leftover does not say why its walk could not run: %+v", left)
|
|
}
|
|
}
|