internal/pkg: exec with specific timeout
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m58s
Test / ShareFS (push) Successful in 5m1s
Test / Sandbox (race detector) (push) Successful in 5m24s
Test / Hpkg (push) Successful in 5m29s
Test / Hakurei (push) Successful in 5m49s
Test / Hakurei (race detector) (push) Successful in 7m37s
Test / Flake checks (push) Successful in 1m47s

This change also updates the documentation of NewExec.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-01-11 17:46:04 +09:00
parent 63e137856e
commit 7ccc2fc5ec
2 changed files with 37 additions and 14 deletions

View File

@@ -42,6 +42,13 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath {
return ExecPath{check.MustAbs(pathname), a, writable}
}
const (
// ExecTimeoutDefault replaces out of range [NewExec] timeout values.
ExecTimeoutDefault = 15 * time.Minute
// ExecTimeoutMax is the arbitrary upper bound of [NewExec] timeout.
ExecTimeoutMax = 48 * time.Hour
)
// An execArtifact is an [Artifact] that produces output by running a program
// part of another [Artifact] in a [container] to produce its output.
//
@@ -59,6 +66,11 @@ type execArtifact struct {
path *check.Absolute
// Passed through to [container.Params].
args []string
// Duration the initial process is allowed to run. The zero value is
// equivalent to execTimeoutDefault. This value is never encoded in Params
// because it cannot affect outcome.
timeout time.Duration
}
// execNetArtifact is like execArtifact but implements [KnownChecksum] and has
@@ -88,10 +100,9 @@ func (a *execNetArtifact) Cure(f *FContext) error {
return a.cure(f, true)
}
// NewExec returns a new [Artifact] bounded by ctx, it cures all [Artifact]
// in paths at the specified maximum concurrent cures limit. Specified paths are
// bind mounted read-only in the specified order in the resulting container.
// A private instance of /proc and /dev is made available to the container.
// NewExec returns a new [Artifact] that executes the program path in a
// container with specified paths bind mounted read-only in order. 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 [AbsWork] and [fhs.AbsTmp] respectively. If one or more paths target
@@ -103,8 +114,14 @@ func (a *execNetArtifact) Cure(f *FContext) error {
//
// If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum]
// and its container runs in the host net namespace.
//
// The container is allowed to run for the specified duration before the initial
// process and all processes originating from it is terminated. A zero or
// negative timeout value is equivalent tp [ExecTimeoutDefault], a timeout value
// greater than [ExecTimeoutMax] is equivalent to [ExecTimeoutMax].
func NewExec(
checksum *Checksum,
timeout time.Duration,
dir *check.Absolute,
env []string,
@@ -113,7 +130,13 @@ func NewExec(
paths ...ExecPath,
) Artifact {
a := execArtifact{paths, dir, env, path, args}
if timeout <= 0 {
timeout = ExecTimeoutDefault
}
if timeout > ExecTimeoutMax {
timeout = ExecTimeoutMax
}
a := execArtifact{paths, dir, env, path, args, timeout}
if checksum == nil {
return &a
}
@@ -197,7 +220,7 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
artifactCount += len(p.A)
}
ctx, cancel := context.WithCancel(f.Unwrap())
ctx, cancel := context.WithTimeout(f.Unwrap(), a.timeout)
defer cancel()
z := container.New(ctx, f.GetMessage())

View File

@@ -38,7 +38,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{
{"container", pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"),
@@ -61,7 +61,7 @@ func TestExec(t *testing.T) {
), ignorePathname, wantChecksumOffline, nil},
{"error passthrough", pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"),
@@ -77,7 +77,7 @@ func TestExec(t *testing.T) {
), nil, pkg.Checksum{}, errors.Join(stub.UniqueError(0xcafe))},
{"invalid paths", pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"),
@@ -90,7 +90,7 @@ func TestExec(t *testing.T) {
// check init failure passthrough
var exitError *exec.ExitError
if _, _, err := c.Cure(pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
nil,
check.MustAbs("/opt/bin/testtool"),
@@ -112,7 +112,7 @@ func TestExec(t *testing.T) {
)
cureMany(t, c, []cureStep{
{"container", pkg.NewExec(
&wantChecksum,
&wantChecksum, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"),
@@ -144,7 +144,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{
{"container", pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/opt/bin/testtool"),
@@ -170,7 +170,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{
{"container", pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/work/bin/testtool"),
@@ -201,7 +201,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{
{"container", pkg.NewExec(
nil,
nil, 0,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/opt/bin/testtool"),