Skip to content

Commit 1fbf24b

Browse files
feat: add --format flag to artifact inspect
Many commands support the `--format` flag which accept a go template to allow for formatting for certain values, but it is not yet implemented for artifact inspect command. Adding this feature will allow easy formatting in scripts as well as running it on a terminal. This feature is implemented for artifact inspect by taking reference from images and network commands implementation. Fixes: [#27112](#27112) Signed-off-by: Akash Yadav <akashyadav256526@gmail.com>
1 parent 7fecff5 commit 1fbf24b

File tree

4 files changed

+108
-13
lines changed

4 files changed

+108
-13
lines changed

cmd/podman/artifact/inspect.go

Lines changed: 33 additions & 8 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{
1317
Use: "inspect [ARTIFACT...]",
1418
Short: "Inspect an OCI artifact",
1519
Long: "Provide details on an OCI artifact",
16-
RunE: inspect,
20+
RunE: artifactInspect,
1721
Args: cobra.MinimumNArgs(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: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,58 @@ 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 |
2637

2738
## EXAMPLES
2839

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

3477
## SEE ALSO

test/e2e/artifact_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,18 @@ var _ = Describe("Podman artifact", func() {
636636
// Verify we have 2 layers
637637
Expect(a.Manifest.Layers).To(HaveLen(2))
638638
})
639+
640+
It("podman artifact inspect with --format", func() {
641+
artifact1File, err := createArtifactFile(4192)
642+
Expect(err).ToNot(HaveOccurred())
643+
artifact1Name := "localhost/test/artifact1"
644+
addArtifact1 := podmanTest.PodmanExitCleanly("artifact", "add", artifact1Name, artifact1File)
645+
646+
artifactDigest := addArtifact1.OutputToString()
647+
648+
podmanTest.PodmanExitCleanly("artifact", "inspect", artifactDigest, "--format", "{{.Digest}}")
649+
podmanTest.PodmanExitCleanly("artifact", "inspect", artifactDigest[:12], "--format", "{{.Name}}")
650+
})
639651
})
640652

641653
func digestToFilename(digest string) string {

0 commit comments

Comments
 (0)