Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
| ... | ... |
@@ -65,7 +65,7 @@ type attachBackend interface {
|
| 65 | 65 |
|
| 66 | 66 |
// systemBackend includes functions to implement to provide system wide containers functionality |
| 67 | 67 |
type systemBackend interface {
|
| 68 |
- ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) |
|
| 68 |
+ ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error) |
|
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 | 71 |
// Backend is all the methods that need to be implemented to provide container specific functionality. |
| ... | ... |
@@ -68,7 +68,7 @@ func (r *containerRouter) initRoutes() {
|
| 68 | 68 |
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
| 69 | 69 |
router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
| 70 | 70 |
router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
| 71 |
- router.NewPostRoute("/containers/prune", r.postContainersPrune),
|
|
| 71 |
+ router.NewPostRoute("/containers/prune", r.postContainersPrune, router.WithCancel),
|
|
| 72 | 72 |
// PUT |
| 73 | 73 |
router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
| 74 | 74 |
// DELETE |
| ... | ... |
@@ -565,7 +565,7 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon |
| 565 | 565 |
return err |
| 566 | 566 |
} |
| 567 | 567 |
|
| 568 |
- pruneReport, err := s.backend.ContainersPrune(pruneFilters) |
|
| 568 |
+ pruneReport, err := s.backend.ContainersPrune(ctx, pruneFilters) |
|
| 569 | 569 |
if err != nil {
|
| 570 | 570 |
return err |
| 571 | 571 |
} |
| ... | ... |
@@ -30,7 +30,7 @@ type imageBackend interface {
|
| 30 | 30 |
Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) |
| 31 | 31 |
LookupImage(name string) (*types.ImageInspect, error) |
| 32 | 32 |
TagImage(imageName, repository, tag string) error |
| 33 |
- ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) |
|
| 33 |
+ ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) |
|
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 | 36 |
type importExportBackend interface {
|
| ... | ... |
@@ -43,7 +43,7 @@ func (r *imageRouter) initRoutes() {
|
| 43 | 43 |
router.NewPostRoute("/images/create", r.postImagesCreate, router.WithCancel),
|
| 44 | 44 |
router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush, router.WithCancel),
|
| 45 | 45 |
router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
|
| 46 |
- router.NewPostRoute("/images/prune", r.postImagesPrune),
|
|
| 46 |
+ router.NewPostRoute("/images/prune", r.postImagesPrune, router.WithCancel),
|
|
| 47 | 47 |
// DELETE |
| 48 | 48 |
router.NewDeleteRoute("/images/{name:.*}", r.deleteImages),
|
| 49 | 49 |
} |
| ... | ... |
@@ -336,7 +336,7 @@ func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter |
| 336 | 336 |
return err |
| 337 | 337 |
} |
| 338 | 338 |
|
| 339 |
- pruneReport, err := s.backend.ImagesPrune(pruneFilters) |
|
| 339 |
+ pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters) |
|
| 340 | 340 |
if err != nil {
|
| 341 | 341 |
return err |
| 342 | 342 |
} |
| ... | ... |
@@ -1,6 +1,8 @@ |
| 1 | 1 |
package network |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "golang.org/x/net/context" |
|
| 5 |
+ |
|
| 4 | 6 |
"github.com/docker/docker/api/types" |
| 5 | 7 |
"github.com/docker/docker/api/types/filters" |
| 6 | 8 |
"github.com/docker/docker/api/types/network" |
| ... | ... |
@@ -16,5 +18,5 @@ type Backend interface {
|
| 16 | 16 |
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error |
| 17 | 17 |
DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error |
| 18 | 18 |
DeleteNetwork(name string) error |
| 19 |
- NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) |
|
| 19 |
+ NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) |
|
| 20 | 20 |
} |
| ... | ... |
@@ -37,7 +37,7 @@ func (r *networkRouter) initRoutes() {
|
| 37 | 37 |
router.NewPostRoute("/networks/create", r.postNetworkCreate),
|
| 38 | 38 |
router.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
|
| 39 | 39 |
router.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
|
| 40 |
- router.NewPostRoute("/networks/prune", r.postNetworksPrune),
|
|
| 40 |
+ router.NewPostRoute("/networks/prune", r.postNetworksPrune, router.WithCancel),
|
|
| 41 | 41 |
// DELETE |
| 42 | 42 |
router.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
|
| 43 | 43 |
} |
| ... | ... |
@@ -455,7 +455,7 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr |
| 455 | 455 |
return err |
| 456 | 456 |
} |
| 457 | 457 |
|
| 458 |
- pruneReport, err := n.backend.NetworksPrune(pruneFilters) |
|
| 458 |
+ pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters) |
|
| 459 | 459 |
if err != nil {
|
| 460 | 460 |
return err |
| 461 | 461 |
} |
| ... | ... |
@@ -1,6 +1,8 @@ |
| 1 | 1 |
package volume |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "golang.org/x/net/context" |
|
| 5 |
+ |
|
| 4 | 6 |
// TODO return types need to be refactored into pkg |
| 5 | 7 |
"github.com/docker/docker/api/types" |
| 6 | 8 |
"github.com/docker/docker/api/types/filters" |
| ... | ... |
@@ -13,5 +15,5 @@ type Backend interface {
|
| 13 | 13 |
VolumeInspect(name string) (*types.Volume, error) |
| 14 | 14 |
VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) |
| 15 | 15 |
VolumeRm(name string, force bool) error |
| 16 |
- VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) |
|
| 16 |
+ VolumesPrune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error) |
|
| 17 | 17 |
} |
| ... | ... |
@@ -29,7 +29,7 @@ func (r *volumeRouter) initRoutes() {
|
| 29 | 29 |
router.NewGetRoute("/volumes/{name:.*}", r.getVolumeByName),
|
| 30 | 30 |
// POST |
| 31 | 31 |
router.NewPostRoute("/volumes/create", r.postVolumesCreate),
|
| 32 |
- router.NewPostRoute("/volumes/prune", r.postVolumesPrune),
|
|
| 32 |
+ router.NewPostRoute("/volumes/prune", r.postVolumesPrune, router.WithCancel),
|
|
| 33 | 33 |
// DELETE |
| 34 | 34 |
router.NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
|
| 35 | 35 |
} |
| ... | ... |
@@ -77,7 +77,7 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit |
| 77 | 77 |
return err |
| 78 | 78 |
} |
| 79 | 79 |
|
| 80 |
- pruneReport, err := v.backend.VolumesPrune(pruneFilters) |
|
| 80 |
+ pruneReport, err := v.backend.VolumesPrune(ctx, pruneFilters) |
|
| 81 | 81 |
if err != nil {
|
| 82 | 82 |
return err |
| 83 | 83 |
} |
| ... | ... |
@@ -5,6 +5,8 @@ import ( |
| 5 | 5 |
"regexp" |
| 6 | 6 |
"time" |
| 7 | 7 |
|
| 8 |
+ "golang.org/x/net/context" |
|
| 9 |
+ |
|
| 8 | 10 |
"github.com/Sirupsen/logrus" |
| 9 | 11 |
"github.com/docker/distribution/reference" |
| 10 | 12 |
"github.com/docker/docker/api/types" |
| ... | ... |
@@ -20,7 +22,7 @@ import ( |
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 | 22 |
// ContainersPrune removes unused containers |
| 23 |
-func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) {
|
|
| 23 |
+func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error) {
|
|
| 24 | 24 |
rep := &types.ContainersPruneReport{}
|
| 25 | 25 |
|
| 26 | 26 |
until, err := getUntilFromPruneFilters(pruneFilters) |
| ... | ... |
@@ -30,6 +32,13 @@ func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.Contain |
| 30 | 30 |
|
| 31 | 31 |
allContainers := daemon.List() |
| 32 | 32 |
for _, c := range allContainers {
|
| 33 |
+ select {
|
|
| 34 |
+ case <-ctx.Done(): |
|
| 35 |
+ logrus.Warnf("ContainersPrune operation cancelled: %#v", *rep)
|
|
| 36 |
+ return rep, ctx.Err() |
|
| 37 |
+ default: |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 33 | 40 |
if !c.IsRunning() {
|
| 34 | 41 |
if !until.IsZero() && c.Created.After(until) {
|
| 35 | 42 |
continue |
| ... | ... |
@@ -55,10 +64,17 @@ func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.Contain |
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 | 57 |
// VolumesPrune removes unused local volumes |
| 58 |
-func (daemon *Daemon) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) {
|
|
| 58 |
+func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error) {
|
|
| 59 | 59 |
rep := &types.VolumesPruneReport{}
|
| 60 | 60 |
|
| 61 | 61 |
pruneVols := func(v volume.Volume) error {
|
| 62 |
+ select {
|
|
| 63 |
+ case <-ctx.Done(): |
|
| 64 |
+ logrus.Warnf("VolumesPrune operation cancelled: %#v", *rep)
|
|
| 65 |
+ return ctx.Err() |
|
| 66 |
+ default: |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 62 | 69 |
name := v.Name() |
| 63 | 70 |
refs := daemon.volumes.Refs(v) |
| 64 | 71 |
|
| ... | ... |
@@ -91,7 +107,7 @@ func (daemon *Daemon) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPru |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 | 93 |
// ImagesPrune removes unused images |
| 94 |
-func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
|
|
| 94 |
+func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
|
|
| 95 | 95 |
rep := &types.ImagesPruneReport{}
|
| 96 | 96 |
|
| 97 | 97 |
danglingOnly := true |
| ... | ... |
@@ -117,27 +133,47 @@ func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPrune |
| 117 | 117 |
allContainers := daemon.List() |
| 118 | 118 |
imageRefs := map[string]bool{}
|
| 119 | 119 |
for _, c := range allContainers {
|
| 120 |
- imageRefs[c.ID] = true |
|
| 120 |
+ select {
|
|
| 121 |
+ case <-ctx.Done(): |
|
| 122 |
+ return nil, ctx.Err() |
|
| 123 |
+ default: |
|
| 124 |
+ imageRefs[c.ID] = true |
|
| 125 |
+ } |
|
| 121 | 126 |
} |
| 122 | 127 |
|
| 123 | 128 |
// Filter intermediary images and get their unique size |
| 124 | 129 |
allLayers := daemon.layerStore.Map() |
| 125 | 130 |
topImages := map[image.ID]*image.Image{}
|
| 126 | 131 |
for id, img := range allImages {
|
| 127 |
- dgst := digest.Digest(id) |
|
| 128 |
- if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
|
|
| 129 |
- continue |
|
| 130 |
- } |
|
| 131 |
- if !until.IsZero() && img.Created.After(until) {
|
|
| 132 |
- continue |
|
| 133 |
- } |
|
| 134 |
- if !matchLabels(pruneFilters, img.Config.Labels) {
|
|
| 135 |
- continue |
|
| 132 |
+ select {
|
|
| 133 |
+ case <-ctx.Done(): |
|
| 134 |
+ return nil, ctx.Err() |
|
| 135 |
+ default: |
|
| 136 |
+ dgst := digest.Digest(id) |
|
| 137 |
+ if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
|
|
| 138 |
+ continue |
|
| 139 |
+ } |
|
| 140 |
+ if !until.IsZero() && img.Created.After(until) {
|
|
| 141 |
+ continue |
|
| 142 |
+ } |
|
| 143 |
+ if !matchLabels(pruneFilters, img.Config.Labels) {
|
|
| 144 |
+ continue |
|
| 145 |
+ } |
|
| 146 |
+ topImages[id] = img |
|
| 136 | 147 |
} |
| 137 |
- topImages[id] = img |
|
| 138 | 148 |
} |
| 139 | 149 |
|
| 150 |
+ canceled := false |
|
| 151 |
+deleteImagesLoop: |
|
| 140 | 152 |
for id := range topImages {
|
| 153 |
+ select {
|
|
| 154 |
+ case <-ctx.Done(): |
|
| 155 |
+ // we still want to calculate freed size and return the data |
|
| 156 |
+ canceled = true |
|
| 157 |
+ break deleteImagesLoop |
|
| 158 |
+ default: |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 141 | 161 |
dgst := digest.Digest(id) |
| 142 | 162 |
hex := dgst.Hex() |
| 143 | 163 |
if _, ok := imageRefs[hex]; ok {
|
| ... | ... |
@@ -198,17 +234,27 @@ func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPrune |
| 198 | 198 |
} |
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 |
+ if canceled {
|
|
| 202 |
+ logrus.Warnf("ImagesPrune operation cancelled: %#v", *rep)
|
|
| 203 |
+ return nil, ctx.Err() |
|
| 204 |
+ } |
|
| 205 |
+ |
|
| 201 | 206 |
return rep, nil |
| 202 | 207 |
} |
| 203 | 208 |
|
| 204 | 209 |
// localNetworksPrune removes unused local networks |
| 205 |
-func (daemon *Daemon) localNetworksPrune(pruneFilters filters.Args) *types.NetworksPruneReport {
|
|
| 210 |
+func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport {
|
|
| 206 | 211 |
rep := &types.NetworksPruneReport{}
|
| 207 | 212 |
|
| 208 | 213 |
until, _ := getUntilFromPruneFilters(pruneFilters) |
| 209 | 214 |
|
| 210 | 215 |
// When the function returns true, the walk will stop. |
| 211 | 216 |
l := func(nw libnetwork.Network) bool {
|
| 217 |
+ select {
|
|
| 218 |
+ case <-ctx.Done(): |
|
| 219 |
+ return true |
|
| 220 |
+ default: |
|
| 221 |
+ } |
|
| 212 | 222 |
if !until.IsZero() && nw.Info().Created().After(until) {
|
| 213 | 223 |
return false |
| 214 | 224 |
} |
| ... | ... |
@@ -234,7 +280,7 @@ func (daemon *Daemon) localNetworksPrune(pruneFilters filters.Args) *types.Netwo |
| 234 | 234 |
} |
| 235 | 235 |
|
| 236 | 236 |
// clusterNetworksPrune removes unused cluster networks |
| 237 |
-func (daemon *Daemon) clusterNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
|
|
| 237 |
+func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
|
|
| 238 | 238 |
rep := &types.NetworksPruneReport{}
|
| 239 | 239 |
|
| 240 | 240 |
until, _ := getUntilFromPruneFilters(pruneFilters) |
| ... | ... |
@@ -251,46 +297,59 @@ func (daemon *Daemon) clusterNetworksPrune(pruneFilters filters.Args) (*types.Ne |
| 251 | 251 |
} |
| 252 | 252 |
networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`) |
| 253 | 253 |
for _, nw := range networks {
|
| 254 |
- if nw.Ingress {
|
|
| 255 |
- // Routing-mesh network removal has to be explicitly invoked by user |
|
| 256 |
- continue |
|
| 257 |
- } |
|
| 258 |
- if !until.IsZero() && nw.Created.After(until) {
|
|
| 259 |
- continue |
|
| 260 |
- } |
|
| 261 |
- if !matchLabels(pruneFilters, nw.Labels) {
|
|
| 262 |
- continue |
|
| 263 |
- } |
|
| 264 |
- // https://github.com/docker/docker/issues/24186 |
|
| 265 |
- // `docker network inspect` unfortunately displays ONLY those containers that are local to that node. |
|
| 266 |
- // So we try to remove it anyway and check the error |
|
| 267 |
- err = cluster.RemoveNetwork(nw.ID) |
|
| 268 |
- if err != nil {
|
|
| 269 |
- // we can safely ignore the "network .. is in use" error |
|
| 270 |
- match := networkIsInUse.FindStringSubmatch(err.Error()) |
|
| 271 |
- if len(match) != 2 || match[1] != nw.ID {
|
|
| 272 |
- logrus.Warnf("could not remove cluster network %s: %v", nw.Name, err)
|
|
| 254 |
+ select {
|
|
| 255 |
+ case <-ctx.Done(): |
|
| 256 |
+ return rep, ctx.Err() |
|
| 257 |
+ default: |
|
| 258 |
+ if nw.Ingress {
|
|
| 259 |
+ // Routing-mesh network removal has to be explicitly invoked by user |
|
| 260 |
+ continue |
|
| 273 | 261 |
} |
| 274 |
- continue |
|
| 262 |
+ if !until.IsZero() && nw.Created.After(until) {
|
|
| 263 |
+ continue |
|
| 264 |
+ } |
|
| 265 |
+ if !matchLabels(pruneFilters, nw.Labels) {
|
|
| 266 |
+ continue |
|
| 267 |
+ } |
|
| 268 |
+ // https://github.com/docker/docker/issues/24186 |
|
| 269 |
+ // `docker network inspect` unfortunately displays ONLY those containers that are local to that node. |
|
| 270 |
+ // So we try to remove it anyway and check the error |
|
| 271 |
+ err = cluster.RemoveNetwork(nw.ID) |
|
| 272 |
+ if err != nil {
|
|
| 273 |
+ // we can safely ignore the "network .. is in use" error |
|
| 274 |
+ match := networkIsInUse.FindStringSubmatch(err.Error()) |
|
| 275 |
+ if len(match) != 2 || match[1] != nw.ID {
|
|
| 276 |
+ logrus.Warnf("could not remove cluster network %s: %v", nw.Name, err)
|
|
| 277 |
+ } |
|
| 278 |
+ continue |
|
| 279 |
+ } |
|
| 280 |
+ rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name) |
|
| 275 | 281 |
} |
| 276 |
- rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name) |
|
| 277 | 282 |
} |
| 278 | 283 |
return rep, nil |
| 279 | 284 |
} |
| 280 | 285 |
|
| 281 | 286 |
// NetworksPrune removes unused networks |
| 282 |
-func (daemon *Daemon) NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
|
|
| 287 |
+func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
|
|
| 283 | 288 |
if _, err := getUntilFromPruneFilters(pruneFilters); err != nil {
|
| 284 | 289 |
return nil, err |
| 285 | 290 |
} |
| 286 | 291 |
|
| 287 | 292 |
rep := &types.NetworksPruneReport{}
|
| 288 |
- if clusterRep, err := daemon.clusterNetworksPrune(pruneFilters); err == nil {
|
|
| 293 |
+ if clusterRep, err := daemon.clusterNetworksPrune(ctx, pruneFilters); err == nil {
|
|
| 289 | 294 |
rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...) |
| 290 | 295 |
} |
| 291 | 296 |
|
| 292 |
- localRep := daemon.localNetworksPrune(pruneFilters) |
|
| 297 |
+ localRep := daemon.localNetworksPrune(ctx, pruneFilters) |
|
| 293 | 298 |
rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...) |
| 299 |
+ |
|
| 300 |
+ select {
|
|
| 301 |
+ case <-ctx.Done(): |
|
| 302 |
+ logrus.Warnf("NetworksPrune operation cancelled: %#v", *rep)
|
|
| 303 |
+ return nil, ctx.Err() |
|
| 304 |
+ default: |
|
| 305 |
+ } |
|
| 306 |
+ |
|
| 294 | 307 |
return rep, nil |
| 295 | 308 |
} |
| 296 | 309 |
|