github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/prune.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "sync/atomic" 8 "time" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/filters" 12 timetypes "github.com/docker/docker/api/types/time" 13 "github.com/docker/docker/errdefs" 14 "github.com/docker/docker/runconfig" 15 "github.com/docker/libnetwork" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 var ( 21 // errPruneRunning is returned when a prune request is received while 22 // one is in progress 23 errPruneRunning = errdefs.Conflict(errors.New("a prune operation is already running")) 24 25 containersAcceptedFilters = map[string]bool{ 26 "label": true, 27 "label!": true, 28 "until": true, 29 } 30 31 networksAcceptedFilters = map[string]bool{ 32 "label": true, 33 "label!": true, 34 "until": true, 35 } 36 ) 37 38 // ContainersPrune removes unused containers 39 func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error) { 40 if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { 41 return nil, errPruneRunning 42 } 43 defer atomic.StoreInt32(&daemon.pruneRunning, 0) 44 45 rep := &types.ContainersPruneReport{} 46 47 // make sure that only accepted filters have been received 48 err := pruneFilters.Validate(containersAcceptedFilters) 49 if err != nil { 50 return nil, err 51 } 52 53 until, err := getUntilFromPruneFilters(pruneFilters) 54 if err != nil { 55 return nil, err 56 } 57 58 allContainers := daemon.List() 59 for _, c := range allContainers { 60 select { 61 case <-ctx.Done(): 62 logrus.Debugf("ContainersPrune operation cancelled: %#v", *rep) 63 return rep, nil 64 default: 65 } 66 67 if !c.IsRunning() { 68 if !until.IsZero() && c.Created.After(until) { 69 continue 70 } 71 if !matchLabels(pruneFilters, c.Config.Labels) { 72 continue 73 } 74 cSize, _ := daemon.imageService.GetContainerLayerSize(c.ID) 75 // TODO: sets RmLink to true? 76 err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{}) 77 if err != nil { 78 logrus.Warnf("failed to prune container %s: %v", c.ID, err) 79 continue 80 } 81 if cSize > 0 { 82 rep.SpaceReclaimed += uint64(cSize) 83 } 84 rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID) 85 } 86 } 87 88 return rep, nil 89 } 90 91 // localNetworksPrune removes unused local networks 92 func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport { 93 rep := &types.NetworksPruneReport{} 94 95 until, _ := getUntilFromPruneFilters(pruneFilters) 96 97 // When the function returns true, the walk will stop. 98 l := func(nw libnetwork.Network) bool { 99 select { 100 case <-ctx.Done(): 101 // context cancelled 102 return true 103 default: 104 } 105 if nw.Info().ConfigOnly() { 106 return false 107 } 108 if !until.IsZero() && nw.Info().Created().After(until) { 109 return false 110 } 111 if !matchLabels(pruneFilters, nw.Info().Labels()) { 112 return false 113 } 114 nwName := nw.Name() 115 if runconfig.IsPreDefinedNetwork(nwName) { 116 return false 117 } 118 if len(nw.Endpoints()) > 0 { 119 return false 120 } 121 if err := daemon.DeleteNetwork(nw.ID()); err != nil { 122 logrus.Warnf("could not remove local network %s: %v", nwName, err) 123 return false 124 } 125 rep.NetworksDeleted = append(rep.NetworksDeleted, nwName) 126 return false 127 } 128 daemon.netController.WalkNetworks(l) 129 return rep 130 } 131 132 // clusterNetworksPrune removes unused cluster networks 133 func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) { 134 rep := &types.NetworksPruneReport{} 135 136 until, _ := getUntilFromPruneFilters(pruneFilters) 137 138 cluster := daemon.GetCluster() 139 140 if !cluster.IsManager() { 141 return rep, nil 142 } 143 144 networks, err := cluster.GetNetworks(pruneFilters) 145 if err != nil { 146 return rep, err 147 } 148 networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`) 149 for _, nw := range networks { 150 select { 151 case <-ctx.Done(): 152 return rep, nil 153 default: 154 if nw.Ingress { 155 // Routing-mesh network removal has to be explicitly invoked by user 156 continue 157 } 158 if !until.IsZero() && nw.Created.After(until) { 159 continue 160 } 161 if !matchLabels(pruneFilters, nw.Labels) { 162 continue 163 } 164 // https://github.com/docker/docker/issues/24186 165 // `docker network inspect` unfortunately displays ONLY those containers that are local to that node. 166 // So we try to remove it anyway and check the error 167 err = cluster.RemoveNetwork(nw.ID) 168 if err != nil { 169 // we can safely ignore the "network .. is in use" error 170 match := networkIsInUse.FindStringSubmatch(err.Error()) 171 if len(match) != 2 || match[1] != nw.ID { 172 logrus.Warnf("could not remove cluster network %s: %v", nw.Name, err) 173 } 174 continue 175 } 176 rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name) 177 } 178 } 179 return rep, nil 180 } 181 182 // NetworksPrune removes unused networks 183 func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) { 184 if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { 185 return nil, errPruneRunning 186 } 187 defer atomic.StoreInt32(&daemon.pruneRunning, 0) 188 189 // make sure that only accepted filters have been received 190 err := pruneFilters.Validate(networksAcceptedFilters) 191 if err != nil { 192 return nil, err 193 } 194 195 if _, err := getUntilFromPruneFilters(pruneFilters); err != nil { 196 return nil, err 197 } 198 199 rep := &types.NetworksPruneReport{} 200 if clusterRep, err := daemon.clusterNetworksPrune(ctx, pruneFilters); err == nil { 201 rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...) 202 } 203 204 localRep := daemon.localNetworksPrune(ctx, pruneFilters) 205 rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...) 206 207 select { 208 case <-ctx.Done(): 209 logrus.Debugf("NetworksPrune operation cancelled: %#v", *rep) 210 return rep, nil 211 default: 212 } 213 214 return rep, nil 215 } 216 217 func getUntilFromPruneFilters(pruneFilters filters.Args) (time.Time, error) { 218 until := time.Time{} 219 if !pruneFilters.Contains("until") { 220 return until, nil 221 } 222 untilFilters := pruneFilters.Get("until") 223 if len(untilFilters) > 1 { 224 return until, fmt.Errorf("more than one until filter specified") 225 } 226 ts, err := timetypes.GetTimestamp(untilFilters[0], time.Now()) 227 if err != nil { 228 return until, err 229 } 230 seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0) 231 if err != nil { 232 return until, err 233 } 234 until = time.Unix(seconds, nanoseconds) 235 return until, nil 236 } 237 238 func matchLabels(pruneFilters filters.Args, labels map[string]string) bool { 239 if !pruneFilters.MatchKVList("label", labels) { 240 return false 241 } 242 // By default MatchKVList will return true if field (like 'label!') does not exist 243 // So we have to add additional Contains("label!") check 244 if pruneFilters.Contains("label!") { 245 if pruneFilters.MatchKVList("label!", labels) { 246 return false 247 } 248 } 249 return true 250 }