Skip to content

Commit dc65c53

Browse files
committed
history: inspect json format
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent b066ee1 commit dc65c53

File tree

2 files changed

+280
-32
lines changed

2 files changed

+280
-32
lines changed

commands/history/inspect.go

Lines changed: 159 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/docker/buildx/util/confutil"
2626
"github.com/docker/buildx/util/desktop"
2727
"github.com/docker/cli/cli/command"
28+
"github.com/docker/cli/cli/command/formatter"
2829
"github.com/docker/cli/cli/debug"
2930
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
3031
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
@@ -48,6 +49,7 @@ import (
4849
type inspectOptions struct {
4950
builder string
5051
ref string
52+
format string
5153
}
5254

5355
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
@@ -92,7 +94,135 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
9294
}
9395
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
9496

95-
tw := tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
97+
var sg *localstate.StateGroup
98+
if st != nil && st.GroupRef != "" {
99+
sg, _ = ls.ReadGroup(st.GroupRef)
100+
}
101+
102+
switch opts.format {
103+
case formatter.JSONFormatKey:
104+
return inspectPrintJSON(ctx, rec, st, sg, dockerCli.Out())
105+
case formatter.RawFormatKey:
106+
return inspectPrintRaw(ctx, rec, st, dockerCli.Out())
107+
default:
108+
return errors.Errorf("unsupported format %q", opts.format)
109+
}
110+
}
111+
112+
func inspectPrintJSON(ctx context.Context, rec *historyRecord, ls *localstate.State, lsg *localstate.StateGroup, w io.Writer) error {
113+
type buildError struct {
114+
Name string `json:"name,omitempty"`
115+
Sources string `json:"sources,omitempty"`
116+
Logs string `json:"logs,omitempty"`
117+
}
118+
119+
out := struct {
120+
Record *controlapi.BuildHistoryRecord `json:"record"`
121+
LocalState *localstate.State `json:"localState,omitempty"`
122+
LocalStateGroup *localstate.StateGroup `json:"localStateGroup,omitempty"`
123+
Name string `json:"name,omitempty"`
124+
DefaultPlatform string `json:"defaultPlatform,omitempty"`
125+
Status string `json:"status,omitempty"`
126+
Duration time.Duration `json:"duration,omitempty"`
127+
Error *buildError `json:"error,omitempty"`
128+
}{
129+
Record: rec.BuildHistoryRecord,
130+
Name: buildName(rec.FrontendAttrs, ls),
131+
LocalState: ls,
132+
LocalStateGroup: lsg,
133+
Status: "completed",
134+
}
135+
136+
if rec.CompletedAt != nil {
137+
out.Duration = rec.CompletedAt.AsTime().Sub(rec.CreatedAt.AsTime())
138+
} else {
139+
out.Duration = rec.currentTimestamp.Sub(rec.CreatedAt.AsTime())
140+
out.Status = "running"
141+
}
142+
143+
c, err := rec.node.Driver.Client(ctx)
144+
if err != nil {
145+
return err
146+
}
147+
148+
workers, err := c.ListWorkers(ctx)
149+
if err != nil {
150+
return errors.Wrap(err, "failed to list workers")
151+
}
152+
workers0:
153+
for _, w := range workers {
154+
for _, p := range w.Platforms {
155+
out.DefaultPlatform = platforms.FormatAll(platforms.Normalize(p))
156+
break workers0
157+
}
158+
}
159+
160+
store := proxy.NewContentStore(c.ContentClient())
161+
162+
if rec.Error != nil || rec.ExternalError != nil {
163+
out.Error = new(buildError)
164+
if rec.Error != nil {
165+
if rec.Error.Code == int32(codes.Canceled) {
166+
out.Status = "canceled"
167+
} else {
168+
out.Status = "error"
169+
out.Error.Logs = rec.Error.Message
170+
}
171+
}
172+
if rec.ExternalError != nil {
173+
dt, err := content.ReadBlob(ctx, store, ociDesc(rec.ExternalError))
174+
if err != nil {
175+
return errors.Wrapf(err, "failed to read external error %s", rec.ExternalError.Digest)
176+
}
177+
var st spb.Status
178+
if err := proto.Unmarshal(dt, &st); err != nil {
179+
return errors.Wrapf(err, "failed to unmarshal external error %s", rec.ExternalError.Digest)
180+
}
181+
182+
if st.Code == int32(codes.Canceled) {
183+
out.Status = "canceled"
184+
} else {
185+
out.Status = "error"
186+
}
187+
188+
retErr := grpcerrors.FromGRPC(status.ErrorProto(&st))
189+
190+
var bsources bytes.Buffer
191+
for _, s := range errdefs.Sources(retErr) {
192+
s.Print(&bsources)
193+
bsources.WriteString("\n")
194+
}
195+
out.Error.Sources = bsources.String()
196+
197+
var ve *errdefs.VertexError
198+
if errors.As(retErr, &ve) {
199+
dgst, err := digest.Parse(ve.Vertex.Digest)
200+
if err != nil {
201+
return errors.Wrapf(err, "failed to parse vertex digest %s", ve.Vertex.Digest)
202+
}
203+
name, logs, err := loadVertexLogs(ctx, c, rec.Ref, dgst, -1)
204+
if err != nil {
205+
return errors.Wrapf(err, "failed to load vertex logs %s", dgst)
206+
}
207+
out.Error.Name = name
208+
if len(logs) > 0 {
209+
var blogs bytes.Buffer
210+
for _, l := range logs {
211+
fmt.Fprintln(&blogs, l)
212+
}
213+
out.Error.Logs = blogs.String()
214+
}
215+
}
216+
}
217+
}
218+
219+
enc := json.NewEncoder(w)
220+
enc.SetIndent("", " ")
221+
return enc.Encode(out)
222+
}
223+
224+
func inspectPrintRaw(ctx context.Context, rec *historyRecord, ls *localstate.State, w io.Writer) error {
225+
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
96226

97227
attrs := rec.FrontendAttrs
98228
delete(attrs, "frontend.caps")
@@ -111,9 +241,9 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
111241

112242
var context string
113243
var dockerfile string
114-
if st != nil {
115-
context = st.LocalPath
116-
dockerfile = st.DockerfilePath
244+
if ls != nil {
245+
context = ls.LocalPath
246+
dockerfile = ls.DockerfilePath
117247
wd, _ := os.Getwd()
118248

119249
if dockerfile != "" && dockerfile != "-" {
@@ -187,11 +317,11 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
187317

188318
tw.Flush()
189319

190-
fmt.Fprintln(dockerCli.Out())
320+
fmt.Fprintln(w)
191321

192-
printTable(dockerCli.Out(), attrs, "context:", "Named Context")
322+
printTable(w, attrs, "context:", "Named Context")
193323

194-
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
324+
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
195325

196326
fmt.Fprintf(tw, "Started:\t%s\n", rec.CreatedAt.AsTime().Local().Format("2006-01-02 15:04:05"))
197327
var duration time.Duration
@@ -213,9 +343,9 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
213343
fmt.Fprintf(tw, "Build Steps:\t%d/%d (%.0f%% cached)\n", rec.NumCompletedSteps, rec.NumTotalSteps, float64(rec.NumCachedSteps)/float64(rec.NumTotalSteps)*100)
214344
tw.Flush()
215345

216-
fmt.Fprintln(dockerCli.Out())
346+
fmt.Fprintln(w)
217347

218-
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
348+
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
219349

220350
writeAttr("force-network-mode", "Network", nil)
221351
writeAttr("hostname", "Hostname", nil)
@@ -260,10 +390,10 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
260390

261391
tw.Flush()
262392

263-
fmt.Fprintln(dockerCli.Out())
393+
fmt.Fprintln(w)
264394

265-
printTable(dockerCli.Out(), attrs, "build-arg:", "Build Arg")
266-
printTable(dockerCli.Out(), attrs, "label:", "Label")
395+
printTable(w, attrs, "build-arg:", "Build Arg")
396+
printTable(w, attrs, "label:", "Label")
267397

268398
c, err := rec.node.Driver.Client(ctx)
269399
if err != nil {
@@ -293,19 +423,19 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
293423
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
294424
}
295425

296-
fmt.Fprintln(dockerCli.Out(), "Materials:")
297-
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
426+
fmt.Fprintln(w, "Materials:")
427+
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
298428
fmt.Fprintf(tw, "URI\tDIGEST\n")
299429
for _, m := range pred.Materials {
300430
fmt.Fprintf(tw, "%s\t%s\n", m.URI, strings.Join(digestSetToDigests(m.Digest), ", "))
301431
}
302432
tw.Flush()
303-
fmt.Fprintln(dockerCli.Out())
433+
fmt.Fprintln(w)
304434
}
305435

306436
if len(attachments) > 0 {
307437
fmt.Fprintf(tw, "Attachments:\n")
308-
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
438+
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
309439
fmt.Fprintf(tw, "DIGEST\tPLATFORM\tTYPE\n")
310440
for _, a := range attachments {
311441
p := ""
@@ -315,7 +445,7 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
315445
fmt.Fprintf(tw, "%s\t%s\t%s\n", a.descr.Digest, p, descrType(a.descr))
316446
}
317447
tw.Flush()
318-
fmt.Fprintln(dockerCli.Out())
448+
fmt.Fprintln(w)
319449
}
320450

321451
if rec.ExternalError != nil {
@@ -329,9 +459,9 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
329459
}
330460
retErr := grpcerrors.FromGRPC(status.ErrorProto(&st))
331461
for _, s := range errdefs.Sources(retErr) {
332-
s.Print(dockerCli.Out())
462+
s.Print(w)
333463
}
334-
fmt.Fprintln(dockerCli.Out())
464+
fmt.Fprintln(w)
335465

336466
var ve *errdefs.VertexError
337467
if errors.As(retErr, &ve) {
@@ -344,25 +474,25 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
344474
return errors.Wrapf(err, "failed to load vertex logs %s", dgst)
345475
}
346476
if len(logs) > 0 {
347-
fmt.Fprintln(dockerCli.Out(), "Logs:")
348-
fmt.Fprintf(dockerCli.Out(), "> => %s:\n", name)
477+
fmt.Fprintln(w, "Logs:")
478+
fmt.Fprintf(w, "> => %s:\n", name)
349479
for _, l := range logs {
350-
fmt.Fprintln(dockerCli.Out(), "> "+l)
480+
fmt.Fprintln(w, "> "+l)
351481
}
352-
fmt.Fprintln(dockerCli.Out())
482+
fmt.Fprintln(w)
353483
}
354484
}
355485

356486
if debug.IsEnabled() {
357-
fmt.Fprintf(dockerCli.Out(), "\n%+v\n", stack.Formatter(retErr))
487+
fmt.Fprintf(w, "\n%+v\n", stack.Formatter(retErr))
358488
} else if len(stack.Traces(retErr)) > 0 {
359-
fmt.Fprintf(dockerCli.Out(), "Enable --debug to see stack traces for error\n")
489+
fmt.Fprintf(w, "Enable --debug to see stack traces for error\n")
360490
}
361491
}
362492

363-
fmt.Fprintf(dockerCli.Out(), "Print build logs: docker buildx history logs %s\n", rec.Ref)
493+
fmt.Fprintf(w, "Print build logs: docker buildx history logs %s\n", rec.Ref)
364494

365-
fmt.Fprintf(dockerCli.Out(), "View build in Docker Desktop: %s\n", desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref)))
495+
fmt.Fprintf(w, "View build in Docker Desktop: %s\n", desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref)))
366496

367497
return nil
368498
}
@@ -388,7 +518,8 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
388518
attachmentCmd(dockerCli, rootOpts),
389519
)
390520

391-
// flags := cmd.Flags()
521+
flags := cmd.Flags()
522+
flags.StringVar(&options.format, "format", formatter.RawFormatKey, "Format the output")
392523

393524
return cmd
394525
}

0 commit comments

Comments
 (0)