Skip to content

Commit

Permalink
history: add error details to history inspect command
Browse files Browse the repository at this point in the history
For failed builds, show source with error location and last
logs for vertex that caused the error. When debug mode is on,
stacktrace is printed.

Signed-off-by: Tonis Tiigi <[email protected]>
  • Loading branch information
tonistiigi committed Jan 17, 2025
1 parent a18ff4d commit 2e05415
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 4 deletions.
125 changes: 122 additions & 3 deletions commands/history/inspect.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package history

import (
"bytes"
"cmp"
"context"
"encoding/json"
Expand All @@ -24,16 +25,24 @@ import (
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/desktop"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/debug"
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/solver/errdefs"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/stack"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/tonistiigi/go-csvvalue"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
proto "google.golang.org/protobuf/proto"
)

type inspectOptions struct {
Expand Down Expand Up @@ -186,14 +195,14 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)

fmt.Fprintf(tw, "Started:\t%s\n", rec.CreatedAt.AsTime().Format("2006-01-02 15:04:05"))
var duration time.Duration
var status string
var statusStr string
if rec.CompletedAt != nil {
duration = rec.CompletedAt.AsTime().Sub(rec.CreatedAt.AsTime())
} else {
duration = rec.currentTimestamp.Sub(rec.CreatedAt.AsTime())
status = " (running)"
statusStr = " (running)"
}
fmt.Fprintf(tw, "Duration:\t%s%s\n", formatDuration(duration), status)
fmt.Fprintf(tw, "Duration:\t%s%s\n", formatDuration(duration), statusStr)
if rec.Error != nil {
if codes.Code(rec.Error.Code) == codes.Canceled {
fmt.Fprintf(tw, "Status:\tCanceled\n")
Expand Down Expand Up @@ -309,6 +318,49 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
fmt.Fprintln(dockerCli.Out())
}

if rec.ExternalError != nil {
dt, err := content.ReadBlob(ctx, store, ociDesc(rec.ExternalError))
if err != nil {
return errors.Wrapf(err, "failed to read external error %s", rec.ExternalError.Digest)
}
var st spb.Status
if err := proto.Unmarshal(dt, &st); err != nil {
return errors.Wrapf(err, "failed to unmarshal external error %s", rec.ExternalError.Digest)
}
retErr := grpcerrors.FromGRPC(status.ErrorProto(&st))
for _, s := range errdefs.Sources(retErr) {
s.Print(dockerCli.Out())
}

var ve *errdefs.VertexError
if errors.As(retErr, &ve) {
dgst, err := digest.Parse(ve.Vertex.Digest)
if err != nil {
return errors.Wrapf(err, "failed to parse vertex digest %s", ve.Vertex.Digest)
}
name, logs, err := loadVertexLogs(ctx, c, rec.Ref, dgst, 16)
if err != nil {
return errors.Wrapf(err, "failed to load vertex logs %s", dgst)
}
if len(logs) > 0 {
fmt.Fprintf(dockerCli.Out(), "\n => %s:\n", name)
for _, l := range logs {
fmt.Fprintln(dockerCli.Out(), l)
}
fmt.Fprintln(dockerCli.Out())
}
}

if debug.IsEnabled() {
fmt.Fprintf(dockerCli.Out(), "\n%+v\n", stack.Formatter(retErr))
} else {
if len(stack.Traces(retErr)) > 0 {
fmt.Fprintf(dockerCli.Out(), "Enable --debug to see stack traces for error\n")
}
}

}

fmt.Fprintf(dockerCli.Out(), "Print build logs: docker buildx history logs %s\n", rec.Ref)

fmt.Fprintf(dockerCli.Out(), "View build in Docker Desktop: %s\n", desktop.BuildURL(rec.Ref))
Expand Down Expand Up @@ -342,6 +394,73 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
return cmd
}

func loadVertexLogs(ctx context.Context, c *client.Client, ref string, dgst digest.Digest, limit int) (string, []string, error) {
st, err := c.ControlClient().Status(ctx, &controlapi.StatusRequest{
Ref: ref,
})
if err != nil {
return "", nil, err
}

var name string
var logs []string
lastState := map[int]int{}

loop0:
for {
select {
case <-ctx.Done():
st.CloseSend()
return "", nil, context.Cause(ctx)
default:
ev, err := st.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break loop0
}
return "", nil, err
}
ss := client.NewSolveStatus(ev)
for _, v := range ss.Vertexes {
if v.Digest == dgst {
name = v.Name
break
}
}
for _, l := range ss.Logs {
if l.Vertex == dgst {
parts := bytes.Split(l.Data, []byte("\n"))
for i, p := range parts {
var wrote bool
if i == 0 {
idx, ok := lastState[l.Stream]
if ok && idx != -1 {
logs[idx] = logs[idx] + string(p)
wrote = true
}
}
if !wrote {
if len(p) > 0 {
logs = append(logs, string(p))
}
lastState[l.Stream] = len(logs) - 1
}
if i == len(parts)-1 && len(p) == 0 {
lastState[l.Stream] = -1
}
}
}
}
}
}

if limit > 0 && len(logs) > limit {
logs = logs[len(logs)-limit:]
}

return name, logs, nil
}

type attachment struct {
platform *ocispecs.Platform
descr ocispecs.Descriptor
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ require (
golang.org/x/sys v0.28.0
golang.org/x/term v0.27.0
golang.org/x/text v0.21.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
google.golang.org/grpc v1.68.1
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
google.golang.org/protobuf v1.35.2
Expand Down Expand Up @@ -173,7 +174,6 @@ require (
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.25.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
Expand Down

0 comments on commit 2e05415

Please sign in to comment.