github.com/kelda-inc/moby@v1.13.1/daemon/prune.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/Sirupsen/logrus" 8 "github.com/docker/distribution/digest" 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/filters" 11 "github.com/docker/docker/image" 12 "github.com/docker/docker/layer" 13 "github.com/docker/docker/pkg/directory" 14 "github.com/docker/docker/reference" 15 "github.com/docker/docker/runconfig" 16 "github.com/docker/docker/volume" 17 "github.com/docker/libnetwork" 18 ) 19 20 // ContainersPrune removes unused containers 21 func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) { 22 rep := &types.ContainersPruneReport{} 23 24 allContainers := daemon.List() 25 for _, c := range allContainers { 26 if !c.IsRunning() { 27 cSize, _ := daemon.getSize(c) 28 // TODO: sets RmLink to true? 29 err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{}) 30 if err != nil { 31 logrus.Warnf("failed to prune container %s: %v", c.ID, err) 32 continue 33 } 34 if cSize > 0 { 35 rep.SpaceReclaimed += uint64(cSize) 36 } 37 rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID) 38 } 39 } 40 41 return rep, nil 42 } 43 44 // VolumesPrune removes unused local volumes 45 func (daemon *Daemon) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) { 46 rep := &types.VolumesPruneReport{} 47 48 pruneVols := func(v volume.Volume) error { 49 name := v.Name() 50 refs := daemon.volumes.Refs(v) 51 52 if len(refs) == 0 { 53 vSize, err := directory.Size(v.Path()) 54 if err != nil { 55 logrus.Warnf("could not determine size of volume %s: %v", name, err) 56 } 57 err = daemon.volumes.Remove(v) 58 if err != nil { 59 logrus.Warnf("could not remove volume %s: %v", name, err) 60 return nil 61 } 62 rep.SpaceReclaimed += uint64(vSize) 63 rep.VolumesDeleted = append(rep.VolumesDeleted, name) 64 } 65 66 return nil 67 } 68 69 err := daemon.traverseLocalVolumes(pruneVols) 70 71 return rep, err 72 } 73 74 // ImagesPrune removes unused images 75 func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) { 76 rep := &types.ImagesPruneReport{} 77 78 danglingOnly := true 79 if pruneFilters.Include("dangling") { 80 if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") { 81 danglingOnly = false 82 } else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") { 83 return nil, fmt.Errorf("Invalid filter 'dangling=%s'", pruneFilters.Get("dangling")) 84 } 85 } 86 87 var allImages map[image.ID]*image.Image 88 if danglingOnly { 89 allImages = daemon.imageStore.Heads() 90 } else { 91 allImages = daemon.imageStore.Map() 92 } 93 allContainers := daemon.List() 94 imageRefs := map[string]bool{} 95 for _, c := range allContainers { 96 imageRefs[c.ID] = true 97 } 98 99 // Filter intermediary images and get their unique size 100 allLayers := daemon.layerStore.Map() 101 topImages := map[image.ID]*image.Image{} 102 for id, img := range allImages { 103 dgst := digest.Digest(id) 104 if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { 105 continue 106 } 107 topImages[id] = img 108 } 109 110 for id := range topImages { 111 dgst := digest.Digest(id) 112 hex := dgst.Hex() 113 if _, ok := imageRefs[hex]; ok { 114 continue 115 } 116 117 deletedImages := []types.ImageDelete{} 118 refs := daemon.referenceStore.References(dgst) 119 if len(refs) > 0 { 120 if danglingOnly { 121 // Not a dangling image 122 continue 123 } 124 125 nrRefs := len(refs) 126 for _, ref := range refs { 127 // If nrRefs == 1, we have an image marked as myreponame:<none> 128 // i.e. the tag content was changed 129 if _, ok := ref.(reference.Canonical); ok && nrRefs > 1 { 130 continue 131 } 132 imgDel, err := daemon.ImageDelete(ref.String(), false, true) 133 if err != nil { 134 logrus.Warnf("could not delete reference %s: %v", ref.String(), err) 135 continue 136 } 137 deletedImages = append(deletedImages, imgDel...) 138 } 139 } else { 140 imgDel, err := daemon.ImageDelete(hex, false, true) 141 if err != nil { 142 logrus.Warnf("could not delete image %s: %v", hex, err) 143 continue 144 } 145 deletedImages = append(deletedImages, imgDel...) 146 } 147 148 rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...) 149 } 150 151 // Compute how much space was freed 152 for _, d := range rep.ImagesDeleted { 153 if d.Deleted != "" { 154 chid := layer.ChainID(d.Deleted) 155 if l, ok := allLayers[chid]; ok { 156 diffSize, err := l.DiffSize() 157 if err != nil { 158 logrus.Warnf("failed to get layer %s size: %v", chid, err) 159 continue 160 } 161 rep.SpaceReclaimed += uint64(diffSize) 162 } 163 } 164 } 165 166 return rep, nil 167 } 168 169 // localNetworksPrune removes unused local networks 170 func (daemon *Daemon) localNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) { 171 rep := &types.NetworksPruneReport{} 172 var err error 173 // When the function returns true, the walk will stop. 174 l := func(nw libnetwork.Network) bool { 175 nwName := nw.Name() 176 predefined := runconfig.IsPreDefinedNetwork(nwName) 177 if !predefined && len(nw.Endpoints()) == 0 { 178 if err = daemon.DeleteNetwork(nw.ID()); err != nil { 179 logrus.Warnf("could not remove network %s: %v", nwName, err) 180 return false 181 } 182 rep.NetworksDeleted = append(rep.NetworksDeleted, nwName) 183 } 184 return false 185 } 186 daemon.netController.WalkNetworks(l) 187 return rep, err 188 } 189 190 // clusterNetworksPrune removes unused cluster networks 191 func (daemon *Daemon) clusterNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) { 192 rep := &types.NetworksPruneReport{} 193 cluster := daemon.GetCluster() 194 networks, err := cluster.GetNetworks() 195 if err != nil { 196 return rep, err 197 } 198 networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`) 199 for _, nw := range networks { 200 if nw.Name == "ingress" { 201 continue 202 } 203 // https://github.com/docker/docker/issues/24186 204 // `docker network inspect` unfortunately displays ONLY those containers that are local to that node. 205 // So we try to remove it anyway and check the error 206 err = cluster.RemoveNetwork(nw.ID) 207 if err != nil { 208 // we can safely ignore the "network .. is in use" error 209 match := networkIsInUse.FindStringSubmatch(err.Error()) 210 if len(match) != 2 || match[1] != nw.ID { 211 logrus.Warnf("could not remove network %s: %v", nw.Name, err) 212 } 213 continue 214 } 215 rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name) 216 } 217 return rep, nil 218 } 219 220 // NetworksPrune removes unused networks 221 func (daemon *Daemon) NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) { 222 rep := &types.NetworksPruneReport{} 223 clusterRep, err := daemon.clusterNetworksPrune(pruneFilters) 224 if err != nil { 225 logrus.Warnf("could not remove cluster networks: %v", err) 226 } else { 227 rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...) 228 } 229 localRep, err := daemon.localNetworksPrune(pruneFilters) 230 if err != nil { 231 logrus.Warnf("could not remove local networks: %v", err) 232 } else { 233 rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...) 234 } 235 return rep, err 236 }