mirror of
https://github.com/containers/podman
synced 2024-10-20 01:03:51 +00:00
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>
This commit is contained in:
parent
75b19ca8ab
commit
e037427035
|
@ -22,7 +22,7 @@ var (
|
|||
mountCommand,
|
||||
pauseCommand,
|
||||
portCommand,
|
||||
// pruneCommand,
|
||||
pruneContainersCommand,
|
||||
refreshCommand,
|
||||
restartCommand,
|
||||
restoreCommand,
|
||||
|
|
74
cmd/podman/containers_prune.go
Normal file
74
cmd/podman/containers_prune.go
Normal file
|
@ -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)
|
||||
}
|
|
@ -13,7 +13,7 @@ var (
|
|||
inspectCommand,
|
||||
loadCommand,
|
||||
lsImagesCommand,
|
||||
// pruneCommand,
|
||||
pruneImagesCommand,
|
||||
pullCommand,
|
||||
pushCommand,
|
||||
rmImageCommand,
|
||||
|
|
34
cmd/podman/images_prune.go
Normal file
34
cmd/podman/images_prune.go
Normal file
|
@ -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())
|
||||
}
|
24
cmd/podman/shared/prune.go
Normal file
24
cmd/podman/shared/prune.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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="
|
||||
"
|
||||
|
|
31
docs/podman-container-prune.1.md
Normal file
31
docs/podman-container-prune.1.md
Normal file
|
@ -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)
|
|
@ -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. |
|
||||
|
|
32
docs/podman-image-prune.1.md
Normal file
32
docs/podman-image-prune.1.md
Normal file
|
@ -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)
|
|
@ -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. |
|
||||
|
|
|
@ -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)
|
||||
|
|
26
libpod/image/prune.go
Normal file
26
libpod/image/prune.go
Normal file
|
@ -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
|
||||
}
|
88
test/e2e/prune_test.go
Normal file
88
test/e2e/prune_test.go
Normal file
|
@ -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))
|
||||
})
|
||||
|
||||
})
|
Loading…
Reference in a new issue