From 3279ca3c009be8d4746df483ea45203ff0c701d8 Mon Sep 17 00:00:00 2001 From: Kenfe-Mickael Laventure Date: Wed, 12 Apr 2017 14:04:49 -0700 Subject: [PATCH] Prevent multiple identical parallel pruning operations Signed-off-by: Kenfe-Mickael Laventure --- daemon/daemon.go | 7 ++----- daemon/prune.go | 30 ++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index 87697ad144..59d84a0260 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -112,11 +112,8 @@ type Daemon struct { seccompProfile []byte seccompProfilePath string - diskUsageRunning int32 - containersPruneRunning int32 - volumesPruneRunning int32 - imagesPruneRunning int32 - networksPruneRunning int32 + diskUsageRunning int32 + pruneRunning int32 } // HasExperimental returns whether the experimental features of the daemon are enabled or not diff --git a/daemon/prune.go b/daemon/prune.go index ed3cb6eabb..7b8895946e 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -3,10 +3,9 @@ package daemon import ( "fmt" "regexp" + "sync/atomic" "time" - "golang.org/x/net/context" - "github.com/Sirupsen/logrus" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -19,10 +18,22 @@ import ( "github.com/docker/docker/volume" "github.com/docker/libnetwork" digest "github.com/opencontainers/go-digest" + "golang.org/x/net/context" +) + +var ( + // ErrPruneRunning is returned when a prune request is received while + // one is in progress + ErrPruneRunning = fmt.Errorf("a prune operation is already running") ) // ContainersPrune removes unused containers func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error) { + if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { + return nil, ErrPruneRunning + } + defer atomic.StoreInt32(&daemon.pruneRunning, 0) + rep := &types.ContainersPruneReport{} until, err := getUntilFromPruneFilters(pruneFilters) @@ -65,6 +76,11 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters. // VolumesPrune removes unused local volumes func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error) { + if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { + return nil, ErrPruneRunning + } + defer atomic.StoreInt32(&daemon.pruneRunning, 0) + rep := &types.VolumesPruneReport{} pruneVols := func(v volume.Volume) error { @@ -108,6 +124,11 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg // ImagesPrune removes unused images func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) { + if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { + return nil, ErrPruneRunning + } + defer atomic.StoreInt32(&daemon.pruneRunning, 0) + rep := &types.ImagesPruneReport{} danglingOnly := true @@ -331,6 +352,11 @@ func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters fil // NetworksPrune removes unused networks func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) { + if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { + return nil, ErrPruneRunning + } + defer atomic.StoreInt32(&daemon.pruneRunning, 0) + if _, err := getUntilFromPruneFilters(pruneFilters); err != nil { return nil, err }