Browse code

Move ImagePrune

Signed-off-by: Daniel Nephin <dnephin@docker.com>

Daniel Nephin authored on 2018/02/22 07:10:18
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,168 @@
0
+package daemon
1
+
2
+import (
3
+	"sync/atomic"
4
+
5
+	"github.com/docker/distribution/reference"
6
+	"github.com/docker/docker/api/types"
7
+	"github.com/docker/docker/api/types/filters"
8
+	"github.com/docker/docker/errdefs"
9
+	"github.com/docker/docker/image"
10
+	"github.com/docker/docker/layer"
11
+	digest "github.com/opencontainers/go-digest"
12
+	"github.com/sirupsen/logrus"
13
+	"golang.org/x/net/context"
14
+)
15
+
16
+var imagesAcceptedFilters = map[string]bool{
17
+	"dangling": true,
18
+	"label":    true,
19
+	"label!":   true,
20
+	"until":    true,
21
+}
22
+
23
+// ImagesPrune removes unused images
24
+func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
25
+	if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
26
+		return nil, errPruneRunning
27
+	}
28
+	defer atomic.StoreInt32(&daemon.pruneRunning, 0)
29
+
30
+	// make sure that only accepted filters have been received
31
+	err := pruneFilters.Validate(imagesAcceptedFilters)
32
+	if err != nil {
33
+		return nil, err
34
+	}
35
+
36
+	rep := &types.ImagesPruneReport{}
37
+
38
+	danglingOnly := true
39
+	if pruneFilters.Contains("dangling") {
40
+		if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") {
41
+			danglingOnly = false
42
+		} else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") {
43
+			return nil, invalidFilter{"dangling", pruneFilters.Get("dangling")}
44
+		}
45
+	}
46
+
47
+	until, err := getUntilFromPruneFilters(pruneFilters)
48
+	if err != nil {
49
+		return nil, err
50
+	}
51
+
52
+	var allImages map[image.ID]*image.Image
53
+	if danglingOnly {
54
+		allImages = daemon.imageStore.Heads()
55
+	} else {
56
+		allImages = daemon.imageStore.Map()
57
+	}
58
+
59
+	// Filter intermediary images and get their unique size
60
+	allLayers := make(map[layer.ChainID]layer.Layer)
61
+	for _, ls := range daemon.layerStores {
62
+		for k, v := range ls.Map() {
63
+			allLayers[k] = v
64
+		}
65
+	}
66
+	topImages := map[image.ID]*image.Image{}
67
+	for id, img := range allImages {
68
+		select {
69
+		case <-ctx.Done():
70
+			return nil, ctx.Err()
71
+		default:
72
+			dgst := digest.Digest(id)
73
+			if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
74
+				continue
75
+			}
76
+			if !until.IsZero() && img.Created.After(until) {
77
+				continue
78
+			}
79
+			if img.Config != nil && !matchLabels(pruneFilters, img.Config.Labels) {
80
+				continue
81
+			}
82
+			topImages[id] = img
83
+		}
84
+	}
85
+
86
+	canceled := false
87
+deleteImagesLoop:
88
+	for id := range topImages {
89
+		select {
90
+		case <-ctx.Done():
91
+			// we still want to calculate freed size and return the data
92
+			canceled = true
93
+			break deleteImagesLoop
94
+		default:
95
+		}
96
+
97
+		deletedImages := []types.ImageDeleteResponseItem{}
98
+		refs := daemon.referenceStore.References(id.Digest())
99
+		if len(refs) > 0 {
100
+			shouldDelete := !danglingOnly
101
+			if !shouldDelete {
102
+				hasTag := false
103
+				for _, ref := range refs {
104
+					if _, ok := ref.(reference.NamedTagged); ok {
105
+						hasTag = true
106
+						break
107
+					}
108
+				}
109
+
110
+				// Only delete if it's untagged (i.e. repo:<none>)
111
+				shouldDelete = !hasTag
112
+			}
113
+
114
+			if shouldDelete {
115
+				for _, ref := range refs {
116
+					imgDel, err := daemon.ImageDelete(ref.String(), false, true)
117
+					if imageDeleteFailed(ref.String(), err) {
118
+						continue
119
+					}
120
+					deletedImages = append(deletedImages, imgDel...)
121
+				}
122
+			}
123
+		} else {
124
+			hex := id.Digest().Hex()
125
+			imgDel, err := daemon.ImageDelete(hex, false, true)
126
+			if imageDeleteFailed(hex, err) {
127
+				continue
128
+			}
129
+			deletedImages = append(deletedImages, imgDel...)
130
+		}
131
+
132
+		rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...)
133
+	}
134
+
135
+	// Compute how much space was freed
136
+	for _, d := range rep.ImagesDeleted {
137
+		if d.Deleted != "" {
138
+			chid := layer.ChainID(d.Deleted)
139
+			if l, ok := allLayers[chid]; ok {
140
+				diffSize, err := l.DiffSize()
141
+				if err != nil {
142
+					logrus.Warnf("failed to get layer %s size: %v", chid, err)
143
+					continue
144
+				}
145
+				rep.SpaceReclaimed += uint64(diffSize)
146
+			}
147
+		}
148
+	}
149
+
150
+	if canceled {
151
+		logrus.Debugf("ImagesPrune operation cancelled: %#v", *rep)
152
+	}
153
+
154
+	return rep, nil
155
+}
156
+
157
+func imageDeleteFailed(ref string, err error) bool {
158
+	switch {
159
+	case err == nil:
160
+		return false
161
+	case errdefs.IsConflict(err):
162
+		return true
163
+	default:
164
+		logrus.Warnf("failed to prune image %s: %v", ref, err)
165
+		return true
166
+	}
167
+}
... ...
@@ -6,18 +6,13 @@ import (
6 6
 	"sync/atomic"
7 7
 	"time"
8 8
 
9
-	"github.com/docker/distribution/reference"
10 9
 	"github.com/docker/docker/api/types"
11 10
 	"github.com/docker/docker/api/types/filters"
12 11
 	timetypes "github.com/docker/docker/api/types/time"
13
-	"github.com/docker/docker/errdefs"
14
-	"github.com/docker/docker/image"
15
-	"github.com/docker/docker/layer"
16 12
 	"github.com/docker/docker/pkg/directory"
17 13
 	"github.com/docker/docker/runconfig"
18 14
 	"github.com/docker/docker/volume"
19 15
 	"github.com/docker/libnetwork"
20
-	digest "github.com/opencontainers/go-digest"
21 16
 	"github.com/sirupsen/logrus"
22 17
 	"golang.org/x/net/context"
23 18
 )
... ...
@@ -36,12 +31,7 @@ var (
36 36
 		"label":  true,
37 37
 		"label!": true,
38 38
 	}
39
-	imagesAcceptedFilters = map[string]bool{
40
-		"dangling": true,
41
-		"label":    true,
42
-		"label!":   true,
43
-		"until":    true,
44
-	}
39
+
45 40
 	networksAcceptedFilters = map[string]bool{
46 41
 		"label":  true,
47 42
 		"label!": true,
... ...
@@ -159,152 +149,6 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg
159 159
 	return rep, err
160 160
 }
161 161
 
162
-// ImagesPrune removes unused images
163
-func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
164
-	if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
165
-		return nil, errPruneRunning
166
-	}
167
-	defer atomic.StoreInt32(&daemon.pruneRunning, 0)
168
-
169
-	// make sure that only accepted filters have been received
170
-	err := pruneFilters.Validate(imagesAcceptedFilters)
171
-	if err != nil {
172
-		return nil, err
173
-	}
174
-
175
-	rep := &types.ImagesPruneReport{}
176
-
177
-	danglingOnly := true
178
-	if pruneFilters.Contains("dangling") {
179
-		if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") {
180
-			danglingOnly = false
181
-		} else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") {
182
-			return nil, invalidFilter{"dangling", pruneFilters.Get("dangling")}
183
-		}
184
-	}
185
-
186
-	until, err := getUntilFromPruneFilters(pruneFilters)
187
-	if err != nil {
188
-		return nil, err
189
-	}
190
-
191
-	var allImages map[image.ID]*image.Image
192
-	if danglingOnly {
193
-		allImages = daemon.imageStore.Heads()
194
-	} else {
195
-		allImages = daemon.imageStore.Map()
196
-	}
197
-
198
-	// Filter intermediary images and get their unique size
199
-	allLayers := make(map[layer.ChainID]layer.Layer)
200
-	for _, ls := range daemon.layerStores {
201
-		for k, v := range ls.Map() {
202
-			allLayers[k] = v
203
-		}
204
-	}
205
-	topImages := map[image.ID]*image.Image{}
206
-	for id, img := range allImages {
207
-		select {
208
-		case <-ctx.Done():
209
-			return nil, ctx.Err()
210
-		default:
211
-			dgst := digest.Digest(id)
212
-			if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
213
-				continue
214
-			}
215
-			if !until.IsZero() && img.Created.After(until) {
216
-				continue
217
-			}
218
-			if img.Config != nil && !matchLabels(pruneFilters, img.Config.Labels) {
219
-				continue
220
-			}
221
-			topImages[id] = img
222
-		}
223
-	}
224
-
225
-	canceled := false
226
-deleteImagesLoop:
227
-	for id := range topImages {
228
-		select {
229
-		case <-ctx.Done():
230
-			// we still want to calculate freed size and return the data
231
-			canceled = true
232
-			break deleteImagesLoop
233
-		default:
234
-		}
235
-
236
-		deletedImages := []types.ImageDeleteResponseItem{}
237
-		refs := daemon.referenceStore.References(id.Digest())
238
-		if len(refs) > 0 {
239
-			shouldDelete := !danglingOnly
240
-			if !shouldDelete {
241
-				hasTag := false
242
-				for _, ref := range refs {
243
-					if _, ok := ref.(reference.NamedTagged); ok {
244
-						hasTag = true
245
-						break
246
-					}
247
-				}
248
-
249
-				// Only delete if it's untagged (i.e. repo:<none>)
250
-				shouldDelete = !hasTag
251
-			}
252
-
253
-			if shouldDelete {
254
-				for _, ref := range refs {
255
-					imgDel, err := daemon.ImageDelete(ref.String(), false, true)
256
-					if imageDeleteFailed(ref.String(), err) {
257
-						continue
258
-					}
259
-					deletedImages = append(deletedImages, imgDel...)
260
-				}
261
-			}
262
-		} else {
263
-			hex := id.Digest().Hex()
264
-			imgDel, err := daemon.ImageDelete(hex, false, true)
265
-			if imageDeleteFailed(hex, err) {
266
-				continue
267
-			}
268
-			deletedImages = append(deletedImages, imgDel...)
269
-		}
270
-
271
-		rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...)
272
-	}
273
-
274
-	// Compute how much space was freed
275
-	for _, d := range rep.ImagesDeleted {
276
-		if d.Deleted != "" {
277
-			chid := layer.ChainID(d.Deleted)
278
-			if l, ok := allLayers[chid]; ok {
279
-				diffSize, err := l.DiffSize()
280
-				if err != nil {
281
-					logrus.Warnf("failed to get layer %s size: %v", chid, err)
282
-					continue
283
-				}
284
-				rep.SpaceReclaimed += uint64(diffSize)
285
-			}
286
-		}
287
-	}
288
-
289
-	if canceled {
290
-		logrus.Debugf("ImagesPrune operation cancelled: %#v", *rep)
291
-	}
292
-
293
-	return rep, nil
294
-}
295
-
296
-func imageDeleteFailed(ref string, err error) bool {
297
-	switch {
298
-	case err == nil:
299
-		return false
300
-	case errdefs.IsConflict(err):
301
-		return true
302
-	default:
303
-		logrus.Warnf("failed to prune image %s: %v", ref, err)
304
-		return true
305
-	}
306
-}
307
-
308 162
 // localNetworksPrune removes unused local networks
309 163
 func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport {
310 164
 	rep := &types.NetworksPruneReport{}