mirror of https://github.com/containers/podman.git
Merge pull request #25355 from baude/artifactrmall
Add --all to artifact rm
This commit is contained in:
commit
1e7f810f71
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue