//go:build linux package apply import ( "context" "os" "path/filepath" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/declaration" "github.com/novox/mesh-host/internal/store" ) // novox/hq issue 1873: a module's directory in the mesh's own tree (/mesh/, place "mesh") that is // there before its declaration, owned by the engine's own account, was made by the mesh on the way to something below // it: it is the mesh's, given its declared mode and owner, never used as found. Only the engine's account makes a // directory in a tree that is the engine's (0755, root); on ace and novox /var/lib/mesh/postgres was there as // root:root 0700 before postgres's data directory was applied, and the engine left it to root, so the postgres // provider, running as the operator's account, could not reach its own files. func TestADirectoryInTheMeshsOwnTreeMadeByTheEngineIsTheMeshs(t *testing.T) { root := t.TempDir() before := meshDataRoot meshDataRoot = filepath.Join(root, "mesh") t.Cleanup(func() { meshDataRoot = before }) made := filepath.Join(meshDataRoot, "postgres") if err := os.MkdirAll(made, 0o700); err != nil { t.Fatal(err) } if err := os.Chmod(made, 0o700); err != nil { t.Fatal(err) } decl := `{"id":"postgres.data","type":"directory","path":"` + made + `","mode":"0750"}` var said []string _, state, err := Apply(context.Background(), archHost(t), declare(t, decl), store.State{}, store.OriginDeclared, noServices, func(line string) { said = append(said, line) }, nil) if err != nil { t.Fatal(err) } if strings.Contains(strings.Join(said, "\n"), "WARNING postgres.data") { t.Errorf("a directory the engine's account made in the mesh's tree was used as found: %q", said) } if a, _ := state.Find("postgres.data"); a.AsFound { t.Error("it is recorded as found") } if info, _ := os.Stat(made); info.Mode().Perm() != 0o750 { t.Errorf("it was not given its declared mode: %04o", info.Mode().Perm()) } // Outside the mesh's tree the rule of issue 339 stands: found, used as found. other := filepath.Join(root, "var-lib", "notes") if err := os.MkdirAll(other, 0o700); err != nil { t.Fatal(err) } said = nil decl = `{"id":"notes.data","type":"directory","path":"` + other + `","mode":"0750"}` if _, _, err := Apply(context.Background(), archHost(t), declare(t, decl), store.State{}, store.OriginDeclared, noServices, func(line string) { said = append(said, line) }, nil); err != nil { t.Fatal(err) } if !strings.Contains(strings.Join(said, "\n"), "WARNING notes.data") { t.Errorf("a directory found outside the mesh's tree was not used as found: %q", said) } } // Review of issue 1873's fix: the tree is judged by where the path leads, not by its text. A root-only link inside the // mesh's tree pointing out of it (to /var/lib/systemd, say) would otherwise make a directory outside the tree, owned by // the engine's account, the mesh's to chown: issue 1873's fault again. It is used as found. func TestALinkInTheMeshsTreeLeadingOutOfItIsNotTheTree(t *testing.T) { rootAlone(t, "") root := t.TempDir() before := meshDataRoot meshDataRoot = filepath.Join(root, "mesh") t.Cleanup(func() { meshDataRoot = before }) outside := filepath.Join(root, "var-lib", "systemd") if err := os.MkdirAll(filepath.Join(outside, "data"), 0o755); err != nil { t.Fatal(err) } if err := os.Chmod(filepath.Join(outside, "data"), 0o700); err != nil { t.Fatal(err) } if err := os.MkdirAll(meshDataRoot, 0o755); err != nil { t.Fatal(err) } if err := os.Symlink(outside, filepath.Join(meshDataRoot, "postgres")); err != nil { t.Fatal(err) } r := &declaration.Directory{ID: "postgres.data", Path: filepath.Join(meshDataRoot, "postgres", "data"), Mode: "0750"} out, err := applyDirectory(r, store.Applied{}, true) if err != nil { t.Fatal(err) } if !out.asFound { t.Errorf("a directory reached through a link out of the mesh's tree was taken as the mesh's: %+v", out) } if got := permOf(t, filepath.Join(outside, "data")); got != 0o700 { t.Errorf("the directory outside the tree was given mode %o (was 700)", got) } }