internal/pkg: support explicit overlay mount
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m48s
Test / ShareFS (push) Successful in 4m43s
Test / Sandbox (race detector) (push) Successful in 5m15s
Test / Hpkg (push) Successful in 5m25s
Test / Hakurei (push) Successful in 5m38s
Test / Hakurei (race detector) (push) Successful in 7m28s
Test / Flake checks (push) Successful in 1m44s
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m48s
Test / ShareFS (push) Successful in 4m43s
Test / Sandbox (race detector) (push) Successful in 5m15s
Test / Hpkg (push) Successful in 5m25s
Test / Hakurei (push) Successful in 5m38s
Test / Hakurei (race detector) (push) Successful in 7m28s
Test / Flake checks (push) Successful in 1m44s
This removes all but the /work/ auto overlay behaviour and enables much greater flexibility. This also renames ExecContainerPath to ExecPath so it is easier to type. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -20,17 +20,27 @@ import (
|
||||
// AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on.
|
||||
var AbsWork = fhs.AbsRoot.Append("work/")
|
||||
|
||||
// ExecContainerPath is an [Artifact] and the [check.Absolute] pathname to make
|
||||
// it available under in the container.
|
||||
type ExecContainerPath struct {
|
||||
// ExecPath is a slice of [Artifact] and the [check.Absolute] pathname to make
|
||||
// it available at under in the container.
|
||||
type ExecPath struct {
|
||||
// Pathname in the container mount namespace.
|
||||
P *check.Absolute
|
||||
A Artifact
|
||||
// Artifacts to mount on the pathname, must contain at least one [Artifact].
|
||||
// If there are multiple entries or W is true, P is set up as an overlay
|
||||
// mount, and entries of A must not implement [File].
|
||||
A []Artifact
|
||||
// Whether to make the mount point writable via an invisible tmpfs upperdir.
|
||||
W bool
|
||||
}
|
||||
|
||||
// MustPath returns [ExecContainerPath] for pathname and [Artifact] and panics
|
||||
// if pathname is not absolute.
|
||||
func MustPath(pathname string, a Artifact) ExecContainerPath {
|
||||
return ExecContainerPath{check.MustAbs(pathname), a}
|
||||
// Path returns a populated [ExecPath].
|
||||
func Path(pathname *check.Absolute, writable bool, a ...Artifact) ExecPath {
|
||||
return ExecPath{pathname, a, writable}
|
||||
}
|
||||
|
||||
// MustPath is like [Path], but takes a string pathname via [check.MustAbs].
|
||||
func MustPath(pathname string, writable bool, a ...Artifact) ExecPath {
|
||||
return ExecPath{check.MustAbs(pathname), a, writable}
|
||||
}
|
||||
|
||||
// An execArtifact is an [Artifact] that produces output by running a program
|
||||
@@ -41,8 +51,8 @@ func MustPath(pathname string, a Artifact) ExecContainerPath {
|
||||
type execArtifact struct {
|
||||
// Caller-supplied context.
|
||||
ctx context.Context
|
||||
// Caller-supplied inner read-only bind mounts.
|
||||
paths []ExecContainerPath
|
||||
// Caller-supplied inner mount points.
|
||||
paths []ExecPath
|
||||
// Caller-supplied logging facility, passed through to [container] and used
|
||||
// internally to produce verbose output.
|
||||
msg message.Msg
|
||||
@@ -95,20 +105,11 @@ func (a *execNetArtifact) Cure(c *CureContext) error {
|
||||
//
|
||||
// The working and temporary directories are both created and mounted writable
|
||||
// on [AbsWork] and [fhs.AbsTmp] 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 one or more paths target
|
||||
// [AbsWork], the final entry is set up as a writable overlay mount on /work for
|
||||
// which the upperdir is the host side work directory. In this configuration,
|
||||
// the program must avoid causing whiteout files to be created, cure fails if
|
||||
// upperdir ends up with anything other than directory, regular or symlink
|
||||
// entries.
|
||||
//
|
||||
// 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
|
||||
// container. This is primarily to make it possible for [container] to set up
|
||||
// mount points targeting paths not available in the [Artifact] backing root,
|
||||
// and to accommodate poorly written programs that insist on writing to awkward
|
||||
// paths, it must not be used as scratch space.
|
||||
// the W field is ignored, and the program must avoid causing whiteout files to
|
||||
// be created. Cure fails if upperdir ends up with entries other than directory,
|
||||
// regular or symlink.
|
||||
//
|
||||
// If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum]
|
||||
// and its container runs in the host net namespace.
|
||||
@@ -126,7 +127,7 @@ func NewExec(
|
||||
path *check.Absolute,
|
||||
args []string,
|
||||
|
||||
paths ...ExecContainerPath,
|
||||
paths ...ExecPath,
|
||||
) Artifact {
|
||||
a := execArtifact{ctx, paths, msg, cures, dir, env, path, args}
|
||||
if checksum == nil {
|
||||
@@ -142,17 +143,22 @@ func (a *execArtifact) Kind() Kind { return KindExec }
|
||||
func (a *execArtifact) Params() []byte {
|
||||
var buf bytes.Buffer
|
||||
for _, p := range a.paths {
|
||||
if p.W {
|
||||
buf.WriteByte(1)
|
||||
} else {
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if p.P != nil {
|
||||
buf.WriteString(p.P.String())
|
||||
} else {
|
||||
buf.WriteString("invalid P\x00")
|
||||
}
|
||||
if p.A != nil {
|
||||
id := Ident(p.A)
|
||||
buf.WriteByte(0)
|
||||
for _, d := range p.A {
|
||||
id := Ident(d)
|
||||
buf.Write(id[:])
|
||||
} else {
|
||||
buf.WriteString("invalid A\x00")
|
||||
}
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
buf.WriteByte(0)
|
||||
buf.WriteString(a.dir.String())
|
||||
@@ -170,15 +176,13 @@ func (a *execArtifact) Params() []byte {
|
||||
}
|
||||
|
||||
// Dependencies returns a slice of all artifacts collected from caller-supplied
|
||||
// [ExecContainerPath].
|
||||
// [ExecPath].
|
||||
func (a *execArtifact) Dependencies() []Artifact {
|
||||
artifacts := make([]Artifact, 0, len(a.paths))
|
||||
artifacts := make([][]Artifact, 0, len(a.paths))
|
||||
for _, p := range a.paths {
|
||||
if p.A != nil {
|
||||
artifacts = append(artifacts, p.A)
|
||||
}
|
||||
artifacts = append(artifacts, p.A)
|
||||
}
|
||||
return artifacts
|
||||
return slices.Concat(artifacts...)
|
||||
}
|
||||
|
||||
// Cure cures the [Artifact] by curing all its dependencies then running the
|
||||
@@ -200,35 +204,42 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
cures = runtime.NumCPU()
|
||||
}
|
||||
|
||||
overlayTempIndex, overlayWorkIndex := -1, -1
|
||||
paths := make([][2]*check.Absolute, len(a.paths))
|
||||
overlayWorkIndex := -1
|
||||
type curePath struct {
|
||||
// Copied from ExecPath.P.
|
||||
dst *check.Absolute
|
||||
// Cured from ExecPath.A.
|
||||
src []*check.Absolute
|
||||
}
|
||||
paths := make([]curePath, len(a.paths))
|
||||
for i, p := range a.paths {
|
||||
if p.P == nil || p.A == nil {
|
||||
if p.P == nil || len(p.A) == 0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
if p.P.Is(fhs.AbsTmp) {
|
||||
overlayTempIndex = i
|
||||
} else if p.P.Is(AbsWork) {
|
||||
if p.P.Is(AbsWork) {
|
||||
overlayWorkIndex = i
|
||||
}
|
||||
paths[i][1] = p.P
|
||||
paths[i].dst = p.P
|
||||
paths[i].src = make([]*check.Absolute, len(p.A))
|
||||
}
|
||||
|
||||
if len(paths) > 0 {
|
||||
type cureArtifact struct {
|
||||
// Index of pending Artifact in paths.
|
||||
index int
|
||||
index [2]int
|
||||
// Pending artifact.
|
||||
a Artifact
|
||||
}
|
||||
ac := make(chan cureArtifact, len(paths))
|
||||
for i, p := range a.paths {
|
||||
ac <- cureArtifact{i, p.A}
|
||||
for j, d := range p.A {
|
||||
ac <- cureArtifact{[2]int{i, j}, d}
|
||||
}
|
||||
}
|
||||
|
||||
type cureRes struct {
|
||||
// Index of result in paths.
|
||||
index int
|
||||
index [2]int
|
||||
// Cured pathname.
|
||||
pathname *check.Absolute
|
||||
// Error returned by c.
|
||||
@@ -260,7 +271,7 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
if cr.err != nil {
|
||||
errs = append(errs, cr.err)
|
||||
} else {
|
||||
paths[cr.index][0] = cr.pathname
|
||||
paths[cr.index[0]].src[cr.index[1]] = cr.pathname
|
||||
}
|
||||
|
||||
if count == len(paths) {
|
||||
@@ -293,32 +304,9 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
|
||||
z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args
|
||||
z.Grow(len(paths) + 4)
|
||||
if len(paths) > 0 && paths[0][1].Is(fhs.AbsRoot) {
|
||||
z.OverlayEphemeral(fhs.AbsRoot, paths[0][0])
|
||||
paths = paths[1:]
|
||||
overlayTempIndex--
|
||||
overlayWorkIndex--
|
||||
}
|
||||
|
||||
temp, work := c.GetTempDir(), c.GetWorkDir()
|
||||
for i, b := range paths {
|
||||
if i == overlayTempIndex {
|
||||
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,
|
||||
b[0],
|
||||
)
|
||||
continue
|
||||
}
|
||||
if i == overlayWorkIndex {
|
||||
if err = os.MkdirAll(work.String(), 0700); err != nil {
|
||||
return
|
||||
@@ -331,11 +319,18 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
AbsWork,
|
||||
work,
|
||||
tempWork,
|
||||
b[0],
|
||||
b.src...,
|
||||
)
|
||||
continue
|
||||
}
|
||||
z.Bind(b[0], b[1], 0)
|
||||
|
||||
if a.paths[i].W {
|
||||
z.OverlayEphemeral(b.dst, b.src...)
|
||||
} else if len(b.src) == 1 {
|
||||
z.Bind(b.src[0], b.dst, 0)
|
||||
} else {
|
||||
z.OverlayReadonly(b.dst, b.src...)
|
||||
}
|
||||
}
|
||||
if overlayWorkIndex < 0 {
|
||||
z.Bind(
|
||||
@@ -344,13 +339,11 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
std.BindWritable|std.BindEnsure,
|
||||
)
|
||||
}
|
||||
if overlayTempIndex < 0 {
|
||||
z.Bind(
|
||||
c.GetTempDir(),
|
||||
fhs.AbsTmp,
|
||||
std.BindWritable|std.BindEnsure,
|
||||
)
|
||||
}
|
||||
z.Bind(
|
||||
c.GetTempDir(),
|
||||
fhs.AbsTmp,
|
||||
std.BindWritable|std.BindEnsure,
|
||||
)
|
||||
z.Proc(fhs.AbsProc).Dev(fhs.AbsDev, true)
|
||||
|
||||
if err = z.Start(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user