Merge pull request #25355 from baude/artifactrmall

Add --all to artifact rm
This commit is contained in:
openshift-merge-bot[bot] 2025-02-20 19:35:12 +00:00 committed by GitHub
commit 1e7f810f71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 129 additions and 29 deletions

View File

@ -1,6 +1,7 @@
package artifact
import (
"errors"
"fmt"
"github.com/containers/podman/v5/cmd/podman/common"
@ -11,40 +12,67 @@ import (
var (
rmCmd = &cobra.Command{
Use: "rm ARTIFACT",
Short: "Remove an OCI artifact",
Long: "Remove an OCI from local storage",
RunE: rm,
Aliases: []string{"remove"},
Args: cobra.ExactArgs(1),
Use: "rm [options] ARTIFACT",
Short: "Remove an OCI artifact",
Long: "Remove an OCI artifact from local storage",
RunE: rm,
Aliases: []string{"remove"},
Args: func(cmd *cobra.Command, args []string) error { //nolint: gocritic
return checkAllAndArgs(cmd, args)
},
ValidArgsFunction: common.AutocompleteArtifacts,
Example: `podman artifact rm quay.io/myimage/myartifact:latest`,
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
Example: `podman artifact rm quay.io/myimage/myartifact:latest
podman artifact rm -a`,
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
}
// The lint avoid here is because someday soon we will need flags for
// this command
rmFlag = rmFlagType{} //nolint:unused
rmOptions = entities.ArtifactRemoveOptions{}
)
// TODO at some point force will be a required option; but this cannot be
// until we have artifacts being consumed by other parts of libpod like
// volumes
type rmFlagType struct { //nolint:unused
force bool
func rmFlags(cmd *cobra.Command) {
flags := cmd.Flags()
flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all artifacts")
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: rmCmd,
Parent: artifactCmd,
})
rmFlags(rmCmd)
}
func rm(cmd *cobra.Command, args []string) error {
artifactRemoveReport, err := registry.ImageEngine().ArtifactRm(registry.Context(), args[0], entities.ArtifactRemoveOptions{})
var nameOrID string
if len(args) > 0 {
nameOrID = args[0]
}
artifactRemoveReport, err := registry.ImageEngine().ArtifactRm(registry.Context(), nameOrID, rmOptions)
if err != nil {
return err
}
fmt.Println(artifactRemoveReport.ArtfactDigest.Encoded())
for _, d := range artifactRemoveReport.ArtifactDigests {
fmt.Println(d.Encoded())
}
return nil
}
// checkAllAndArgs takes a cobra command and args and checks if
// all is used, then no args can be passed. note: this was created
// as an unexported local func for now and could be moved to pkg
// validate. if we add "--latest" to the command, then perhaps
// one of the existing plg validate funcs would be appropriate.
func checkAllAndArgs(c *cobra.Command, args []string) error {
all, _ := c.Flags().GetBool("all")
if all && len(args) > 0 {
return fmt.Errorf("when using the --all switch, you may not pass any artifact names or digests")
}
if !all {
if len(args) < 1 {
return errors.New("a single artifact name or digest must be specified")
}
if len(args) > 1 {
return errors.New("too many arguments: only accepts one artifact name or digest ")
}
}
return nil
}

View File

@ -9,7 +9,7 @@ subject to change.*
podman\-artifact\-rm - Remove an OCI from local storage
## SYNOPSIS
**podman artifact rm** *name*
**podman artifact rm** [*options*] *name*
## DESCRIPTION
@ -18,6 +18,11 @@ qualified artifact name or a full or partial artifact digest.
## OPTIONS
#### **--all**, **-a**
Remove all artifacts in the local store. The use of this option conflicts with
providing a name or digest of the artifact.
#### **--help**
Print usage statement.
@ -29,14 +34,21 @@ Remove an artifact by name
```
$ podman artifact rm quay.io/artifact/foobar2:test
e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
Deleted: e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
```
Remove an artifact by partial digest
```
$ podman artifact rm e7b417f49fc
e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
Deleted: e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
```
Remove all artifacts in local storage
```
$ podman artifact rm -a
Deleted: cee15f7c5ce3e86ae6ce60d84bebdc37ad34acfa9a2611cf47501469ac83a1ab
Deleted: 72875f8f6f78d5b8ba98b2dd2c0a6f395fde8f05ff63a1df580d7a88f5afa97b
```
## SEE ALSO

View File

@ -59,6 +59,8 @@ type ArtifactPushOptions struct {
}
type ArtifactRemoveOptions struct {
// Remove all artifacts
All bool
}
type ArtifactPullReport struct{}
@ -79,5 +81,5 @@ type ArtifactAddReport struct {
}
type ArtifactRemoveReport struct {
ArtfactDigest *digest.Digest
ArtifactDigests []*digest.Digest
}

View File

@ -12,6 +12,7 @@ import (
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/libartifact/store"
"github.com/containers/podman/v5/pkg/libartifact/types"
"github.com/opencontainers/go-digest"
)
func getDefaultArtifactStore(ir *ImageEngine) string {
@ -86,17 +87,45 @@ func (ir *ImageEngine) ArtifactPull(ctx context.Context, name string, opts entit
return nil, artStore.Pull(ctx, name, *pullOptions)
}
func (ir *ImageEngine) ArtifactRm(ctx context.Context, name string, _ entities.ArtifactRemoveOptions) (*entities.ArtifactRemoveReport, error) {
func (ir *ImageEngine) ArtifactRm(ctx context.Context, name string, opts entities.ArtifactRemoveOptions) (*entities.ArtifactRemoveReport, error) {
var (
namesOrDigests []string
)
artifactDigests := make([]*digest.Digest, 0, len(namesOrDigests))
artStore, err := store.NewArtifactStore(getDefaultArtifactStore(ir), ir.Libpod.SystemContext())
if err != nil {
return nil, err
}
artifactDigest, err := artStore.Remove(ctx, name)
if err != nil {
return nil, err
if opts.All {
allArtifacts, err := artStore.List(ctx)
if err != nil {
return nil, err
}
for _, art := range allArtifacts {
// Using the digest here instead of name to protect against
// an artifact that lacks a name
manifestDigest, err := art.GetDigest()
if err != nil {
return nil, err
}
namesOrDigests = append(namesOrDigests, manifestDigest.Encoded())
}
}
if name != "" {
namesOrDigests = append(namesOrDigests, name)
}
for _, namesOrDigest := range namesOrDigests {
artifactDigest, err := artStore.Remove(ctx, namesOrDigest)
if err != nil {
return nil, err
}
artifactDigests = append(artifactDigests, artifactDigest)
}
artifactRemoveReport := entities.ArtifactRemoveReport{
ArtfactDigest: artifactDigest,
ArtifactDigests: artifactDigests,
}
return &artifactRemoveReport, err
}

View File

@ -175,12 +175,41 @@ var _ = Describe("Podman artifact", func() {
// Removing that artifact should work
rmWorks := podmanTest.PodmanExitCleanly("artifact", "rm", artifact1Name)
// The digests printed by removal should be the same as the digest that was added
Expect(addArtifact1.OutputToString()).To(Equal(rmWorks.OutputToString()))
Expect(rmWorks.OutputToString()).To(ContainSubstring(addArtifact1.OutputToString()))
// Inspecting that the removed artifact should fail
inspectArtifact := podmanTest.Podman([]string{"artifact", "inspect", artifact1Name})
inspectArtifact.WaitWithDefaultTimeout()
Expect(inspectArtifact).Should(ExitWithError(125, fmt.Sprintf("Error: %s: artifact does not exist", artifact1Name)))
// Add some artifacts back in
artifact2File, err := createArtifactFile(8096)
Expect(err).ToNot(HaveOccurred())
artifact2Name := "localhost/test/artifact2"
podmanTest.PodmanExitCleanly("artifact", "add", artifact2Name, artifact2File)
podmanTest.PodmanExitCleanly("artifact", "add", artifact1Name, artifact1File)
// Using -a and an arg should trigger an error
failArgs := podmanTest.Podman([]string{"artifact", "rm", "-a", artifact1Name})
failArgs.WaitWithDefaultTimeout()
Expect(failArgs).Should(ExitWithError(125, "Error: when using the --all switch, you may not pass any artifact names or digests"))
// No args is an error
failNoArgs := podmanTest.Podman([]string{"artifact", "rm"})
failNoArgs.WaitWithDefaultTimeout()
Expect(failNoArgs).Should(ExitWithError(125, "Error: a single artifact name or digest must be specified"))
// Multiple args is an error
multipleArgs := podmanTest.Podman([]string{"artifact", "rm", artifact1Name, artifact2File})
multipleArgs.WaitWithDefaultTimeout()
Expect(multipleArgs).Should(ExitWithError(125, "Error: too many arguments: only accepts one artifact name or digest"))
// Remove all
podmanTest.PodmanExitCleanly("artifact", "rm", "-a")
// There should be no artifacts in the store
rmAll := podmanTest.PodmanExitCleanly("artifact", "ls", "--noheading")
Expect(rmAll.OutputToString()).To(BeEmpty())
})
It("podman artifact inspect with full or partial digest", func() {