Add Solve API for performing a build on a build definition

This commit adds Solve API to the controller. This receives a build definition,
performs that build using ResultHandler held by that session. After Solve
completes, the client can debug the result using other APIs including Invoke.
Note that the ResultHandle provided by Solve overwrites the ResultHandle
previously stored in that session (possibly generated by the past Build or Solve
API call).

Using this API, user can perform build-and-debugging loop on the same session.

Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
This commit is contained in:
Kohei Tokunaga
2023-06-30 20:13:10 +09:00
parent de693264a8
commit f72ea677f1
7 changed files with 415 additions and 120 deletions

View File

@@ -8,6 +8,7 @@ import (
"sync"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
gateway "github.com/moby/buildkit/frontend/gateway/client"
@@ -93,6 +94,8 @@ func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt
if res != nil && err == nil {
if noEval {
respHandle = &ResultHandle{
client: cc,
solveOpt: opt,
done: make(chan struct{}),
gwClient: c,
gwCtx: ctx,
@@ -127,6 +130,8 @@ func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt
var se *errdefs.SolveError
if errors.As(err, &se) {
respHandle = &ResultHandle{
client: cc,
solveOpt: opt,
done: make(chan struct{}),
solveErr: se,
gwClient: c,
@@ -183,6 +188,8 @@ func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt
return nil, errors.Wrap(err, "inconsistent solve result")
}
respHandle = &ResultHandle{
client: cc,
solveOpt: opt,
done: make(chan struct{}),
res: res,
gwClient: c,
@@ -265,8 +272,27 @@ func evalDefinition(ctx context.Context, c gateway.Client, defs *result.Result[*
return res, nil
}
func SolveWithResultHandler(ctx context.Context, product string, resultCtx *ResultHandle, target *pb.Definition, pw progress.Writer) (*ResultHandle, error) {
opt := resultCtx.solveOpt
opt.Ref = ""
opt.Exports = nil
opt.CacheExports = nil
ch, done := progress.NewChannel(pw)
defer func() { <-done }()
h, _, err := NewResultHandle(ctx, resultCtx.client, opt, product, func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
return c.Solve(ctx, gateway.SolveRequest{
Evaluate: true,
Definition: target,
})
}, ch, false)
return h, err
}
// ResultHandle is a build result with the client that built it.
type ResultHandle struct {
client *client.Client
solveOpt client.SolveOpt
res *gateway.Result
solveErr *errdefs.SolveError
@@ -295,6 +321,10 @@ func (r *ResultHandle) Done() {
})
}
func (r *ResultHandle) SolveError() *errdefs.SolveError {
return r.solveErr
}
func (r *ResultHandle) registerCleanup(f func()) {
r.cleanupsMu.Lock()
r.cleanups = append(r.cleanups, f)