Skip to content

Commit d9abc78

Browse files
tonistiigicrazy-max
authored andcommitted
update history inspect formatting
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
1 parent 3313026 commit d9abc78

1 file changed

Lines changed: 188 additions & 11 deletions

File tree

commands/history/inspect.go

Lines changed: 188 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package history
22

33
import (
4+
"cmp"
45
"context"
6+
"encoding/json"
57
"fmt"
68
"io"
7-
"log"
89
"os"
910
"path/filepath"
1011
"slices"
@@ -13,12 +14,22 @@ import (
1314
"text/tabwriter"
1415
"time"
1516

17+
"github.com/containerd/containerd/content"
18+
"github.com/containerd/containerd/content/proxy"
19+
"github.com/containerd/containerd/images"
1620
"github.com/containerd/platforms"
1721
"github.com/docker/buildx/builder"
1822
"github.com/docker/buildx/localstate"
1923
"github.com/docker/buildx/util/cobrautil/completion"
2024
"github.com/docker/buildx/util/confutil"
25+
"github.com/docker/buildx/util/desktop"
2126
"github.com/docker/cli/cli/command"
27+
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
28+
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
29+
controlapi "github.com/moby/buildkit/api/services/control"
30+
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
31+
"github.com/opencontainers/go-digest"
32+
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
2233
"github.com/pkg/errors"
2334
"github.com/spf13/cobra"
2435
"github.com/tonistiigi/go-csvvalue"
@@ -72,9 +83,6 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
7283
}
7384
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
7485

75-
log.Printf("rec %+v", rec)
76-
log.Printf("st %+v", st)
77-
7886
tw := tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
7987

8088
attrs := rec.FrontendAttrs
@@ -101,12 +109,16 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
101109

102110
if dockerfile != "" && dockerfile != "-" {
103111
if rel, err := filepath.Rel(context, dockerfile); err == nil {
104-
dockerfile = rel
112+
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
113+
dockerfile = rel
114+
}
105115
}
106116
}
107117
if context != "" {
108118
if rel, err := filepath.Rel(wd, context); err == nil {
109-
context = rel
119+
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
120+
context = rel
121+
}
110122
}
111123
}
112124
}
@@ -226,7 +238,7 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
226238

227239
var unusedAttrs []string
228240
for k := range attrs {
229-
if strings.HasPrefix(k, "vcs:") || strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") || strings.HasPrefix(k, "context:") {
241+
if strings.HasPrefix(k, "vcs:") || strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") || strings.HasPrefix(k, "context:") || strings.HasPrefix(k, "attest:") {
230242
continue
231243
}
232244
unusedAttrs = append(unusedAttrs, k)
@@ -244,10 +256,62 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
244256
printTable(dockerCli.Out(), attrs, "build-arg:", "Build Arg")
245257
printTable(dockerCli.Out(), attrs, "label:", "Label")
246258

247-
// exporters (image)
248-
// commands
249-
// error
250-
// materials
259+
c, err := rec.node.Driver.Client(ctx)
260+
if err != nil {
261+
return err
262+
}
263+
264+
store := proxy.NewContentStore(c.ContentClient())
265+
266+
attachments, err := allAttachments(ctx, store, *rec)
267+
if err != nil {
268+
return err
269+
}
270+
271+
provIndex := slices.IndexFunc(attachments, func(a attachment) bool {
272+
return descrType(a.descr) == slsa02.PredicateSLSAProvenance
273+
})
274+
if provIndex != -1 {
275+
prov := attachments[provIndex]
276+
277+
dt, err := content.ReadBlob(ctx, store, prov.descr)
278+
if err != nil {
279+
return errors.Errorf("failed to read provenance %s: %v", prov.descr.Digest, err)
280+
}
281+
282+
var pred provenancetypes.ProvenancePredicate
283+
if err := json.Unmarshal(dt, &pred); err != nil {
284+
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
285+
}
286+
287+
fmt.Fprintln(dockerCli.Out(), "Materials:")
288+
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
289+
fmt.Fprintf(tw, "URI\tDIGEST\n")
290+
for _, m := range pred.Materials {
291+
fmt.Fprintf(tw, "%s\t%s\n", m.URI, strings.Join(digestSetToDigests(m.Digest), ", "))
292+
}
293+
tw.Flush()
294+
fmt.Fprintln(dockerCli.Out())
295+
}
296+
297+
if len(attachments) > 0 {
298+
fmt.Fprintf(tw, "Attachments:\n")
299+
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
300+
fmt.Fprintf(tw, "DIGEST\tPLATFORM\tTYPE\n")
301+
for _, a := range attachments {
302+
p := ""
303+
if a.platform != nil {
304+
p = platforms.FormatAll(*a.platform)
305+
}
306+
fmt.Fprintf(tw, "%s\t%s\t%s\n", a.descr.Digest, p, descrType(a.descr))
307+
}
308+
tw.Flush()
309+
fmt.Fprintln(dockerCli.Out())
310+
}
311+
312+
fmt.Fprintf(dockerCli.Out(), "Print build logs: docker buildx history logs %s\n", rec.Ref)
313+
314+
fmt.Fprintf(dockerCli.Out(), "View build in Docker Desktop: %s\n", desktop.BuildURL(rec.Ref))
251315

252316
return nil
253317
}
@@ -274,6 +338,111 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
274338
return cmd
275339
}
276340

341+
type attachment struct {
342+
platform *ocispecs.Platform
343+
descr ocispecs.Descriptor
344+
}
345+
346+
func allAttachments(ctx context.Context, store content.Store, rec historyRecord) ([]attachment, error) {
347+
var attachments []attachment
348+
349+
if rec.Result != nil {
350+
for _, a := range rec.Result.Attestations {
351+
attachments = append(attachments, attachment{
352+
descr: ociDesc(a),
353+
})
354+
}
355+
for _, r := range rec.Result.Results {
356+
attachments = append(attachments, walkAttachments(ctx, store, ociDesc(r), nil)...)
357+
}
358+
}
359+
360+
for key, ri := range rec.Results {
361+
p, err := platforms.Parse(key)
362+
if err != nil {
363+
return nil, err
364+
}
365+
for _, a := range ri.Attestations {
366+
attachments = append(attachments, attachment{
367+
platform: &p,
368+
descr: ociDesc(a),
369+
})
370+
}
371+
for _, r := range ri.Results {
372+
attachments = append(attachments, walkAttachments(ctx, store, ociDesc(r), &p)...)
373+
}
374+
}
375+
376+
slices.SortFunc(attachments, func(a, b attachment) int {
377+
pCmp := 0
378+
if a.platform == nil && b.platform != nil {
379+
return -1
380+
} else if a.platform != nil && b.platform == nil {
381+
return 1
382+
} else if a.platform != nil && b.platform != nil {
383+
pCmp = cmp.Compare(platforms.FormatAll(*a.platform), platforms.FormatAll(*b.platform))
384+
}
385+
return cmp.Or(
386+
pCmp,
387+
cmp.Compare(descrType(a.descr), descrType(b.descr)),
388+
)
389+
})
390+
391+
return attachments, nil
392+
}
393+
394+
func walkAttachments(ctx context.Context, store content.Store, desc ocispecs.Descriptor, platform *ocispecs.Platform) []attachment {
395+
_, err := store.Info(ctx, desc.Digest)
396+
if err != nil {
397+
return nil
398+
}
399+
400+
var out []attachment
401+
402+
if desc.Annotations["vnd.docker.reference.type"] != "attestation-manifest" {
403+
out = append(out, attachment{platform: platform, descr: desc})
404+
}
405+
406+
if desc.MediaType != ocispecs.MediaTypeImageIndex && desc.MediaType != images.MediaTypeDockerSchema2ManifestList {
407+
return out
408+
}
409+
410+
dt, err := content.ReadBlob(ctx, store, desc)
411+
if err != nil {
412+
return out
413+
}
414+
415+
var idx ocispecs.Index
416+
if err := json.Unmarshal(dt, &idx); err != nil {
417+
return out
418+
}
419+
420+
for _, d := range idx.Manifests {
421+
p := platform
422+
if d.Platform != nil {
423+
p = d.Platform
424+
}
425+
out = append(out, walkAttachments(ctx, store, d, p)...)
426+
}
427+
428+
return out
429+
}
430+
431+
func ociDesc(in *controlapi.Descriptor) ocispecs.Descriptor {
432+
return ocispecs.Descriptor{
433+
MediaType: in.MediaType,
434+
Digest: digest.Digest(in.Digest),
435+
Size: in.Size,
436+
Annotations: in.Annotations,
437+
}
438+
}
439+
func descrType(desc ocispecs.Descriptor) string {
440+
if typ, ok := desc.Annotations["in-toto.io/predicate-type"]; ok {
441+
return typ
442+
}
443+
return desc.MediaType
444+
}
445+
277446
func tryParseValue(s string, f func(string) (string, error)) string {
278447
v, err := f(s)
279448
if err != nil {
@@ -303,3 +472,11 @@ func printTable(w io.Writer, attrs map[string]string, prefix, title string) {
303472
tw.Flush()
304473
fmt.Fprintln(w)
305474
}
475+
476+
func digestSetToDigests(ds slsa.DigestSet) []string {
477+
var out []string
478+
for k, v := range ds {
479+
out = append(out, fmt.Sprintf("%s:%s", k, v))
480+
}
481+
return out
482+
}

0 commit comments

Comments
 (0)