diff options
-rw-r--r-- | cmd/podman/container.go | 2 | ||||
-rw-r--r-- | cmd/podman/containers_prune.go | 74 | ||||
-rw-r--r-- | cmd/podman/image.go | 2 | ||||
-rw-r--r-- | cmd/podman/images_prune.go | 34 | ||||
-rw-r--r-- | cmd/podman/shared/prune.go | 24 | ||||
-rw-r--r-- | completions/bash/podman | 24 | ||||
-rw-r--r-- | docs/podman-container-prune.1.md | 31 | ||||
-rw-r--r-- | docs/podman-container.1.md | 1 | ||||
-rw-r--r-- | docs/podman-image-prune.1.md | 32 | ||||
-rw-r--r-- | docs/podman-image.1.md | 1 | ||||
-rw-r--r-- | docs/podman-rmi.1.md | 18 | ||||
-rw-r--r-- | libpod/image/prune.go | 26 | ||||
-rw-r--r-- | test/e2e/prune_test.go | 88 |
13 files changed, 351 insertions, 6 deletions
diff --git a/cmd/podman/container.go b/cmd/podman/container.go index b6262f890..b0232c874 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -22,7 +22,7 @@ var ( mountCommand, pauseCommand, portCommand, - // pruneCommand, + pruneContainersCommand, refreshCommand, restartCommand, restoreCommand, diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go new file mode 100644 index 000000000..92604e82f --- /dev/null +++ b/cmd/podman/containers_prune.go @@ -0,0 +1,74 @@ +package main + +import ( + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var ( + pruneContainersDescription = ` + podman container prune + + Removes all exited containers +` + + pruneContainersCommand = cli.Command{ + Name: "prune", + Usage: "Remove all stopped containers", + Description: pruneContainersDescription, + Action: pruneContainersCmd, + OnUsageError: usageErrorHandler, + } +) + +func pruneContainersCmd(c *cli.Context) error { + var ( + deleteFuncs []shared.ParallelWorkerInput + ) + + ctx := getContext() + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + filter := func(c *libpod.Container) bool { + state, _ := c.State() + if state == libpod.ContainerStateStopped || (state == libpod.ContainerStateExited && err == nil && c.PodID() == "") { + return true + } + return false + } + delContainers, err := runtime.GetContainers(filter) + if err != nil { + return err + } + if len(delContainers) < 1 { + return nil + } + for _, container := range delContainers { + con := container + f := func() error { + return runtime.RemoveContainer(ctx, con, c.Bool("force")) + } + + deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ + ContainerID: con.ID(), + ParallelFunc: f, + }) + } + maxWorkers := shared.Parallelize("rm") + if c.GlobalIsSet("max-workers") { + maxWorkers = c.GlobalInt("max-workers") + } + logrus.Debugf("Setting maximum workers to %d", maxWorkers) + + // Run the parallel funcs + deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs) + return printParallelOutput(deleteErrors, errCount) +} diff --git a/cmd/podman/image.go b/cmd/podman/image.go index 418b442e3..95af36df5 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -13,7 +13,7 @@ var ( inspectCommand, loadCommand, lsImagesCommand, - // pruneCommand, + pruneImagesCommand, pullCommand, pushCommand, rmImageCommand, diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go new file mode 100644 index 000000000..cb72a498f --- /dev/null +++ b/cmd/podman/images_prune.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + pruneImagesDescription = ` + podman image prune + + Removes all unnamed images from local storage +` + + pruneImagesCommand = cli.Command{ + Name: "prune", + Usage: "Remove unused images", + Description: pruneImagesDescription, + Action: pruneImagesCmd, + OnUsageError: usageErrorHandler, + } +) + +func pruneImagesCmd(c *cli.Context) error { + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + return shared.Prune(runtime.ImageRuntime()) +} diff --git a/cmd/podman/shared/prune.go b/cmd/podman/shared/prune.go new file mode 100644 index 000000000..90cfe4475 --- /dev/null +++ b/cmd/podman/shared/prune.go @@ -0,0 +1,24 @@ +package shared + +import ( + "fmt" + "github.com/pkg/errors" + + "github.com/containers/libpod/libpod/image" +) + +// Prune removes all unnamed and unused images from the local store +func Prune(ir *image.Runtime) error { + pruneImages, err := ir.GetPruneImages() + if err != nil { + return err + } + + for _, i := range pruneImages { + if err := i.Remove(true); err != nil { + return errors.Wrapf(err, "failed to remove %s", i.ID()) + } + fmt.Println(i.ID()) + } + return nil +} diff --git a/completions/bash/podman b/completions/bash/podman index 80ea53a74..b8b6b353e 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -871,6 +871,7 @@ _podman_container() { create diff exec + exists export inspect kill @@ -879,6 +880,7 @@ _podman_container() { mount pause port + prune refresh restart restore @@ -1210,11 +1212,13 @@ _podman_image() { " subcommands=" build + exists history import inspect load ls + prune pull push rm @@ -2228,6 +2232,26 @@ _podman_container_runlabel() { esac } +_podman_images_prune() { + local options_with_args=" + " + + local boolean_options=" + -h + --help + " +} + +_podman_container_prune() { + local options_with_args=" + " + + local boolean_options=" + -h + --help + " +} + _podman_container_exists() { local options_with_args=" " diff --git a/docs/podman-container-prune.1.md b/docs/podman-container-prune.1.md new file mode 100644 index 000000000..1f3ef1d41 --- /dev/null +++ b/docs/podman-container-prune.1.md @@ -0,0 +1,31 @@ +% PODMAN(1) Podman Man Pages +% Brent Baude +% December 2018 +# NAME +podman-container-prune - Remove all stopped containers + +# SYNOPSIS +**podman container prune** +[**-h**|**--help**] + +# DESCRIPTION +**podman container prune** removes all stopped containers from local storage. + +## Examples ## + +Remove all stopped containers from local storage +``` +$ sudo podman container prune +878392adf2e6c5c9bb1fc19b69d37d2e98c8abf9d539c0bce4b15b46bbcce471 +37664467fbe3618bf9479c34393ac29c02696675addf1750f9e346581636cde7 +ed0c6468b8e1cb641b4621d1fe30cb477e1fefc5c0bceb66feaf2f7cb50e5962 +6ac6c8f0067b7a4682e6b8e18902665b57d1a0e07e885d9abcd382232a543ccd +fff1c5b6c3631746055ec40598ce8ecaa4b82aef122f9e3a85b03b55c0d06c23 +602d343cd47e7cb3dfc808282a9900a3e4555747787ec6723bb68cedab8384d5 +``` + +## SEE ALSO +podman(1), podman-ps + +# HISTORY +December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/podman-container.1.md b/docs/podman-container.1.md index aa5dfa82c..3675d9719 100644 --- a/docs/podman-container.1.md +++ b/docs/podman-container.1.md @@ -29,6 +29,7 @@ The container command allows you to manage containers | mount | [podman-mount(1)](podman-mount.1.md) | Mount a working container's root filesystem. | | pause | [podman-pause(1)](podman-pause.1.md) | Pause one or more containers. | | port | [podman-port(1)](podman-port.1.md) | List port mappings for the container. | +| prune | [podman-container-prune(1)](podman-container-prune.1.md) | Remove all stopped containers from local storage | | refresh | [podman-refresh(1)](podman-container-refresh.1.md) | Refresh the state of all containers | | restart | [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. | | restore | [podman-container-restore(1)](podman-container-restore.1.md) | Restores one or more containers from a checkpoint. | diff --git a/docs/podman-image-prune.1.md b/docs/podman-image-prune.1.md new file mode 100644 index 000000000..db76b26e0 --- /dev/null +++ b/docs/podman-image-prune.1.md @@ -0,0 +1,32 @@ +% PODMAN(1) Podman Man Pages +% Brent Baude +% December 2018 +# NAME +podman-image-prune - Remove all unused images + +# SYNOPSIS +**podman image prune** +[**-h**|**--help**] + +# DESCRIPTION +**podman image prune** removes all unused images from local storage. An unused image +is defined as an image that does not have any containers based on it. + +## Examples ## + +Remove all unused images from local storage +``` +$ sudo podman image prune +f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e +324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907 +6125002719feb1ddf3030acab1df6156da7ce0e78e571e9b6e9c250424d6220c +91e732da5657264c6f4641b8d0c4001c218ae6c1adb9dcef33ad00cafd37d8b6 +e4e5109420323221f170627c138817770fb64832da7d8fe2babd863148287fca +77a57fa8285e9656dbb7b23d9efa837a106957409ddd702f995605af27a45ebe +``` + +## SEE ALSO +podman(1), podman-images + +# HISTORY +December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/podman-image.1.md b/docs/podman-image.1.md index 446f8667d..8b812af11 100644 --- a/docs/podman-image.1.md +++ b/docs/podman-image.1.md @@ -21,6 +21,7 @@ The image command allows you to manage images | load | [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. | | ls | [podman-images(1)](podman-images.1.md) | Prints out information about images. | | pull | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | +| prune| [podman-container-prune(1)](podman-container-prune.1.md) | Removed all unused images from the local store | | push | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | | rm | [podman-rm(1)](podman-rmi.1.md) | Removes one or more locally stored images. | | save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | diff --git a/docs/podman-rmi.1.md b/docs/podman-rmi.1.md index f035897ee..9c080c9f1 100644 --- a/docs/podman-rmi.1.md +++ b/docs/podman-rmi.1.md @@ -19,15 +19,25 @@ Remove all images in the local storage. This option will cause podman to remove all containers that are using the image before removing the image from the system. -## EXAMPLE - -podman rmi imageID +Remove an image by its short ID +``` +podman rmi c0ed59d05ff7 +``` +Remove an image and its associated containers. +``` podman rmi --force imageID +```` -podman rmi imageID1 imageID2 imageID3 +Remove multiple images by their shortened IDs. +``` +podman rmi c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7 +``` +Remove all images and containers. +``` podman rmi -a -f +``` ## SEE ALSO podman(1) diff --git a/libpod/image/prune.go b/libpod/image/prune.go new file mode 100644 index 000000000..6a1f160d5 --- /dev/null +++ b/libpod/image/prune.go @@ -0,0 +1,26 @@ +package image + +// GetPruneImages returns a slice of images that have no names/unused +func (ir *Runtime) GetPruneImages() ([]*Image, error) { + var ( + unamedImages []*Image + ) + allImages, err := ir.GetImages() + if err != nil { + return nil, err + } + for _, i := range allImages { + if len(i.Names()) == 0 { + unamedImages = append(unamedImages, i) + continue + } + containers, err := i.Containers() + if err != nil { + return nil, err + } + if len(containers) < 1 { + unamedImages = append(unamedImages, i) + } + } + return unamedImages, nil +} diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go new file mode 100644 index 000000000..6679a676c --- /dev/null +++ b/test/e2e/prune_test.go @@ -0,0 +1,88 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var pruneImage = ` +FROM alpine:latest +LABEL RUN podman --version +RUN apk update +RUN apk add bash` + +var _ = Describe("Podman rm", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + }) + + It("podman container prune containers", func() { + top := podmanTest.RunTopContainer("") + top.WaitWithDefaultTimeout() + Expect(top.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + prune := podmanTest.Podman([]string{"container", "prune"}) + prune.WaitWithDefaultTimeout() + Expect(prune.ExitCode()).To(Equal(0)) + + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + }) + + It("podman image prune none images", func() { + podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") + + none := podmanTest.Podman([]string{"images", "-a"}) + none.WaitWithDefaultTimeout() + Expect(none.ExitCode()).To(Equal(0)) + hasNone, _ := none.GrepString("<none>") + Expect(hasNone).To(BeTrue()) + + prune := podmanTest.Podman([]string{"image", "prune"}) + prune.WaitWithDefaultTimeout() + Expect(prune.ExitCode()).To(Equal(0)) + + after := podmanTest.Podman([]string{"images", "-a"}) + after.WaitWithDefaultTimeout() + Expect(none.ExitCode()).To(Equal(0)) + hasNoneAfter, _ := after.GrepString("<none>") + Expect(hasNoneAfter).To(BeFalse()) + }) + + It("podman image prune unused images", func() { + prune := podmanTest.Podman([]string{"image", "prune"}) + prune.WaitWithDefaultTimeout() + Expect(prune.ExitCode()).To(Equal(0)) + + images := podmanTest.Podman([]string{"images", "-a"}) + images.WaitWithDefaultTimeout() + // all images are unused, so they all should be deleted! + Expect(len(images.OutputToStringArray())).To(Equal(0)) + }) + +}) |