diff --git a/internal/apply/hold.go b/internal/apply/hold.go index 74ca6c7..818ecc4 100644 --- a/internal/apply/hold.go +++ b/internal/apply/hold.go @@ -109,7 +109,7 @@ func lookBefore(ctx context.Context, sys system.System, d *declaration.Declarati switch { case src == "": case strings.HasPrefix(src, "/"): - if present(src) && !recordedPath(known, src) { + if !systemPath(src) && present(src) && !recordedPath(known, src) { seen["path:"+src] = true } default: @@ -146,6 +146,26 @@ func recordedPath(known store.State, path string) bool { return false } +// systemPath is whether a bind-mount source is the machine's own plumbing — the runtime's socket, +// the kernel's filesystems, the devices, the clock — which every machine has and no predecessor's +// data lives in. Mounting it shares nothing that was found. +func systemPath(src string) bool { + clean := filepath.Clean(src) + for _, exact := range []string{"/etc/localtime", "/etc/timezone", "/etc/hosts", "/etc/resolv.conf", + "/etc/machine-id", "/etc/passwd", "/etc/group"} { + if clean == exact { + return true + } + } + for _, under := range []string{"/run", "/var/run", "/sys", "/proc", "/dev", "/usr/share/zoneinfo", + "/etc/ssl", "/etc/ca-certificates", "/etc/pki", "/lib/modules", "/usr/lib/modules"} { + if clean == under || strings.HasPrefix(clean, under+"/") { + return true + } + } + return false +} + // mountSource is what a volume mapping mounts: a path on the machine, or a named volume. Empty for // an anonymous volume, which mounts nothing that could already be there. func mountSource(mapping string) string { diff --git a/internal/apply/hold_test.go b/internal/apply/hold_test.go index 66148e5..a6c65b0 100644 --- a/internal/apply/hold_test.go +++ b/internal/apply/hold_test.go @@ -754,3 +754,20 @@ func TestAUserFoundOnTheMachineKeepsItsShellAndGroups(t *testing.T) { } } } + +func TestMountingTheMachinesOwnPlumbingIsNotFoundData(t *testing.T) { + // The runtime's socket, the kernel's filesystems and the clock are on every machine; a + // container mounting them shares nothing a predecessor kept (novox/hq ADR 0103). + dir := t.TempDir() + m := &machine{containers: map[string]*fakeContainer{}} + report, state := applyAdopted(t, adopted(t, untaken("hello-web.server"), + `{"id":"hello-web.server","type":"container","name":"hello-web","image":"`+pinned+`", + "volumes":["/var/run/docker.sock:/var/run/docker.sock","/etc/localtime:/etc/localtime:ro", + "/proc/cpuinfo:/host/cpuinfo:ro","/dev/null:/data/null"]}`), store.State{}, m, dir) + if o := outcomeOf(report, "hello-web.server"); o.Action != "created" { + t.Errorf("a container mounting only system paths was not created: %+v", o) + } + if len(state.Held) != 0 { + t.Errorf("held: %+v", state.Held) + } +}