Skip to content

Commit

Permalink
Merge pull request #25355 from baude/artifactrmall
Browse files Browse the repository at this point in the history
Add --all to artifact rm
  • Loading branch information
openshift-merge-bot[bot] authored Feb 20, 2025
2 parents ca1c029 + cbc7345 commit 1e7f810
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 29 deletions.
66 changes: 47 additions & 19 deletions cmd/podman/artifact/rm.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package artifact

import (
"errors"
"fmt"

"github.com/containers/podman/v5/cmd/podman/common"
Expand All @@ -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
}
18 changes: 15 additions & 3 deletions docs/source/markdown/podman-artifact-rm.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion pkg/domain/entities/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type ArtifactPushOptions struct {
}

type ArtifactRemoveOptions struct {
// Remove all artifacts
All bool
}

type ArtifactPullReport struct{}
Expand All @@ -79,5 +81,5 @@ type ArtifactAddReport struct {
}

type ArtifactRemoveReport struct {
ArtfactDigest *digest.Digest
ArtifactDigests []*digest.Digest
}
39 changes: 34 additions & 5 deletions pkg/domain/infra/abi/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down
31 changes: 30 additions & 1 deletion test/e2e/artifact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit 1e7f810

Please sign in to comment.