package main import ( "fmt" "regexp" "strings" "testing" ) // Docker's own directory is never the mesh's (novox/hq issue 452). The node-engine places nothing in or // below the container runtimes' data (novox/hq issue 339), so a module directory that resolves there is // refused on the machine and fails the whole apply. The merge gate composes a change but does not apply it, // so it passed #205's `"place": "."`, which the controller resolves to /: /var/lib/docker. // runtimeTrees are the trees the node-engine refuses to place in (mesh-host internal/apply/placement_guard.go). var runtimeTrees = []string{"/var/lib/docker", "/var/lib/containers", "/var/lib/containerd"} // defaultDataRoot is the controller's root for placed directories when a node states none // (mesh-controller internal/catalogue/dir_into.go). const defaultDataRoot = "/var/lib" // placedDirs resolves each directory resource as the controller's dirsFor does on a node with the default // data root: a stated path as written, `"place": "."` as /, `"place": "mesh"` as // /mesh/, and neither as //. func placedDirs(t *testing.T, m manifestForLogs) map[string]string { t.Helper() dirs := map[string]string{} for _, r := range m.Resources { if r["type"] != "directory" { continue } id := fmt.Sprint(r["id"]) if p, ok := r["path"].(string); ok && p != "" { dirs[id] = strings.TrimRight(p, "/") continue } switch r["place"] { case ".": dirs[id] = defaultDataRoot + "/docker" case "mesh": dirs[id] = defaultDataRoot + "/mesh/docker" default: dirs[id] = defaultDataRoot + "/docker/" + id } } return dirs } func inRuntimeTree(p string) string { for _, tree := range runtimeTrees { if p == tree || strings.HasPrefix(p, tree+"/") { return tree } } return "" } // TestNothingIsPlacedInTheRuntimesData: no directory of the module resolves into Docker's own directory or // another runtime's, and no file, process or environment value names a path there through ${dir:…} or as // written. func TestNothingIsPlacedInTheRuntimesData(t *testing.T) { m := readManifestForLogs(t) dirs := placedDirs(t, m) for id, p := range dirs { if tree := inRuntimeTree(p); tree != "" { t.Errorf("directory %q resolves to %s, inside %s: the container runtime's data, never the mesh's (novox/hq issues 339, 452)", id, p, tree) } } ref := regexp.MustCompile(`\$\{dir:([a-z0-9][a-z0-9-]*)\}`) fill := func(s string) string { return ref.ReplaceAllStringFunc(s, func(r string) string { if p, ok := dirs[ref.FindStringSubmatch(r)[1]]; ok { return p } return r }) } for _, r := range m.Resources { id := fmt.Sprint(r["id"]) var named []string if p, ok := r["path"].(string); ok { named = append(named, p) } if env, ok := r["env"].(map[string]any); ok { for _, v := range env { if s, ok := v.(string); ok { named = append(named, s) } } } for _, p := range named { if tree := inRuntimeTree(fill(p)); tree != "" { t.Errorf("resource %q names %s, inside %s: the container runtime's data, never the mesh's (novox/hq issues 339, 452)", id, fill(p), tree) } } } } // TestLogRotationIsInTheModulesOwnDirectory: the step reads the file the mesh writes, in a directory the mesh // places for the module, not one Docker keeps. func TestLogRotationIsInTheModulesOwnDirectory(t *testing.T) { m := readManifestForLogs(t) dirs := placedDirs(t, m) if got, want := dirs["state"], "/var/lib/mesh/docker"; got != want { t.Fatalf("the module's state directory resolves to %q, want %q (novox/hq issue 452)", got, want) } file, _ := resourceByID(m, "log-rotation") step, _ := resourceByID(m, "log-rotation-check") if file == nil || step == nil { t.Fatal("log-rotation and log-rotation-check are declared") } env, _ := step["env"].(map[string]any) if file["path"] != env["DOCKER_LOG_ROTATION"] { t.Errorf("the step reads %v, the mesh writes %v: one file", env["DOCKER_LOG_ROTATION"], file["path"]) } if file["path"] != "${dir:state}/log-rotation.conf" { t.Errorf("log-rotation is at %v, want ${dir:state}/log-rotation.conf", file["path"]) } }