summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2018-12-01 09:49:46 -0600
committerbaude <bbaude@redhat.com>2018-12-05 19:57:54 -0600
commite037427035dc57e536478362fc19e30a400bc327 (patch)
tree3fb14dc437f58f29e197a43aaefa0e0254251f43
parent75b19ca8abe1957f3c48035767960a6b20c10519 (diff)
downloadpodman-e037427035dc57e536478362fc19e30a400bc327.tar.gz
podman-e037427035dc57e536478362fc19e30a400bc327.tar.bz2
podman-e037427035dc57e536478362fc19e30a400bc327.zip
Add ability to prune containers and images
Allow user to prune unused/unnamed images, the layer images from building, via podman rmi --prune. Allow user to prune stopped/exiuted containers via podman rm --prune. This should resolve #1910 Signed-off-by: baude <bbaude@redhat.com>
-rw-r--r--cmd/podman/container.go2
-rw-r--r--cmd/podman/containers_prune.go74
-rw-r--r--cmd/podman/image.go2
-rw-r--r--cmd/podman/images_prune.go34
-rw-r--r--cmd/podman/shared/prune.go24
-rw-r--r--completions/bash/podman24
-rw-r--r--docs/podman-container-prune.1.md31
-rw-r--r--docs/podman-container.1.md1
-rw-r--r--docs/podman-image-prune.1.md32
-rw-r--r--docs/podman-image.1.md1
-rw-r--r--docs/podman-rmi.1.md18
-rw-r--r--libpod/image/prune.go26
-rw-r--r--test/e2e/prune_test.go88
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 9518cfa22..79ab91410 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
@@ -2227,6 +2231,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))
+ })
+
+})