Skip to content

Commit 4a2e52f

Browse files
Merge pull request #27182 from skyraider256526/main
feat: add `--format` flag to artifact inspect
2 parents c15e845 + 339a432 commit 4a2e52f

File tree

5 files changed

+122
-17
lines changed

5 files changed

+122
-17
lines changed

cmd/podman/artifact/inspect.go

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
package artifact
22

33
import (
4+
"os"
5+
46
"github.com/containers/podman/v5/cmd/podman/common"
7+
"github.com/containers/podman/v5/cmd/podman/inspect"
58
"github.com/containers/podman/v5/cmd/podman/registry"
69
"github.com/containers/podman/v5/cmd/podman/utils"
710
"github.com/containers/podman/v5/pkg/domain/entities"
811
"github.com/spf13/cobra"
12+
"go.podman.io/common/pkg/report"
913
)
1014

1115
var (
1216
inspectCmd = &cobra.Command{
13-
Use: "inspect [ARTIFACT...]",
17+
Use: "inspect [options] ARTIFACT",
1418
Short: "Inspect an OCI artifact",
1519
Long: "Provide details on an OCI artifact",
16-
RunE: inspect,
17-
Args: cobra.MinimumNArgs(1),
20+
RunE: artifactInspect,
21+
Args: cobra.ExactArgs(1),
1822
ValidArgsFunction: common.AutocompleteArtifacts,
1923
Example: `podman artifact inspect quay.io/myimage/myartifact:latest`,
2024
}
25+
inspectOpts *entities.InspectOptions
2126
)
2227

2328
func init() {
@@ -26,25 +31,45 @@ func init() {
2631
Parent: artifactCmd,
2732
})
2833

29-
// TODO When things firm up on inspect looks, we can do a format implementation
30-
// flags := inspectCmd.Flags()
31-
// formatFlagName := "format"
32-
// flags.StringVar(&inspectFlag.format, formatFlagName, "", "Format volume output using JSON or a Go template")
34+
inspectOpts = new(entities.InspectOptions)
35+
36+
flags := inspectCmd.Flags()
37+
formatFlagName := "format"
38+
flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format volume output using JSON or a Go template")
3339

3440
// This is something we wanted to do but did not seem important enough for initial PR
3541
// remoteFlagName := "remote"
3642
// flags.BoolVar(&inspectFlag.remote, remoteFlagName, false, "Inspect the image on a container image registry")
3743

3844
// TODO When the inspect structure has been defined, we need to uncomment and redirect this. Reminder, this
3945
// will also need to be reflected in the podman-artifact-inspect man page
40-
// _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&machine.InspectInfo{}))
46+
_ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ArtifactInspectReport{}))
4147
}
4248

43-
func inspect(_ *cobra.Command, args []string) error {
49+
func artifactInspect(_ *cobra.Command, args []string) error {
4450
artifactOptions := entities.ArtifactInspectOptions{}
4551
inspectData, err := registry.ImageEngine().ArtifactInspect(registry.Context(), args[0], artifactOptions)
4652
if err != nil {
4753
return err
4854
}
49-
return utils.PrintGenericJSON(inspectData)
55+
56+
switch {
57+
case report.IsJSON(inspectOpts.Format) || inspectOpts.Format == "":
58+
return utils.PrintGenericJSON(inspectData)
59+
default:
60+
// Landing here implies user has given a custom --format
61+
var rpt *report.Formatter
62+
format := inspect.InspectNormalize(inspectOpts.Format, inspectOpts.Type)
63+
rpt, err = report.New(os.Stdout, "inspect").Parse(report.OriginUser, format)
64+
if err != nil {
65+
return err
66+
}
67+
defer rpt.Flush()
68+
69+
// Storing and passing inspectData in an array to [Execute] is workaround to avoid getting an error.
70+
// Which seems to happen when type passed to [Execute] is not a slice.
71+
// Error: template: inspect:1:8: executing "inspect" at <.>: range can't iterate over {0x6600020c444 sha256:4bafff5c1b2c950651101d22d3dbf76744446aeb5f79fc926674e0db1083qew456}
72+
data := []any{inspectData}
73+
return rpt.Execute(data)
74+
}
5075
}

cmd/podman/inspect/inspect.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
176176
default:
177177
// Landing here implies user has given a custom --format
178178
var rpt *report.Formatter
179-
format := inspectNormalize(i.options.Format, i.options.Type)
179+
format := InspectNormalize(i.options.Format, i.options.Type)
180180
rpt, err = report.New(os.Stdout, "inspect").Parse(report.OriginUser, format)
181181
if err != nil {
182182
return err
@@ -258,7 +258,22 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]any,
258258
return data, allErrs, nil
259259
}
260260

261-
func inspectNormalize(row string, inspectType string) string {
261+
// InspectNormalize modifies a given row string based on the specified inspect type.
262+
// It replaces specific field names within the row string for standardization.
263+
// For the `image` inspect type, it includes additional field replacements like `.Config.Healthcheck`.
264+
//
265+
// Parameters:
266+
// - row: The input string that represents a data row to be modified.
267+
// - inspectType: The type of inspection (e.g., "image") to determine specific replacements.
268+
//
269+
// Returns:
270+
// - A new string with the necessary replacements applied based on the inspect type.
271+
//
272+
// InspectNormalize does not need to be exported but to avoid de-duplication of code. We had to export it.
273+
// It can be reverted back once `podman artifact inspect` can use [Inspect] to fetch artifact data instead of
274+
// fetching it itself.
275+
// The reason why we did it in this way can be further read [here](https://github.com/containers/podman/pull/27182#issuecomment-3402465389).
276+
func InspectNormalize(row string, inspectType string) string {
262277
m := regexp.MustCompile(`{{\s*\.Id\s*}}`)
263278
row = m.ReplaceAllString(row, "{{.ID}}")
264279

docs/source/markdown/podman-artifact-inspect.1.md

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
podman\-artifact\-inspect - Inspect an OCI artifact
55

66
## SYNOPSIS
7-
**podman artifact inspect** [*name*] ...
7+
**podman artifact inspect** *name*
88

99
## DESCRIPTION
1010

@@ -20,15 +20,62 @@ annotation using RFC3339Nano format, showing when the artifact was initially cre
2020

2121
## OPTIONS
2222

23-
#### **--help**
23+
#### **--format**, **-f**=*format*
2424

25-
Print usage statement.
25+
Format the output using the given Go template.
26+
The keys of the returned JSON can be used as the values for the --format flag (see examples below).
27+
28+
Valid placeholders for the Go template are listed below:
29+
30+
| **Placeholder** | **Description** |
31+
| ------------------------ | -------------------------------------------------- |
32+
| .Artifact ... | Artifact details (nested struct) |
33+
| .Digest | Artifact digest (sha256:+64-char hash) |
34+
| .Manifest ... | Artifact manifest details (struct) |
35+
| .Name | Artifact name (string) |
36+
| .TotalSizeBytes | Total Size of the artifact in bytes |
37+
38+
#### **--help**, **-h**
39+
40+
Print usage statement
2641

2742
## EXAMPLES
2843

2944
Inspect an OCI image in the local store.
30-
```
45+
46+
```shell
3147
$ podman artifact inspect quay.io/myartifact/myml:latest
48+
{
49+
"Manifest": {
50+
"schemaVersion": 2,
51+
"mediaType": "application/vnd.oci.image.manifest.v1+json",
52+
"config": {
53+
"mediaType": "application/vnd.oci.empty.v1+json",
54+
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
55+
"size": 2,
56+
"data": "e30="
57+
},
58+
"layers": [
59+
{
60+
"mediaType": "text/plain; charset=utf-8",
61+
"digest": "sha256:f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2",
62+
"size": 5,
63+
"annotations": {
64+
"org.opencontainers.image.title": "foobar.txt"
65+
}
66+
}
67+
]
68+
},
69+
"Name": "quay.io/myartifact/mytxt:latest",
70+
"Digest": "sha256:6c28fa07a5b0a1cee29862c1f6ea38a66df982495b14da2c052427eb628ed8c6"
71+
}
72+
```
73+
74+
Inspect artifact digest for the specified artifact:
75+
76+
```shell
77+
$ podman artifact inspect quay.io/myartifact/mytxt:latest --format {{.Digest}}
78+
sha256:6c28fa07a5b0a1cee29862c1f6ea38a66df982495b14da2c052427eb628ed8c6
3279
```
3380

3481
## SEE ALSO

test/e2e/artifact_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,20 @@ var _ = Describe("Podman artifact", func() {
668668
// Verify we have 2 layers
669669
Expect(a.Manifest.Layers).To(HaveLen(2))
670670
})
671+
672+
It("podman artifact inspect with --format", func() {
673+
artifact1File, err := createArtifactFile(4192)
674+
Expect(err).ToNot(HaveOccurred())
675+
artifact1Name := "localhost/test/artifact1"
676+
addArtifact1 := podmanTest.PodmanExitCleanly("artifact", "add", artifact1Name, artifact1File)
677+
678+
artifactDigest := addArtifact1.OutputToString()
679+
680+
session := podmanTest.PodmanExitCleanly("artifact", "inspect", artifactDigest, "--format", "{{.Digest}}")
681+
Expect(session.OutputToString()).To(Equal("sha256:" + artifactDigest))
682+
session = podmanTest.PodmanExitCleanly("artifact", "inspect", artifactDigest[:12], "-f", "{{.Name}}")
683+
Expect(session.OutputToString()).To(Equal(artifact1Name))
684+
})
671685
})
672686

673687
func digestToFilename(digest string) string {

test/system/610-format.bats

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function teardown() {
1212
run_podman '?' secret rm "s-$(safename)"
1313
run_podman '?' pod rm -f "p-$(safename)"
1414
run_podman '?' rm -f -t0 "c-$(safename)"
15+
run_podman '?' artifact rm "a-$(safename)"
1516

1617
basic_teardown
1718
}
@@ -125,10 +126,12 @@ function check_subcommand() {
125126
ctrname="c-$(safename)"
126127
podname="p-$(safename)"
127128
secretname="s-$(safename)"
129+
artifactname="a-$(safename)"
128130
# Setup: some commands need a container, pod, secret, ...
129131
run_podman run -d --name $ctrname $IMAGE top
130132
run_podman pod create $podname
131133
run_podman secret create $secretname /etc/hosts
134+
run_podman artifact add $artifactname /etc/hosts
132135

133136
# For 'search' and 'image search': if local cache registry is available,
134137
# use it. This bypasses quay, and thus prevents flakes.
@@ -147,7 +150,7 @@ image inspect | $IMAGE
147150
container inspect | $ctrname
148151
inspect | $ctrname
149152
150-
153+
artifact inspect | $artifactname
151154
volume inspect | -a
152155
secret inspect | $secretname
153156
network inspect | podman
@@ -200,6 +203,7 @@ stats | --no-stream
200203
run_podman rm -f -t0 $ctrname
201204
run_podman secret rm $secretname
202205
run_podman '?' machine rm -f $machinename
206+
run_podman artifact rm $artifactname
203207

204208
# Make sure there are no leftover commands in our table - this would
205209
# indicate a typo in the table, or a flaw in our logic such that

0 commit comments

Comments
 (0)