mirror of
https://github.com/containers/podman.git
synced 2025-07-01 00:01:02 +08:00
Merge pull request #5548 from kunalkushwaha/image-prune
image prune skips images with child images.
This commit is contained in:
@ -11,6 +11,8 @@ podman-image-prune - Remove all unused images from the local store
|
|||||||
you can delete all unused images. Unused images are dangling images as well as any image that
|
you can delete all unused images. Unused images are dangling images as well as any image that
|
||||||
does not have any containers based on it.
|
does not have any containers based on it.
|
||||||
|
|
||||||
|
The image prune command does not prune cache images that only use layers that are necessary for other images.
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
**--all**, **-a**
|
**--all**, **-a**
|
||||||
|
|
||||||
|
@ -32,10 +32,10 @@ import (
|
|||||||
"github.com/containers/libpod/pkg/registries"
|
"github.com/containers/libpod/pkg/registries"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -847,6 +847,26 @@ func (i *Image) Dangling() bool {
|
|||||||
return len(i.Names()) == 0
|
return len(i.Names()) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intermediate returns true if the image is cache or intermediate image.
|
||||||
|
// Cache image has parent and child.
|
||||||
|
func (i *Image) Intermediate(ctx context.Context) (bool, error) {
|
||||||
|
parent, err := i.IsParent(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !parent {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
img, err := i.GetParent(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if img != nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Labels returns the image's labels
|
// Labels returns the image's labels
|
||||||
func (i *Image) Labels(ctx context.Context) (map[string]string, error) {
|
func (i *Image) Labels(ctx context.Context) (map[string]string, error) {
|
||||||
imgInspect, err := i.imageInspectInfo(ctx)
|
imgInspect, err := i.imageInspectInfo(ctx)
|
||||||
|
@ -57,7 +57,7 @@ func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPruneImages returns a slice of images that have no names/unused
|
// GetPruneImages returns a slice of images that have no names/unused
|
||||||
func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image, error) {
|
func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []ImageFilter) ([]*Image, error) {
|
||||||
var (
|
var (
|
||||||
pruneImages []*Image
|
pruneImages []*Image
|
||||||
)
|
)
|
||||||
@ -74,10 +74,6 @@ func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(i.Names()) == 0 {
|
|
||||||
pruneImages = append(pruneImages, i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if all {
|
if all {
|
||||||
containers, err := i.Containers()
|
containers, err := i.Containers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -85,8 +81,22 @@ func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image
|
|||||||
}
|
}
|
||||||
if len(containers) < 1 {
|
if len(containers) < 1 {
|
||||||
pruneImages = append(pruneImages, i)
|
pruneImages = append(pruneImages, i)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//skip the cache or intermediate images
|
||||||
|
intermediate, err := i.Intermediate(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if intermediate {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.Dangling() {
|
||||||
|
pruneImages = append(pruneImages, i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return pruneImages, nil
|
return pruneImages, nil
|
||||||
}
|
}
|
||||||
@ -111,7 +121,7 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) (
|
|||||||
filterFuncs = append(filterFuncs, generatedFunc)
|
filterFuncs = append(filterFuncs, generatedFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneImages, err := ir.GetPruneImages(all, filterFuncs)
|
pruneImages, err := ir.GetPruneImages(ctx, all, filterFuncs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to get images to prune")
|
return nil, errors.Wrap(err, "unable to get images to prune")
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ var _ = Describe("Podman prune", func() {
|
|||||||
Expect(podmanTest.NumberOfContainers()).To(Equal(0))
|
Expect(podmanTest.NumberOfContainers()).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman image prune none images", func() {
|
It("podman image prune skip cache images", func() {
|
||||||
SkipIfRemote()
|
SkipIfRemote()
|
||||||
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
|
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
|
||||||
|
|
||||||
@ -105,10 +105,35 @@ var _ = Describe("Podman prune", func() {
|
|||||||
after.WaitWithDefaultTimeout()
|
after.WaitWithDefaultTimeout()
|
||||||
Expect(none.ExitCode()).To(Equal(0))
|
Expect(none.ExitCode()).To(Equal(0))
|
||||||
hasNoneAfter, _ := after.GrepString("<none>")
|
hasNoneAfter, _ := after.GrepString("<none>")
|
||||||
Expect(hasNoneAfter).To(BeFalse())
|
Expect(hasNoneAfter).To(BeTrue())
|
||||||
Expect(len(after.OutputToStringArray()) > 1).To(BeTrue())
|
Expect(len(after.OutputToStringArray()) > 1).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman image prune dangling images", func() {
|
||||||
|
SkipIfRemote()
|
||||||
|
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
|
||||||
|
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
|
||||||
|
|
||||||
|
none := podmanTest.Podman([]string{"images", "-a"})
|
||||||
|
none.WaitWithDefaultTimeout()
|
||||||
|
Expect(none.ExitCode()).To(Equal(0))
|
||||||
|
hasNone, result := none.GrepString("<none>")
|
||||||
|
Expect(len(result)).To(Equal(2))
|
||||||
|
Expect(hasNone).To(BeTrue())
|
||||||
|
|
||||||
|
prune := podmanTest.Podman([]string{"image", "prune", "-f"})
|
||||||
|
prune.WaitWithDefaultTimeout()
|
||||||
|
Expect(prune.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
after := podmanTest.Podman([]string{"images", "-a"})
|
||||||
|
after.WaitWithDefaultTimeout()
|
||||||
|
Expect(none.ExitCode()).To(Equal(0))
|
||||||
|
hasNoneAfter, result := none.GrepString("<none>")
|
||||||
|
Expect(hasNoneAfter).To(BeTrue())
|
||||||
|
Expect(len(after.OutputToStringArray()) > 1).To(BeTrue())
|
||||||
|
Expect(len(result) > 0).To(BeTrue())
|
||||||
|
})
|
||||||
|
|
||||||
It("podman image prune unused images", func() {
|
It("podman image prune unused images", func() {
|
||||||
podmanTest.RestoreAllArtifacts()
|
podmanTest.RestoreAllArtifacts()
|
||||||
prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-af"})
|
prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-af"})
|
||||||
|
Reference in New Issue
Block a user