mesh/merge-gate pass: builds docker → 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, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
#205's "place": "." resolved to /var/lib/docker, where the node-engine places nothing (issue 339), so ace refused the apply and put the module back. The state directory now says "place": "mesh", <root>/mesh/docker, and a test holds every directory and path of the module out of the container runtimes' data (novox/hq issue 452).
120 lines
4.1 KiB
Go
120 lines
4.1 KiB
Go
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 <root>/<module>: /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 <root>/<module>, `"place": "mesh"` as
|
|
// <root>/mesh/<module>, and neither as <root>/<module>/<id>.
|
|
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"])
|
|
}
|
|
}
|