Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 22 additions & 29 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,12 @@ func toRepoOnly(in string) (string, error) {
return strings.Join(out, ","), nil
}

type Handler struct {
Evaluate func(ctx context.Context, c gateway.Client, res *gateway.Result) error
}
type (
EvaluateFunc func(ctx context.Context, name string, c gateway.Client, res *gateway.Result) error
Handler struct {
Evaluate EvaluateFunc
}
)

func Build(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
return BuildWithResultHandler(ctx, nodes, opts, docker, cfg, w, nil)
Expand Down Expand Up @@ -510,12 +513,25 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
rKey := resultKey(dp.driverIndex, k)
results.Set(rKey, res)

forceEval := false
if children := childTargets[rKey]; len(children) > 0 {
if err := waitForChildren(ctx, bh, c, res, results, children); err != nil {
// wait for the child targets to register their LLB before evaluating
_, err := results.Get(ctx, children...)
if err != nil {
return nil, err
}
} else if bh != nil && bh.Evaluate != nil {
if err := bh.Evaluate(ctx, c, res); err != nil {
forceEval = true
}

// invoke custom evaluate handler if it is present
if bh != nil && bh.Evaluate != nil {
if err := bh.Evaluate(ctx, k, c, res); err != nil {
return nil, err
}
} else if forceEval {
if err := res.EachRef(func(ref gateway.Reference) error {
return ref.Evaluate(ctx)
}); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -1199,29 +1215,6 @@ func solve(ctx context.Context, c gateway.Client, req gateway.SolveRequest) (*ga
return res, nil
}

func waitForChildren(ctx context.Context, bh *Handler, c gateway.Client, res *gateway.Result, results *waitmap.Map, children []string) error {
// wait for the child targets to register their LLB before evaluating
_, err := results.Get(ctx, children...)
if err != nil {
return err
}
// we need to wait until the child targets have completed before we can release
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
if bh != nil && bh.Evaluate != nil {
return bh.Evaluate(ctx, c, res)
}
return res.EachRef(func(ref gateway.Reference) error {
return ref.Evaluate(ctx)
})
})
eg.Go(func() error {
_, err := results.Get(ctx, children...)
return err
})
return eg.Wait()
}

func catchFrontendError(retErr, frontendErr *error) {
*frontendErr = *retErr
if errors.Is(*retErr, ErrRestart) {
Expand Down
36 changes: 24 additions & 12 deletions build/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ import (
)

type InvokeConfig struct {
Entrypoint []string
Cmd []string
NoCmd bool
Env []string
User string
NoUser bool
Cwd string
NoCwd bool
Tty bool
Rollback bool
Initial bool
SuspendOn SuspendOn
Entrypoint []string `json:"entrypoint,omitempty"`
Cmd []string `json:"cmd,omitempty"`
NoCmd bool `json:"noCmd,omitempty"`
Env []string `json:"env,omitempty"`
User string `json:"user,omitempty"`
NoUser bool `json:"noUser,omitempty"`
Cwd string `json:"cwd,omitempty"`
NoCwd bool `json:"noCwd,omitempty"`
Tty bool `json:"tty,omitempty"`
Rollback bool `json:"rollback,omitempty"`
Initial bool `json:"initial,omitempty"`
SuspendOn SuspendOn `json:"suspendOn,omitempty"`
}

func (cfg *InvokeConfig) NeedsDebug(err error) bool {
Expand All @@ -43,6 +43,18 @@ func (s SuspendOn) DebugEnabled(err error) bool {
return err != nil || s == SuspendAlways
}

func (s *SuspendOn) UnmarshalText(text []byte) error {
switch string(text) {
case "error":
*s = SuspendError
case "always":
*s = SuspendAlways
default:
return errors.Errorf("unknown suspend name: %s", string(text))
}
return nil
}

type Container struct {
cancelOnce sync.Once
containerCancel func(error)
Expand Down
33 changes: 13 additions & 20 deletions build/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ import (
"github.com/sirupsen/logrus"
)

// NewResultHandle stores a gateway client, gateway result, and the error from
// NewResultHandle stores a gateway client, gateway reference, and the error from
// an evaluate call if it is present.
//
// This ResultHandle can be used to execute additional build steps in the same
// context as the build occurred, which can allow easy debugging of build
// failures and successes.
//
// If the returned ResultHandle is not nil, the caller must call Done() on it.
func NewResultHandle(ctx context.Context, c gateway.Client, res *gateway.Result, err error) *ResultHandle {
func NewResultHandle(ctx context.Context, c gateway.Client, ref gateway.Reference, meta map[string][]byte, err error) *ResultHandle {
rCtx := &ResultHandle{
res: res,
ref: ref,
meta: meta,
gwClient: c,
}
if err != nil && !errors.As(err, &rCtx.solveErr) {
Expand All @@ -37,8 +38,9 @@ func NewResultHandle(ctx context.Context, c gateway.Client, res *gateway.Result,

// ResultHandle is a build result with the client that built it.
type ResultHandle struct {
res *gateway.Result
ref gateway.Reference
solveErr *errdefs.SolveError
meta map[string][]byte
gwClient gateway.Client

doneOnce sync.Once
Expand Down Expand Up @@ -74,9 +76,9 @@ func (r *ResultHandle) NewContainer(ctx context.Context, cfg *InvokeConfig) (gat
}

func (r *ResultHandle) getContainerConfig(cfg *InvokeConfig) (containerCfg gateway.NewContainerRequest, _ error) {
if r.res != nil && r.solveErr == nil {
if r.ref != nil && r.solveErr == nil {
logrus.Debugf("creating container from successful build")
ccfg, err := containerConfigFromResult(r.res, cfg)
ccfg, err := containerConfigFromResult(r.ref, cfg)
if err != nil {
return containerCfg, err
}
Expand All @@ -94,9 +96,9 @@ func (r *ResultHandle) getContainerConfig(cfg *InvokeConfig) (containerCfg gatew

func (r *ResultHandle) getProcessConfig(cfg *InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) (_ gateway.StartRequest, err error) {
processCfg := newStartRequest(stdin, stdout, stderr)
if r.res != nil && r.solveErr == nil {
if r.ref != nil && r.solveErr == nil {
logrus.Debugf("creating container from successful build")
if err := populateProcessConfigFromResult(&processCfg, r.res, cfg); err != nil {
if err := populateProcessConfigFromResult(&processCfg, r.meta, cfg); err != nil {
return processCfg, err
}
} else {
Expand All @@ -108,20 +110,11 @@ func (r *ResultHandle) getProcessConfig(cfg *InvokeConfig, stdin io.ReadCloser,
return processCfg, nil
}

func containerConfigFromResult(res *gateway.Result, cfg *InvokeConfig) (*gateway.NewContainerRequest, error) {
func containerConfigFromResult(ref gateway.Reference, cfg *InvokeConfig) (*gateway.NewContainerRequest, error) {
if cfg.Initial {
return nil, errors.Errorf("starting from the container from the initial state of the step is supported only on the failed steps")
}

ps, err := exptypes.ParsePlatforms(res.Metadata)
if err != nil {
return nil, err
}
ref, ok := res.FindRef(ps.Platforms[0].ID)
if !ok {
return nil, errors.Errorf("no reference found")
}

return &gateway.NewContainerRequest{
Mounts: []gateway.Mount{
{
Expand All @@ -133,8 +126,8 @@ func containerConfigFromResult(res *gateway.Result, cfg *InvokeConfig) (*gateway
}, nil
}

func populateProcessConfigFromResult(req *gateway.StartRequest, res *gateway.Result, cfg *InvokeConfig) error {
imgData := res.Metadata[exptypes.ExporterImageConfigKey]
func populateProcessConfigFromResult(req *gateway.StartRequest, meta map[string][]byte, cfg *InvokeConfig) error {
imgData := meta[exptypes.ExporterImageConfigKey]
var img *ocispecs.Image
if len(imgData) > 0 {
img = &ocispecs.Image{}
Expand Down
Loading