mirror of
https://github.com/containers/podman.git
synced 2025-08-02 17:22:30 +08:00
Merge pull request #1912 from baude/prune
Add ability to prune containers and images
This commit is contained in:
@ -22,7 +22,7 @@ var (
|
|||||||
mountCommand,
|
mountCommand,
|
||||||
pauseCommand,
|
pauseCommand,
|
||||||
portCommand,
|
portCommand,
|
||||||
// pruneCommand,
|
pruneContainersCommand,
|
||||||
refreshCommand,
|
refreshCommand,
|
||||||
restartCommand,
|
restartCommand,
|
||||||
restoreCommand,
|
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,
|
inspectCommand,
|
||||||
loadCommand,
|
loadCommand,
|
||||||
lsImagesCommand,
|
lsImagesCommand,
|
||||||
// pruneCommand,
|
pruneImagesCommand,
|
||||||
pullCommand,
|
pullCommand,
|
||||||
pushCommand,
|
pushCommand,
|
||||||
rmImageCommand,
|
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
|
create
|
||||||
diff
|
diff
|
||||||
exec
|
exec
|
||||||
|
exists
|
||||||
export
|
export
|
||||||
inspect
|
inspect
|
||||||
kill
|
kill
|
||||||
@ -879,6 +880,7 @@ _podman_container() {
|
|||||||
mount
|
mount
|
||||||
pause
|
pause
|
||||||
port
|
port
|
||||||
|
prune
|
||||||
refresh
|
refresh
|
||||||
restart
|
restart
|
||||||
restore
|
restore
|
||||||
@ -1210,11 +1212,13 @@ _podman_image() {
|
|||||||
"
|
"
|
||||||
subcommands="
|
subcommands="
|
||||||
build
|
build
|
||||||
|
exists
|
||||||
history
|
history
|
||||||
import
|
import
|
||||||
inspect
|
inspect
|
||||||
load
|
load
|
||||||
ls
|
ls
|
||||||
|
prune
|
||||||
pull
|
pull
|
||||||
push
|
push
|
||||||
rm
|
rm
|
||||||
@ -2228,6 +2232,26 @@ _podman_container_runlabel() {
|
|||||||
esac
|
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() {
|
_podman_container_exists() {
|
||||||
local options_with_args="
|
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. |
|
| 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. |
|
| 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. |
|
| 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 |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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.
|
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 --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
|
podman rmi -a -f
|
||||||
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
podman(1)
|
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))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
Reference in New Issue
Block a user