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](https://github.com/containers/podman/issues/27112)

Signed-off-by: Akash Yadav <akashyadav256526@gmail.com>
This commit is contained in:
Akash Yadav
2025-10-21 16:58:03 +05:30
parent 7fecff5c29
commit 1fbf24b65b
4 changed files with 108 additions and 13 deletions

View File

@@ -1,11 +1,15 @@
package artifact
import (
"os"
"github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/inspect"
"github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/utils"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/spf13/cobra"
"go.podman.io/common/pkg/report"
)
var (
@@ -13,11 +17,12 @@ var (
Use: "inspect [ARTIFACT...]",
Short: "Inspect an OCI artifact",
Long: "Provide details on an OCI artifact",
RunE: inspect,
RunE: artifactInspect,
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: common.AutocompleteArtifacts,
Example: `podman artifact inspect quay.io/myimage/myartifact:latest`,
}
inspectOpts *entities.InspectOptions
)
func init() {
@@ -26,10 +31,11 @@ func init() {
Parent: artifactCmd,
})
// TODO When things firm up on inspect looks, we can do a format implementation
// flags := inspectCmd.Flags()
// formatFlagName := "format"
// flags.StringVar(&inspectFlag.format, formatFlagName, "", "Format volume output using JSON or a Go template")
inspectOpts = new(entities.InspectOptions)
flags := inspectCmd.Flags()
formatFlagName := "format"
flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format volume output using JSON or a Go template")
// This is something we wanted to do but did not seem important enough for initial PR
// remoteFlagName := "remote"
@@ -37,14 +43,33 @@ func init() {
// TODO When the inspect structure has been defined, we need to uncomment and redirect this. Reminder, this
// will also need to be reflected in the podman-artifact-inspect man page
// _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&machine.InspectInfo{}))
_ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ArtifactInspectReport{}))
}
func inspect(_ *cobra.Command, args []string) error {
func artifactInspect(_ *cobra.Command, args []string) error {
artifactOptions := entities.ArtifactInspectOptions{}
inspectData, err := registry.ImageEngine().ArtifactInspect(registry.Context(), args[0], artifactOptions)
if err != nil {
return err
}
return utils.PrintGenericJSON(inspectData)
switch {
case report.IsJSON(inspectOpts.Format) || inspectOpts.Format == "":
return utils.PrintGenericJSON(inspectData)
default:
// Landing here implies user has given a custom --format
var rpt *report.Formatter
format := inspect.InspectNormalize(inspectOpts.Format, inspectOpts.Type)
rpt, err = report.New(os.Stdout, "inspect").Parse(report.OriginUser, format)
if err != nil {
return err
}
defer rpt.Flush()
// Storing and passing inspectData in an array to [Execute] is workaround to avoid getting an error.
// Which seems to happen when type passed to [Execute] is not a slice.
// Error: template: inspect:1:8: executing "inspect" at <.>: range can't iterate over {0x6600020c444 sha256:4bafff5c1b2c950651101d22d3dbf76744446aeb5f79fc926674e0db1083qew456}
data := []any{inspectData}
return rpt.Execute(data)
}
}

View File

@@ -176,7 +176,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
default:
// Landing here implies user has given a custom --format
var rpt *report.Formatter
format := inspectNormalize(i.options.Format, i.options.Type)
format := InspectNormalize(i.options.Format, i.options.Type)
rpt, err = report.New(os.Stdout, "inspect").Parse(report.OriginUser, format)
if err != nil {
return err
@@ -258,7 +258,22 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]any,
return data, allErrs, nil
}
func inspectNormalize(row string, inspectType string) string {
// InspectNormalize modifies a given row string based on the specified inspect type.
// It replaces specific field names within the row string for standardization.
// For the `image` inspect type, it includes additional field replacements like `.Config.Healthcheck`.
//
// Parameters:
// - row: The input string that represents a data row to be modified.
// - inspectType: The type of inspection (e.g., "image") to determine specific replacements.
//
// Returns:
// - A new string with the necessary replacements applied based on the inspect type.
//
// InspectNormalize does not need to be exported but to avoid de-duplication of code. We had to export it.
// It can be reverted back once `podman artifact inspect` can use [Inspect] to fetch artifact data instead of
// fetching it itself.
// The reason why we did it in this way can be further read [here](https://github.com/containers/podman/pull/27182#issuecomment-3402465389).
func InspectNormalize(row string, inspectType string) string {
m := regexp.MustCompile(`{{\s*\.Id\s*}}`)
row = m.ReplaceAllString(row, "{{.ID}}")