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