internal/pkg: move dependency flooding to cache
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m50s
Test / ShareFS (push) Successful in 4m46s
Test / Sandbox (race detector) (push) Successful in 5m16s
Test / Hpkg (push) Successful in 5m24s
Test / Hakurei (push) Successful in 5m40s
Test / Hakurei (race detector) (push) Successful in 7m27s
Test / Flake checks (push) Successful in 1m42s
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m50s
Test / ShareFS (push) Successful in 4m46s
Test / Sandbox (race detector) (push) Successful in 5m16s
Test / Hpkg (push) Successful in 5m24s
Test / Hakurei (push) Successful in 5m40s
Test / Hakurei (race detector) (push) Successful in 7m27s
Test / Flake checks (push) Successful in 1m42s
This imposes a hard upper limit to concurrency during dependency satisfaction and moves all dependency-related code out of individual implementations of Artifact. This change also includes ctx and msg as part of Cache. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"syscall"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/container/std"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on.
|
||||
@@ -30,7 +28,7 @@ type ExecPath struct {
|
||||
// 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.
|
||||
// Whether to make the mount point writable via the temp directory.
|
||||
W bool
|
||||
}
|
||||
|
||||
@@ -50,17 +48,8 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath {
|
||||
// Methods of execArtifact does not modify any struct field or underlying arrays
|
||||
// referred to by slices.
|
||||
type execArtifact struct {
|
||||
// Caller-supplied context.
|
||||
ctx context.Context
|
||||
// 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
|
||||
|
||||
// Number of [Artifact] to concurrently cure. A value of 0 or lower is
|
||||
// equivalent to the value returned by [runtime.NumCPU].
|
||||
cures int
|
||||
|
||||
// Passed through to [container.Params].
|
||||
dir *check.Absolute
|
||||
@@ -93,10 +82,10 @@ func (a *execNetArtifact) Params() []byte {
|
||||
return slices.Concat(a.checksum[:], a.execArtifact.Params())
|
||||
}
|
||||
|
||||
// Cure cures the [Artifact] by curing all its dependencies then running the
|
||||
// container described by the caller. The container retains host networking.
|
||||
func (a *execNetArtifact) Cure(c *CureContext) error {
|
||||
return a.cure(c, true)
|
||||
// Cure cures the [Artifact] in the container described by the caller. The
|
||||
// container retains host networking.
|
||||
func (a *execNetArtifact) Cure(f *FContext) error {
|
||||
return a.cure(f, true)
|
||||
}
|
||||
|
||||
// NewExec returns a new [Artifact] bounded by ctx, it cures all [Artifact]
|
||||
@@ -114,13 +103,7 @@ func (a *execNetArtifact) Cure(c *CureContext) error {
|
||||
//
|
||||
// If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum]
|
||||
// and its container runs in the host net namespace.
|
||||
//
|
||||
// A cures value of 0 or lower is equivalent to the value returned by
|
||||
// [runtime.NumCPU].
|
||||
func NewExec(
|
||||
ctx context.Context,
|
||||
msg message.Msg,
|
||||
cures int,
|
||||
checksum *Checksum,
|
||||
|
||||
dir *check.Absolute,
|
||||
@@ -130,7 +113,7 @@ func NewExec(
|
||||
|
||||
paths ...ExecPath,
|
||||
) Artifact {
|
||||
a := execArtifact{ctx, paths, msg, cures, dir, env, path, args}
|
||||
a := execArtifact{paths, dir, env, path, args}
|
||||
if checksum == nil {
|
||||
return &a
|
||||
}
|
||||
@@ -186,10 +169,9 @@ func (a *execArtifact) Dependencies() []Artifact {
|
||||
return slices.Concat(artifacts...)
|
||||
}
|
||||
|
||||
// Cure cures the [Artifact] by curing all its dependencies then running the
|
||||
// container described by the caller.
|
||||
func (a *execArtifact) Cure(c *CureContext) (err error) {
|
||||
return a.cure(c, false)
|
||||
// Cure cures the [Artifact] in the container described by the caller.
|
||||
func (a *execArtifact) Cure(f *FContext) (err error) {
|
||||
return a.cure(f, false)
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -199,20 +181,8 @@ const (
|
||||
|
||||
// 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(c *CureContext, hostNet bool) (err error) {
|
||||
cures := a.cures
|
||||
if cures < 1 {
|
||||
cures = runtime.NumCPU()
|
||||
}
|
||||
|
||||
func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
|
||||
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 || len(p.A) == 0 {
|
||||
return os.ErrInvalid
|
||||
@@ -220,8 +190,6 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
if p.P.Is(AbsWork) {
|
||||
overlayWorkIndex = i
|
||||
}
|
||||
paths[i].dst = p.P
|
||||
paths[i].src = make([]*check.Absolute, len(p.A))
|
||||
}
|
||||
|
||||
var artifactCount int
|
||||
@@ -229,71 +197,10 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
artifactCount += len(p.A)
|
||||
}
|
||||
|
||||
if len(paths) > 0 {
|
||||
type cureArtifact struct {
|
||||
// Index of pending Artifact in paths.
|
||||
index [2]int
|
||||
// Pending artifact.
|
||||
a Artifact
|
||||
}
|
||||
ac := make(chan cureArtifact, artifactCount)
|
||||
for i, p := range a.paths {
|
||||
for j, d := range p.A {
|
||||
ac <- cureArtifact{[2]int{i, j}, d}
|
||||
}
|
||||
}
|
||||
|
||||
type cureRes struct {
|
||||
// Index of result in paths.
|
||||
index [2]int
|
||||
// Cured pathname.
|
||||
pathname *check.Absolute
|
||||
// Error returned by c.
|
||||
err error
|
||||
}
|
||||
res := make(chan cureRes)
|
||||
|
||||
for i := 0; i < cures; i++ {
|
||||
go func() {
|
||||
for d := range ac {
|
||||
// computing and encoding identifier is expensive
|
||||
if a.msg.IsVerbose() {
|
||||
a.msg.Verbosef("curing %s...", Encode(Ident(d.a)))
|
||||
}
|
||||
|
||||
var cr cureRes
|
||||
cr.index = d.index
|
||||
cr.pathname, _, cr.err = c.Cure(d.a)
|
||||
res <- cr
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var count int
|
||||
errs := make([]error, 0, artifactCount)
|
||||
for cr := range res {
|
||||
count++
|
||||
|
||||
if cr.err != nil {
|
||||
errs = append(errs, cr.err)
|
||||
} else {
|
||||
paths[cr.index[0]].src[cr.index[1]] = cr.pathname
|
||||
}
|
||||
|
||||
if count == artifactCount {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(ac)
|
||||
if err = errors.Join(errs...); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(a.ctx)
|
||||
ctx, cancel := context.WithCancel(f.Unwrap())
|
||||
defer cancel()
|
||||
|
||||
z := container.New(ctx, a.msg)
|
||||
z := container.New(ctx, f.GetMessage())
|
||||
z.WaitDelay = execWaitDelay
|
||||
z.SeccompPresets |= std.PresetStrict
|
||||
z.ParentPerm = 0700
|
||||
@@ -303,15 +210,20 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
z.Hostname = "cure-net"
|
||||
}
|
||||
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
|
||||
if a.msg.IsVerbose() {
|
||||
if f.GetMessage().IsVerbose() {
|
||||
z.Stdout, z.Stderr = os.Stdout, os.Stderr
|
||||
}
|
||||
|
||||
z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args
|
||||
z.Grow(len(paths) + 4)
|
||||
z.Grow(len(a.paths) + 4)
|
||||
|
||||
temp, work := f.GetTempDir(), f.GetWorkDir()
|
||||
for i, b := range a.paths {
|
||||
layers := make([]*check.Absolute, len(b.A))
|
||||
for j, d := range b.A {
|
||||
layers[j] = f.Pathname(d)
|
||||
}
|
||||
|
||||
temp, work := c.GetTempDir(), c.GetWorkDir()
|
||||
for i, b := range paths {
|
||||
if i == overlayWorkIndex {
|
||||
if err = os.MkdirAll(work.String(), 0700); err != nil {
|
||||
return
|
||||
@@ -324,7 +236,7 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
AbsWork,
|
||||
work,
|
||||
tempWork,
|
||||
b.src...,
|
||||
layers...,
|
||||
)
|
||||
continue
|
||||
}
|
||||
@@ -341,11 +253,11 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
if err = os.MkdirAll(tempWork.String(), 0700); err != nil {
|
||||
return
|
||||
}
|
||||
z.Overlay(b.dst, tempUpper, tempWork, b.src...)
|
||||
} else if len(b.src) == 1 {
|
||||
z.Bind(b.src[0], b.dst, 0)
|
||||
z.Overlay(b.P, tempUpper, tempWork, layers...)
|
||||
} else if len(layers) == 1 {
|
||||
z.Bind(layers[0], b.P, 0)
|
||||
} else {
|
||||
z.OverlayReadonly(b.dst, b.src...)
|
||||
z.OverlayReadonly(b.P, layers...)
|
||||
}
|
||||
}
|
||||
if overlayWorkIndex < 0 {
|
||||
@@ -356,7 +268,7 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
|
||||
)
|
||||
}
|
||||
z.Bind(
|
||||
c.GetTempDir(),
|
||||
f.GetTempDir(),
|
||||
fhs.AbsTmp,
|
||||
std.BindWritable|std.BindEnsure,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user