aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/exec.go')
-rw-r--r--internal/pkg/exec.go164
1 files changed, 97 insertions, 67 deletions
diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go
index e0512bac..244b353b 100644
--- a/internal/pkg/exec.go
+++ b/internal/pkg/exec.go
@@ -40,14 +40,17 @@ type ExecPath struct {
W bool
}
-// SetSchedIdle is whether to set [std.SCHED_IDLE] scheduling priority.
+// SetSchedIdle is whether to set [ext.SCHED_IDLE] scheduling priority.
var SetSchedIdle bool
+// GetArtifactFunc is the function signature of [FContext.GetArtifact].
+type GetArtifactFunc func(Artifact) (*check.Absolute, unique.Handle[Checksum])
+
// PromoteLayers returns artifacts with identical-by-content layers promoted to
// the highest priority instance, as if mounted via [ExecPath].
func PromoteLayers(
artifacts []Artifact,
- getArtifact func(Artifact) (*check.Absolute, unique.Handle[Checksum]),
+ getArtifact GetArtifactFunc,
report func(i int, d Artifact),
) []*check.Absolute {
layers := make([]*check.Absolute, 0, len(artifacts))
@@ -67,14 +70,14 @@ func PromoteLayers(
}
// layers returns pathnames collected from A deduplicated via [PromoteLayers].
-func (p *ExecPath) layers(f *FContext) []*check.Absolute {
- msg := f.GetMessage()
- return PromoteLayers(p.A, f.GetArtifact, func(i int, d Artifact) {
+func (p *ExecPath) layers(
+ msg message.Msg,
+ getArtifact GetArtifactFunc,
+ ident func(a Artifact) unique.Handle[ID],
+) []*check.Absolute {
+ return PromoteLayers(p.A, getArtifact, func(i int, d Artifact) {
if msg.IsVerbose() {
- msg.Verbosef(
- "promoted layer %d as %s",
- i, reportName(d, f.cache.Ident(d)),
- )
+ msg.Verbosef("promoted layer %d as %s", i, reportName(d, ident(d)))
}
})
}
@@ -382,17 +385,30 @@ func scanVerbose(
}
}
+var (
+ // ErrInvalidPaths is returned for an [Artifact] of [KindExec] or
+ // [KindExecNet] specified with invalid paths.
+ ErrInvalidPaths = errors.New("invalid mount point")
+)
+
// SeccompPresets is the [seccomp] presets used by exec artifacts.
const SeccompPresets = std.PresetStrict &
^(std.PresetDenyNS | std.PresetDenyDevel)
-// cure is like Cure but allows optional host net namespace. This is used for
-// the [KnownChecksum] variant where networking is allowed.
-func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
+// makeContainer sets up the specified temp and work directories and returns the
+// corresponding [container.Container] that would have run for cure.
+func (a *execArtifact) makeContainer(
+ ctx context.Context,
+ msg message.Msg,
+ hostNet bool,
+ temp, work *check.Absolute,
+ getArtifact GetArtifactFunc,
+ ident func(a Artifact) unique.Handle[ID],
+) (z *container.Container, err error) {
overlayWorkIndex := -1
for i, p := range a.paths {
if p.P == nil || len(p.A) == 0 {
- return os.ErrInvalid
+ return nil, ErrInvalidPaths
}
if p.P.Is(AbsWork) {
overlayWorkIndex = i
@@ -404,10 +420,7 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
artifactCount += len(p.A)
}
- ctx, cancel := context.WithTimeout(f.Unwrap(), a.timeout)
- defer cancel()
-
- z := container.New(ctx, f.GetMessage())
+ z = container.New(ctx, msg)
z.WaitDelay = execWaitDelay
z.SeccompPresets = SeccompPresets
z.SeccompFlags |= seccomp.AllowMultiarch
@@ -421,53 +434,9 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
}
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
- var status io.Writer
- if status, err = f.GetStatusWriter(); err != nil {
- return
- }
-
- if msg := f.GetMessage(); msg.IsVerbose() {
- var stdout, stderr io.ReadCloser
- if stdout, err = z.StdoutPipe(); err != nil {
- return
- }
- if stderr, err = z.StderrPipe(); err != nil {
- _ = stdout.Close()
- return
- }
- defer func() {
- if err != nil && !errors.As(err, new(*exec.ExitError)) {
- _ = stdout.Close()
- _ = stderr.Close()
- }
- }()
-
- brStdout, brStderr := f.cache.getReader(stdout), f.cache.getReader(stderr)
- stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
- go scanVerbose(
- msg, cancel, stdoutDone,
- "("+a.name+":1)",
- io.TeeReader(brStdout, status),
- )
- go scanVerbose(
- msg, cancel, stderrDone,
- "("+a.name+":2)",
- io.TeeReader(brStderr, status),
- )
- defer func() {
- <-stdoutDone
- <-stderrDone
- f.cache.putReader(brStdout)
- f.cache.putReader(brStderr)
- }()
- } else {
- z.Stdout, z.Stderr = status, status
- }
-
z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args
z.Grow(len(a.paths) + 4)
- temp, work := f.GetTempDir(), f.GetWorkDir()
for i, b := range a.paths {
if i == overlayWorkIndex {
if err = os.MkdirAll(work.String(), 0700); err != nil {
@@ -481,7 +450,7 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
AbsWork,
work,
tempWork,
- b.layers(f)...,
+ b.layers(msg, getArtifact, ident)...,
)
continue
}
@@ -498,12 +467,12 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
if err = os.MkdirAll(tempWork.String(), 0700); err != nil {
return
}
- z.Overlay(b.P, tempUpper, tempWork, b.layers(f)...)
+ z.Overlay(b.P, tempUpper, tempWork, b.layers(msg, getArtifact, ident)...)
} else if len(b.A) == 1 {
- pathname, _ := f.GetArtifact(b.A[0])
+ pathname, _ := getArtifact(b.A[0])
z.Bind(pathname, b.P, 0)
} else {
- z.OverlayReadonly(b.P, b.layers(f)...)
+ z.OverlayReadonly(b.P, b.layers(msg, getArtifact, ident)...)
}
}
if overlayWorkIndex < 0 {
@@ -514,11 +483,72 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
)
}
z.Bind(
- f.GetTempDir(),
+ temp,
fhs.AbsTmp,
std.BindWritable|std.BindEnsure,
)
z.Proc(fhs.AbsProc).Dev(fhs.AbsDev, true)
+ return
+}
+
+// cure is like Cure but allows optional host net namespace.
+func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
+ ctx, cancel := context.WithTimeout(f.Unwrap(), a.timeout)
+ defer cancel()
+
+ msg := f.GetMessage()
+ var z *container.Container
+ if z, err = a.makeContainer(
+ ctx, msg, hostNet,
+ f.GetTempDir(), f.GetWorkDir(),
+ f.GetArtifact,
+ f.cache.Ident,
+ ); err != nil {
+ return
+ }
+
+ var status io.Writer
+ if status, err = f.GetStatusWriter(); err != nil {
+ return
+ }
+
+ if msg.IsVerbose() {
+ var stdout, stderr io.ReadCloser
+ if stdout, err = z.StdoutPipe(); err != nil {
+ return
+ }
+ if stderr, err = z.StderrPipe(); err != nil {
+ _ = stdout.Close()
+ return
+ }
+ defer func() {
+ if err != nil && !errors.As(err, new(*exec.ExitError)) {
+ _ = stdout.Close()
+ _ = stderr.Close()
+ }
+ }()
+
+ brStdout, brStderr := f.cache.getReader(stdout), f.cache.getReader(stderr)
+ stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
+ go scanVerbose(
+ msg, cancel, stdoutDone,
+ "("+a.name+":1)",
+ io.TeeReader(brStdout, status),
+ )
+ go scanVerbose(
+ msg, cancel, stderrDone,
+ "("+a.name+":2)",
+ io.TeeReader(brStderr, status),
+ )
+ defer func() {
+ <-stdoutDone
+ <-stderrDone
+ f.cache.putReader(brStdout)
+ f.cache.putReader(brStderr)
+ }()
+ } else {
+ z.Stdout, z.Stderr = status, status
+ }
if err = z.Start(); err != nil {
return
@@ -532,7 +562,7 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
// do not allow empty directories to succeed
for {
- err = syscall.Rmdir(work.String())
+ err = syscall.Rmdir(f.GetWorkDir().String())
if err != syscall.EINTR {
break
}