build: basic buildkit progress support

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2018-04-19 10:07:27 -07:00
committed by Tibor Vass
parent 656fe85c74
commit 0f97642915
65 changed files with 14551 additions and 170 deletions

View File

@ -396,6 +396,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
Target: options.target,
RemoteContext: remote,
Platform: options.platform,
Version: types.BuilderV1,
}
if s != nil {
@ -420,9 +421,9 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
defer response.Body.Close()
imageID := ""
aux := func(m jsonmessage.JSONMessage) {
aux := func(msg jsonmessage.JSONMessage) {
var result types.BuildResult
if err := json.Unmarshal(*m.Aux, &result); err != nil {
if err := json.Unmarshal(*msg.Aux, &result); err != nil {
fmt.Fprintf(dockerCli.Err(), "Failed to parse aux message: %s", err)
} else {
imageID = result.ID

View File

@ -1,18 +1,27 @@
package image
import (
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"github.com/containerd/console"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/urlutil"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/session/filesync"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
"golang.org/x/sync/errgroup"
@ -29,13 +38,13 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
return errors.Errorf("buildkit not supported by daemon")
}
remote := clientSessionRemote
local := false
var remote string
var body io.Reader
switch {
case options.contextFromStdin():
return errors.Errorf("stdin not implemented")
body = os.Stdin
case isLocalDir(options.context):
local = true
remote = clientSessionRemote
case urlutil.IsGitURL(options.context):
remote = options.context
case urlutil.IsURL(options.context):
@ -51,7 +60,7 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
// statusContext = opentracing.ContextWithSpan(statusContext, span)
// }
if local {
if remote == clientSessionRemote {
s.Allow(filesync.NewFSSyncProvider([]filesync.SyncedDir{
{
Name: "context",
@ -70,7 +79,7 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
return s.Run(ctx, dockerCli.Client().DialSession)
return s.Run(context.TODO(), dockerCli.Client().DialSession)
})
eg.Go(func() error {
@ -78,6 +87,8 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
s.Close()
}()
buildID := stringid.GenerateRandomID()
configFile := dockerCli.ConfigFile()
buildOptions := types.ImageBuildOptions{
Memory: options.memory.Value(),
@ -109,16 +120,50 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
Target: options.target,
RemoteContext: remote,
Platform: options.platform,
SessionID: "buildkit:" + s.ID(),
SessionID: s.ID(),
Version: types.BuilderBuildKit,
BuildID: buildID,
}
response, err := dockerCli.Client().ImageBuild(ctx, nil, buildOptions)
response, err := dockerCli.Client().ImageBuild(context.Background(), body, buildOptions)
if err != nil {
return err
}
defer response.Body.Close()
if _, err := io.Copy(os.Stdout, response.Body); err != nil {
done := make(chan struct{})
defer close(done)
eg.Go(func() error {
select {
case <-ctx.Done():
return dockerCli.Client().BuildCancel(context.TODO(), buildID)
case <-done:
}
return nil
})
t := newTracer()
var auxCb func(jsonmessage.JSONMessage)
if c, err := console.ConsoleFromFile(os.Stderr); err == nil {
// not using shared context to not disrupt display but let is finish reporting errors
auxCb = t.write
eg.Go(func() error {
return progressui.DisplaySolveStatus(context.TODO(), c, t.displayCh)
})
defer close(t.displayCh)
}
err = jsonmessage.DisplayJSONMessagesStream(response.Body, os.Stdout, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), auxCb)
if err != nil {
if jerr, ok := err.(*jsonmessage.JSONError); ok {
// If no error code is set, default to 1
if jerr.Code == 0 {
jerr.Code = 1
}
// if options.quiet {
// fmt.Fprintf(dockerCli.Err(), "%s%s", progBuff, buildBuff)
// }
return cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
}
return err
}
@ -133,3 +178,60 @@ func resetUIDAndGID(s *fsutil.Stat) bool {
s.Gid = uint32(0)
return true
}
type tracer struct {
displayCh chan *client.SolveStatus
}
func newTracer() *tracer {
return &tracer{
displayCh: make(chan *client.SolveStatus),
}
}
func (t *tracer) write(msg jsonmessage.JSONMessage) {
var resp controlapi.StatusResponse
var dt []byte
if err := json.Unmarshal(*msg.Aux, &dt); err != nil {
return
}
if err := (&resp).Unmarshal(dt); err != nil {
return
}
s := client.SolveStatus{}
for _, v := range resp.Vertexes {
s.Vertexes = append(s.Vertexes, &client.Vertex{
Digest: v.Digest,
Inputs: v.Inputs,
Name: v.Name,
Started: v.Started,
Completed: v.Completed,
Error: v.Error,
Cached: v.Cached,
})
}
for _, v := range resp.Statuses {
s.Statuses = append(s.Statuses, &client.VertexStatus{
ID: v.ID,
Vertex: v.Vertex,
Name: v.Name,
Total: v.Total,
Current: v.Current,
Timestamp: v.Timestamp,
Started: v.Started,
Completed: v.Completed,
})
}
for _, v := range resp.Logs {
s.Logs = append(s.Logs, &client.VertexLog{
Vertex: v.Vertex,
Stream: int(v.Stream),
Data: v.Msg,
Timestamp: v.Timestamp,
})
}
t.displayCh <- &s
}

View File

@ -49,7 +49,7 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
// Count the times of calling for handleTarget,
// if it is called more that once, that should be considered an error in a trusted push.
cnt := 0
handleTarget := func(m jsonmessage.JSONMessage) {
handleTarget := func(msg jsonmessage.JSONMessage) {
cnt++
if cnt > 1 {
// handleTarget should only be called once. This will be treated as an error.
@ -57,7 +57,7 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
}
var pushResult types.PushResult
err := json.Unmarshal(*m.Aux, &pushResult)
err := json.Unmarshal(*msg.Aux, &pushResult)
if err == nil && pushResult.Tag != "" {
if dgst, err := digest.Parse(pushResult.Digest); err == nil {
h, err := hex.DecodeString(dgst.Hex())