fix compile error
[platform/upstream/docker-engine.git] / daemon / prune.go
1 package daemon
2
3 import (
4         "fmt"
5         "regexp"
6         "runtime"
7         "sync/atomic"
8         "time"
9
10         "github.com/Sirupsen/logrus"
11         "github.com/docker/distribution/reference"
12         "github.com/docker/docker/api/types"
13         "github.com/docker/docker/api/types/filters"
14         timetypes "github.com/docker/docker/api/types/time"
15         "github.com/docker/docker/image"
16         "github.com/docker/docker/layer"
17         "github.com/docker/docker/pkg/directory"
18         "github.com/docker/docker/pkg/system"
19         "github.com/docker/docker/runconfig"
20         "github.com/docker/docker/volume"
21         "github.com/docker/libnetwork"
22         digest "github.com/opencontainers/go-digest"
23         "golang.org/x/net/context"
24 )
25
26 var (
27         // errPruneRunning is returned when a prune request is received while
28         // one is in progress
29         errPruneRunning = fmt.Errorf("a prune operation is already running")
30
31         containersAcceptedFilters = map[string]bool{
32                 "label":  true,
33                 "label!": true,
34                 "until":  true,
35         }
36         volumesAcceptedFilters = map[string]bool{
37                 "label":  true,
38                 "label!": true,
39         }
40         imagesAcceptedFilters = map[string]bool{
41                 "dangling": true,
42                 "label":    true,
43                 "label!":   true,
44                 "until":    true,
45         }
46         networksAcceptedFilters = map[string]bool{
47                 "label":  true,
48                 "label!": true,
49                 "until":  true,
50         }
51 )
52
53 // ContainersPrune removes unused containers
54 func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error) {
55         if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
56                 return nil, errPruneRunning
57         }
58         defer atomic.StoreInt32(&daemon.pruneRunning, 0)
59
60         rep := &types.ContainersPruneReport{}
61
62         // make sure that only accepted filters have been received
63         err := pruneFilters.Validate(containersAcceptedFilters)
64         if err != nil {
65                 return nil, err
66         }
67
68         until, err := getUntilFromPruneFilters(pruneFilters)
69         if err != nil {
70                 return nil, err
71         }
72
73         allContainers := daemon.List()
74         for _, c := range allContainers {
75                 select {
76                 case <-ctx.Done():
77                         logrus.Warnf("ContainersPrune operation cancelled: %#v", *rep)
78                         return rep, ctx.Err()
79                 default:
80                 }
81
82                 if !c.IsRunning() {
83                         if !until.IsZero() && c.Created.After(until) {
84                                 continue
85                         }
86                         if !matchLabels(pruneFilters, c.Config.Labels) {
87                                 continue
88                         }
89                         cSize, _ := daemon.getSize(c.ID)
90                         // TODO: sets RmLink to true?
91                         err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
92                         if err != nil {
93                                 logrus.Warnf("failed to prune container %s: %v", c.ID, err)
94                                 continue
95                         }
96                         if cSize > 0 {
97                                 rep.SpaceReclaimed += uint64(cSize)
98                         }
99                         rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID)
100                 }
101         }
102
103         return rep, nil
104 }
105
106 // VolumesPrune removes unused local volumes
107 func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error) {
108         if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
109                 return nil, errPruneRunning
110         }
111         defer atomic.StoreInt32(&daemon.pruneRunning, 0)
112
113         // make sure that only accepted filters have been received
114         err := pruneFilters.Validate(volumesAcceptedFilters)
115         if err != nil {
116                 return nil, err
117         }
118
119         rep := &types.VolumesPruneReport{}
120
121         pruneVols := func(v volume.Volume) error {
122                 select {
123                 case <-ctx.Done():
124                         logrus.Warnf("VolumesPrune operation cancelled: %#v", *rep)
125                         return ctx.Err()
126                 default:
127                 }
128
129                 name := v.Name()
130                 refs := daemon.volumes.Refs(v)
131
132                 if len(refs) == 0 {
133                         detailedVolume, ok := v.(volume.DetailedVolume)
134                         if ok {
135                                 if !matchLabels(pruneFilters, detailedVolume.Labels()) {
136                                         return nil
137                                 }
138                         }
139                         vSize, err := directory.Size(v.Path())
140                         if err != nil {
141                                 logrus.Warnf("could not determine size of volume %s: %v", name, err)
142                         }
143                         err = daemon.volumes.Remove(v)
144                         if err != nil {
145                                 logrus.Warnf("could not remove volume %s: %v", name, err)
146                                 return nil
147                         }
148                         rep.SpaceReclaimed += uint64(vSize)
149                         rep.VolumesDeleted = append(rep.VolumesDeleted, name)
150                 }
151
152                 return nil
153         }
154
155         err = daemon.traverseLocalVolumes(pruneVols)
156
157         return rep, err
158 }
159
160 // ImagesPrune removes unused images
161 func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
162         // TODO @jhowardmsft LCOW Support: This will need revisiting later.
163         platform := runtime.GOOS
164         if system.LCOWSupported() {
165                 platform = "linux"
166         }
167
168         if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
169                 return nil, errPruneRunning
170         }
171         defer atomic.StoreInt32(&daemon.pruneRunning, 0)
172
173         // make sure that only accepted filters have been received
174         err := pruneFilters.Validate(imagesAcceptedFilters)
175         if err != nil {
176                 return nil, err
177         }
178
179         rep := &types.ImagesPruneReport{}
180
181         danglingOnly := true
182         if pruneFilters.Include("dangling") {
183                 if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") {
184                         danglingOnly = false
185                 } else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") {
186                         return nil, fmt.Errorf("Invalid filter 'dangling=%s'", pruneFilters.Get("dangling"))
187                 }
188         }
189
190         until, err := getUntilFromPruneFilters(pruneFilters)
191         if err != nil {
192                 return nil, err
193         }
194
195         var allImages map[image.ID]*image.Image
196         if danglingOnly {
197                 allImages = daemon.stores[platform].imageStore.Heads()
198         } else {
199                 allImages = daemon.stores[platform].imageStore.Map()
200         }
201         allContainers := daemon.List()
202         imageRefs := map[string]bool{}
203         for _, c := range allContainers {
204                 select {
205                 case <-ctx.Done():
206                         return nil, ctx.Err()
207                 default:
208                         imageRefs[c.ID] = true
209                 }
210         }
211
212         // Filter intermediary images and get their unique size
213         allLayers := daemon.stores[platform].layerStore.Map()
214         topImages := map[image.ID]*image.Image{}
215         for id, img := range allImages {
216                 select {
217                 case <-ctx.Done():
218                         return nil, ctx.Err()
219                 default:
220                         dgst := digest.Digest(id)
221                         if len(daemon.stores[platform].referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 {
222                                 continue
223                         }
224                         if !until.IsZero() && img.Created.After(until) {
225                                 continue
226                         }
227                         if img.Config != nil && !matchLabels(pruneFilters, img.Config.Labels) {
228                                 continue
229                         }
230                         topImages[id] = img
231                 }
232         }
233
234         canceled := false
235 deleteImagesLoop:
236         for id := range topImages {
237                 select {
238                 case <-ctx.Done():
239                         // we still want to calculate freed size and return the data
240                         canceled = true
241                         break deleteImagesLoop
242                 default:
243                 }
244
245                 dgst := digest.Digest(id)
246                 hex := dgst.Hex()
247                 if _, ok := imageRefs[hex]; ok {
248                         continue
249                 }
250
251                 deletedImages := []types.ImageDeleteResponseItem{}
252                 refs := daemon.stores[platform].referenceStore.References(dgst)
253                 if len(refs) > 0 {
254                         shouldDelete := !danglingOnly
255                         if !shouldDelete {
256                                 hasTag := false
257                                 for _, ref := range refs {
258                                         if _, ok := ref.(reference.NamedTagged); ok {
259                                                 hasTag = true
260                                                 break
261                                         }
262                                 }
263
264                                 // Only delete if it's untagged (i.e. repo:<none>)
265                                 shouldDelete = !hasTag
266                         }
267
268                         if shouldDelete {
269                                 for _, ref := range refs {
270                                         imgDel, err := daemon.ImageDelete(ref.String(), false, true)
271                                         if err != nil {
272                                                 logrus.Warnf("could not delete reference %s: %v", ref.String(), err)
273                                                 continue
274                                         }
275                                         deletedImages = append(deletedImages, imgDel...)
276                                 }
277                         }
278                 } else {
279                         imgDel, err := daemon.ImageDelete(hex, false, true)
280                         if err != nil {
281                                 logrus.Warnf("could not delete image %s: %v", hex, err)
282                                 continue
283                         }
284                         deletedImages = append(deletedImages, imgDel...)
285                 }
286
287                 rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...)
288         }
289
290         // Compute how much space was freed
291         for _, d := range rep.ImagesDeleted {
292                 if d.Deleted != "" {
293                         chid := layer.ChainID(d.Deleted)
294                         if l, ok := allLayers[chid]; ok {
295                                 diffSize, err := l.DiffSize()
296                                 if err != nil {
297                                         logrus.Warnf("failed to get layer %s size: %v", chid, err)
298                                         continue
299                                 }
300                                 rep.SpaceReclaimed += uint64(diffSize)
301                         }
302                 }
303         }
304
305         if canceled {
306                 logrus.Warnf("ImagesPrune operation cancelled: %#v", *rep)
307                 return nil, ctx.Err()
308         }
309
310         return rep, nil
311 }
312
313 // localNetworksPrune removes unused local networks
314 func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport {
315         rep := &types.NetworksPruneReport{}
316
317         until, _ := getUntilFromPruneFilters(pruneFilters)
318
319         // When the function returns true, the walk will stop.
320         l := func(nw libnetwork.Network) bool {
321                 select {
322                 case <-ctx.Done():
323                         return true
324                 default:
325                 }
326                 if nw.Info().ConfigOnly() {
327                         return false
328                 }
329                 if !until.IsZero() && nw.Info().Created().After(until) {
330                         return false
331                 }
332                 if !matchLabels(pruneFilters, nw.Info().Labels()) {
333                         return false
334                 }
335                 nwName := nw.Name()
336                 if runconfig.IsPreDefinedNetwork(nwName) {
337                         return false
338                 }
339                 if len(nw.Endpoints()) > 0 {
340                         return false
341                 }
342                 if err := daemon.DeleteNetwork(nw.ID()); err != nil {
343                         logrus.Warnf("could not remove local network %s: %v", nwName, err)
344                         return false
345                 }
346                 rep.NetworksDeleted = append(rep.NetworksDeleted, nwName)
347                 return false
348         }
349         daemon.netController.WalkNetworks(l)
350         return rep
351 }
352
353 // clusterNetworksPrune removes unused cluster networks
354 func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
355         rep := &types.NetworksPruneReport{}
356
357         until, _ := getUntilFromPruneFilters(pruneFilters)
358
359         cluster := daemon.GetCluster()
360
361         if !cluster.IsManager() {
362                 return rep, nil
363         }
364
365         networks, err := cluster.GetNetworks()
366         if err != nil {
367                 return rep, err
368         }
369         networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`)
370         for _, nw := range networks {
371                 select {
372                 case <-ctx.Done():
373                         return rep, ctx.Err()
374                 default:
375                         if nw.Ingress {
376                                 // Routing-mesh network removal has to be explicitly invoked by user
377                                 continue
378                         }
379                         if !until.IsZero() && nw.Created.After(until) {
380                                 continue
381                         }
382                         if !matchLabels(pruneFilters, nw.Labels) {
383                                 continue
384                         }
385                         // https://github.com/docker/docker/issues/24186
386                         // `docker network inspect` unfortunately displays ONLY those containers that are local to that node.
387                         // So we try to remove it anyway and check the error
388                         err = cluster.RemoveNetwork(nw.ID)
389                         if err != nil {
390                                 // we can safely ignore the "network .. is in use" error
391                                 match := networkIsInUse.FindStringSubmatch(err.Error())
392                                 if len(match) != 2 || match[1] != nw.ID {
393                                         logrus.Warnf("could not remove cluster network %s: %v", nw.Name, err)
394                                 }
395                                 continue
396                         }
397                         rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name)
398                 }
399         }
400         return rep, nil
401 }
402
403 // NetworksPrune removes unused networks
404 func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
405         if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
406                 return nil, errPruneRunning
407         }
408         defer atomic.StoreInt32(&daemon.pruneRunning, 0)
409
410         // make sure that only accepted filters have been received
411         err := pruneFilters.Validate(networksAcceptedFilters)
412         if err != nil {
413                 return nil, err
414         }
415
416         if _, err := getUntilFromPruneFilters(pruneFilters); err != nil {
417                 return nil, err
418         }
419
420         rep := &types.NetworksPruneReport{}
421
422         localRep := daemon.localNetworksPrune(ctx, pruneFilters)
423         rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...)
424
425         select {
426         case <-ctx.Done():
427                 logrus.Warnf("NetworksPrune operation cancelled: %#v", *rep)
428                 return nil, ctx.Err()
429         default:
430         }
431
432         return rep, nil
433 }
434
435 func getUntilFromPruneFilters(pruneFilters filters.Args) (time.Time, error) {
436         until := time.Time{}
437         if !pruneFilters.Include("until") {
438                 return until, nil
439         }
440         untilFilters := pruneFilters.Get("until")
441         if len(untilFilters) > 1 {
442                 return until, fmt.Errorf("more than one until filter specified")
443         }
444         ts, err := timetypes.GetTimestamp(untilFilters[0], time.Now())
445         if err != nil {
446                 return until, err
447         }
448         seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0)
449         if err != nil {
450                 return until, err
451         }
452         until = time.Unix(seconds, nanoseconds)
453         return until, nil
454 }
455
456 func matchLabels(pruneFilters filters.Args, labels map[string]string) bool {
457         if !pruneFilters.MatchKVList("label", labels) {
458                 return false
459         }
460         // By default MatchKVList will return true if field (like 'label!') does not exist
461         // So we have to add additional Include("label!") check
462         if pruneFilters.Include("label!") {
463                 if pruneFilters.MatchKVList("label!", labels) {
464                         return false
465                 }
466         }
467         return true
468 }