From 9dc9f5cf4cc5d0c688263d5532ece0d3079c6d4a Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Thu, 19 Mar 2020 11:39:39 +0900 Subject: image prune skips images with child images. While image build process, intermediate images are created. These images are also used as cache images, used in rebuilding same images. This fix the deletion of cache images. Signed-off-by: Kunal Kushwaha --- libpod/image/image.go | 24 ++++++++++++++++++++++-- libpod/image/prune.go | 22 ++++++++++++++++------ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/libpod/image/image.go b/libpod/image/image.go index 80cc6f15a..7198a42a3 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -32,10 +32,10 @@ import ( "github.com/containers/libpod/pkg/registries" "github.com/containers/libpod/pkg/util" "github.com/containers/storage" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" imgspecv1 "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/sirupsen/logrus" ) @@ -847,6 +847,26 @@ func (i *Image) Dangling() bool { 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 func (i *Image) Labels(ctx context.Context) (map[string]string, error) { imgInspect, err := i.imageInspectInfo(ctx) diff --git a/libpod/image/prune.go b/libpod/image/prune.go index 3afff22af..3b4ea74c4 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -57,7 +57,7 @@ func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) { } // 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 ( 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 { containers, err := i.Containers() if err != nil { @@ -85,8 +81,22 @@ func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image } if len(containers) < 1 { 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 } @@ -111,7 +121,7 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( filterFuncs = append(filterFuncs, generatedFunc) } - pruneImages, err := ir.GetPruneImages(all, filterFuncs) + pruneImages, err := ir.GetPruneImages(ctx, all, filterFuncs) if err != nil { return nil, errors.Wrap(err, "unable to get images to prune") } -- cgit v1.2.3-54-g00ecf From 1e8e4bb331c7b42bf17e75748a60b91850b2d5fb Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Thu, 19 Mar 2020 11:51:25 +0900 Subject: note for skipping cache image added. Signed-off-by: Kunal Kushwaha --- docs/source/markdown/podman-image-prune.1.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/markdown/podman-image-prune.1.md b/docs/source/markdown/podman-image-prune.1.md index c76e9bd3f..cf9e50366 100644 --- a/docs/source/markdown/podman-image-prune.1.md +++ b/docs/source/markdown/podman-image-prune.1.md @@ -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 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 **--all**, **-a** -- cgit v1.2.3-54-g00ecf From d8321363498c349647409dea1b6e15f8cf0ac17a Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Fri, 20 Mar 2020 16:22:50 +0900 Subject: test case added for image prune cache image test case added for skipping cache images and fixed condition in test case for prune dangling image Signed-off-by: Kunal Kushwaha --- test/e2e/prune_test.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 672b0e103..83a8d3b18 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -87,7 +87,7 @@ var _ = Describe("Podman prune", func() { Expect(podmanTest.NumberOfContainers()).To(Equal(0)) }) - It("podman image prune none images", func() { + It("podman image prune skip cache images", func() { SkipIfRemote() podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") @@ -105,10 +105,35 @@ var _ = Describe("Podman prune", func() { after.WaitWithDefaultTimeout() Expect(none.ExitCode()).To(Equal(0)) hasNoneAfter, _ := after.GrepString("") - Expect(hasNoneAfter).To(BeFalse()) + Expect(hasNoneAfter).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("") + 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("") + Expect(hasNoneAfter).To(BeTrue()) + Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) + Expect(len(result) > 0).To(BeTrue()) + }) + It("podman image prune unused images", func() { podmanTest.RestoreAllArtifacts() prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-af"}) -- cgit v1.2.3-54-g00ecf