mirror of
https://github.com/containers/podman.git
synced 2025-10-20 20:54:45 +08:00
Add --all to artifact rm
Add the ability to remove all artifacts with a --all|-a option in podman artifact rm. Fixes: https://issues.redhat.com/browse/RUN-2512 Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
package artifact
|
package artifact
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/cmd/podman/common"
|
"github.com/containers/podman/v5/cmd/podman/common"
|
||||||
@ -11,40 +12,67 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
rmCmd = &cobra.Command{
|
rmCmd = &cobra.Command{
|
||||||
Use: "rm ARTIFACT",
|
Use: "rm [options] ARTIFACT",
|
||||||
Short: "Remove an OCI artifact",
|
Short: "Remove an OCI artifact",
|
||||||
Long: "Remove an OCI from local storage",
|
Long: "Remove an OCI artifact from local storage",
|
||||||
RunE: rm,
|
RunE: rm,
|
||||||
Aliases: []string{"remove"},
|
Aliases: []string{"remove"},
|
||||||
Args: cobra.ExactArgs(1),
|
Args: func(cmd *cobra.Command, args []string) error { //nolint: gocritic
|
||||||
|
return checkAllAndArgs(cmd, args)
|
||||||
|
},
|
||||||
ValidArgsFunction: common.AutocompleteArtifacts,
|
ValidArgsFunction: common.AutocompleteArtifacts,
|
||||||
Example: `podman artifact rm quay.io/myimage/myartifact:latest`,
|
Example: `podman artifact rm quay.io/myimage/myartifact:latest
|
||||||
|
podman artifact rm -a`,
|
||||||
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
|
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
|
||||||
}
|
}
|
||||||
// The lint avoid here is because someday soon we will need flags for
|
|
||||||
// this command
|
rmOptions = entities.ArtifactRemoveOptions{}
|
||||||
rmFlag = rmFlagType{} //nolint:unused
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO at some point force will be a required option; but this cannot be
|
func rmFlags(cmd *cobra.Command) {
|
||||||
// until we have artifacts being consumed by other parts of libpod like
|
flags := cmd.Flags()
|
||||||
// volumes
|
flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all artifacts")
|
||||||
type rmFlagType struct { //nolint:unused
|
|
||||||
force bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
Command: rmCmd,
|
Command: rmCmd,
|
||||||
Parent: artifactCmd,
|
Parent: artifactCmd,
|
||||||
})
|
})
|
||||||
|
rmFlags(rmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rm(cmd *cobra.Command, args []string) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ subject to change.*
|
|||||||
podman\-artifact\-rm - Remove an OCI from local storage
|
podman\-artifact\-rm - Remove an OCI from local storage
|
||||||
|
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
**podman artifact rm** *name*
|
**podman artifact rm** [*options*] *name*
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
|
|
||||||
@ -18,6 +18,11 @@ qualified artifact name or a full or partial artifact digest.
|
|||||||
|
|
||||||
## OPTIONS
|
## 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**
|
#### **--help**
|
||||||
|
|
||||||
Print usage statement.
|
Print usage statement.
|
||||||
@ -29,14 +34,21 @@ Remove an artifact by name
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ podman artifact rm quay.io/artifact/foobar2:test
|
$ podman artifact rm quay.io/artifact/foobar2:test
|
||||||
e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
|
Deleted: e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
|
||||||
```
|
```
|
||||||
|
|
||||||
Remove an artifact by partial digest
|
Remove an artifact by partial digest
|
||||||
|
|
||||||
```
|
```
|
||||||
$ podman artifact rm e7b417f49fc
|
$ podman artifact rm e7b417f49fc
|
||||||
e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
|
Deleted: e7b417f49fc24fc7ead6485da0ebd5bc4419d8a3f394c169fee5a6f38faa4056
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove all artifacts in local storage
|
||||||
|
```
|
||||||
|
$ podman artifact rm -a
|
||||||
|
Deleted: cee15f7c5ce3e86ae6ce60d84bebdc37ad34acfa9a2611cf47501469ac83a1ab
|
||||||
|
Deleted: 72875f8f6f78d5b8ba98b2dd2c0a6f395fde8f05ff63a1df580d7a88f5afa97b
|
||||||
```
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
|
@ -59,6 +59,8 @@ type ArtifactPushOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ArtifactRemoveOptions struct {
|
type ArtifactRemoveOptions struct {
|
||||||
|
// Remove all artifacts
|
||||||
|
All bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArtifactPullReport struct{}
|
type ArtifactPullReport struct{}
|
||||||
@ -79,5 +81,5 @@ type ArtifactAddReport struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ArtifactRemoveReport struct {
|
type ArtifactRemoveReport struct {
|
||||||
ArtfactDigest *digest.Digest
|
ArtifactDigests []*digest.Digest
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/containers/podman/v5/pkg/domain/entities"
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
"github.com/containers/podman/v5/pkg/libartifact/store"
|
"github.com/containers/podman/v5/pkg/libartifact/store"
|
||||||
"github.com/containers/podman/v5/pkg/libartifact/types"
|
"github.com/containers/podman/v5/pkg/libartifact/types"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDefaultArtifactStore(ir *ImageEngine) string {
|
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)
|
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())
|
artStore, err := store.NewArtifactStore(getDefaultArtifactStore(ir), ir.Libpod.SystemContext())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
artifactDigest, err := artStore.Remove(ctx, name)
|
|
||||||
|
if opts.All {
|
||||||
|
allArtifacts, err := artStore.List(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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{
|
artifactRemoveReport := entities.ArtifactRemoveReport{
|
||||||
ArtfactDigest: artifactDigest,
|
ArtifactDigests: artifactDigests,
|
||||||
}
|
}
|
||||||
return &artifactRemoveReport, err
|
return &artifactRemoveReport, err
|
||||||
}
|
}
|
||||||
|
@ -175,12 +175,41 @@ var _ = Describe("Podman artifact", func() {
|
|||||||
// Removing that artifact should work
|
// Removing that artifact should work
|
||||||
rmWorks := podmanTest.PodmanExitCleanly("artifact", "rm", artifact1Name)
|
rmWorks := podmanTest.PodmanExitCleanly("artifact", "rm", artifact1Name)
|
||||||
// The digests printed by removal should be the same as the digest that was added
|
// 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
|
// Inspecting that the removed artifact should fail
|
||||||
inspectArtifact := podmanTest.Podman([]string{"artifact", "inspect", artifact1Name})
|
inspectArtifact := podmanTest.Podman([]string{"artifact", "inspect", artifact1Name})
|
||||||
inspectArtifact.WaitWithDefaultTimeout()
|
inspectArtifact.WaitWithDefaultTimeout()
|
||||||
Expect(inspectArtifact).Should(ExitWithError(125, fmt.Sprintf("Error: %s: artifact does not exist", artifact1Name)))
|
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() {
|
It("podman artifact inspect with full or partial digest", func() {
|
||||||
|
Reference in New Issue
Block a user