internal/pkg: optionally measure exec artifact
All checks were successful
Test / Create distribution (push) Successful in 1m55s
Test / Sandbox (push) Successful in 5m22s
Test / Hakurei (push) Successful in 9m50s
Test / ShareFS (push) Successful in 11m20s
Test / Sandbox (race detector) (push) Successful in 3m29s
Test / Hakurei (race detector) (push) Successful in 12m9s
Test / Flake checks (push) Successful in 4m23s

Useful for verifying deterministic output without enabling network access.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-05-12 15:10:43 +09:00
parent dcde38f2e9
commit 6643cfbeee
9 changed files with 77 additions and 38 deletions

View File

@@ -167,6 +167,9 @@ var _ fmt.Stringer = new(execArtifact)
type execNetArtifact struct { type execNetArtifact struct {
checksum Checksum checksum Checksum
// Whether to keep host net namespace.
hostNet bool
execArtifact execArtifact
} }
@@ -175,15 +178,24 @@ var _ KnownChecksum = new(execNetArtifact)
// Checksum returns the caller-supplied checksum. // Checksum returns the caller-supplied checksum.
func (a *execNetArtifact) Checksum() Checksum { return a.checksum } func (a *execNetArtifact) Checksum() Checksum { return a.checksum }
// Kind returns the hardcoded [Kind] constant. // Kind returns [KindExecNet], or [KindExec] if hostNet is false.
func (*execNetArtifact) Kind() Kind { return KindExecNet } func (a *execNetArtifact) Kind() Kind {
if a == nil || a.hostNet {
return KindExecNet
}
return KindExec
}
// Cure cures the [Artifact] in the container described by the caller. The // Cure cures the [Artifact] in the container described by the caller. The
// container retains host networking. // container retains host networking.
func (a *execNetArtifact) Cure(f *FContext) error { func (a *execNetArtifact) Cure(f *FContext) error {
return a.cure(f, true) return a.cure(f, a.hostNet)
} }
// ErrNetChecksum is panicked by [NewExec] if host net namespace is requested
// with a nil checksum.
var ErrNetChecksum = errors.New("attempting to keep net namespace without checksum")
// NewExec returns a new [Artifact] that executes the program path in a // NewExec returns a new [Artifact] that executes the program path in a
// container with specified paths bind mounted read-only in order. A private // container with specified paths bind mounted read-only in order. A private
// instance of /proc and /dev is made available to the container. // instance of /proc and /dev is made available to the container.
@@ -197,7 +209,7 @@ func (a *execNetArtifact) Cure(f *FContext) error {
// regular or symlink. // regular or symlink.
// //
// If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum] // If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum]
// and its container runs in the host net namespace. // and its container optionally runs in the host net namespace.
// //
// The container is allowed to run for the specified duration before the initial // 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 // process and all processes originating from it is terminated. A zero or
@@ -211,7 +223,7 @@ func NewExec(
name, arch string, name, arch string,
checksum *Checksum, checksum *Checksum,
timeout time.Duration, timeout time.Duration,
exclusive bool, hostNet, exclusive bool,
dir *check.Absolute, dir *check.Absolute,
env []string, env []string,
@@ -234,9 +246,12 @@ func NewExec(
} }
a := execArtifact{name, arch, paths, dir, env, pathname, args, timeout, exclusive} a := execArtifact{name, arch, paths, dir, env, pathname, args, timeout, exclusive}
if checksum == nil { if checksum == nil {
if hostNet {
panic(ErrNetChecksum)
}
return &a return &a
} }
return &execNetArtifact{*checksum, a} return &execNetArtifact{*checksum, hostNet, a}
} }
// Kind returns the hardcoded [Kind] constant. // Kind returns the hardcoded [Kind] constant.
@@ -361,22 +376,17 @@ func readExecArtifact(r *IRReader, net bool) Artifact {
exclusive := r.ReadUint32() != 0 exclusive := r.ReadUint32() != 0
checksum, ok := r.Finalise() checksum, ok := r.Finalise()
var checksumP *Checksum var checksumP *Checksum
if net {
if !ok {
panic(ErrExpectedChecksum)
}
checksumVal := checksum.Value()
checksumP = &checksumVal
} else {
if ok { if ok {
panic(ErrUnexpectedChecksum) checksumP = new(checksum.Value())
} }
if net && !ok {
panic(ErrExpectedChecksum)
} }
return NewExec( return NewExec(
name, arch, checksumP, timeout, exclusive, dir, env, pathname, args, paths..., name, arch, checksumP, timeout, net, exclusive, dir, env, pathname, args, paths...,
) )
} }
@@ -590,7 +600,7 @@ func (c *Cache) EnterExec(
case *execNetArtifact: case *execNetArtifact:
e = &f.execArtifact e = &f.execArtifact
hostNet = true hostNet = f.hostNet
default: default:
return ErrNotExec return ErrNotExec

View File

@@ -57,7 +57,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-offline", "", nil, 0, false, "exec-offline", "", new(wantOffline.hash()), 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1"}, []string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -80,7 +80,7 @@ func TestExec(t *testing.T) {
), ignorePathname, wantOffline, nil}, ), ignorePathname, wantOffline, nil},
{"error passthrough", pkg.NewExec( {"error passthrough", pkg.NewExec(
"", "", nil, 0, true, "", "", nil, 0, false, true,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1"}, []string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -103,7 +103,7 @@ func TestExec(t *testing.T) {
}}, }},
{"invalid paths", pkg.NewExec( {"invalid paths", pkg.NewExec(
"", "", nil, 0, false, "", "", nil, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1"}, []string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -116,7 +116,7 @@ func TestExec(t *testing.T) {
// check init failure passthrough // check init failure passthrough
var exitError *exec.ExitError var exitError *exec.ExitError
if _, _, err := c.Cure(pkg.NewExec( if _, _, err := c.Cure(pkg.NewExec(
"", "", nil, 0, false, "", "", nil, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
nil, nil,
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -157,7 +157,7 @@ func TestExec(t *testing.T) {
} }
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-net", "", new(wantNet.hash()), 0, false, "exec-net", "", new(wantNet.hash()), 0, true, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1"}, []string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -206,7 +206,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-overlay-root", "", nil, 0, false, "exec-overlay-root", "", nil, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -247,7 +247,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-overlay-work", "", nil, 0, false, "exec-overlay-work", "", nil, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/work/bin/testtool"), check.MustAbs("/work/bin/testtool"),
@@ -293,7 +293,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-multiple-layers", "", nil, 0, false, "exec-multiple-layers", "", nil, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -366,7 +366,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-layer-promotion", "", nil, 0, true, "exec-layer-promotion", "", nil, 0, false, true,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -416,7 +416,7 @@ func TestExec(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"container", pkg.NewExec( {"container", pkg.NewExec(
"exec-binfmt", "cafe", nil, 0, true, "exec-binfmt", "cafe", nil, 0, false, true,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1", "HAKUREI_BINFMT=1"}, []string{"HAKUREI_TEST=1", "HAKUREI_BINFMT=1"},
check.MustAbs("/opt/bin/sample"), check.MustAbs("/opt/bin/sample"),

View File

@@ -1,7 +1,7 @@
package expected package expected
const ( const (
Offline = "oe7Uv1u5BwxcuX3HLQzZRg1Q5oetJo6jWiKGMOeqLiqBkaVgyKzvx82N81_IzUAz" Offline = "q5ktDTq0miP-VvB2blxqXQeaRXCUWgP_KbC18KNtUDtyoaI_h5mHmGuPMArVEBDs"
OvlRoot = "NacZGXwuRkTvcHaG08a22ujJ8qCWN0RSoFlRSR5FSt0ZcBbJ28FRvkYsHEtX7G8i" OvlRoot = "NacZGXwuRkTvcHaG08a22ujJ8qCWN0RSoFlRSR5FSt0ZcBbJ28FRvkYsHEtX7G8i"
Layers = "WBJDrATtX6rIE5yAu8ePX3WmDF0Tt9kFiue0m3cRnyRoVx1my8a67fh3CAW486oP" Layers = "WBJDrATtX6rIE5yAu8ePX3WmDF0Tt9kFiue0m3cRnyRoVx1my8a67fh3CAW486oP"
Net = "CmYtj2sNB3LHtqiDuck_Lz3MjLLIiwyP8N4NDitQ1Icvv__LVP9p8tm-sHeQaKKp" Net = "CmYtj2sNB3LHtqiDuck_Lz3MjLLIiwyP8N4NDitQ1Icvv__LVP9p8tm-sHeQaKKp"

View File

@@ -1,7 +1,7 @@
package expected package expected
const ( const (
Offline = "K88Nrt1Mt6NZTdAjdq8_Zq2_Jv2mZYyNOfJzkjhTdxiV9G8FY1NtKNh21Kj-H3CO" Offline = "WapqyoPxbWSnq07dWHt71mHaJXq99pAjJfFlELlJljSiZMhTFqqlzU1_mN86shSj"
OvlRoot = "V9anFOiRvjGfAeBhLl14AL8TKdWZyD0WTPYe4fS9mOBw8iW5Lmarvt6TG6MV8uWm" OvlRoot = "V9anFOiRvjGfAeBhLl14AL8TKdWZyD0WTPYe4fS9mOBw8iW5Lmarvt6TG6MV8uWm"
Layers = "tKx7JNRoSBdK_7MdzI-nwTNV2wmiPzwYdcd17oLmXKL_iLmUzUiA79qTqdrTasrv" Layers = "tKx7JNRoSBdK_7MdzI-nwTNV2wmiPzwYdcd17oLmXKL_iLmUzUiA79qTqdrTasrv"
Net = "aXyDLzBCJ9XltXZIfetEVsEkrqHfcXuD5XE_FcUnYbN3emwL55N6P8LlHzNfGnM5" Net = "aXyDLzBCJ9XltXZIfetEVsEkrqHfcXuD5XE_FcUnYbN3emwL55N6P8LlHzNfGnM5"

View File

@@ -1,7 +1,7 @@
package expected package expected
const ( const (
Offline = "9pGgev_rDp5fUxMjgM-Z67p_IZYYAVv-cwhgS8XUtJeMWhsaf_YycQPfYLQFSfWk" Offline = "Z6yXE5gOJScL3srmnVMWgCXccDiUNZ5snSrf6RkXuU1_U0rX_kGVwsfHUgNG_awd"
OvlRoot = "zYXJHFRLuxvUhuisZEXgGgVvdQd6piMfp5jmtT6jdVjvC2gICXquOq-UTwlrSD5I" OvlRoot = "zYXJHFRLuxvUhuisZEXgGgVvdQd6piMfp5jmtT6jdVjvC2gICXquOq-UTwlrSD5I"
Layers = "_F8EDazHbcLeT0sVSQXRN_kn9IjduqJcDYgzXpsT-hpKU4EBcZ0PISN2zchpqMbm" Layers = "_F8EDazHbcLeT0sVSQXRN_kn9IjduqJcDYgzXpsT-hpKU4EBcZ0PISN2zchpqMbm"
Net = "CA_FAaSIYJgapBEHV40doxpH23PdUEy_6s1TZc7wfSPN0XYqwGpMceXXDSabGveO" Net = "CA_FAaSIYJgapBEHV40doxpH23PdUEy_6s1TZc7wfSPN0XYqwGpMceXXDSabGveO"

View File

@@ -39,7 +39,7 @@ func TestIRRoundtrip(t *testing.T) {
)}, )},
{"exec offline", pkg.NewExec( {"exec offline", pkg.NewExec(
"exec-offline", "", nil, 0, false, "exec-offline", "", nil, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1"}, []string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -61,7 +61,7 @@ func TestIRRoundtrip(t *testing.T) {
{"exec net", pkg.NewExec( {"exec net", pkg.NewExec(
"exec-net", "", "exec-net", "",
(*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))), (*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
0, false, 0, false, false,
pkg.AbsWork, pkg.AbsWork,
[]string{"HAKUREI_TEST=1"}, []string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"), check.MustAbs("/opt/bin/testtool"),
@@ -80,6 +80,28 @@ func TestIRRoundtrip(t *testing.T) {
)), )),
)}, )},
{"exec measured", pkg.NewExec(
"exec-measured", "",
(*pkg.Checksum)(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
0, false, false,
pkg.AbsWork,
[]string{"HAKUREI_TEST=1"},
check.MustAbs("/opt/bin/testtool"),
[]string{"testtool", "measured"},
pkg.MustPath("/file", false, pkg.NewFile("file", []byte(
"stub file",
))), pkg.MustPath("/.hakurei", false, pkg.NewHTTPGetTar(
nil, "file:///hakurei.tar",
pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
pkg.TarUncompressed,
)), pkg.MustPath("/opt", false, pkg.NewHTTPGetTar(
nil, "file:///testtool.tar.gz",
pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
pkg.TarGzip,
)),
)},
{"file anonymous", pkg.NewFile("", []byte{0})}, {"file anonymous", pkg.NewFile("", []byte{0})},
{"file", pkg.NewFile("stub", []byte("stub"))}, {"file", pkg.NewFile("stub", []byte("stub"))},
} }

View File

@@ -104,7 +104,7 @@ func newBusyboxBin() pkg.Artifact {
} }
return pkg.NewExec( return pkg.NewExec(
"busybox-bin-"+version, arch, nil, pkg.ExecTimeoutMax, false, "busybox-bin-"+version, arch, nil, pkg.ExecTimeoutMax, false, false,
fhs.AbsRoot, []string{ fhs.AbsRoot, []string{
"PATH=/system/bin", "PATH=/system/bin",
}, },

View File

@@ -111,7 +111,7 @@ func (t Toolchain) NewViaGit(
return t.New(strings.TrimSuffix( return t.New(strings.TrimSuffix(
path.Base(url), path.Base(url),
".git", ".git",
)+"-src-"+path.Base(rev), 0, t.AppendPresets(nil, )+"-src-"+path.Base(rev), THostNet, t.AppendPresets(nil,
NSSCACert, NSSCACert,
Git, Git,
), &checksum, nil, ` ), &checksum, nil, `

View File

@@ -220,6 +220,8 @@ const (
TEarly TEarly
// TNoToolchain excludes the LLVM toolchain. // TNoToolchain excludes the LLVM toolchain.
TNoToolchain TNoToolchain
// THostNet arranges for a [pkg.KindExecNet] to be created.
THostNet
) )
var ( var (
@@ -325,7 +327,9 @@ mkdir -vp /work/system/bin
} }
return pkg.NewExec( return pkg.NewExec(
name, arch, knownChecksum, pkg.ExecTimeoutMax, flag&TExclusive != 0, name, arch, knownChecksum, pkg.ExecTimeoutMax,
flag&THostNet != 0,
flag&TExclusive != 0,
fhs.AbsRoot, env, fhs.AbsRoot, env,
AbsSystem.Append("bin", "sh"), AbsSystem.Append("bin", "sh"),
[]string{"sh", absCureScript.String()}, []string{"sh", absCureScript.String()},
@@ -408,6 +412,9 @@ type Helper interface {
// PackageAttr holds build-system-agnostic attributes. // PackageAttr holds build-system-agnostic attributes.
type PackageAttr struct { type PackageAttr struct {
// Measure output if populated. Required by [THostNet].
KnownChecksum *pkg.Checksum
// Mount the source tree writable. // Mount the source tree writable.
Writable bool Writable bool
// Do not pass through [Toolchain.NewPatchedSource]. // Do not pass through [Toolchain.NewPatchedSource].
@@ -545,7 +552,7 @@ cd '/usr/src/` + name + `/'
name+"-"+version, name+"-"+version,
attr.Flag, attr.Flag,
extraRes, extraRes,
nil, attr.KnownChecksum,
attr.Env, attr.Env,
scriptEarly+helper.script(name), scriptEarly+helper.script(name),
slices.Concat(attr.Paths, []pkg.ExecPath{ slices.Concat(attr.Paths, []pkg.ExecPath{