From 6405925f79806f7dd524bb622aaac53fc56dadce Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Thu, 2 Oct 2025 06:52:32 -0400 Subject: [PATCH] Add artifact fallback to podman inspect command This commit implements automatic artifact fallback for the podman inspect command as requested in GitHub issue #27075. Changes made: - Add ArtifactType constant to cmd/podman/common/inspect.go - Update AutocompleteInspectType to include artifact type in completions - Add artifact case to main inspect switch statement for explicit --type artifact - Implement artifact fallback in inspectAll function for automatic detection - Update shell completion to recognize artifacts in getEntityType function - Update command help text, usage, and examples to include artifacts - Update podman-inspect.1.md man page with artifact documentation - Add comprehensive e2e tests for artifact inspect functionality The inspect command now automatically falls back to artifact inspection when no container, image, volume, network, or pod matches the specified name. Users can also explicitly use --type artifact for direct artifact inspection. This maintains backward compatibility while extending functionality to support the artifact object type seamlessly. Examples: podman inspect myartifact # Auto-detects artifact podman inspect --type artifact myartifact # Explicit artifact type podman inspect --format '{{.Name}}' myartifact # Format support Fixes: https://github.com/containers/podman/issues/27075 Signed-off-by: Daniel J Walsh --- cmd/podman/common/completion.go | 6 ++- cmd/podman/common/inspect.go | 2 + cmd/podman/inspect.go | 4 +- cmd/podman/inspect/inspect.go | 18 ++++++++- docs/source/markdown/podman-inspect.1.md.in | 25 +++++++++--- docs/source/markdown/podman.1.md | 2 +- test/e2e/inspect_test.go | 42 +++++++++++++++++++++ 7 files changed, 89 insertions(+), 10 deletions(-) diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index b3a5efc4e2..e860dc7073 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1476,6 +1476,10 @@ func getEntityType(cmd *cobra.Command, args []string, o any) any { if networks, _ := getNetworks(cmd, args[0], completeDefault); len(networks) > 0 { return &entities.NetworkInspectReport{} } + // artifact logic + if artifacts, _ := getArtifacts(cmd, args[0]); len(artifacts) > 0 { + return &entities.ArtifactInspectReport{} + } return o } @@ -1641,7 +1645,7 @@ func AutocompleteImageSort(_ *cobra.Command, _ []string, _ string) ([]string, co // AutocompleteInspectType - Autocomplete inspect type options. func AutocompleteInspectType(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { - types := []string{AllType, ContainerType, ImageType, NetworkType, PodType, VolumeType} + types := []string{AllType, ArtifactType, ContainerType, ImageType, NetworkType, PodType, VolumeType} return types, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/podman/common/inspect.go b/cmd/podman/common/inspect.go index 12a5af5a92..b3dee58c50 100644 --- a/cmd/podman/common/inspect.go +++ b/cmd/podman/common/inspect.go @@ -3,6 +3,8 @@ package common const ( // AllType can be of type ImageType or ContainerType. AllType = "all" + // ArtifactType is the artifact type. + ArtifactType = "artifact" // ContainerType is the container type. ContainerType = "container" // ImageType is the image type. diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index ed15fcdef4..425327dba0 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -12,6 +12,7 @@ var ( inspectDescription = `Displays the low-level information on an object identified by name or ID. For more inspection options, see: + podman artifact inspect podman container inspect podman image inspect podman network inspect @@ -20,7 +21,7 @@ var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [options] {CONTAINER|IMAGE|POD|NETWORK|VOLUME} [...]", + Use: "inspect [options] {ARTIFACT|CONTAINER|IMAGE|POD|NETWORK|VOLUME} [...]", Short: "Display the configuration of object denoted by ID", RunE: inspectExec, Long: inspectDescription, @@ -28,6 +29,7 @@ var ( ValidArgsFunction: common.AutocompleteInspect, Example: `podman inspect fedora podman inspect --type image fedora + podman inspect --type artifact quay.io/myimage/myartifact:latest podman inspect CtrID ImgID podman inspect --format "imageId: {{.Id}} size: {{.Size}}" fedora`, } diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index c4ff61c546..cbba736a61 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -151,9 +151,18 @@ func (i *inspector) inspect(namesOrIDs []string) error { for i := range volumeData { data = append(data, volumeData[i]) } + case common.ArtifactType: + for _, name := range namesOrIDs { + artifactData, err := i.imageEngine.ArtifactInspect(ctx, name, entities.ArtifactInspectOptions{}) + if err != nil { + errs = append(errs, err) + continue + } + data = append(data, artifactData) + } default: - return fmt.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", i.options.Type, - common.ImageType, common.ContainerType, common.PodType, common.NetworkType, common.VolumeType, common.AllType) + return fmt.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, %q, or %q", i.options.Type, + common.ImageType, common.ContainerType, common.PodType, common.NetworkType, common.VolumeType, common.ArtifactType, common.AllType) } // Always print an empty array if data == nil { @@ -236,6 +245,11 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]any, data = append(data, podData[0]) continue } + artifactData, err := i.imageEngine.ArtifactInspect(ctx, name, entities.ArtifactInspectOptions{}) + if err == nil { + data = append(data, artifactData) + continue + } if len(errs) > 0 { allErrs = append(allErrs, fmt.Errorf("no such object: %q", name)) continue diff --git a/docs/source/markdown/podman-inspect.1.md.in b/docs/source/markdown/podman-inspect.1.md.in index 587b6d3e5e..c88a1dba54 100644 --- a/docs/source/markdown/podman-inspect.1.md.in +++ b/docs/source/markdown/podman-inspect.1.md.in @@ -1,19 +1,20 @@ % podman-inspect 1 ## NAME -podman\-inspect - Display a container, image, volume, network, or pod's configuration +podman\-inspect - Display artifact, container, image, volume, network, or pod's configuration ## SYNOPSIS **podman inspect** [*options*] *name* [...] ## DESCRIPTION -This displays the low-level information on containers and images identified by name or ID. By default, this renders -all results in a JSON array. If the inspect type is all, the order of inspection is: containers, images, volumes, network, pods. +This displays the low-level information on artifacts, containers, and images identified by name or ID. By default, this renders +all results in a JSON array. If the inspect type is all, the order of inspection is: containers, images, volumes, network, pods, artifacts. If a container has the same name as an image, then the container JSON is returned, and so on. If a format is specified, the given template is executed for each result. For more inspection options, see also +[podman-artifact-inspect(1)](podman-artifact-inspect.1.md), [podman-container-inspect(1)](podman-container-inspect.1.md), [podman-image-inspect(1)](podman-image-inspect.1.md), [podman-network-inspect(1)](podman-network-inspect.1.md), @@ -35,7 +36,7 @@ In addition to normal output, display the total file size if the type is a conta #### **--type**, **-t**=*type* -Return JSON for the specified type. Type can be 'container', 'image', 'volume', 'network', 'pod', or 'all' (default: all) +Return JSON for the specified type. Type can be 'artifact', 'container', 'image', 'volume', 'network', 'pod', or 'all' (default: all) (Only meaningful when invoked as *podman inspect*) ## EXAMPLES @@ -164,8 +165,22 @@ Inspect the specified network for the `Name` format specifier: myNetwork ``` +Inspect the specified artifact: +``` +# podman inspect quay.io/myimage/myartifact:latest --type artifact +{ + "Manifest": { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + ... + }, + "Name": "quay.io/myimage/myartifact:latest", + "Digest": "sha256:..." +} +``` + ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-container-inspect(1)](podman-container-inspect.1.md)**, **[podman-image-inspect(1)](podman-image-inspect.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-pod-inspect(1)](podman-pod-inspect.1.md)**, **[podman-volume-inspect(1)](podman-volume-inspect.1.md)** +**[podman(1)](podman.1.md)**, **[podman-artifact-inspect(1)](podman-artifact-inspect.1.md)**, **[podman-container-inspect(1)](podman-container-inspect.1.md)**, **[podman-image-inspect(1)](podman-image-inspect.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-pod-inspect(1)](podman-pod-inspect.1.md)**, **[podman-volume-inspect(1)](podman-volume-inspect.1.md)** ## HISTORY July 2017, Originally compiled by Dan Walsh diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 492068c231..8f46dd081a 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -367,7 +367,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | | [podman-info(1)](podman-info.1.md) | Display Podman related system information. | | [podman-init(1)](podman-init.1.md) | Initialize one or more containers | -| [podman-inspect(1)](podman-inspect.1.md) | Display a container, image, volume, network, or pod's configuration. | +| [podman-inspect(1)](podman-inspect.1.md) | Display artifact, container, image, volume, network, or pod's configuration. | | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | | [podman-load(1)](podman-load.1.md) | Load image(s) from a tar archive into container storage. | | [podman-login(1)](podman-login.1.md) | Log in to a container registry. | diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index 61f3f50f9d..7451ad57c6 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -597,4 +597,46 @@ var _ = Describe("Podman inspect", func() { Expect(session.OutputToString()).To(ContainSubstring(commandNotFound)) }) + It("podman inspect artifact", func() { + artifactFile, err := createArtifactFile(1024) + Expect(err).ToNot(HaveOccurred()) + + artifactName := "localhost/test/myartifact" + add := podmanTest.Podman([]string{"artifact", "add", artifactName, artifactFile}) + add.WaitWithDefaultTimeout() + Expect(add).Should(ExitCleanly()) + + // Test explicit artifact type + inspect := podmanTest.Podman([]string{"inspect", "--type", "artifact", artifactName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(ExitCleanly()) + Expect(inspect.OutputToString()).To(BeValidJSON()) + + // Test fallback to artifact when no other object type matches + inspect = podmanTest.Podman([]string{"inspect", artifactName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(ExitCleanly()) + Expect(inspect.OutputToString()).To(BeValidJSON()) + + // Verify that the output contains artifact-specific fields + Expect(inspect.OutputToString()).To(ContainSubstring("Manifest")) + Expect(inspect.OutputToString()).To(ContainSubstring("Digest")) + + // Test format with artifact-specific fields + inspect = podmanTest.Podman([]string{"inspect", "--format", "{{.Name}}", artifactName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(ExitCleanly()) + Expect(inspect.OutputToString()).To(Equal(artifactName)) + + inspect2 := podmanTest.Podman([]string{"inspect", "--format", "{{.Digest}}", artifactName}) + inspect2.WaitWithDefaultTimeout() + Expect(inspect2).Should(ExitCleanly()) + Expect(inspect2.OutputToString()).To(ContainSubstring("sha256:")) + + // Clean up + rm := podmanTest.Podman([]string{"artifact", "rm", artifactName}) + rm.WaitWithDefaultTimeout() + Expect(rm).Should(ExitCleanly()) + }) + })