From 9c25df0fa201279f78d555c91ae000cc4f9b3036 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 21 Feb 2018 17:10:18 -0500 Subject: [PATCH] Move ImagePrune Signed-off-by: Daniel Nephin --- daemon/image_prune.go | 168 ++++++++++++++++++++++++++++++++++++++++++ daemon/prune.go | 158 +-------------------------------------- 2 files changed, 169 insertions(+), 157 deletions(-) create mode 100644 daemon/image_prune.go diff --git a/daemon/image_prune.go b/daemon/image_prune.go new file mode 100644 index 0000000000..8a1c2cd225 --- /dev/null +++ b/daemon/image_prune.go @@ -0,0 +1,168 @@ +package daemon + +import ( + "sync/atomic" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" + digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" +) + +var imagesAcceptedFilters = map[string]bool{ + "dangling": true, + "label": true, + "label!": true, + "until": true, +} + +// ImagesPrune removes unused images +func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) { + if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { + return nil, errPruneRunning + } + defer atomic.StoreInt32(&daemon.pruneRunning, 0) + + // make sure that only accepted filters have been received + err := pruneFilters.Validate(imagesAcceptedFilters) + if err != nil { + return nil, err + } + + rep := &types.ImagesPruneReport{} + + danglingOnly := true + if pruneFilters.Contains("dangling") { + if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") { + danglingOnly = false + } else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") { + return nil, invalidFilter{"dangling", pruneFilters.Get("dangling")} + } + } + + until, err := getUntilFromPruneFilters(pruneFilters) + if err != nil { + return nil, err + } + + var allImages map[image.ID]*image.Image + if danglingOnly { + allImages = daemon.imageStore.Heads() + } else { + allImages = daemon.imageStore.Map() + } + + // Filter intermediary images and get their unique size + allLayers := make(map[layer.ChainID]layer.Layer) + for _, ls := range daemon.layerStores { + for k, v := range ls.Map() { + allLayers[k] = v + } + } + topImages := map[image.ID]*image.Image{} + for id, img := range allImages { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + dgst := digest.Digest(id) + if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { + continue + } + if !until.IsZero() && img.Created.After(until) { + continue + } + if img.Config != nil && !matchLabels(pruneFilters, img.Config.Labels) { + continue + } + topImages[id] = img + } + } + + canceled := false +deleteImagesLoop: + for id := range topImages { + select { + case <-ctx.Done(): + // we still want to calculate freed size and return the data + canceled = true + break deleteImagesLoop + default: + } + + deletedImages := []types.ImageDeleteResponseItem{} + refs := daemon.referenceStore.References(id.Digest()) + if len(refs) > 0 { + shouldDelete := !danglingOnly + if !shouldDelete { + hasTag := false + for _, ref := range refs { + if _, ok := ref.(reference.NamedTagged); ok { + hasTag = true + break + } + } + + // Only delete if it's untagged (i.e. repo:) + shouldDelete = !hasTag + } + + if shouldDelete { + for _, ref := range refs { + imgDel, err := daemon.ImageDelete(ref.String(), false, true) + if imageDeleteFailed(ref.String(), err) { + continue + } + deletedImages = append(deletedImages, imgDel...) + } + } + } else { + hex := id.Digest().Hex() + imgDel, err := daemon.ImageDelete(hex, false, true) + if imageDeleteFailed(hex, err) { + continue + } + deletedImages = append(deletedImages, imgDel...) + } + + rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...) + } + + // Compute how much space was freed + for _, d := range rep.ImagesDeleted { + if d.Deleted != "" { + chid := layer.ChainID(d.Deleted) + if l, ok := allLayers[chid]; ok { + diffSize, err := l.DiffSize() + if err != nil { + logrus.Warnf("failed to get layer %s size: %v", chid, err) + continue + } + rep.SpaceReclaimed += uint64(diffSize) + } + } + } + + if canceled { + logrus.Debugf("ImagesPrune operation cancelled: %#v", *rep) + } + + return rep, nil +} + +func imageDeleteFailed(ref string, err error) bool { + switch { + case err == nil: + return false + case errdefs.IsConflict(err): + return true + default: + logrus.Warnf("failed to prune image %s: %v", ref, err) + return true + } +} diff --git a/daemon/prune.go b/daemon/prune.go index eee9e106bd..f16e448ba8 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -6,18 +6,13 @@ import ( "sync/atomic" "time" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" timetypes "github.com/docker/docker/api/types/time" - "github.com/docker/docker/errdefs" - "github.com/docker/docker/image" - "github.com/docker/docker/layer" "github.com/docker/docker/pkg/directory" "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" "github.com/docker/libnetwork" - digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -36,12 +31,7 @@ var ( "label": true, "label!": true, } - imagesAcceptedFilters = map[string]bool{ - "dangling": true, - "label": true, - "label!": true, - "until": true, - } + networksAcceptedFilters = map[string]bool{ "label": true, "label!": true, @@ -159,152 +149,6 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg return rep, err } -// ImagesPrune removes unused images -func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) { - if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { - return nil, errPruneRunning - } - defer atomic.StoreInt32(&daemon.pruneRunning, 0) - - // make sure that only accepted filters have been received - err := pruneFilters.Validate(imagesAcceptedFilters) - if err != nil { - return nil, err - } - - rep := &types.ImagesPruneReport{} - - danglingOnly := true - if pruneFilters.Contains("dangling") { - if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") { - danglingOnly = false - } else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") { - return nil, invalidFilter{"dangling", pruneFilters.Get("dangling")} - } - } - - until, err := getUntilFromPruneFilters(pruneFilters) - if err != nil { - return nil, err - } - - var allImages map[image.ID]*image.Image - if danglingOnly { - allImages = daemon.imageStore.Heads() - } else { - allImages = daemon.imageStore.Map() - } - - // Filter intermediary images and get their unique size - allLayers := make(map[layer.ChainID]layer.Layer) - for _, ls := range daemon.layerStores { - for k, v := range ls.Map() { - allLayers[k] = v - } - } - topImages := map[image.ID]*image.Image{} - for id, img := range allImages { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - dgst := digest.Digest(id) - if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { - continue - } - if !until.IsZero() && img.Created.After(until) { - continue - } - if img.Config != nil && !matchLabels(pruneFilters, img.Config.Labels) { - continue - } - topImages[id] = img - } - } - - canceled := false -deleteImagesLoop: - for id := range topImages { - select { - case <-ctx.Done(): - // we still want to calculate freed size and return the data - canceled = true - break deleteImagesLoop - default: - } - - deletedImages := []types.ImageDeleteResponseItem{} - refs := daemon.referenceStore.References(id.Digest()) - if len(refs) > 0 { - shouldDelete := !danglingOnly - if !shouldDelete { - hasTag := false - for _, ref := range refs { - if _, ok := ref.(reference.NamedTagged); ok { - hasTag = true - break - } - } - - // Only delete if it's untagged (i.e. repo:) - shouldDelete = !hasTag - } - - if shouldDelete { - for _, ref := range refs { - imgDel, err := daemon.ImageDelete(ref.String(), false, true) - if imageDeleteFailed(ref.String(), err) { - continue - } - deletedImages = append(deletedImages, imgDel...) - } - } - } else { - hex := id.Digest().Hex() - imgDel, err := daemon.ImageDelete(hex, false, true) - if imageDeleteFailed(hex, err) { - continue - } - deletedImages = append(deletedImages, imgDel...) - } - - rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...) - } - - // Compute how much space was freed - for _, d := range rep.ImagesDeleted { - if d.Deleted != "" { - chid := layer.ChainID(d.Deleted) - if l, ok := allLayers[chid]; ok { - diffSize, err := l.DiffSize() - if err != nil { - logrus.Warnf("failed to get layer %s size: %v", chid, err) - continue - } - rep.SpaceReclaimed += uint64(diffSize) - } - } - } - - if canceled { - logrus.Debugf("ImagesPrune operation cancelled: %#v", *rep) - } - - return rep, nil -} - -func imageDeleteFailed(ref string, err error) bool { - switch { - case err == nil: - return false - case errdefs.IsConflict(err): - return true - default: - logrus.Warnf("failed to prune image %s: %v", ref, err) - return true - } -} - // localNetworksPrune removes unused local networks func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport { rep := &types.NetworksPruneReport{}