mirror of
https://github.com/containers/podman.git
synced 2025-06-02 10:46:09 +08:00
V2 podman image prune
* Fixed header for `podman image ls` * Implemented prune `all` flag, preserved filter method for backwards capability * Updated binding tests Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
@ -15,7 +15,7 @@ var (
|
||||
Args: listCmd.Args,
|
||||
Short: listCmd.Short,
|
||||
Long: listCmd.Long,
|
||||
PreRunE: listCmd.PreRunE,
|
||||
PreRunE: preRunE,
|
||||
RunE: listCmd.RunE,
|
||||
Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ func imageListFormat(flags listFlagType) (string, string) {
|
||||
row += "\t{{.Digest}}"
|
||||
}
|
||||
|
||||
hdr += "\tID"
|
||||
hdr += "\tIMAGE ID"
|
||||
if flags.noTrunc {
|
||||
row += "\tsha256:{{.ID}}"
|
||||
} else {
|
||||
|
86
cmd/podmanV2/images/prune.go
Normal file
86
cmd/podmanV2/images/prune.go
Normal file
@ -0,0 +1,86 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
pruneDescription = `Removes all unnamed images from local storage.
|
||||
|
||||
If an image is not being used by a container, it will be removed from the system.`
|
||||
pruneCmd = &cobra.Command{
|
||||
Use: "prune",
|
||||
Args: cobra.NoArgs,
|
||||
Short: "Remove unused images",
|
||||
Long: pruneDescription,
|
||||
RunE: prune,
|
||||
Example: `podman image prune`,
|
||||
}
|
||||
|
||||
pruneOpts = entities.ImagePruneOptions{}
|
||||
force bool
|
||||
filter = []string{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: pruneCmd,
|
||||
Parent: imageCmd,
|
||||
})
|
||||
|
||||
flags := pruneCmd.Flags()
|
||||
flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all unused images, not just dangling ones")
|
||||
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.StringArrayVar(&filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
|
||||
|
||||
}
|
||||
|
||||
func prune(cmd *cobra.Command, args []string) error {
|
||||
if !force {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf(`
|
||||
WARNING! This will remove all dangling images.
|
||||
Are you sure you want to continue? [y/N] `)
|
||||
answer, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading input")
|
||||
}
|
||||
if strings.ToLower(answer)[0] != 'y' {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Remove once filter refactor is finished and url.Values rules :)
|
||||
for _, f := range filter {
|
||||
t := strings.SplitN(f, "=", 2)
|
||||
pruneOpts.Filters.Add(t[0], t[1])
|
||||
}
|
||||
|
||||
results, err := registry.ImageEngine().Prune(registry.GetContext(), pruneOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, i := range results.Report.Id {
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
for _, e := range results.Report.Err {
|
||||
fmt.Fprint(os.Stderr, e.Error()+"\n")
|
||||
}
|
||||
|
||||
if results.Size > 0 {
|
||||
fmt.Fprintf(os.Stdout, "Size: %d\n", results.Size)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -64,6 +64,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
|
||||
query := struct {
|
||||
All bool
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{
|
||||
// This is where you can override the golang default value for one of fields
|
||||
@ -80,7 +81,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
|
||||
}
|
||||
}
|
||||
pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), false, filters)
|
||||
pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
|
@ -119,12 +119,12 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
all bool
|
||||
err error
|
||||
)
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
All bool `schema:"all"`
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{
|
||||
// override any golang type defaults
|
||||
@ -140,7 +140,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
if _, found := r.URL.Query()["filters"]; found {
|
||||
dangling := query.Filters["all"]
|
||||
if len(dangling) > 0 {
|
||||
all, err = strconv.ParseBool(query.Filters["all"][0])
|
||||
query.All, err = strconv.ParseBool(query.Filters["all"][0])
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
@ -152,7 +152,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
|
||||
}
|
||||
}
|
||||
cids, err := runtime.ImageRuntime().PruneImages(r.Context(), all, libpodFilters)
|
||||
|
||||
cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
||||
return
|
||||
|
@ -154,7 +154,7 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c
|
||||
|
||||
// Prune removes unused images from local storage. The optional filters can be used to further
|
||||
// define which images should be pruned.
|
||||
func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
|
||||
func Prune(ctx context.Context, all *bool, filters map[string][]string) ([]string, error) {
|
||||
var (
|
||||
deleted []string
|
||||
)
|
||||
@ -163,6 +163,9 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
params := url.Values{}
|
||||
if all != nil {
|
||||
params.Set("all", strconv.FormatBool(*all))
|
||||
}
|
||||
if filters != nil {
|
||||
stringFilter, err := bindings.FiltersToString(filters)
|
||||
if err != nil {
|
||||
@ -174,7 +177,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
|
||||
if err != nil {
|
||||
return deleted, err
|
||||
}
|
||||
return deleted, response.Process(nil)
|
||||
return deleted, response.Process(&deleted)
|
||||
}
|
||||
|
||||
// Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required.
|
||||
|
@ -16,22 +16,22 @@ import (
|
||||
|
||||
var _ = Describe("Podman images", func() {
|
||||
var (
|
||||
//tempdir string
|
||||
//err error
|
||||
//podmanTest *PodmanTestIntegration
|
||||
// tempdir string
|
||||
// err error
|
||||
// podmanTest *PodmanTestIntegration
|
||||
bt *bindingTest
|
||||
s *gexec.Session
|
||||
err error
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
//tempdir, err = CreateTempDirInTempDir()
|
||||
//if err != nil {
|
||||
// tempdir, err = CreateTempDirInTempDir()
|
||||
// if err != nil {
|
||||
// os.Exit(1)
|
||||
//}
|
||||
//podmanTest = PodmanTestCreate(tempdir)
|
||||
//podmanTest.Setup()
|
||||
//podmanTest.SeedImages()
|
||||
// }
|
||||
// podmanTest = PodmanTestCreate(tempdir)
|
||||
// podmanTest.Setup()
|
||||
// podmanTest.SeedImages()
|
||||
bt = newBindingTest()
|
||||
bt.RestoreImagesFromCache()
|
||||
s = bt.startAPIService()
|
||||
@ -41,12 +41,13 @@ var _ = Describe("Podman images", func() {
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
//podmanTest.Cleanup()
|
||||
//f := CurrentGinkgoTestDescription()
|
||||
//processTestResult(f)
|
||||
// podmanTest.Cleanup()
|
||||
// f := CurrentGinkgoTestDescription()
|
||||
// processTestResult(f)
|
||||
s.Kill()
|
||||
bt.cleanup()
|
||||
})
|
||||
|
||||
It("inspect image", func() {
|
||||
// Inspect invalid image be 404
|
||||
_, err = images.GetImage(bt.conn, "foobar5000", nil)
|
||||
@ -71,7 +72,7 @@ var _ = Describe("Podman images", func() {
|
||||
Expect(err).To(BeNil())
|
||||
// TODO it looks like the images API alwaays returns size regardless
|
||||
// of bool or not. What should we do ?
|
||||
//Expect(data.Size).To(BeZero())
|
||||
// Expect(data.Size).To(BeZero())
|
||||
|
||||
// Enabling the size parameter should result in size being populated
|
||||
data, err = images.GetImage(bt.conn, alpine.name, &bindings.PTrue)
|
||||
@ -142,7 +143,7 @@ var _ = Describe("Podman images", func() {
|
||||
err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
//Validates if name updates when the image is retagged.
|
||||
// Validates if name updates when the image is retagged.
|
||||
_, err := images.GetImage(bt.conn, "alpine:demo", nil)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
@ -165,7 +166,7 @@ var _ = Describe("Podman images", func() {
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(imageSummary)).To(Equal(3))
|
||||
|
||||
//Validate the image names.
|
||||
// Validate the image names.
|
||||
var names []string
|
||||
for _, i := range imageSummary {
|
||||
names = append(names, i.RepoTags...)
|
||||
@ -289,6 +290,7 @@ var _ = Describe("Podman images", func() {
|
||||
Expect(data.Comment).To(Equal(testMessage))
|
||||
|
||||
})
|
||||
|
||||
It("History Image", func() {
|
||||
// a bogus name should return a 404
|
||||
_, err := images.History(bt.conn, "foobar")
|
||||
@ -343,4 +345,12 @@ var _ = Describe("Podman images", func() {
|
||||
Expect(len(imgs)).To(BeNumerically(">=", 1))
|
||||
})
|
||||
|
||||
It("Prune images", func() {
|
||||
trueBoxed := true
|
||||
results, err := images.Prune(bt.conn, &trueBoxed, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(results)).To(BeNumerically(">", 0))
|
||||
Expect(results).To(ContainElement("docker.io/library/alpine:latest"))
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -119,7 +119,7 @@ type ImageInspectOptions struct {
|
||||
|
||||
type ImageListOptions struct {
|
||||
All bool `json:"all" schema:"all"`
|
||||
Filter []string `json:",omitempty"`
|
||||
Filter []string `json:"Filter,omitempty"`
|
||||
Filters url.Values `json:"filters" schema:"filters"`
|
||||
}
|
||||
|
||||
@ -128,8 +128,9 @@ type ImageListOptions struct {
|
||||
// }
|
||||
|
||||
type ImagePruneOptions struct {
|
||||
All bool
|
||||
Filter ImageFilter
|
||||
All bool `json:"all" schema:"all"`
|
||||
Filter []string `json:"filter" schema:"filter"`
|
||||
Filters url.Values `json:"filters" schema:"filters"`
|
||||
}
|
||||
|
||||
type ImagePruneReport struct {
|
||||
|
@ -88,13 +88,18 @@ func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image,
|
||||
}
|
||||
|
||||
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
|
||||
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, []string{})
|
||||
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
report := entities.ImagePruneReport{}
|
||||
copy(report.Report.Id, results)
|
||||
report := entities.ImagePruneReport{
|
||||
Report: entities.Report{
|
||||
Id: results,
|
||||
Err: nil,
|
||||
},
|
||||
Size: 0,
|
||||
}
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package tunnel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
images "github.com/containers/libpod/pkg/bindings/images"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
@ -72,12 +71,17 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entiti
|
||||
}
|
||||
|
||||
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
|
||||
results, err := images.Prune(ir.ClientCxt, url.Values{})
|
||||
results, err := images.Prune(ir.ClientCxt, &opts.All, opts.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
report := entities.ImagePruneReport{}
|
||||
copy(report.Report.Id, results)
|
||||
report := entities.ImagePruneReport{
|
||||
Report: entities.Report{
|
||||
Id: results,
|
||||
Err: nil,
|
||||
},
|
||||
Size: 0,
|
||||
}
|
||||
return &report, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user