diff --git a/build/result.go b/build/result.go index 3d33adb8..de8a6728 100644 --- a/build/result.go +++ b/build/result.go @@ -15,9 +15,11 @@ import ( gateway "github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/solver/result" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" ) func NewResultContext(ctx context.Context, c *client.Client, solveOpt client.SolveOpt, res *gateway.Result) (*ResultContext, error) { @@ -28,23 +30,21 @@ func NewResultContext(ctx context.Context, c *client.Client, solveOpt client.Sol return getResultAt(ctx, c, solveOpt, def, nil) } -func getDefinition(ctx context.Context, res *gateway.Result) (*pb.Definition, error) { - ref, err := res.SingleRef() - if err != nil { - return nil, err - } - st, err := ref.ToState() - if err != nil { - return nil, err - } - def, err := st.Marshal(ctx) - if err != nil { - return nil, err - } - return def.ToPB(), nil +func getDefinition(ctx context.Context, res *gateway.Result) (*result.Result[*pb.Definition], error) { + return result.ConvertResult(res, func(ref gateway.Reference) (*pb.Definition, error) { + st, err := ref.ToState() + if err != nil { + return nil, err + } + def, err := st.Marshal(ctx) + if err != nil { + return nil, err + } + return def.ToPB(), nil + }) } -func getResultAt(ctx context.Context, c *client.Client, solveOpt client.SolveOpt, target *pb.Definition, statusChan chan *client.SolveStatus) (*ResultContext, error) { +func getResultAt(ctx context.Context, c *client.Client, solveOpt client.SolveOpt, targets *result.Result[*pb.Definition], statusChan chan *client.SolveStatus) (*ResultContext, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -80,12 +80,29 @@ func getResultAt(ctx context.Context, c *client.Client, solveOpt client.SolveOpt doneErr := errors.Errorf("done") ctx, cancel := context.WithCancelCause(ctx) defer cancel(doneErr) - resultCtx := ResultContext{} - res2, err := c.Solve(ctx, gateway.SolveRequest{ - Evaluate: true, - Definition: target, + + // force evaluation of all targets in parallel + results := make(map[*pb.Definition]*gateway.Result) + resultsMu := sync.Mutex{} + eg, egCtx := errgroup.WithContext(ctx) + targets.EachRef(func(def *pb.Definition) error { + eg.Go(func() error { + res2, err := c.Solve(egCtx, gateway.SolveRequest{ + Evaluate: true, + Definition: def, + }) + if err != nil { + return err + } + resultsMu.Lock() + results[def] = res2 + resultsMu.Unlock() + return nil + }) + return nil }) - if err != nil { + resultCtx := ResultContext{} + if err := eg.Wait(); err != nil { var se *errdefs.SolveError if errors.As(err, &se) { resultCtx.solveErr = se @@ -93,6 +110,13 @@ func getResultAt(ctx context.Context, c *client.Client, solveOpt client.SolveOpt return nil, err } } + res2, _ := result.ConvertResult(targets, func(def *pb.Definition) (gateway.Reference, error) { + if res, ok := results[def]; ok { + return res.Ref, nil + } + return nil, nil + }) + // Record the client and ctx as well so that containers can be created from the SolveError. resultCtx.res = res2 resultCtx.gwClient = c @@ -208,32 +232,25 @@ func (r *ResultContext) getProcessConfig(cfg *controllerapi.InvokeConfig, stdin } func containerConfigFromResult(ctx context.Context, res *gateway.Result, c gateway.Client, cfg controllerapi.InvokeConfig) (*gateway.NewContainerRequest, error) { - if res.Ref == nil { - return nil, errors.Errorf("no reference is registered") - } 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") } - st, err := res.Ref.ToState() - if err != nil { - return nil, err - } - def, err := st.Marshal(ctx) + + ps, err := exptypes.ParsePlatforms(res.Metadata) if err != nil { return nil, err } - imgRef, err := c.Solve(ctx, gateway.SolveRequest{ - Definition: def.ToPB(), - }) - 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{ { Dest: "/", MountType: pb.MountType_BIND, - Ref: imgRef.Ref, + Ref: ref, }, }, }, nil