From 0df87ab1117bf4da5163ad486a82628f1bada7ed Mon Sep 17 00:00:00 2001 From: Ophestra Date: Tue, 6 Jan 2026 23:34:43 +0900 Subject: [PATCH] internal/pkg: automatic overlay mount on tmp This sets up the last Artifact to target /tmp as a writable overlay mount backed by the host side temp directory. This is useful for an Artifact containing source code to be built for another Artifact for example. Signed-off-by: Ophestra --- internal/pkg/dir_test.go | 30 ++++++++++++++++++++++++ internal/pkg/exec.go | 42 +++++++++++++++++++++++++++++----- internal/pkg/exec_test.go | 43 +++++++++++++++++++++++++++++++++++ internal/pkg/testdata/main.go | 36 ++++++++++++++++++++++++----- 4 files changed, 139 insertions(+), 12 deletions(-) diff --git a/internal/pkg/dir_test.go b/internal/pkg/dir_test.go index c6cf1c4..ab456c9 100644 --- a/internal/pkg/dir_test.go +++ b/internal/pkg/dir_test.go @@ -380,6 +380,36 @@ func TestFlatten(t *testing.T) { {Mode: fs.ModeDir | 0700, Path: "temp"}, {Mode: fs.ModeDir | 0700, Path: "work"}, }, pkg.MustDecode("gFT9kprYBqEJKifJIl2sHn_3TgULWVLTU4DrYAHiGcRmcdFRZ0YtjiROW820cAEc")}, + + {"sample exec container overlay temp", fstest.MapFS{ + ".": {Mode: fs.ModeDir | 0700}, + + "checksum": {Mode: fs.ModeDir | 0700}, + "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500}, + "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}}, + "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500}, + + "identifier": {Mode: fs.ModeDir | 0700}, + "identifier/3ELJ8l42g6XeIoLGR9LheVhMIwSIleD6VrhsliBuon5DAdBOwFSMqd7aiUI4fll7": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, + "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, + + "temp": {Mode: fs.ModeDir | 0700}, + "work": {Mode: fs.ModeDir | 0700}, + }, []pkg.FlatEntry{ + {Mode: fs.ModeDir | 0700, Path: "."}, + + {Mode: fs.ModeDir | 0700, Path: "checksum"}, + {Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"}, + {Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}}, + {Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"}, + + {Mode: fs.ModeDir | 0700, Path: "identifier"}, + {Mode: fs.ModeSymlink | 0777, Path: "identifier/3ELJ8l42g6XeIoLGR9LheVhMIwSIleD6VrhsliBuon5DAdBOwFSMqd7aiUI4fll7", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, + {Mode: fs.ModeSymlink | 0777, Path: "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, + + {Mode: fs.ModeDir | 0700, Path: "temp"}, + {Mode: fs.ModeDir | 0700, Path: "work"}, + }, pkg.MustDecode("_r1IBeMWCkLwQ9Im9w0tV9_CWIOfQlXkkP2CogPHLmZp_AB6W3_8HVZqDV00dNAm")}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index c1e0cc3..8b12694 100644 --- a/internal/pkg/exec.go +++ b/internal/pkg/exec.go @@ -89,7 +89,9 @@ func (a *execNetArtifact) Cure(c *CureContext) error { // A private instance of /proc and /dev is made available to the container. // // The working and temporary directories are both created and mounted writable -// on /work and /tmp respectively. +// on /work and /tmp respectively. If one or more paths target [fhs.AbsTmp], the +// final entry is set up as a writable overlay mount on /tmp backed by the host +// side temporary directory. // // If the first path targets [fhs.AbsRoot], it is made writable via an overlay // mount with writes going to an ephemeral tmpfs bound to the lifetime of the @@ -273,18 +275,46 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) { z.OverlayEphemeral(fhs.AbsRoot, paths[0][0]) paths = paths[1:] } + var overlayTemp *check.Absolute for _, b := range paths { + if b[1].Is(fhs.AbsTmp) { + if overlayTemp != nil { + z.Bind(overlayTemp, fhs.AbsTmp, 0) + } + overlayTemp = b[0] + continue + } z.Bind(b[0], b[1], 0) } z.Bind( c.GetWorkDir(), fhs.AbsRoot.Append("work"), std.BindWritable|std.BindEnsure, - ).Bind( - c.GetTempDir(), - fhs.AbsTmp, - std.BindWritable|std.BindEnsure, - ).Proc(fhs.AbsProc).Dev(fhs.AbsDev, true) + ) + if overlayTemp == nil { + z.Bind( + c.GetTempDir(), + fhs.AbsTmp, + std.BindWritable|std.BindEnsure, + ) + } else { + temp := c.GetTempDir() + tempUpper := temp.Append("upper") + if err = os.MkdirAll(tempUpper.String(), 0700); err != nil { + return + } + tempWork := temp.Append("work") + if err = os.MkdirAll(tempWork.String(), 0700); err != nil { + return + } + z.Overlay( + fhs.AbsTmp, + tempUpper, + tempWork, + overlayTemp, + ) + } + z.Proc(fhs.AbsProc).Dev(fhs.AbsDev, true) if err = z.Start(); err != nil { return diff --git a/internal/pkg/exec_test.go b/internal/pkg/exec_test.go index 76bb756..d1b8876 100644 --- a/internal/pkg/exec_test.go +++ b/internal/pkg/exec_test.go @@ -11,6 +11,7 @@ import ( "testing" "hakurei.app/container/check" + "hakurei.app/container/fhs" "hakurei.app/container/stub" "hakurei.app/hst" "hakurei.app/internal/pkg" @@ -189,6 +190,48 @@ func TestExec(t *testing.T) { testtoolDestroy(t, base, c) }, pkg.MustDecode("gFT9kprYBqEJKifJIl2sHn_3TgULWVLTU4DrYAHiGcRmcdFRZ0YtjiROW820cAEc")}, + + {"overlay temp", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { + c.SetStrict(true) + testtool, testtoolDestroy := newTesttool() + + msg := message.New(log.New(os.Stderr, "container: ", 0)) + msg.SwapVerbose(testing.Verbose()) + + cureMany(t, c, []cureStep{ + {"container", pkg.NewExec( + t.Context(), + msg, + 0, + nil, + check.MustAbs("/work"), + []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, + check.MustAbs("/tmp/bin/testtool"), + []string{"testtool"}, + + pkg.MustPath("/", stubArtifact{ + kind: pkg.KindTar, + params: []byte("empty directory"), + cure: func(c *pkg.CureContext) error { + return os.MkdirAll(c.GetWorkDir().String(), 0700) + }, + }), + pkg.MustPath("/tmp/", stubArtifact{ + kind: pkg.KindTar, + params: []byte("empty directory"), + cure: func(c *pkg.CureContext) error { + return os.MkdirAll(c.GetWorkDir().String(), 0700) + }, + }), + pkg.ExecContainerPath{ + P: fhs.AbsTmp, + A: testtool, + }, + ), ignorePathname, wantChecksumOffline, nil}, + }) + + testtoolDestroy(t, base, c) + }, pkg.MustDecode("_r1IBeMWCkLwQ9Im9w0tV9_CWIOfQlXkkP2CogPHLmZp_AB6W3_8HVZqDV00dNAm")}, }) } diff --git a/internal/pkg/testdata/main.go b/internal/pkg/testdata/main.go index bbf2340..4f3d047 100644 --- a/internal/pkg/testdata/main.go +++ b/internal/pkg/testdata/main.go @@ -39,11 +39,19 @@ func main() { if !slices.Equal(wantEnv, os.Environ()) { log.Fatalf("Environ: %q, want %q", os.Environ(), wantEnv) } - const wantExec = "/opt/bin/testtool" + + var overlayTmp bool + const ( + wantExec = "/opt/bin/testtool" + wantExecOverlay = "/tmp/bin/testtool" + ) if got, err := os.Executable(); err != nil { log.Fatalf("Executable: error = %v", err) } else if got != wantExec { - log.Fatalf("Executable: %q, want %q", got, wantExec) + if got != wantExecOverlay { + log.Fatalf("Executable: %q, want %q", got, wantExec) + } + overlayTmp = true } wantHostname := "cure" @@ -105,7 +113,6 @@ func main() { if path.Base(lowerdir) != checksumEmptyDir { log.Fatal("unexpected artifact checksum") } - } else { if hostNet { ident = "I3T53NtN6HPAyyodHtq2B0clcsoS1nPdvCEb-Zc5K-hoqFGL2od1mftHhwG7gX1S" @@ -126,7 +133,16 @@ func main() { } } - next() // testtool artifact + if !overlayTmp { + next() // testtool artifact + } else { + ident = "3ELJ8l42g6XeIoLGR9LheVhMIwSIleD6VrhsliBuon5DAdBOwFSMqd7aiUI4fll7" + + next() + if path.Base(m.Root) != checksumEmptyDir || m.Target != "/tmp" { + log.Fatal("unexpected artifact checksum") + } + } next() if path.Base(m.Root) != ident || m.Target != "/work" { @@ -134,8 +150,16 @@ func main() { } next() - if path.Base(m.Root) != ident || m.Target != "/tmp" { - log.Fatal("unexpected temp mount entry") + if !overlayTmp { + if path.Base(m.Root) != ident || m.Target != "/tmp" { + log.Fatal("unexpected temp mount entry") + } + } else { + // testtool artifact + if m.Root != "/" || m.Target != "/tmp" || + m.Source != "overlay" || m.FsType != "overlay" { + log.Fatal("unexpected temp mount entry") + } } next()