Skip to content

Commit 6220e0a

Browse files
tonistiigicrazy-max
authored andcommitted
add history inspect attachment command
Signed-off-by: Tonis Tiigi <[email protected]>
1 parent d9abc78 commit 6220e0a

File tree

4 files changed

+180
-0
lines changed

4 files changed

+180
-0
lines changed

commands/history/inspect.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
333333
ValidArgsFunction: completion.Disable,
334334
}
335335

336+
cmd.AddCommand(
337+
attachmentCmd(dockerCli, rootOpts),
338+
)
339+
336340
// flags := cmd.Flags()
337341

338342
return cmd
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package history
2+
3+
import (
4+
"context"
5+
"io"
6+
"slices"
7+
8+
"github.com/containerd/containerd/content/proxy"
9+
"github.com/containerd/platforms"
10+
"github.com/docker/buildx/builder"
11+
"github.com/docker/buildx/util/cobrautil/completion"
12+
"github.com/docker/cli/cli/command"
13+
intoto "github.com/in-toto/in-toto-golang/in_toto"
14+
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
15+
"github.com/opencontainers/go-digest"
16+
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
17+
"github.com/pkg/errors"
18+
"github.com/spf13/cobra"
19+
)
20+
21+
type attachmentOptions struct {
22+
builder string
23+
typ string
24+
platform string
25+
ref string
26+
digest digest.Digest
27+
}
28+
29+
func runAttachment(ctx context.Context, dockerCli command.Cli, opts attachmentOptions) error {
30+
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
31+
if err != nil {
32+
return err
33+
}
34+
35+
nodes, err := b.LoadNodes(ctx)
36+
if err != nil {
37+
return err
38+
}
39+
for _, node := range nodes {
40+
if node.Err != nil {
41+
return node.Err
42+
}
43+
}
44+
45+
recs, err := queryRecords(ctx, opts.ref, nodes)
46+
if err != nil {
47+
return err
48+
}
49+
50+
if len(recs) == 0 {
51+
if opts.ref == "" {
52+
return errors.New("no records found")
53+
}
54+
return errors.Errorf("no record found for ref %q", opts.ref)
55+
}
56+
57+
if opts.ref == "" {
58+
slices.SortFunc(recs, func(a, b historyRecord) int {
59+
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
60+
})
61+
}
62+
63+
rec := &recs[0]
64+
65+
c, err := rec.node.Driver.Client(ctx)
66+
if err != nil {
67+
return err
68+
}
69+
70+
store := proxy.NewContentStore(c.ContentClient())
71+
72+
if opts.digest != "" {
73+
ra, err := store.ReaderAt(ctx, ocispecs.Descriptor{Digest: opts.digest})
74+
if err != nil {
75+
return err
76+
}
77+
_, err = io.Copy(dockerCli.Out(), io.NewSectionReader(ra, 0, ra.Size()))
78+
return err
79+
}
80+
81+
attachments, err := allAttachments(ctx, store, *rec)
82+
if err != nil {
83+
return err
84+
}
85+
86+
typ := opts.typ
87+
switch typ {
88+
case "index":
89+
typ = ocispecs.MediaTypeImageIndex
90+
case "manifest":
91+
typ = ocispecs.MediaTypeImageManifest
92+
case "image":
93+
typ = ocispecs.MediaTypeImageConfig
94+
case "provenance":
95+
typ = slsa02.PredicateSLSAProvenance
96+
case "sbom":
97+
typ = intoto.PredicateSPDX
98+
}
99+
100+
for _, a := range attachments {
101+
if opts.platform != "" && (a.platform == nil || platforms.FormatAll(*a.platform) != opts.platform) {
102+
continue
103+
}
104+
if typ != "" && descrType(a.descr) != typ {
105+
continue
106+
}
107+
ra, err := store.ReaderAt(ctx, a.descr)
108+
if err != nil {
109+
return err
110+
}
111+
_, err = io.Copy(dockerCli.Out(), io.NewSectionReader(ra, 0, ra.Size()))
112+
return err
113+
}
114+
115+
return errors.Errorf("no matching attachment found for ref %q", opts.ref)
116+
}
117+
118+
func attachmentCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
119+
var options attachmentOptions
120+
121+
cmd := &cobra.Command{
122+
Use: "attachment [OPTIONS] REF [DIGEST]",
123+
Short: "Inspect a build attachment",
124+
Args: cobra.RangeArgs(1, 2),
125+
RunE: func(cmd *cobra.Command, args []string) error {
126+
if len(args) > 0 {
127+
options.ref = args[0]
128+
}
129+
if len(args) > 1 {
130+
dgst, err := digest.Parse(args[1])
131+
if err != nil {
132+
return errors.Wrapf(err, "invalid digest %q", args[1])
133+
}
134+
options.digest = dgst
135+
}
136+
137+
if options.digest == "" && options.platform == "" && options.typ == "" {
138+
return errors.New("at least one of --type, --platform or DIGEST must be specified")
139+
}
140+
141+
options.builder = *rootOpts.Builder
142+
return runAttachment(cmd.Context(), dockerCli, options)
143+
},
144+
ValidArgsFunction: completion.Disable,
145+
}
146+
147+
flags := cmd.Flags()
148+
flags.StringVar(&options.typ, "type", "", "Type of attachment")
149+
flags.StringVar(&options.platform, "platform", "", "Platform of attachment")
150+
151+
return cmd
152+
}

docs/reference/buildx_history_inspect.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
<!---MARKER_GEN_START-->
44
Inspect a build
55

6+
### Subcommands
7+
8+
| Name | Description |
9+
|:-----------------------------------------------------|:---------------------------|
10+
| [`attachment`](buildx_history_inspect_attachment.md) | Inspect a build attachment |
11+
12+
613
### Options
714

815
| Name | Type | Default | Description |
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# docker buildx history inspect attachment
2+
3+
<!---MARKER_GEN_START-->
4+
Inspect a build attachment
5+
6+
### Options
7+
8+
| Name | Type | Default | Description |
9+
|:----------------|:---------|:--------|:-----------------------------------------|
10+
| `--builder` | `string` | | Override the configured builder instance |
11+
| `-D`, `--debug` | `bool` | | Enable debug logging |
12+
| `--platform` | `string` | | Platform of attachment |
13+
| `--type` | `string` | | Type of attachment |
14+
15+
16+
<!---MARKER_GEN_END-->
17+

0 commit comments

Comments
 (0)