//go:build linux package apply import ( "context" "errors" "os" "path/filepath" "strings" "testing" "github.com/novox/mesh-host/internal/store" ) // Review 47 of novox/hq issue 358: a directory the walk refuses gates its module, and nothing below it is written, // whether the link was there when the apply began or appeared while it ran. // aLinkedModule is a module's directory at /parent/state, its parent a link to /elsewhere, and a file of // the module below the directory; and another module's file elsewhere. func aLinkedModule(t *testing.T) (target, other, decl string) { t.Helper() base := t.TempDir() target = filepath.Join(base, "elsewhere", "state") if err := os.MkdirAll(target, 0o755); err != nil { t.Fatal(err) } if err := os.Symlink(filepath.Join(base, "elsewhere"), filepath.Join(base, "parent")); err != nil { t.Fatal(err) } dir := filepath.Join(base, "parent", "state") other = filepath.Join(base, "other.conf") decl = `{"id":"mod.state","type":"directory","path":"` + dir + `","mode":"0700"},` + `{"id":"mod.conf","type":"file","path":"` + dir + `/settings.conf","content":"a=1\n"},` + `{"id":"other.conf","type":"file","path":"` + other + `","content":"kept apart\n"}` return target, other, decl } func holdsTheGate(t *testing.T, target, other string, report Report, err error) { t.Helper() var refusal *PlacementRefusedError if !errors.As(err, &refusal) { t.Fatalf("the directory behind the link was not refused: %v", err) } if _, statErr := os.Stat(filepath.Join(target, "settings.conf")); statErr == nil { t.Error("a file was written into the link's target, below the directory the engine refused") } if info, _ := os.Stat(target); info.Mode().Perm() != 0o755 { t.Errorf("the link's target was given mode %o (was 755)", info.Mode().Perm()) } if o := outcomeOf(report, "mod.conf"); o.Action != "skipped" { t.Errorf("the file below the refused directory is not reported skipped: %+v", o) } if strings.Contains(err.Error(), "everything was attempted") || !strings.Contains(err.Error(), "the rest of mod was not attempted") { t.Errorf("the report does not say the module was gated: %v", err) } if got, readErr := os.ReadFile(other); readErr != nil || string(got) != "kept apart\n" { t.Errorf("another module's file was not applied: %q, %v", got, readErr) } } // The link there when the apply begins: refused by the guard before anything is applied. func TestADirectoryBehindALinkGatesItsModuleThroughApply(t *testing.T) { target, other, decl := aLinkedModule(t) report, _, err := Apply(context.Background(), archHost(t), declare(t, decl), store.State{}, store.OriginDeclared, noServices, nil, nil) holdsTheGate(t, target, other, report, err) } // The link found only at the moment of use: refused by the walk during the apply, and still a refusal that gates its // module and what is below it. Simulated by trusting / for the guard's walk (two walks of /: one to the link, one // from its target) and not for the apply's. func TestADirectoryRefusedDuringTheApplyGatesItsModule(t *testing.T) { target, other, decl := aLinkedModule(t) wasDirs, wasLinks := couldReplace, linkReplacer walksOfRoot := 0 couldReplace = func(path string, _, _, _ uint32) string { if path == "/" { walksOfRoot++ if walksOfRoot > 2 { return "every account (a test's /)" } } return "" } linkReplacer = func(string, uint32) string { return "" } t.Cleanup(func() { couldReplace, linkReplacer = wasDirs, wasLinks }) report, _, err := Apply(context.Background(), archHost(t), declare(t, decl), store.State{}, store.OriginDeclared, noServices, nil, nil) if walksOfRoot < 3 { t.Fatalf("the apply did not walk again at the moment of use (%d walks)", walksOfRoot) } holdsTheGate(t, target, other, report, err) }