mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/358-judge-directories-read-only delivered: every member is delivered
Once a mesh directory's mode and owner go only through a walk that follows no link another account could make, a directory behind such a link is refused and its module gated: on the control-node that could stop the controller. The operator is to be told which directories that walk would refuse on every node before it lands. So the engine judges the declaration it keeps with the same walk, making and changing nothing: on a signed ask from the controller (mesh.node.<node>.ask.judge-directories, held to the hand-over's checks) and as `nox-mesh-host judge-directories` at the machine.
73 lines
2.9 KiB
Go
73 lines
2.9 KiB
Go
//go:build linux
|
|
|
|
package apply
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The read-only judgement of a node's directories (novox/hq issue 358): one line each — placed, would be refused
|
|
// naming the link and who could replace it, or not there yet (placed: an apply makes it) — and nothing made or
|
|
// changed.
|
|
func TestJudgeDirectoriesSaysWhatTheWalkWouldRefuseAndChangesNothing(t *testing.T) {
|
|
base := t.TempDir()
|
|
plain := filepath.Join(base, "plain")
|
|
if err := os.Mkdir(plain, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
elsewhere := filepath.Join(base, "elsewhere", "state")
|
|
if err := os.MkdirAll(elsewhere, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Symlink(filepath.Join(base, "elsewhere"), filepath.Join(base, "linked")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
missing := filepath.Join(base, "missing", "deeper")
|
|
d := declare(t, `{"id":"a.plain","type":"directory","path":"`+plain+`","mode":"0700"},`+
|
|
`{"id":"a.linked","type":"directory","path":"`+filepath.Join(base, "linked", "state")+`","mode":"0700"},`+
|
|
`{"id":"a.missing","type":"directory","path":"`+missing+`","mode":"0700"}`)
|
|
|
|
said := JudgeDirectories(d)
|
|
lines := strings.Split(said, "\n")
|
|
if len(lines) != 4 || !strings.HasPrefix(lines[0], "3 directories declared: 2 placed, 1 would be refused") {
|
|
t.Fatalf("the judgement: %q", said)
|
|
}
|
|
if lines[1] != plain+": placed" || lines[3] != missing+": placed" {
|
|
t.Errorf("a directory the walk reaches is not said placed: %q", said)
|
|
}
|
|
if !strings.Contains(lines[2], "would be refused") || !strings.Contains(lines[2], filepath.Join(base, "linked")) ||
|
|
!strings.Contains(lines[2], "every account") {
|
|
t.Errorf("the linked directory is not said refused naming the link and who: %q", lines[2])
|
|
}
|
|
if _, err := os.Stat(filepath.Join(base, "missing")); !os.IsNotExist(err) {
|
|
t.Error("the judgement made a directory")
|
|
}
|
|
for _, p := range []string{plain, elsewhere} {
|
|
if info, _ := os.Stat(p); info.Mode().Perm() != 0o755 {
|
|
t.Errorf("the judgement changed %s to %o", p, info.Mode().Perm())
|
|
}
|
|
}
|
|
}
|
|
|
|
// A link only root could have made is followed by the judgement as by the apply.
|
|
func TestJudgeDirectoriesFollowsARootOnlyLink(t *testing.T) {
|
|
wasDirs, wasLinks := couldReplace, linkReplacer
|
|
couldReplace = func(string, uint32, uint32, uint32) string { return "" }
|
|
linkReplacer = func(string, uint32) string { return "" }
|
|
t.Cleanup(func() { couldReplace, linkReplacer = wasDirs, wasLinks })
|
|
base := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(base, "real", "state"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Symlink("real", filepath.Join(base, "run")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(base, "run", "state")
|
|
if said := JudgeDirectories(declare(t, `{"id":"a.s","type":"directory","path":"`+path+`"}`)); !strings.Contains(said, path+": placed") {
|
|
t.Fatalf("a directory behind a root-only link is not said placed: %q", said)
|
|
}
|
|
}
|