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