Merge pull request #5548 from kunalkushwaha/image-prune

image prune skips images with child images.
This commit is contained in:
OpenShift Merge Robot
2020-04-17 15:19:45 -04:00
committed by GitHub
4 changed files with 67 additions and 10 deletions

View File

@ -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**

View File

@ -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)

View File

@ -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")
} }

View File

@ -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"})