Files
mesh-host/internal/apply/mount_waits.go
T
jschoubben 0d12fb11a4
mesh/merge-gate head 0d12fb11a48b merged into trunk 59e6a6fa2a4a — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head 0d12fb11a48b merged into trunk 59e6a6fa2a4a — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/check failed: mesh-build-go: no go build node qualifies: builders disagree: go1.27.1-X:nodwarf5 recipe 74744709d277ded9 on ace, g14, shanks; go1.…
Make no container before a file it mounts is there, and clear the empty directory a runtime left at a file's path (issue 1873)
On ace the postgres container was replaced at 22:11:51 mounting superuser.secret, a
file the provisioner's client fills only after the apply. The runtime made the
missing source a directory, and every fill after it failed with "is a directory".

A container whose mount source is a file the declaration declares, not yet on the
machine, now waits and is neither created nor replaced. The write of that file
(WriteFilled) applies what reads it, as before, so the container is made once the
file is there.

What the runtime already left is cured by the mesh: a declared file whose path is
an empty directory in the mesh's own tree (by its path and by where it leads),
owned by the engine's account, has that directory removed before the file is
written, and the outcome says so. A directory with anything in it, or outside the
tree, is never removed.
2026-10-22 22:29:39 +02:00

59 lines
1.9 KiB
Go

package apply
import (
"errors"
"os"
"path/filepath"
"git.novox.be/novox/mesh-sdk/go/declaration"
)
// declaredFiles is the path of every file a declaration declares.
func declaredFiles(resources []declaration.Resource) map[string]bool {
out := map[string]bool{}
for _, r := range resources {
if f, ok := r.(*declaration.File); ok {
out[filepath.Clean(f.Path)] = true
}
}
return out
}
// mountsAFileNotThere is the first mount source of a container that is a file the declaration declares and is not
// on the machine yet, or "" (novox/hq issue 1873): the runtime makes a missing source a directory.
func mountsAFileNotThere(c *declaration.Container, files map[string]bool) string {
for _, v := range c.Volumes {
src := mountSource(v)
if !filepath.IsAbs(src) || !files[filepath.Clean(src)] {
continue
}
if _, err := os.Lstat(src); errors.Is(err, os.ErrNotExist) {
return src
}
}
return ""
}
// clearRuntimesEmptyDirectory removes what a container runtime leaves at a file's path when it mounted the file before
// it was there (novox/hq issue 1873): an empty directory, in the mesh's own tree by its path and by where its path
// leads, owned by the engine's own account. It says what it removed, or "" when nothing was. Anything else at the path
// is left for the write to meet as before: a directory with anything in it, or one outside the tree, is never removed.
func clearRuntimesEmptyDirectory(path string) (string, error) {
info, err := os.Lstat(path)
if err != nil || !info.IsDir() || !inTheMeshsTree(path) {
return "", nil
}
if uid, _, ok := ownerOf(info); !ok || uid != os.Geteuid() {
return "", nil
}
entries, err := os.ReadDir(path)
if err != nil || len(entries) > 0 {
return "", nil
}
if err := os.Remove(path); err != nil {
return "", err
}
return "removed the empty directory a container runtime left at " + path + ", where this file belongs " +
"(novox/hq issue 1873)", nil
}