Browse code

Extract volume interaction to a volumes service

This cleans up some of the package API's used for interacting with
volumes, and simplifies management.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2018/03/23 06:11:03
Showing 59 changed files
... ...
@@ -3,6 +3,7 @@ package volume // import "github.com/docker/docker/api/server/router/volume"
3 3
 import (
4 4
 	"context"
5 5
 
6
+	"github.com/docker/docker/volume/service/opts"
6 7
 	// TODO return types need to be refactored into pkg
7 8
 	"github.com/docker/docker/api/types"
8 9
 	"github.com/docker/docker/api/types/filters"
... ...
@@ -11,9 +12,9 @@ import (
11 11
 // Backend is the methods that need to be implemented to provide
12 12
 // volume specific functionality
13 13
 type Backend interface {
14
-	Volumes(filter string) ([]*types.Volume, []string, error)
15
-	VolumeInspect(name string) (*types.Volume, error)
16
-	VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
17
-	VolumeRm(name string, force bool) error
18
-	VolumesPrune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error)
14
+	List(ctx context.Context, filter filters.Args) ([]*types.Volume, []string, error)
15
+	Get(ctx context.Context, name string, opts ...opts.GetOption) (*types.Volume, error)
16
+	Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*types.Volume, error)
17
+	Remove(ctx context.Context, name string, opts ...opts.RemoveOption) error
18
+	Prune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error)
19 19
 }
... ...
@@ -3,7 +3,6 @@ package volume // import "github.com/docker/docker/api/server/router/volume"
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
-	"errors"
7 6
 	"io"
8 7
 	"net/http"
9 8
 
... ...
@@ -11,6 +10,8 @@ import (
11 11
 	"github.com/docker/docker/api/types/filters"
12 12
 	volumetypes "github.com/docker/docker/api/types/volume"
13 13
 	"github.com/docker/docker/errdefs"
14
+	"github.com/docker/docker/volume/service/opts"
15
+	"github.com/pkg/errors"
14 16
 )
15 17
 
16 18
 func (v *volumeRouter) getVolumesList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -18,7 +19,11 @@ func (v *volumeRouter) getVolumesList(ctx context.Context, w http.ResponseWriter
18 18
 		return err
19 19
 	}
20 20
 
21
-	volumes, warnings, err := v.backend.Volumes(r.Form.Get("filters"))
21
+	filters, err := filters.FromJSON(r.Form.Get("filters"))
22
+	if err != nil {
23
+		return errdefs.InvalidParameter(errors.Wrap(err, "error reading volume filters"))
24
+	}
25
+	volumes, warnings, err := v.backend.List(ctx, filters)
22 26
 	if err != nil {
23 27
 		return err
24 28
 	}
... ...
@@ -30,7 +35,7 @@ func (v *volumeRouter) getVolumeByName(ctx context.Context, w http.ResponseWrite
30 30
 		return err
31 31
 	}
32 32
 
33
-	volume, err := v.backend.VolumeInspect(vars["name"])
33
+	volume, err := v.backend.Get(ctx, vars["name"], opts.WithGetResolveStatus)
34 34
 	if err != nil {
35 35
 		return err
36 36
 	}
... ...
@@ -54,7 +59,7 @@ func (v *volumeRouter) postVolumesCreate(ctx context.Context, w http.ResponseWri
54 54
 		return err
55 55
 	}
56 56
 
57
-	volume, err := v.backend.VolumeCreate(req.Name, req.Driver, req.DriverOpts, req.Labels)
57
+	volume, err := v.backend.Create(ctx, req.Name, req.Driver, opts.WithCreateOptions(req.DriverOpts), opts.WithCreateLabels(req.Labels))
58 58
 	if err != nil {
59 59
 		return err
60 60
 	}
... ...
@@ -66,7 +71,7 @@ func (v *volumeRouter) deleteVolumes(ctx context.Context, w http.ResponseWriter,
66 66
 		return err
67 67
 	}
68 68
 	force := httputils.BoolValue(r, "force")
69
-	if err := v.backend.VolumeRm(vars["name"], force); err != nil {
69
+	if err := v.backend.Remove(ctx, vars["name"], opts.WithPurgeOnError(force)); err != nil {
70 70
 		return err
71 71
 	}
72 72
 	w.WriteHeader(http.StatusNoContent)
... ...
@@ -83,7 +88,7 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit
83 83
 		return err
84 84
 	}
85 85
 
86
-	pruneReport, err := v.backend.VolumesPrune(ctx, pruneFilters)
86
+	pruneReport, err := v.backend.Prune(ctx, pruneFilters)
87 87
 	if err != nil {
88 88
 		return err
89 89
 	}
... ...
@@ -453,7 +453,7 @@ func initRouter(opts routerOptions) {
453 453
 		container.NewRouter(opts.daemon, decoder),
454 454
 		image.NewRouter(opts.daemon.ImageService()),
455 455
 		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache),
456
-		volume.NewRouter(opts.daemon),
456
+		volume.NewRouter(opts.daemon.VolumesService()),
457 457
 		build.NewRouter(opts.buildBackend, opts.daemon),
458 458
 		sessionrouter.NewRouter(opts.sessionManager),
459 459
 		swarmrouter.NewRouter(opts.cluster),
... ...
@@ -595,6 +595,7 @@ func createAndStartCluster(cli *DaemonCli, d *daemon.Daemon) (*cluster.Cluster,
595 595
 		Root:                   cli.Config.Root,
596 596
 		Name:                   name,
597 597
 		Backend:                d,
598
+		VolumeBackend:          d.VolumesService(),
598 599
 		ImageBackend:           d.ImageService(),
599 600
 		PluginBackend:          d.PluginManager(),
600 601
 		NetworkSubnetsProvider: d,
... ...
@@ -127,7 +127,7 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st
127 127
 		return err
128 128
 	}
129 129
 
130
-	if _, err = ioutil.ReadDir(rootfs); err != nil {
130
+	if _, err := os.Stat(rootfs); err != nil {
131 131
 		if os.IsNotExist(err) {
132 132
 			return nil
133 133
 		}
... ...
@@ -85,6 +85,7 @@ type Config struct {
85 85
 	Backend                executorpkg.Backend
86 86
 	ImageBackend           executorpkg.ImageBackend
87 87
 	PluginBackend          plugin.Backend
88
+	VolumeBackend          executorpkg.VolumeBackend
88 89
 	NetworkSubnetsProvider NetworkSubnetsProvider
89 90
 
90 91
 	// DefaultAdvertiseAddr is the default host/IP or network interface to use
... ...
@@ -18,6 +18,7 @@ import (
18 18
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
19 19
 	networkSettings "github.com/docker/docker/daemon/network"
20 20
 	"github.com/docker/docker/plugin"
21
+	volumeopts "github.com/docker/docker/volume/service/opts"
21 22
 	"github.com/docker/libnetwork"
22 23
 	"github.com/docker/libnetwork/cluster"
23 24
 	networktypes "github.com/docker/libnetwork/types"
... ...
@@ -47,7 +48,6 @@ type Backend interface {
47 47
 	SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
48 48
 	SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error
49 49
 	SystemInfo() (*types.Info, error)
50
-	VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
51 50
 	Containers(config *types.ContainerListOptions) ([]*types.Container, error)
52 51
 	SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error
53 52
 	DaemonJoinsCluster(provider cluster.Provider)
... ...
@@ -62,6 +62,11 @@ type Backend interface {
62 62
 	GetAttachmentStore() *networkSettings.AttachmentStore
63 63
 }
64 64
 
65
+// VolumeBackend is used by an executor to perform volume operations
66
+type VolumeBackend interface {
67
+	Create(ctx context.Context, name, driverName string, opts ...volumeopts.CreateOption) (*types.Volume, error)
68
+}
69
+
65 70
 // ImageBackend is used by an executor to perform image operations
66 71
 type ImageBackend interface {
67 72
 	PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
... ...
@@ -22,6 +22,7 @@ import (
22 22
 	"github.com/docker/docker/daemon"
23 23
 	"github.com/docker/docker/daemon/cluster/convert"
24 24
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
25
+	volumeopts "github.com/docker/docker/volume/service/opts"
25 26
 	"github.com/docker/libnetwork"
26 27
 	"github.com/docker/swarmkit/agent/exec"
27 28
 	"github.com/docker/swarmkit/api"
... ...
@@ -36,23 +37,25 @@ import (
36 36
 // are mostly naked calls to the client API, seeded with information from
37 37
 // containerConfig.
38 38
 type containerAdapter struct {
39
-	backend      executorpkg.Backend
40
-	imageBackend executorpkg.ImageBackend
41
-	container    *containerConfig
42
-	dependencies exec.DependencyGetter
39
+	backend       executorpkg.Backend
40
+	imageBackend  executorpkg.ImageBackend
41
+	volumeBackend executorpkg.VolumeBackend
42
+	container     *containerConfig
43
+	dependencies  exec.DependencyGetter
43 44
 }
44 45
 
45
-func newContainerAdapter(b executorpkg.Backend, i executorpkg.ImageBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*containerAdapter, error) {
46
+func newContainerAdapter(b executorpkg.Backend, i executorpkg.ImageBackend, v executorpkg.VolumeBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*containerAdapter, error) {
46 47
 	ctnr, err := newContainerConfig(task, node)
47 48
 	if err != nil {
48 49
 		return nil, err
49 50
 	}
50 51
 
51 52
 	return &containerAdapter{
52
-		container:    ctnr,
53
-		backend:      b,
54
-		imageBackend: i,
55
-		dependencies: dependencies,
53
+		container:     ctnr,
54
+		backend:       b,
55
+		imageBackend:  i,
56
+		volumeBackend: v,
57
+		dependencies:  dependencies,
56 58
 	}, nil
57 59
 }
58 60
 
... ...
@@ -388,7 +391,10 @@ func (c *containerAdapter) createVolumes(ctx context.Context) error {
388 388
 		req := c.container.volumeCreateRequest(&mount)
389 389
 
390 390
 		// Check if this volume exists on the engine
391
-		if _, err := c.backend.VolumeCreate(req.Name, req.Driver, req.DriverOpts, req.Labels); err != nil {
391
+		if _, err := c.volumeBackend.Create(ctx, req.Name, req.Driver,
392
+			volumeopts.WithCreateOptions(req.DriverOpts),
393
+			volumeopts.WithCreateLabels(req.Labels),
394
+		); err != nil {
392 395
 			// TODO(amitshukla): Today, volume create through the engine api does not return an error
393 396
 			// when the named volume with the same parameters already exists.
394 397
 			// It returns an error if the driver name is different - that is a valid error
... ...
@@ -21,8 +21,8 @@ type networkAttacherController struct {
21 21
 	closed  chan struct{}
22 22
 }
23 23
 
24
-func newNetworkAttacherController(b executorpkg.Backend, i executorpkg.ImageBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
25
-	adapter, err := newContainerAdapter(b, i, task, node, dependencies)
24
+func newNetworkAttacherController(b executorpkg.Backend, i executorpkg.ImageBackend, v executorpkg.VolumeBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
25
+	adapter, err := newContainerAdapter(b, i, v, task, node, dependencies)
26 26
 	if err != nil {
27 27
 		return nil, err
28 28
 	}
... ...
@@ -40,8 +40,8 @@ type controller struct {
40 40
 var _ exec.Controller = &controller{}
41 41
 
42 42
 // NewController returns a docker exec runner for the provided task.
43
-func newController(b executorpkg.Backend, i executorpkg.ImageBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*controller, error) {
44
-	adapter, err := newContainerAdapter(b, i, task, node, dependencies)
43
+func newController(b executorpkg.Backend, i executorpkg.ImageBackend, v executorpkg.VolumeBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*controller, error) {
44
+	adapter, err := newContainerAdapter(b, i, v, task, node, dependencies)
45 45
 	if err != nil {
46 46
 		return nil, err
47 47
 	}
... ...
@@ -28,17 +28,19 @@ type executor struct {
28 28
 	backend       executorpkg.Backend
29 29
 	imageBackend  executorpkg.ImageBackend
30 30
 	pluginBackend plugin.Backend
31
+	volumeBackend executorpkg.VolumeBackend
31 32
 	dependencies  exec.DependencyManager
32 33
 	mutex         sync.Mutex // This mutex protects the following node field
33 34
 	node          *api.NodeDescription
34 35
 }
35 36
 
36 37
 // NewExecutor returns an executor from the docker client.
37
-func NewExecutor(b executorpkg.Backend, p plugin.Backend, i executorpkg.ImageBackend) exec.Executor {
38
+func NewExecutor(b executorpkg.Backend, p plugin.Backend, i executorpkg.ImageBackend, v executorpkg.VolumeBackend) exec.Executor {
38 39
 	return &executor{
39 40
 		backend:       b,
40 41
 		pluginBackend: p,
41 42
 		imageBackend:  i,
43
+		volumeBackend: v,
42 44
 		dependencies:  agent.NewDependencyManager(),
43 45
 	}
44 46
 }
... ...
@@ -211,7 +213,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
211 211
 	e.mutex.Unlock()
212 212
 
213 213
 	if t.Spec.GetAttachment() != nil {
214
-		return newNetworkAttacherController(e.backend, e.imageBackend, t, nodeDescription, dependencyGetter)
214
+		return newNetworkAttacherController(e.backend, e.imageBackend, e.volumeBackend, t, nodeDescription, dependencyGetter)
215 215
 	}
216 216
 
217 217
 	var ctlr exec.Controller
... ...
@@ -240,7 +242,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
240 240
 			return ctlr, fmt.Errorf("unsupported runtime type: %q", runtimeKind)
241 241
 		}
242 242
 	case *api.TaskSpec_Container:
243
-		c, err := newController(e.backend, e.imageBackend, t, nodeDescription, dependencyGetter)
243
+		c, err := newController(e.backend, e.imageBackend, e.volumeBackend, t, nodeDescription, dependencyGetter)
244 244
 		if err != nil {
245 245
 			return ctlr, err
246 246
 		}
... ...
@@ -52,7 +52,7 @@ func TestHealthStates(t *testing.T) {
52 52
 		EventsService: e,
53 53
 	}
54 54
 
55
-	controller, err := newController(daemon, nil, task, nil, nil)
55
+	controller, err := newController(daemon, nil, nil, task, nil, nil)
56 56
 	if err != nil {
57 57
 		t.Fatalf("create controller fail %v", err)
58 58
 	}
... ...
@@ -12,7 +12,7 @@ import (
12 12
 )
13 13
 
14 14
 func newTestControllerWithMount(m api.Mount) (*controller, error) {
15
-	return newController(&daemon.Daemon{}, nil, &api.Task{
15
+	return newController(&daemon.Daemon{}, nil, nil, &api.Task{
16 16
 		ID:        stringid.GenerateRandomID(),
17 17
 		ServiceID: stringid.GenerateRandomID(),
18 18
 		Spec: api.TaskSpec{
... ...
@@ -123,7 +123,9 @@ func (n *nodeRunner) start(conf nodeStartConfig) error {
123 123
 		Executor: container.NewExecutor(
124 124
 			n.cluster.config.Backend,
125 125
 			n.cluster.config.PluginBackend,
126
-			n.cluster.config.ImageBackend),
126
+			n.cluster.config.ImageBackend,
127
+			n.cluster.config.VolumeBackend,
128
+		),
127 129
 		HeartbeatTick: n.cluster.config.RaftHeartbeatTick,
128 130
 		// Recommended value in etcd/raft is 10 x (HeartbeatTick).
129 131
 		// Lower values were seen to have caused instability because of
... ...
@@ -7,8 +7,6 @@ import (
7 7
 	"strings"
8 8
 	"time"
9 9
 
10
-	"github.com/pkg/errors"
11
-
12 10
 	"github.com/docker/docker/api/types"
13 11
 	containertypes "github.com/docker/docker/api/types/container"
14 12
 	networktypes "github.com/docker/docker/api/types/network"
... ...
@@ -16,10 +14,10 @@ import (
16 16
 	"github.com/docker/docker/errdefs"
17 17
 	"github.com/docker/docker/image"
18 18
 	"github.com/docker/docker/pkg/idtools"
19
-	"github.com/docker/docker/pkg/stringid"
20 19
 	"github.com/docker/docker/pkg/system"
21 20
 	"github.com/docker/docker/runconfig"
22 21
 	"github.com/opencontainers/selinux/go-selinux/label"
22
+	"github.com/pkg/errors"
23 23
 	"github.com/sirupsen/logrus"
24 24
 )
25 25
 
... ...
@@ -255,24 +253,6 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig)
255 255
 	return nil, nil
256 256
 }
257 257
 
258
-// VolumeCreate creates a volume with the specified name, driver, and opts
259
-// This is called directly from the Engine API
260
-func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) {
261
-	if name == "" {
262
-		name = stringid.GenerateNonCryptoID()
263
-	}
264
-
265
-	v, err := daemon.volumes.Create(name, driverName, opts, labels)
266
-	if err != nil {
267
-		return nil, err
268
-	}
269
-
270
-	daemon.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()})
271
-	apiV := volumeToAPIType(v)
272
-	apiV.Mountpoint = v.Path()
273
-	return apiV, nil
274
-}
275
-
276 258
 func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error {
277 259
 	if img != nil && img.Config != nil {
278 260
 		if err := merge(config, img.Config); err != nil {
... ...
@@ -3,6 +3,7 @@
3 3
 package daemon // import "github.com/docker/docker/daemon"
4 4
 
5 5
 import (
6
+	"context"
6 7
 	"fmt"
7 8
 	"os"
8 9
 	"path/filepath"
... ...
@@ -11,6 +12,7 @@ import (
11 11
 	mounttypes "github.com/docker/docker/api/types/mount"
12 12
 	"github.com/docker/docker/container"
13 13
 	"github.com/docker/docker/pkg/stringid"
14
+	volumeopts "github.com/docker/docker/volume/service/opts"
14 15
 	"github.com/opencontainers/selinux/go-selinux/label"
15 16
 	"github.com/sirupsen/logrus"
16 17
 )
... ...
@@ -46,16 +48,16 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
46 46
 			return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
47 47
 		}
48 48
 
49
-		v, err := daemon.volumes.CreateWithRef(name, hostConfig.VolumeDriver, container.ID, nil, nil)
49
+		v, err := daemon.volumes.Create(context.TODO(), name, hostConfig.VolumeDriver, volumeopts.WithCreateReference(container.ID))
50 50
 		if err != nil {
51 51
 			return err
52 52
 		}
53 53
 
54
-		if err := label.Relabel(v.Path(), container.MountLabel, true); err != nil {
54
+		if err := label.Relabel(v.Mountpoint, container.MountLabel, true); err != nil {
55 55
 			return err
56 56
 		}
57 57
 
58
-		container.AddMountPointWithVolume(destination, v, true)
58
+		container.AddMountPointWithVolume(destination, &volumeWrapper{v: v, s: daemon.volumes}, true)
59 59
 	}
60 60
 	return daemon.populateVolumes(container)
61 61
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package daemon // import "github.com/docker/docker/daemon"
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"fmt"
5 6
 	"runtime"
6 7
 
... ...
@@ -8,6 +9,7 @@ import (
8 8
 	"github.com/docker/docker/container"
9 9
 	"github.com/docker/docker/pkg/stringid"
10 10
 	volumemounts "github.com/docker/docker/volume/mounts"
11
+	volumeopts "github.com/docker/docker/volume/service/opts"
11 12
 )
12 13
 
13 14
 // createContainerOSSpecificSettings performs host-OS specific container create functionality
... ...
@@ -49,7 +51,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
49 49
 
50 50
 		// Create the volume in the volume driver. If it doesn't exist,
51 51
 		// a new one will be created.
52
-		v, err := daemon.volumes.CreateWithRef(mp.Name, volumeDriver, container.ID, nil, nil)
52
+		v, err := daemon.volumes.Create(context.TODO(), mp.Name, volumeDriver, volumeopts.WithCreateReference(container.ID))
53 53
 		if err != nil {
54 54
 			return err
55 55
 		}
... ...
@@ -85,7 +87,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
85 85
 		//	}
86 86
 
87 87
 		// Add it to container.MountPoints
88
-		container.AddMountPointWithVolume(mp.Destination, v, mp.RW)
88
+		container.AddMountPointWithVolume(mp.Destination, &volumeWrapper{v: v, s: daemon.volumes}, mp.RW)
89 89
 	}
90 90
 	return nil
91 91
 }
... ...
@@ -52,9 +52,7 @@ import (
52 52
 	refstore "github.com/docker/docker/reference"
53 53
 	"github.com/docker/docker/registry"
54 54
 	"github.com/docker/docker/runconfig"
55
-	volumedrivers "github.com/docker/docker/volume/drivers"
56
-	"github.com/docker/docker/volume/local"
57
-	"github.com/docker/docker/volume/store"
55
+	volumesservice "github.com/docker/docker/volume/service"
58 56
 	"github.com/docker/libnetwork"
59 57
 	"github.com/docker/libnetwork/cluster"
60 58
 	nwconfig "github.com/docker/libnetwork/config"
... ...
@@ -83,7 +81,7 @@ type Daemon struct {
83 83
 	RegistryService   registry.Service
84 84
 	EventsService     *events.Events
85 85
 	netController     libnetwork.NetworkController
86
-	volumes           *store.VolumeStore
86
+	volumes           *volumesservice.VolumesService
87 87
 	discoveryWatcher  discovery.Reloader
88 88
 	root              string
89 89
 	seccompEnabled    bool
... ...
@@ -784,8 +782,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
784 784
 		return nil, err
785 785
 	}
786 786
 
787
-	// Configure the volumes driver
788
-	volStore, err := d.configureVolumes(rootIDs)
787
+	d.volumes, err = volumesservice.NewVolumeService(config.Root, d.PluginStore, rootIDs, d)
789 788
 	if err != nil {
790 789
 		return nil, err
791 790
 	}
... ...
@@ -855,7 +852,6 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
855 855
 	d.statsCollector = d.newStatsCollector(1 * time.Second)
856 856
 
857 857
 	d.EventsService = events.New()
858
-	d.volumes = volStore
859 858
 	d.root = config.Root
860 859
 	d.idMappings = idMappings
861 860
 	d.seccompEnabled = sysInfo.Seccomp
... ...
@@ -1144,18 +1140,6 @@ func setDefaultMtu(conf *config.Config) {
1144 1144
 	conf.Mtu = config.DefaultNetworkMtu
1145 1145
 }
1146 1146
 
1147
-func (daemon *Daemon) configureVolumes(rootIDs idtools.IDPair) (*store.VolumeStore, error) {
1148
-	volumeDriver, err := local.New(daemon.configStore.Root, rootIDs)
1149
-	if err != nil {
1150
-		return nil, err
1151
-	}
1152
-	drivers := volumedrivers.NewStore(daemon.PluginStore)
1153
-	if !drivers.Register(volumeDriver, volumeDriver.Name()) {
1154
-		return nil, errors.New("local volume driver could not be registered")
1155
-	}
1156
-	return store.New(daemon.configStore.Root, drivers)
1157
-}
1158
-
1159 1147
 // IsShuttingDown tells whether the daemon is shutting down or not
1160 1148
 func (daemon *Daemon) IsShuttingDown() bool {
1161 1149
 	return daemon.shutdown
... ...
@@ -13,9 +13,7 @@ import (
13 13
 	_ "github.com/docker/docker/pkg/discovery/memory"
14 14
 	"github.com/docker/docker/pkg/idtools"
15 15
 	"github.com/docker/docker/pkg/truncindex"
16
-	volumedrivers "github.com/docker/docker/volume/drivers"
17
-	"github.com/docker/docker/volume/local"
18
-	"github.com/docker/docker/volume/store"
16
+	volumesservice "github.com/docker/docker/volume/service"
19 17
 	"github.com/docker/go-connections/nat"
20 18
 	"github.com/docker/libnetwork"
21 19
 	"github.com/gotestyourself/gotestyourself/assert"
... ...
@@ -120,18 +118,10 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
120 120
 		repository: tmp,
121 121
 		root:       tmp,
122 122
 	}
123
-	drivers := volumedrivers.NewStore(nil)
124
-	daemon.volumes, err = store.New(tmp, drivers)
123
+	daemon.volumes, err = volumesservice.NewVolumeService(tmp, nil, idtools.IDPair{UID: 0, GID: 0}, daemon)
125 124
 	if err != nil {
126 125
 		return nil, err
127 126
 	}
128
-
129
-	volumesDriver, err := local.New(tmp, idtools.IDPair{UID: 0, GID: 0})
130
-	if err != nil {
131
-		return nil, err
132
-	}
133
-	drivers.Register(volumesDriver, volumesDriver.Name())
134
-
135 127
 	return daemon, nil
136 128
 }
137 129
 
... ...
@@ -11,8 +11,6 @@ import (
11 11
 	"github.com/docker/docker/container"
12 12
 	"github.com/docker/docker/errdefs"
13 13
 	"github.com/docker/docker/pkg/system"
14
-	"github.com/docker/docker/volume"
15
-	volumestore "github.com/docker/docker/volume/store"
16 14
 	"github.com/pkg/errors"
17 15
 	"github.com/sirupsen/logrus"
18 16
 )
... ...
@@ -152,35 +150,3 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
152 152
 	daemon.LogContainerEvent(container, "destroy")
153 153
 	return nil
154 154
 }
155
-
156
-// VolumeRm removes the volume with the given name.
157
-// If the volume is referenced by a container it is not removed
158
-// This is called directly from the Engine API
159
-func (daemon *Daemon) VolumeRm(name string, force bool) error {
160
-	v, err := daemon.volumes.Get(name)
161
-	if err != nil {
162
-		if force && volumestore.IsNotExist(err) {
163
-			return nil
164
-		}
165
-		return err
166
-	}
167
-
168
-	err = daemon.volumeRm(v)
169
-	if err != nil && volumestore.IsInUse(err) {
170
-		return errdefs.Conflict(err)
171
-	}
172
-
173
-	if err == nil || force {
174
-		daemon.volumes.Purge(name)
175
-		return nil
176
-	}
177
-	return err
178
-}
179
-
180
-func (daemon *Daemon) volumeRm(v volume.Volume) error {
181
-	if err := daemon.volumes.Remove(v); err != nil {
182
-		return errors.Wrap(err, "unable to remove volume")
183
-	}
184
-	daemon.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()})
185
-	return nil
186
-}
... ...
@@ -7,9 +7,6 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9 9
 	"github.com/docker/docker/api/types/filters"
10
-	"github.com/docker/docker/pkg/directory"
11
-	"github.com/docker/docker/volume"
12
-	"github.com/sirupsen/logrus"
13 10
 )
14 11
 
15 12
 // SystemDiskUsage returns information about the daemon data disk usage
... ...
@@ -34,39 +31,11 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
34 34
 		return nil, fmt.Errorf("failed to retrieve image list: %v", err)
35 35
 	}
36 36
 
37
-	volumes, err := daemon.volumes.FilterByDriver(volume.DefaultDriverName)
37
+	localVolumes, err := daemon.volumes.LocalVolumesSize(ctx)
38 38
 	if err != nil {
39 39
 		return nil, err
40 40
 	}
41 41
 
42
-	var allVolumes []*types.Volume
43
-	for _, v := range volumes {
44
-		select {
45
-		case <-ctx.Done():
46
-			return nil, ctx.Err()
47
-		default:
48
-		}
49
-		if d, ok := v.(volume.DetailedVolume); ok {
50
-			if len(d.Options()) > 0 {
51
-				// skip local volumes with mount options since these could have external
52
-				// mounted filesystems that will be slow to enumerate.
53
-				continue
54
-			}
55
-		}
56
-
57
-		name := v.Name()
58
-		refs := daemon.volumes.Refs(v)
59
-
60
-		tv := volumeToAPIType(v)
61
-		sz, err := directory.Size(ctx, v.Path())
62
-		if err != nil {
63
-			logrus.Warnf("failed to determine size of volume %v", name)
64
-			sz = -1
65
-		}
66
-		tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))}
67
-		allVolumes = append(allVolumes, tv)
68
-	}
69
-
70 42
 	allLayersSize, err := daemon.imageService.LayerDiskUsage(ctx)
71 43
 	if err != nil {
72 44
 		return nil, err
... ...
@@ -75,7 +44,7 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
75 75
 	return &types.DiskUsage{
76 76
 		LayersSize: allLayersSize,
77 77
 		Containers: allContainers,
78
-		Volumes:    allVolumes,
78
+		Volumes:    localVolumes,
79 79
 		Images:     allImages,
80 80
 	}, nil
81 81
 }
... ...
@@ -18,10 +18,6 @@ func containerNotFound(id string) error {
18 18
 	return objNotFoundError{"container", id}
19 19
 }
20 20
 
21
-func volumeNotFound(id string) error {
22
-	return objNotFoundError{"volume", id}
23
-}
24
-
25 21
 type objNotFoundError struct {
26 22
 	object string
27 23
 	id     string
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"github.com/docker/docker/image"
15 15
 	"github.com/docker/docker/layer"
16 16
 	"github.com/opencontainers/go-digest"
17
+	"github.com/pkg/errors"
17 18
 	"github.com/sirupsen/logrus"
18 19
 )
19 20
 
... ...
@@ -26,7 +27,7 @@ var imagesAcceptedFilters = map[string]bool{
26 26
 
27 27
 // errPruneRunning is returned when a prune request is received while
28 28
 // one is in progress
29
-var errPruneRunning = fmt.Errorf("a prune operation is already running")
29
+var errPruneRunning = errdefs.Conflict(errors.New("a prune operation is already running"))
30 30
 
31 31
 // ImagesPrune removes unused images
32 32
 func (i *ImageService) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
... ...
@@ -13,7 +13,6 @@ import (
13 13
 	"github.com/docker/docker/container"
14 14
 	"github.com/docker/docker/daemon/network"
15 15
 	"github.com/docker/docker/errdefs"
16
-	volumestore "github.com/docker/docker/volume/store"
17 16
 	"github.com/docker/go-connections/nat"
18 17
 )
19 18
 
... ...
@@ -236,22 +235,6 @@ func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, err
236 236
 	}, nil
237 237
 }
238 238
 
239
-// VolumeInspect looks up a volume by name. An error is returned if
240
-// the volume cannot be found.
241
-func (daemon *Daemon) VolumeInspect(name string) (*types.Volume, error) {
242
-	v, err := daemon.volumes.Get(name)
243
-	if err != nil {
244
-		if volumestore.IsNotExist(err) {
245
-			return nil, volumeNotFound(name)
246
-		}
247
-		return nil, errdefs.System(err)
248
-	}
249
-	apiV := volumeToAPIType(v)
250
-	apiV.Mountpoint = v.Path()
251
-	apiV.Status = v.Status()
252
-	return apiV, nil
253
-}
254
-
255 239
 func (daemon *Daemon) getBackwardsCompatibleNetworkSettings(settings *network.Settings) *v1p20.NetworkSettings {
256 240
 	result := &v1p20.NetworkSettings{
257 241
 		NetworkSettingsBase: types.NetworkSettingsBase{
... ...
@@ -12,19 +12,11 @@ import (
12 12
 	"github.com/docker/docker/daemon/images"
13 13
 	"github.com/docker/docker/errdefs"
14 14
 	"github.com/docker/docker/image"
15
-	"github.com/docker/docker/volume"
16 15
 	"github.com/docker/go-connections/nat"
17 16
 	"github.com/pkg/errors"
18 17
 	"github.com/sirupsen/logrus"
19 18
 )
20 19
 
21
-var acceptedVolumeFilterTags = map[string]bool{
22
-	"dangling": true,
23
-	"name":     true,
24
-	"driver":   true,
25
-	"label":    true,
26
-}
27
-
28 20
 var acceptedPsFilterTags = map[string]bool{
29 21
 	"ancestor":  true,
30 22
 	"before":    true,
... ...
@@ -605,87 +597,6 @@ func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*ty
605 605
 	return &c, nil
606 606
 }
607 607
 
608
-// Volumes lists known volumes, using the filter to restrict the range
609
-// of volumes returned.
610
-func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
611
-	var (
612
-		volumesOut []*types.Volume
613
-	)
614
-	volFilters, err := filters.FromJSON(filter)
615
-	if err != nil {
616
-		return nil, nil, err
617
-	}
618
-
619
-	if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
620
-		return nil, nil, err
621
-	}
622
-
623
-	volumes, warnings, err := daemon.volumes.List()
624
-	if err != nil {
625
-		return nil, nil, err
626
-	}
627
-
628
-	filterVolumes, err := daemon.filterVolumes(volumes, volFilters)
629
-	if err != nil {
630
-		return nil, nil, err
631
-	}
632
-	for _, v := range filterVolumes {
633
-		apiV := volumeToAPIType(v)
634
-		if vv, ok := v.(interface {
635
-			CachedPath() string
636
-		}); ok {
637
-			apiV.Mountpoint = vv.CachedPath()
638
-		} else {
639
-			apiV.Mountpoint = v.Path()
640
-		}
641
-		volumesOut = append(volumesOut, apiV)
642
-	}
643
-	return volumesOut, warnings, nil
644
-}
645
-
646
-// filterVolumes filters volume list according to user specified filter
647
-// and returns user chosen volumes
648
-func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) {
649
-	// if filter is empty, return original volume list
650
-	if filter.Len() == 0 {
651
-		return vols, nil
652
-	}
653
-
654
-	var retVols []volume.Volume
655
-	for _, vol := range vols {
656
-		if filter.Contains("name") {
657
-			if !filter.Match("name", vol.Name()) {
658
-				continue
659
-			}
660
-		}
661
-		if filter.Contains("driver") {
662
-			if !filter.ExactMatch("driver", vol.DriverName()) {
663
-				continue
664
-			}
665
-		}
666
-		if filter.Contains("label") {
667
-			v, ok := vol.(volume.DetailedVolume)
668
-			if !ok {
669
-				continue
670
-			}
671
-			if !filter.MatchKVList("label", v.Labels()) {
672
-				continue
673
-			}
674
-		}
675
-		retVols = append(retVols, vol)
676
-	}
677
-	danglingOnly := false
678
-	if filter.Contains("dangling") {
679
-		if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
680
-			danglingOnly = true
681
-		} else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
682
-			return nil, invalidFilter{"dangling", filter.Get("dangling")}
683
-		}
684
-		retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly)
685
-	}
686
-	return retVols, nil
687
-}
688
-
689 608
 func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
690 609
 	if !ancestorMap[imageID] {
691 610
 		for _, id := range getChildren(imageID) {
... ...
@@ -1,12 +1,13 @@
1 1
 package daemon // import "github.com/docker/docker/daemon"
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"fmt"
5 6
 	"strings"
6 7
 
7 8
 	mounttypes "github.com/docker/docker/api/types/mount"
8 9
 	"github.com/docker/docker/container"
9
-	volumestore "github.com/docker/docker/volume/store"
10
+	volumesservice "github.com/docker/docker/volume/service"
10 11
 )
11 12
 
12 13
 func (daemon *Daemon) prepareMountPoints(container *container.Container) error {
... ...
@@ -20,11 +21,12 @@ func (daemon *Daemon) prepareMountPoints(container *container.Container) error {
20 20
 
21 21
 func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool) error {
22 22
 	var rmErrors []string
23
+	ctx := context.TODO()
23 24
 	for _, m := range container.MountPoints {
24 25
 		if m.Type != mounttypes.TypeVolume || m.Volume == nil {
25 26
 			continue
26 27
 		}
27
-		daemon.volumes.Dereference(m.Volume, container.ID)
28
+		daemon.volumes.Release(ctx, m.Volume.Name(), container.ID)
28 29
 		if !rm {
29 30
 			continue
30 31
 		}
... ...
@@ -35,13 +37,13 @@ func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool)
35 35
 			continue
36 36
 		}
37 37
 
38
-		err := daemon.volumes.Remove(m.Volume)
38
+		err := daemon.volumes.Remove(ctx, m.Volume.Name())
39 39
 		// Ignore volume in use errors because having this
40 40
 		// volume being referenced by other container is
41 41
 		// not an error, but an implementation detail.
42 42
 		// This prevents docker from logging "ERROR: Volume in use"
43 43
 		// where there is another container using the volume.
44
-		if err != nil && !volumestore.IsInUse(err) {
44
+		if err != nil && !volumesservice.IsInUse(err) {
45 45
 			rmErrors = append(rmErrors, err.Error())
46 46
 		}
47 47
 	}
... ...
@@ -10,27 +10,23 @@ import (
10 10
 	"github.com/docker/docker/api/types"
11 11
 	"github.com/docker/docker/api/types/filters"
12 12
 	timetypes "github.com/docker/docker/api/types/time"
13
-	"github.com/docker/docker/pkg/directory"
13
+	"github.com/docker/docker/errdefs"
14 14
 	"github.com/docker/docker/runconfig"
15
-	"github.com/docker/docker/volume"
16 15
 	"github.com/docker/libnetwork"
16
+	"github.com/pkg/errors"
17 17
 	"github.com/sirupsen/logrus"
18 18
 )
19 19
 
20 20
 var (
21 21
 	// errPruneRunning is returned when a prune request is received while
22 22
 	// one is in progress
23
-	errPruneRunning = fmt.Errorf("a prune operation is already running")
23
+	errPruneRunning = errdefs.Conflict(errors.New("a prune operation is already running"))
24 24
 
25 25
 	containersAcceptedFilters = map[string]bool{
26 26
 		"label":  true,
27 27
 		"label!": true,
28 28
 		"until":  true,
29 29
 	}
30
-	volumesAcceptedFilters = map[string]bool{
31
-		"label":  true,
32
-		"label!": true,
33
-	}
34 30
 
35 31
 	networksAcceptedFilters = map[string]bool{
36 32
 		"label":  true,
... ...
@@ -92,67 +88,6 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
92 92
 	return rep, nil
93 93
 }
94 94
 
95
-// VolumesPrune removes unused local volumes
96
-func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (*types.VolumesPruneReport, error) {
97
-	if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
98
-		return nil, errPruneRunning
99
-	}
100
-	defer atomic.StoreInt32(&daemon.pruneRunning, 0)
101
-
102
-	// make sure that only accepted filters have been received
103
-	err := pruneFilters.Validate(volumesAcceptedFilters)
104
-	if err != nil {
105
-		return nil, err
106
-	}
107
-
108
-	rep := &types.VolumesPruneReport{}
109
-
110
-	volumes, err := daemon.volumes.FilterByDriver(volume.DefaultDriverName)
111
-	if err != nil {
112
-		return nil, err
113
-	}
114
-
115
-	for _, v := range volumes {
116
-		select {
117
-		case <-ctx.Done():
118
-			logrus.Debugf("VolumesPrune operation cancelled: %#v", *rep)
119
-			err := ctx.Err()
120
-			if err == context.Canceled {
121
-				return rep, nil
122
-			}
123
-			return rep, err
124
-		default:
125
-		}
126
-
127
-		name := v.Name()
128
-		refs := daemon.volumes.Refs(v)
129
-
130
-		if len(refs) == 0 {
131
-			detailedVolume, ok := v.(volume.DetailedVolume)
132
-			if ok {
133
-				if !matchLabels(pruneFilters, detailedVolume.Labels()) {
134
-					continue
135
-				}
136
-			}
137
-
138
-			vSize, err := directory.Size(ctx, v.Path())
139
-			if err != nil {
140
-				logrus.Warnf("could not determine size of volume %s: %v", name, err)
141
-			}
142
-			err = daemon.volumeRm(v)
143
-			if err != nil {
144
-				logrus.Warnf("could not remove volume %s: %v", name, err)
145
-				continue
146
-			}
147
-			rep.SpaceReclaimed += uint64(vSize)
148
-			rep.VolumesDeleted = append(rep.VolumesDeleted, name)
149
-		}
150
-
151
-	}
152
-
153
-	return rep, nil
154
-}
155
-
156 95
 // localNetworksPrune removes unused local networks
157 96
 func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport {
158 97
 	rep := &types.NetworksPruneReport{}
... ...
@@ -1,6 +1,7 @@
1 1
 package daemon // import "github.com/docker/docker/daemon"
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"os"
5 6
 	"path/filepath"
6 7
 	"reflect"
... ...
@@ -15,6 +16,8 @@ import (
15 15
 	"github.com/docker/docker/errdefs"
16 16
 	"github.com/docker/docker/volume"
17 17
 	volumemounts "github.com/docker/docker/volume/mounts"
18
+	"github.com/docker/docker/volume/service"
19
+	volumeopts "github.com/docker/docker/volume/service/opts"
18 20
 	"github.com/pkg/errors"
19 21
 	"github.com/sirupsen/logrus"
20 22
 )
... ...
@@ -27,23 +30,6 @@ var (
27 27
 
28 28
 type mounts []container.Mount
29 29
 
30
-// volumeToAPIType converts a volume.Volume to the type used by the Engine API
31
-func volumeToAPIType(v volume.Volume) *types.Volume {
32
-	createdAt, _ := v.CreatedAt()
33
-	tv := &types.Volume{
34
-		Name:      v.Name(),
35
-		Driver:    v.DriverName(),
36
-		CreatedAt: createdAt.Format(time.RFC3339),
37
-	}
38
-	if v, ok := v.(volume.DetailedVolume); ok {
39
-		tv.Labels = v.Labels()
40
-		tv.Options = v.Options()
41
-		tv.Scope = v.Scope()
42
-	}
43
-
44
-	return tv
45
-}
46
-
47 30
 // Len returns the number of mounts. Used in sorting.
48 31
 func (m mounts) Len() int {
49 32
 	return len(m)
... ...
@@ -78,6 +64,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
78 78
 	mountPoints := map[string]*volumemounts.MountPoint{}
79 79
 	parser := volumemounts.NewParser(container.OS)
80 80
 
81
+	ctx := context.TODO()
81 82
 	defer func() {
82 83
 		// clean up the container mountpoints once return with error
83 84
 		if retErr != nil {
... ...
@@ -85,7 +72,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
85 85
 				if m.Volume == nil {
86 86
 					continue
87 87
 				}
88
-				daemon.volumes.Dereference(m.Volume, container.ID)
88
+				daemon.volumes.Release(ctx, m.Volume.Name(), container.ID)
89 89
 			}
90 90
 		}
91 91
 	}()
... ...
@@ -94,7 +81,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
94 94
 		if v, ok := mountPoints[destination]; ok {
95 95
 			logrus.Debugf("Duplicate mount point '%s'", destination)
96 96
 			if v.Volume != nil {
97
-				daemon.volumes.Dereference(v.Volume, container.ID)
97
+				daemon.volumes.Release(ctx, v.Volume.Name(), container.ID)
98 98
 			}
99 99
 		}
100 100
 	}
... ...
@@ -130,11 +117,11 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
130 130
 			}
131 131
 
132 132
 			if len(cp.Source) == 0 {
133
-				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
133
+				v, err := daemon.volumes.Get(ctx, cp.Name, volumeopts.WithGetDriver(cp.Driver), volumeopts.WithGetReference(container.ID))
134 134
 				if err != nil {
135 135
 					return err
136 136
 				}
137
-				cp.Volume = v
137
+				cp.Volume = &volumeWrapper{v: v, s: daemon.volumes}
138 138
 			}
139 139
 			dereferenceIfExists(cp.Destination)
140 140
 			mountPoints[cp.Destination] = cp
... ...
@@ -163,14 +150,14 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
163 163
 
164 164
 		if bind.Type == mounttypes.TypeVolume {
165 165
 			// create the volume
166
-			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil)
166
+			v, err := daemon.volumes.Create(ctx, bind.Name, bind.Driver, volumeopts.WithCreateReference(container.ID))
167 167
 			if err != nil {
168 168
 				return err
169 169
 			}
170
-			bind.Volume = v
171
-			bind.Source = v.Path()
170
+			bind.Volume = &volumeWrapper{v: v, s: daemon.volumes}
171
+			bind.Source = v.Mountpoint
172 172
 			// bind.Name is an already existing volume, we need to use that here
173
-			bind.Driver = v.DriverName()
173
+			bind.Driver = v.Driver
174 174
 			if bind.Driver == volume.DefaultDriverName {
175 175
 				setBindModeIfNull(bind)
176 176
 			}
... ...
@@ -199,30 +186,30 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
199 199
 		}
200 200
 
201 201
 		if mp.Type == mounttypes.TypeVolume {
202
-			var v volume.Volume
202
+			var v *types.Volume
203 203
 			if cfg.VolumeOptions != nil {
204 204
 				var driverOpts map[string]string
205 205
 				if cfg.VolumeOptions.DriverConfig != nil {
206 206
 					driverOpts = cfg.VolumeOptions.DriverConfig.Options
207 207
 				}
208
-				v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, driverOpts, cfg.VolumeOptions.Labels)
208
+				v, err = daemon.volumes.Create(ctx,
209
+					mp.Name,
210
+					mp.Driver,
211
+					volumeopts.WithCreateReference(container.ID),
212
+					volumeopts.WithCreateOptions(driverOpts),
213
+					volumeopts.WithCreateLabels(cfg.VolumeOptions.Labels),
214
+				)
209 215
 			} else {
210
-				v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, nil, nil)
216
+				v, err = daemon.volumes.Create(ctx, mp.Name, mp.Driver, volumeopts.WithCreateReference(container.ID))
211 217
 			}
212 218
 			if err != nil {
213 219
 				return err
214 220
 			}
215 221
 
216
-			mp.Volume = v
217
-			mp.Name = v.Name()
218
-			mp.Driver = v.DriverName()
222
+			mp.Volume = &volumeWrapper{v: v, s: daemon.volumes}
223
+			mp.Name = v.Name
224
+			mp.Driver = v.Driver
219 225
 
220
-			// only use the cached path here since getting the path is not necessary right now and calling `Path()` may be slow
221
-			if cv, ok := v.(interface {
222
-				CachedPath() string
223
-			}); ok {
224
-				mp.Source = cv.CachedPath()
225
-			}
226 226
 			if mp.Driver == volume.DefaultDriverName {
227 227
 				setBindModeIfNull(mp)
228 228
 			}
... ...
@@ -239,7 +226,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
239 239
 	for _, m := range mountPoints {
240 240
 		if parser.IsBackwardCompatible(m) {
241 241
 			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
242
-				daemon.volumes.Dereference(mp.Volume, container.ID)
242
+				daemon.volumes.Release(ctx, mp.Volume.Name(), container.ID)
243 243
 			}
244 244
 		}
245 245
 	}
... ...
@@ -254,11 +241,11 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
254 254
 // This happens after a daemon restart.
255 255
 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volumemounts.MountPoint) error {
256 256
 	if len(m.Driver) > 0 && m.Volume == nil {
257
-		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
257
+		v, err := daemon.volumes.Get(context.TODO(), m.Name, volumeopts.WithGetDriver(m.Driver), volumeopts.WithGetReference(containerID))
258 258
 		if err != nil {
259 259
 			return err
260 260
 		}
261
-		m.Volume = v
261
+		m.Volume = &volumeWrapper{v: v, s: daemon.volumes}
262 262
 	}
263 263
 	return nil
264 264
 }
... ...
@@ -385,3 +372,46 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {
385 385
 		cm.Spec.ReadOnly = !cm.RW
386 386
 	}
387 387
 }
388
+
389
+// VolumesService is used to perform volume operations
390
+func (daemon *Daemon) VolumesService() *service.VolumesService {
391
+	return daemon.volumes
392
+}
393
+
394
+type volumeMounter interface {
395
+	Mount(ctx context.Context, v *types.Volume, ref string) (string, error)
396
+	Unmount(ctx context.Context, v *types.Volume, ref string) error
397
+}
398
+
399
+type volumeWrapper struct {
400
+	v *types.Volume
401
+	s volumeMounter
402
+}
403
+
404
+func (v *volumeWrapper) Name() string {
405
+	return v.v.Name
406
+}
407
+
408
+func (v *volumeWrapper) DriverName() string {
409
+	return v.v.Driver
410
+}
411
+
412
+func (v *volumeWrapper) Path() string {
413
+	return v.v.Mountpoint
414
+}
415
+
416
+func (v *volumeWrapper) Mount(ref string) (string, error) {
417
+	return v.s.Mount(context.TODO(), v.v, ref)
418
+}
419
+
420
+func (v *volumeWrapper) Unmount(ref string) error {
421
+	return v.s.Unmount(context.TODO(), v.v, ref)
422
+}
423
+
424
+func (v *volumeWrapper) CreatedAt() (time.Time, error) {
425
+	return time.Time{}, errors.New("not implemented")
426
+}
427
+
428
+func (v *volumeWrapper) Status() map[string]interface{} {
429
+	return v.v.Status
430
+}
... ...
@@ -185,11 +185,12 @@ func (s *DockerSuite) TestVolumeEvents(c *check.C) {
185 185
 	c.Assert(len(events), checker.GreaterThan, 4)
186 186
 
187 187
 	volumeEvents := eventActionsByIDAndType(c, events, "test-event-volume-local", "volume")
188
-	c.Assert(volumeEvents, checker.HasLen, 4)
188
+	c.Assert(volumeEvents, checker.HasLen, 5)
189 189
 	c.Assert(volumeEvents[0], checker.Equals, "create")
190
-	c.Assert(volumeEvents[1], checker.Equals, "mount")
191
-	c.Assert(volumeEvents[2], checker.Equals, "unmount")
192
-	c.Assert(volumeEvents[3], checker.Equals, "destroy")
190
+	c.Assert(volumeEvents[1], checker.Equals, "create")
191
+	c.Assert(volumeEvents[2], checker.Equals, "mount")
192
+	c.Assert(volumeEvents[3], checker.Equals, "unmount")
193
+	c.Assert(volumeEvents[4], checker.Equals, "destroy")
193 194
 }
194 195
 
195 196
 func (s *DockerSuite) TestNetworkEvents(c *check.C) {
... ...
@@ -517,22 +517,20 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGetEmptyResponse(c *
517 517
 }
518 518
 
519 519
 // Ensure only cached paths are used in volume list to prevent N+1 calls to `VolumeDriver.Path`
520
+//
521
+// TODO(@cpuguy83): This test is testing internal implementation. In all the cases here, there may not even be a path
522
+// 	available because the volume is not even mounted. Consider removing this test.
520 523
 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverPathCalls(c *check.C) {
521 524
 	s.d.Start(c)
522 525
 	c.Assert(s.ec.paths, checker.Equals, 0)
523 526
 
524 527
 	out, err := s.d.Cmd("volume", "create", "test", "--driver=test-external-volume-driver")
525 528
 	c.Assert(err, checker.IsNil, check.Commentf(out))
526
-	c.Assert(s.ec.paths, checker.Equals, 1)
529
+	c.Assert(s.ec.paths, checker.Equals, 0)
527 530
 
528 531
 	out, err = s.d.Cmd("volume", "ls")
529 532
 	c.Assert(err, checker.IsNil, check.Commentf(out))
530
-	c.Assert(s.ec.paths, checker.Equals, 1)
531
-
532
-	out, err = s.d.Cmd("volume", "inspect", "--format='{{.Mountpoint}}'", "test")
533
-	c.Assert(err, checker.IsNil, check.Commentf(out))
534
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
535
-	c.Assert(s.ec.paths, checker.Equals, 1)
533
+	c.Assert(s.ec.paths, checker.Equals, 0)
536 534
 }
537 535
 
538 536
 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverMountID(c *check.C) {
... ...
@@ -1,4 +1,4 @@
1
-// +build linux freebsd
1
+// +build linux freebsd darwin
2 2
 
3 3
 package directory // import "github.com/docker/docker/pkg/directory"
4 4
 
... ...
@@ -94,7 +94,7 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
94 94
 	if err != nil {
95 95
 		// `GetCapabilities` is a not a required endpoint.
96 96
 		// On error assume it's a local-only driver
97
-		logrus.Warnf("Volume driver %s returned an error while trying to query its capabilities, using default capabilities: %v", a.name, err)
97
+		logrus.WithError(err).WithField("driver", a.name).Debug("Volume driver returned an error while trying to query its capabilities, using default capabilities")
98 98
 		return volume.Capability{Scope: volume.LocalScope}
99 99
 	}
100 100
 
... ...
@@ -105,7 +105,7 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
105 105
 
106 106
 	cap.Scope = strings.ToLower(cap.Scope)
107 107
 	if cap.Scope != volume.LocalScope && cap.Scope != volume.GlobalScope {
108
-		logrus.Warnf("Volume driver %q returned an invalid scope: %q", a.Name(), cap.Scope)
108
+		logrus.WithField("driver", a.Name()).WithField("scope", a.Scope).Warn("Volume driver returned an invalid scope")
109 109
 		cap.Scope = volume.LocalScope
110 110
 	}
111 111
 
... ...
@@ -167,10 +167,10 @@ func (s *Store) ReleaseDriver(name string) (volume.Driver, error) {
167 167
 func (s *Store) GetDriverList() []string {
168 168
 	var driverList []string
169 169
 	s.mu.Lock()
170
+	defer s.mu.Unlock()
170 171
 	for driverName := range s.extensions {
171 172
 		driverList = append(driverList, driverName)
172 173
 	}
173
-	s.mu.Unlock()
174 174
 	sort.Strings(driverList)
175 175
 	return driverList
176 176
 }
177 177
new file mode 100644
... ...
@@ -0,0 +1,89 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"github.com/docker/docker/api/types/filters"
4
+	"github.com/docker/docker/volume"
5
+)
6
+
7
+// By is an interface which is used to implement filtering on volumes.
8
+type By interface {
9
+	isBy()
10
+}
11
+
12
+// ByDriver is `By` that filters based on the driver names that are passed in
13
+func ByDriver(drivers ...string) By {
14
+	return byDriver(drivers)
15
+}
16
+
17
+type byDriver []string
18
+
19
+func (byDriver) isBy() {}
20
+
21
+// ByReferenced is a `By` that filters based on if the volume has references
22
+type ByReferenced bool
23
+
24
+func (ByReferenced) isBy() {}
25
+
26
+// And creates a `By` combining all the passed in bys using AND logic.
27
+func And(bys ...By) By {
28
+	and := make(andCombinator, 0, len(bys))
29
+	for _, by := range bys {
30
+		and = append(and, by)
31
+	}
32
+	return and
33
+}
34
+
35
+type andCombinator []By
36
+
37
+func (andCombinator) isBy() {}
38
+
39
+// Or creates a `By` combining all the passed in bys using OR logic.
40
+func Or(bys ...By) By {
41
+	or := make(orCombinator, 0, len(bys))
42
+	for _, by := range bys {
43
+		or = append(or, by)
44
+	}
45
+	return or
46
+}
47
+
48
+type orCombinator []By
49
+
50
+func (orCombinator) isBy() {}
51
+
52
+// CustomFilter is a `By` that is used by callers to provide custom filtering
53
+// logic.
54
+type CustomFilter filterFunc
55
+
56
+func (CustomFilter) isBy() {}
57
+
58
+// FromList returns a By which sets the initial list of volumes to use
59
+func FromList(ls *[]volume.Volume, by By) By {
60
+	return &fromList{by: by, ls: ls}
61
+}
62
+
63
+type fromList struct {
64
+	by By
65
+	ls *[]volume.Volume
66
+}
67
+
68
+func (fromList) isBy() {}
69
+
70
+func byLabelFilter(filter filters.Args) By {
71
+	return CustomFilter(func(v volume.Volume) bool {
72
+		dv, ok := v.(volume.DetailedVolume)
73
+		if !ok {
74
+			return false
75
+		}
76
+
77
+		labels := dv.Labels()
78
+		if !filter.MatchKVList("label", labels) {
79
+			return false
80
+		}
81
+		if filter.Contains("label!") {
82
+			if filter.MatchKVList("label!", labels) {
83
+				return false
84
+			}
85
+		}
86
+		return true
87
+	})
88
+}
0 89
new file mode 100644
... ...
@@ -0,0 +1,132 @@
0
+package service
1
+
2
+import (
3
+	"context"
4
+	"time"
5
+
6
+	"github.com/docker/docker/api/types"
7
+	"github.com/docker/docker/api/types/filters"
8
+	"github.com/docker/docker/pkg/directory"
9
+	"github.com/docker/docker/volume"
10
+	"github.com/sirupsen/logrus"
11
+)
12
+
13
+// convertOpts are used to pass options to `volumeToAPI`
14
+type convertOpt interface {
15
+	isConvertOpt()
16
+}
17
+
18
+type useCachedPath bool
19
+
20
+func (useCachedPath) isConvertOpt() {}
21
+
22
+type calcSize bool
23
+
24
+func (calcSize) isConvertOpt() {}
25
+
26
+type pathCacher interface {
27
+	CachedPath() string
28
+}
29
+
30
+func (s *VolumesService) volumesToAPI(ctx context.Context, volumes []volume.Volume, opts ...convertOpt) []*types.Volume {
31
+	var (
32
+		out        = make([]*types.Volume, 0, len(volumes))
33
+		getSize    bool
34
+		cachedPath bool
35
+	)
36
+
37
+	for _, o := range opts {
38
+		switch t := o.(type) {
39
+		case calcSize:
40
+			getSize = bool(t)
41
+		case useCachedPath:
42
+			cachedPath = bool(t)
43
+		}
44
+	}
45
+	for _, v := range volumes {
46
+		select {
47
+		case <-ctx.Done():
48
+			return nil
49
+		default:
50
+		}
51
+		apiV := volumeToAPIType(v)
52
+
53
+		if cachedPath {
54
+			if vv, ok := v.(pathCacher); ok {
55
+				apiV.Mountpoint = vv.CachedPath()
56
+			}
57
+		} else {
58
+			apiV.Mountpoint = v.Path()
59
+		}
60
+
61
+		if getSize {
62
+			p := v.Path()
63
+			if apiV.Mountpoint == "" {
64
+				apiV.Mountpoint = p
65
+			}
66
+			sz, err := directory.Size(ctx, p)
67
+			if err != nil {
68
+				logrus.WithError(err).WithField("volume", v.Name()).Warnf("Failed to determine size of volume")
69
+				sz = -1
70
+			}
71
+			apiV.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(s.vs.CountReferences(v))}
72
+		}
73
+
74
+		out = append(out, &apiV)
75
+	}
76
+	return out
77
+}
78
+
79
+func volumeToAPIType(v volume.Volume) types.Volume {
80
+	createdAt, _ := v.CreatedAt()
81
+	tv := types.Volume{
82
+		Name:      v.Name(),
83
+		Driver:    v.DriverName(),
84
+		CreatedAt: createdAt.Format(time.RFC3339),
85
+	}
86
+	if v, ok := v.(volume.DetailedVolume); ok {
87
+		tv.Labels = v.Labels()
88
+		tv.Options = v.Options()
89
+		tv.Scope = v.Scope()
90
+	}
91
+	if cp, ok := v.(pathCacher); ok {
92
+		tv.Mountpoint = cp.CachedPath()
93
+	}
94
+	return tv
95
+}
96
+
97
+func filtersToBy(filter filters.Args, acceptedFilters map[string]bool) (By, error) {
98
+	if err := filter.Validate(acceptedFilters); err != nil {
99
+		return nil, err
100
+	}
101
+	var bys []By
102
+	if drivers := filter.Get("driver"); len(drivers) > 0 {
103
+		bys = append(bys, ByDriver(drivers...))
104
+	}
105
+	if filter.Contains("name") {
106
+		bys = append(bys, CustomFilter(func(v volume.Volume) bool {
107
+			return filter.Match("name", v.Name())
108
+		}))
109
+	}
110
+	bys = append(bys, byLabelFilter(filter))
111
+
112
+	if filter.Contains("dangling") {
113
+		var dangling bool
114
+		if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
115
+			dangling = true
116
+		} else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
117
+			return nil, invalidFilter{"dangling", filter.Get("dangling")}
118
+		}
119
+		bys = append(bys, ByReferenced(!dangling))
120
+	}
121
+
122
+	var by By
123
+	switch len(bys) {
124
+	case 0:
125
+	case 1:
126
+		by = bys[0]
127
+	default:
128
+		by = And(bys...)
129
+	}
130
+	return by, nil
131
+}
0 132
new file mode 100644
... ...
@@ -0,0 +1,95 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"encoding/json"
4
+
5
+	"github.com/boltdb/bolt"
6
+	"github.com/docker/docker/errdefs"
7
+	"github.com/pkg/errors"
8
+	"github.com/sirupsen/logrus"
9
+)
10
+
11
+var volumeBucketName = []byte("volumes")
12
+
13
+type volumeMetadata struct {
14
+	Name    string
15
+	Driver  string
16
+	Labels  map[string]string
17
+	Options map[string]string
18
+}
19
+
20
+func (s *VolumeStore) setMeta(name string, meta volumeMetadata) error {
21
+	return s.db.Update(func(tx *bolt.Tx) error {
22
+		return setMeta(tx, name, meta)
23
+	})
24
+}
25
+
26
+func setMeta(tx *bolt.Tx, name string, meta volumeMetadata) error {
27
+	metaJSON, err := json.Marshal(meta)
28
+	if err != nil {
29
+		return err
30
+	}
31
+	b, err := tx.CreateBucketIfNotExists(volumeBucketName)
32
+	if err != nil {
33
+		return errors.Wrap(err, "error creating volume bucket")
34
+	}
35
+	return errors.Wrap(b.Put([]byte(name), metaJSON), "error setting volume metadata")
36
+}
37
+
38
+func (s *VolumeStore) getMeta(name string) (volumeMetadata, error) {
39
+	var meta volumeMetadata
40
+	err := s.db.View(func(tx *bolt.Tx) error {
41
+		return getMeta(tx, name, &meta)
42
+	})
43
+	return meta, err
44
+}
45
+
46
+func getMeta(tx *bolt.Tx, name string, meta *volumeMetadata) error {
47
+	b := tx.Bucket(volumeBucketName)
48
+	if b == nil {
49
+		return errdefs.NotFound(errors.New("volume bucket does not exist"))
50
+	}
51
+	val := b.Get([]byte(name))
52
+	if len(val) == 0 {
53
+		return nil
54
+	}
55
+	if err := json.Unmarshal(val, meta); err != nil {
56
+		return errors.Wrap(err, "error unmarshaling volume metadata")
57
+	}
58
+	return nil
59
+}
60
+
61
+func (s *VolumeStore) removeMeta(name string) error {
62
+	return s.db.Update(func(tx *bolt.Tx) error {
63
+		return removeMeta(tx, name)
64
+	})
65
+}
66
+
67
+func removeMeta(tx *bolt.Tx, name string) error {
68
+	b := tx.Bucket(volumeBucketName)
69
+	return errors.Wrap(b.Delete([]byte(name)), "error removing volume metadata")
70
+}
71
+
72
+// listMeta is used during restore to get the list of volume metadata
73
+// from the on-disk database.
74
+// Any errors that occur are only logged.
75
+func listMeta(tx *bolt.Tx) []volumeMetadata {
76
+	var ls []volumeMetadata
77
+	b := tx.Bucket(volumeBucketName)
78
+	b.ForEach(func(k, v []byte) error {
79
+		if len(v) == 0 {
80
+			// don't try to unmarshal an empty value
81
+			return nil
82
+		}
83
+
84
+		var m volumeMetadata
85
+		if err := json.Unmarshal(v, &m); err != nil {
86
+			// Just log the error
87
+			logrus.Errorf("Error while reading volume metadata for volume %q: %v", string(k), err)
88
+			return nil
89
+		}
90
+		ls = append(ls, m)
91
+		return nil
92
+	})
93
+	return ls
94
+}
0 95
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"path/filepath"
6
+	"testing"
7
+	"time"
8
+
9
+	"github.com/boltdb/bolt"
10
+	"github.com/gotestyourself/gotestyourself/assert"
11
+	is "github.com/gotestyourself/gotestyourself/assert/cmp"
12
+)
13
+
14
+func TestSetGetMeta(t *testing.T) {
15
+	t.Parallel()
16
+
17
+	dir, err := ioutil.TempDir("", "test-set-get")
18
+	assert.NilError(t, err)
19
+	defer os.RemoveAll(dir)
20
+
21
+	db, err := bolt.Open(filepath.Join(dir, "db"), 0600, &bolt.Options{Timeout: 1 * time.Second})
22
+	assert.NilError(t, err)
23
+
24
+	store := &VolumeStore{db: db}
25
+
26
+	_, err = store.getMeta("test")
27
+	assert.Assert(t, is.ErrorContains(err, ""))
28
+
29
+	err = db.Update(func(tx *bolt.Tx) error {
30
+		_, err := tx.CreateBucket(volumeBucketName)
31
+		return err
32
+	})
33
+	assert.NilError(t, err)
34
+
35
+	meta, err := store.getMeta("test")
36
+	assert.NilError(t, err)
37
+	assert.DeepEqual(t, volumeMetadata{}, meta)
38
+
39
+	testMeta := volumeMetadata{
40
+		Name:    "test",
41
+		Driver:  "fake",
42
+		Labels:  map[string]string{"a": "1", "b": "2"},
43
+		Options: map[string]string{"foo": "bar"},
44
+	}
45
+	err = store.setMeta("test", testMeta)
46
+	assert.NilError(t, err)
47
+
48
+	meta, err = store.getMeta("test")
49
+	assert.NilError(t, err)
50
+	assert.DeepEqual(t, testMeta, meta)
51
+}
0 52
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+// +build linux windows
1
+
2
+package service // import "github.com/docker/docker/volume/service"
3
+import (
4
+	"github.com/docker/docker/pkg/idtools"
5
+	"github.com/docker/docker/volume"
6
+	"github.com/docker/docker/volume/drivers"
7
+	"github.com/docker/docker/volume/local"
8
+	"github.com/pkg/errors"
9
+)
10
+
11
+func setupDefaultDriver(store *drivers.Store, root string, rootIDs idtools.IDPair) error {
12
+	d, err := local.New(root, rootIDs)
13
+	if err != nil {
14
+		return errors.Wrap(err, "error setting up default driver")
15
+	}
16
+	if !store.Register(d, volume.DefaultDriverName) {
17
+		return errors.New("local volume driver could not be registered")
18
+	}
19
+	return nil
20
+}
0 21
new file mode 100644
... ...
@@ -0,0 +1,10 @@
0
+// +build !linux,!windows
1
+
2
+package service // import "github.com/docker/docker/volume/service"
3
+
4
+import (
5
+	"github.com/docker/docker/pkg/idtools"
6
+	"github.com/docker/docker/volume/drivers"
7
+)
8
+
9
+func setupDefaultDriver(_ *drivers.Store, _ string, _ idtools.IDPair) error { return nil }
0 10
new file mode 100644
... ...
@@ -0,0 +1,111 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+)
6
+
7
+const (
8
+	// errVolumeInUse is a typed error returned when trying to remove a volume that is currently in use by a container
9
+	errVolumeInUse conflictError = "volume is in use"
10
+	// errNoSuchVolume is a typed error returned if the requested volume doesn't exist in the volume store
11
+	errNoSuchVolume notFoundError = "no such volume"
12
+	// errNameConflict is a typed error returned on create when a volume exists with the given name, but for a different driver
13
+	errNameConflict conflictError = "volume name must be unique"
14
+)
15
+
16
+type conflictError string
17
+
18
+func (e conflictError) Error() string {
19
+	return string(e)
20
+}
21
+func (conflictError) Conflict() {}
22
+
23
+type notFoundError string
24
+
25
+func (e notFoundError) Error() string {
26
+	return string(e)
27
+}
28
+
29
+func (notFoundError) NotFound() {}
30
+
31
+// OpErr is the error type returned by functions in the store package. It describes
32
+// the operation, volume name, and error.
33
+type OpErr struct {
34
+	// Err is the error that occurred during the operation.
35
+	Err error
36
+	// Op is the operation which caused the error, such as "create", or "list".
37
+	Op string
38
+	// Name is the name of the resource being requested for this op, typically the volume name or the driver name.
39
+	Name string
40
+	// Refs is the list of references associated with the resource.
41
+	Refs []string
42
+}
43
+
44
+// Error satisfies the built-in error interface type.
45
+func (e *OpErr) Error() string {
46
+	if e == nil {
47
+		return "<nil>"
48
+	}
49
+	s := e.Op
50
+	if e.Name != "" {
51
+		s = s + " " + e.Name
52
+	}
53
+
54
+	s = s + ": " + e.Err.Error()
55
+	if len(e.Refs) > 0 {
56
+		s = s + " - " + "[" + strings.Join(e.Refs, ", ") + "]"
57
+	}
58
+	return s
59
+}
60
+
61
+// Cause returns the error the caused this error
62
+func (e *OpErr) Cause() error {
63
+	return e.Err
64
+}
65
+
66
+// IsInUse returns a boolean indicating whether the error indicates that a
67
+// volume is in use
68
+func IsInUse(err error) bool {
69
+	return isErr(err, errVolumeInUse)
70
+}
71
+
72
+// IsNotExist returns a boolean indicating whether the error indicates that the volume does not exist
73
+func IsNotExist(err error) bool {
74
+	return isErr(err, errNoSuchVolume)
75
+}
76
+
77
+// IsNameConflict returns a boolean indicating whether the error indicates that a
78
+// volume name is already taken
79
+func IsNameConflict(err error) bool {
80
+	return isErr(err, errNameConflict)
81
+}
82
+
83
+type causal interface {
84
+	Cause() error
85
+}
86
+
87
+func isErr(err error, expected error) bool {
88
+	switch pe := err.(type) {
89
+	case nil:
90
+		return false
91
+	case causal:
92
+		return isErr(pe.Cause(), expected)
93
+	}
94
+	return err == expected
95
+}
96
+
97
+type invalidFilter struct {
98
+	filter string
99
+	value  interface{}
100
+}
101
+
102
+func (e invalidFilter) Error() string {
103
+	msg := "Invalid filter '" + e.filter
104
+	if e.value != nil {
105
+		msg += fmt.Sprintf("=%s", e.value)
106
+	}
107
+	return msg + "'"
108
+}
109
+
110
+func (e invalidFilter) InvalidParameter() {}
0 111
new file mode 100644
... ...
@@ -0,0 +1,89 @@
0
+package opts
1
+
2
+// CreateOption is used to pass options in when creating a volume
3
+type CreateOption func(*CreateConfig)
4
+
5
+// CreateConfig is the set of config options that can be set when creating
6
+// a volume
7
+type CreateConfig struct {
8
+	Options   map[string]string
9
+	Labels    map[string]string
10
+	Reference string
11
+}
12
+
13
+// WithCreateLabels creates a CreateOption which sets the labels to the
14
+// passed in value
15
+func WithCreateLabels(labels map[string]string) CreateOption {
16
+	return func(cfg *CreateConfig) {
17
+		cfg.Labels = labels
18
+	}
19
+}
20
+
21
+// WithCreateOptions creates a CreateOption which sets the options passed
22
+// to the volume driver when creating a volume to the options passed in.
23
+func WithCreateOptions(opts map[string]string) CreateOption {
24
+	return func(cfg *CreateConfig) {
25
+		cfg.Options = opts
26
+	}
27
+}
28
+
29
+// WithCreateReference creats a CreateOption which sets a reference to use
30
+// when creating a volume. This ensures that the volume is created with a reference
31
+// already attached to it to prevent race conditions with Create and volume cleanup.
32
+func WithCreateReference(ref string) CreateOption {
33
+	return func(cfg *CreateConfig) {
34
+		cfg.Reference = ref
35
+	}
36
+}
37
+
38
+// GetConfig is used with `GetOption` to set options for the volumes service's
39
+// `Get` implementation.
40
+type GetConfig struct {
41
+	Driver        string
42
+	Reference     string
43
+	ResolveStatus bool
44
+}
45
+
46
+// GetOption is passed to the service `Get` add extra details on the get request
47
+type GetOption func(*GetConfig)
48
+
49
+// WithGetDriver provides the driver to get the volume from
50
+// If no driver is provided to `Get`, first the available metadata is checked
51
+// to see which driver it belongs to, if that is not available all drivers are
52
+// probed to find the volume.
53
+func WithGetDriver(name string) GetOption {
54
+	return func(o *GetConfig) {
55
+		o.Driver = name
56
+	}
57
+}
58
+
59
+// WithGetReference indicates to `Get` to increment the reference count for the
60
+// retreived volume with the provided reference ID.
61
+func WithGetReference(ref string) GetOption {
62
+	return func(o *GetConfig) {
63
+		o.Reference = ref
64
+	}
65
+}
66
+
67
+// WithGetResolveStatus indicates to `Get` to also fetch the volume status.
68
+// This can cause significant overhead in the volume lookup.
69
+func WithGetResolveStatus(cfg *GetConfig) {
70
+	cfg.ResolveStatus = true
71
+}
72
+
73
+// RemoveConfig is used by `RemoveOption` to store config options for remove
74
+type RemoveConfig struct {
75
+	PurgeOnError bool
76
+}
77
+
78
+// RemoveOption is used to pass options to the volumes service `Remove` implementation
79
+type RemoveOption func(*RemoveConfig)
80
+
81
+// WithPurgeOnError is an option passed to `Remove` which will purge all cached
82
+// data about a volume even if there was an error while attempting to remove the
83
+// volume.
84
+func WithPurgeOnError(b bool) RemoveOption {
85
+	return func(o *RemoveConfig) {
86
+		o.PurgeOnError = b
87
+	}
88
+}
0 89
new file mode 100644
... ...
@@ -0,0 +1,85 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"context"
4
+	"sync"
5
+
6
+	"github.com/boltdb/bolt"
7
+	"github.com/docker/docker/volume"
8
+	"github.com/sirupsen/logrus"
9
+)
10
+
11
+// restore is called when a new volume store is created.
12
+// It's primary purpose is to ensure that all drivers' refcounts are set based
13
+// on known volumes after a restart.
14
+// This only attempts to track volumes that are actually stored in the on-disk db.
15
+// It does not probe the available drivers to find anything that may have been added
16
+// out of band.
17
+func (s *VolumeStore) restore() {
18
+	var ls []volumeMetadata
19
+	s.db.View(func(tx *bolt.Tx) error {
20
+		ls = listMeta(tx)
21
+		return nil
22
+	})
23
+	ctx := context.Background()
24
+
25
+	chRemove := make(chan *volumeMetadata, len(ls))
26
+	var wg sync.WaitGroup
27
+	for _, meta := range ls {
28
+		wg.Add(1)
29
+		// this is potentially a very slow operation, so do it in a goroutine
30
+		go func(meta volumeMetadata) {
31
+			defer wg.Done()
32
+
33
+			var v volume.Volume
34
+			var err error
35
+			if meta.Driver != "" {
36
+				v, err = lookupVolume(ctx, s.drivers, meta.Driver, meta.Name)
37
+				if err != nil && err != errNoSuchVolume {
38
+					logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume")
39
+					return
40
+				}
41
+				if v == nil {
42
+					// doesn't exist in the driver, remove it from the db
43
+					chRemove <- &meta
44
+					return
45
+				}
46
+			} else {
47
+				v, err = s.getVolume(ctx, meta.Name, meta.Driver)
48
+				if err != nil {
49
+					if err == errNoSuchVolume {
50
+						chRemove <- &meta
51
+					}
52
+					return
53
+				}
54
+
55
+				meta.Driver = v.DriverName()
56
+				if err := s.setMeta(v.Name(), meta); err != nil {
57
+					logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", v.Name()).Warn("Error updating volume metadata on restore")
58
+				}
59
+			}
60
+
61
+			// increment driver refcount
62
+			s.drivers.CreateDriver(meta.Driver)
63
+
64
+			// cache the volume
65
+			s.globalLock.Lock()
66
+			s.options[v.Name()] = meta.Options
67
+			s.labels[v.Name()] = meta.Labels
68
+			s.names[v.Name()] = v
69
+			s.refs[v.Name()] = make(map[string]struct{})
70
+			s.globalLock.Unlock()
71
+		}(meta)
72
+	}
73
+
74
+	wg.Wait()
75
+	close(chRemove)
76
+	s.db.Update(func(tx *bolt.Tx) error {
77
+		for meta := range chRemove {
78
+			if err := removeMeta(tx, meta.Name); err != nil {
79
+				logrus.WithField("volume", meta.Name).Warnf("Error removing stale entry from volume db: %v", err)
80
+			}
81
+		}
82
+		return nil
83
+	})
84
+}
0 85
new file mode 100644
... ...
@@ -0,0 +1,58 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"context"
4
+	"io/ioutil"
5
+	"os"
6
+	"testing"
7
+
8
+	"github.com/docker/docker/volume"
9
+	volumedrivers "github.com/docker/docker/volume/drivers"
10
+	"github.com/docker/docker/volume/service/opts"
11
+	volumetestutils "github.com/docker/docker/volume/testutils"
12
+	"github.com/gotestyourself/gotestyourself/assert"
13
+)
14
+
15
+func TestRestore(t *testing.T) {
16
+	t.Parallel()
17
+
18
+	dir, err := ioutil.TempDir("", "test-restore")
19
+	assert.NilError(t, err)
20
+	defer os.RemoveAll(dir)
21
+
22
+	drivers := volumedrivers.NewStore(nil)
23
+	driverName := "test-restore"
24
+	drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
25
+
26
+	s, err := NewStore(dir, drivers)
27
+	assert.NilError(t, err)
28
+	defer s.Shutdown()
29
+
30
+	ctx := context.Background()
31
+	_, err = s.Create(ctx, "test1", driverName)
32
+	assert.NilError(t, err)
33
+
34
+	testLabels := map[string]string{"a": "1"}
35
+	testOpts := map[string]string{"foo": "bar"}
36
+	_, err = s.Create(ctx, "test2", driverName, opts.WithCreateOptions(testOpts), opts.WithCreateLabels(testLabels))
37
+	assert.NilError(t, err)
38
+
39
+	s.Shutdown()
40
+
41
+	s, err = NewStore(dir, drivers)
42
+	assert.NilError(t, err)
43
+
44
+	v, err := s.Get(ctx, "test1")
45
+	assert.NilError(t, err)
46
+
47
+	dv := v.(volume.DetailedVolume)
48
+	var nilMap map[string]string
49
+	assert.DeepEqual(t, nilMap, dv.Options())
50
+	assert.DeepEqual(t, nilMap, dv.Labels())
51
+
52
+	v, err = s.Get(ctx, "test2")
53
+	assert.NilError(t, err)
54
+	dv = v.(volume.DetailedVolume)
55
+	assert.DeepEqual(t, testOpts, dv.Options())
56
+	assert.DeepEqual(t, testLabels, dv.Labels())
57
+}
0 58
new file mode 100644
... ...
@@ -0,0 +1,243 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"context"
4
+	"sync/atomic"
5
+
6
+	"github.com/docker/docker/api/types"
7
+	"github.com/docker/docker/api/types/filters"
8
+	"github.com/docker/docker/errdefs"
9
+	"github.com/docker/docker/pkg/directory"
10
+	"github.com/docker/docker/pkg/idtools"
11
+	"github.com/docker/docker/pkg/plugingetter"
12
+	"github.com/docker/docker/pkg/stringid"
13
+	"github.com/docker/docker/volume"
14
+	"github.com/docker/docker/volume/drivers"
15
+	"github.com/docker/docker/volume/service/opts"
16
+	"github.com/pkg/errors"
17
+	"github.com/sirupsen/logrus"
18
+)
19
+
20
+type ds interface {
21
+	GetDriverList() []string
22
+}
23
+
24
+type volumeEventLogger interface {
25
+	LogVolumeEvent(volumeID, action string, attributes map[string]string)
26
+}
27
+
28
+// VolumesService manages access to volumes
29
+type VolumesService struct {
30
+	vs           *VolumeStore
31
+	ds           ds
32
+	pruneRunning int32
33
+	eventLogger  volumeEventLogger
34
+}
35
+
36
+// NewVolumeService creates a new volume service
37
+func NewVolumeService(root string, pg plugingetter.PluginGetter, rootIDs idtools.IDPair, logger volumeEventLogger) (*VolumesService, error) {
38
+	ds := drivers.NewStore(pg)
39
+	if err := setupDefaultDriver(ds, root, rootIDs); err != nil {
40
+		return nil, err
41
+	}
42
+
43
+	vs, err := NewStore(root, ds)
44
+	if err != nil {
45
+		return nil, err
46
+	}
47
+	return &VolumesService{vs: vs, ds: ds, eventLogger: logger}, nil
48
+}
49
+
50
+// GetDriverList gets the list of registered volume drivers
51
+func (s *VolumesService) GetDriverList() []string {
52
+	return s.ds.GetDriverList()
53
+}
54
+
55
+// Create creates a volume
56
+func (s *VolumesService) Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*types.Volume, error) {
57
+	if name == "" {
58
+		name = stringid.GenerateNonCryptoID()
59
+	}
60
+	v, err := s.vs.Create(ctx, name, driverName, opts...)
61
+	if err != nil {
62
+		return nil, err
63
+	}
64
+
65
+	s.eventLogger.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()})
66
+	apiV := volumeToAPIType(v)
67
+	return &apiV, nil
68
+}
69
+
70
+// Get gets a volume
71
+func (s *VolumesService) Get(ctx context.Context, name string, getOpts ...opts.GetOption) (*types.Volume, error) {
72
+	v, err := s.vs.Get(ctx, name, getOpts...)
73
+	if err != nil {
74
+		return nil, err
75
+	}
76
+	vol := volumeToAPIType(v)
77
+
78
+	var cfg opts.GetConfig
79
+	for _, o := range getOpts {
80
+		o(&cfg)
81
+	}
82
+
83
+	if cfg.ResolveStatus {
84
+		vol.Status = v.Status()
85
+	}
86
+	return &vol, nil
87
+}
88
+
89
+// Mount mounts the volume
90
+func (s *VolumesService) Mount(ctx context.Context, vol *types.Volume, ref string) (string, error) {
91
+	v, err := s.vs.Get(ctx, vol.Name, opts.WithGetDriver(vol.Driver))
92
+	if err != nil {
93
+		if IsNotExist(err) {
94
+			err = errdefs.NotFound(err)
95
+		}
96
+		return "", err
97
+	}
98
+	return v.Mount(ref)
99
+}
100
+
101
+// Unmount unmounts the volume.
102
+// Note that depending on the implementation, the volume may still be mounted due to other resources using it.
103
+func (s *VolumesService) Unmount(ctx context.Context, vol *types.Volume, ref string) error {
104
+	v, err := s.vs.Get(ctx, vol.Name, opts.WithGetDriver(vol.Driver))
105
+	if err != nil {
106
+		if IsNotExist(err) {
107
+			err = errdefs.NotFound(err)
108
+		}
109
+		return err
110
+	}
111
+	return v.Unmount(ref)
112
+}
113
+
114
+// Release releases a volume reference
115
+func (s *VolumesService) Release(ctx context.Context, name string, ref string) error {
116
+	return s.vs.Release(ctx, name, ref)
117
+}
118
+
119
+// Remove removes a volume
120
+func (s *VolumesService) Remove(ctx context.Context, name string, rmOpts ...opts.RemoveOption) error {
121
+	var cfg opts.RemoveConfig
122
+	for _, o := range rmOpts {
123
+		o(&cfg)
124
+	}
125
+
126
+	v, err := s.vs.Get(ctx, name)
127
+	if err != nil {
128
+		if IsNotExist(err) && cfg.PurgeOnError {
129
+			return nil
130
+		}
131
+		return err
132
+	}
133
+
134
+	err = s.vs.Remove(ctx, v, rmOpts...)
135
+	if IsNotExist(err) {
136
+		err = nil
137
+	} else if IsInUse(err) {
138
+		err = errdefs.Conflict(err)
139
+	} else if IsNotExist(err) && cfg.PurgeOnError {
140
+		err = nil
141
+	}
142
+
143
+	if err == nil {
144
+		s.eventLogger.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()})
145
+	}
146
+	return err
147
+}
148
+
149
+var acceptedPruneFilters = map[string]bool{
150
+	"label":  true,
151
+	"label!": true,
152
+}
153
+
154
+var acceptedListFilters = map[string]bool{
155
+	"dangling": true,
156
+	"name":     true,
157
+	"driver":   true,
158
+	"label":    true,
159
+}
160
+
161
+// LocalVolumesSize gets all local volumes and fetches their size on disk
162
+// Note that this intentionally skips volumes which have mount options. Typically
163
+// volumes with mount options are not really local even if they are using the
164
+// local driver.
165
+func (s *VolumesService) LocalVolumesSize(ctx context.Context) ([]*types.Volume, error) {
166
+	ls, _, err := s.vs.Find(ctx, And(ByDriver(volume.DefaultDriverName), CustomFilter(func(v volume.Volume) bool {
167
+		dv, ok := v.(volume.DetailedVolume)
168
+		return ok && len(dv.Options()) == 0
169
+	})))
170
+	if err != nil {
171
+		return nil, err
172
+	}
173
+	return s.volumesToAPI(ctx, ls, calcSize(true)), nil
174
+}
175
+
176
+// Prune removes (local) volumes which match the past in filter arguments.
177
+// Note that this intentionally skips volumes with mount options as there would
178
+// be no space reclaimed in this case.
179
+func (s *VolumesService) Prune(ctx context.Context, filter filters.Args) (*types.VolumesPruneReport, error) {
180
+	if !atomic.CompareAndSwapInt32(&s.pruneRunning, 0, 1) {
181
+		return nil, errdefs.Conflict(errors.New("a prune operation is already running"))
182
+	}
183
+	defer atomic.StoreInt32(&s.pruneRunning, 0)
184
+
185
+	by, err := filtersToBy(filter, acceptedPruneFilters)
186
+	if err != nil {
187
+		return nil, err
188
+	}
189
+	ls, _, err := s.vs.Find(ctx, And(ByDriver(volume.DefaultDriverName), ByReferenced(false), by, CustomFilter(func(v volume.Volume) bool {
190
+		dv, ok := v.(volume.DetailedVolume)
191
+		return ok && len(dv.Options()) == 0
192
+	})))
193
+	if err != nil {
194
+		return nil, err
195
+	}
196
+
197
+	rep := &types.VolumesPruneReport{VolumesDeleted: make([]string, 0, len(ls))}
198
+	for _, v := range ls {
199
+		select {
200
+		case <-ctx.Done():
201
+			err := ctx.Err()
202
+			if err == context.Canceled {
203
+				err = nil
204
+			}
205
+			return rep, err
206
+		default:
207
+		}
208
+
209
+		vSize, err := directory.Size(ctx, v.Path())
210
+		if err != nil {
211
+			logrus.WithField("volume", v.Name()).WithError(err).Warn("could not determine size of volume")
212
+		}
213
+		if err := s.vs.Remove(ctx, v); err != nil {
214
+			logrus.WithError(err).WithField("volume", v.Name()).Warnf("Could not determine size of volume")
215
+			continue
216
+		}
217
+		rep.SpaceReclaimed += uint64(vSize)
218
+		rep.VolumesDeleted = append(rep.VolumesDeleted, v.Name())
219
+	}
220
+	return rep, nil
221
+}
222
+
223
+// List gets the list of volumes which match the past in filters
224
+// If filters is nil or empty all volumes are returned.
225
+func (s *VolumesService) List(ctx context.Context, filter filters.Args) (volumesOut []*types.Volume, warnings []string, err error) {
226
+	by, err := filtersToBy(filter, acceptedListFilters)
227
+	if err != nil {
228
+		return nil, nil, err
229
+	}
230
+
231
+	volumes, warnings, err := s.vs.Find(ctx, by)
232
+	if err != nil {
233
+		return nil, nil, err
234
+	}
235
+
236
+	return s.volumesToAPI(ctx, volumes, useCachedPath(true)), warnings, nil
237
+}
238
+
239
+// Shutdown shuts down the image service and dependencies
240
+func (s *VolumesService) Shutdown() error {
241
+	return s.vs.Shutdown()
242
+}
0 243
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+package service
1
+
2
+import (
3
+	"context"
4
+	"io/ioutil"
5
+	"os"
6
+	"path/filepath"
7
+	"testing"
8
+
9
+	"github.com/docker/docker/pkg/idtools"
10
+	"github.com/docker/docker/volume"
11
+	volumedrivers "github.com/docker/docker/volume/drivers"
12
+	"github.com/docker/docker/volume/local"
13
+	"github.com/docker/docker/volume/service/opts"
14
+	"github.com/docker/docker/volume/testutils"
15
+	"github.com/gotestyourself/gotestyourself/assert"
16
+	is "github.com/gotestyourself/gotestyourself/assert/cmp"
17
+)
18
+
19
+func TestLocalVolumeSize(t *testing.T) {
20
+	t.Parallel()
21
+
22
+	ds := volumedrivers.NewStore(nil)
23
+	dir, err := ioutil.TempDir("", t.Name())
24
+	assert.Assert(t, err)
25
+	defer os.RemoveAll(dir)
26
+
27
+	l, err := local.New(dir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
28
+	assert.Assert(t, err)
29
+	assert.Assert(t, ds.Register(l, volume.DefaultDriverName))
30
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("fake"), "fake"))
31
+
32
+	service, cleanup := newTestService(t, ds)
33
+	defer cleanup()
34
+
35
+	ctx := context.Background()
36
+	v1, err := service.Create(ctx, "test1", volume.DefaultDriverName, opts.WithCreateReference("foo"))
37
+	assert.Assert(t, err)
38
+	v2, err := service.Create(ctx, "test2", volume.DefaultDriverName)
39
+	assert.Assert(t, err)
40
+	_, err = service.Create(ctx, "test3", "fake")
41
+	assert.Assert(t, err)
42
+
43
+	data := make([]byte, 1024)
44
+	err = ioutil.WriteFile(filepath.Join(v1.Mountpoint, "data"), data, 0644)
45
+	assert.Assert(t, err)
46
+	err = ioutil.WriteFile(filepath.Join(v2.Mountpoint, "data"), data[:1], 0644)
47
+	assert.Assert(t, err)
48
+
49
+	ls, err := service.LocalVolumesSize(ctx)
50
+	assert.Assert(t, err)
51
+	assert.Assert(t, is.Len(ls, 2))
52
+
53
+	for _, v := range ls {
54
+		switch v.Name {
55
+		case "test1":
56
+			assert.Assert(t, is.Equal(v.UsageData.Size, int64(len(data))))
57
+			assert.Assert(t, is.Equal(v.UsageData.RefCount, int64(1)))
58
+		case "test2":
59
+			assert.Assert(t, is.Equal(v.UsageData.Size, int64(len(data[:1]))))
60
+			assert.Assert(t, is.Equal(v.UsageData.RefCount, int64(0)))
61
+		default:
62
+			t.Fatalf("got unexpected volume: %+v", v)
63
+		}
64
+	}
65
+	assert.Assert(t, is.Equal(ls[1].UsageData.Size, int64(len(data[:1]))))
66
+}
0 67
new file mode 100644
... ...
@@ -0,0 +1,253 @@
0
+package service
1
+
2
+import (
3
+	"context"
4
+	"io/ioutil"
5
+	"os"
6
+	"testing"
7
+
8
+	"github.com/docker/docker/api/types/filters"
9
+	"github.com/docker/docker/errdefs"
10
+	"github.com/docker/docker/volume"
11
+	volumedrivers "github.com/docker/docker/volume/drivers"
12
+	"github.com/docker/docker/volume/service/opts"
13
+	"github.com/docker/docker/volume/testutils"
14
+	"github.com/gotestyourself/gotestyourself/assert"
15
+	is "github.com/gotestyourself/gotestyourself/assert/cmp"
16
+)
17
+
18
+func TestServiceCreate(t *testing.T) {
19
+	t.Parallel()
20
+
21
+	ds := volumedrivers.NewStore(nil)
22
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d1"), "d1"))
23
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d2"), "d2"))
24
+
25
+	ctx := context.Background()
26
+	service, cleanup := newTestService(t, ds)
27
+	defer cleanup()
28
+
29
+	_, err := service.Create(ctx, "v1", "notexist")
30
+	assert.Assert(t, errdefs.IsNotFound(err), err)
31
+
32
+	v, err := service.Create(ctx, "v1", "d1")
33
+	assert.Assert(t, err)
34
+
35
+	vCopy, err := service.Create(ctx, "v1", "d1")
36
+	assert.Assert(t, err)
37
+	assert.Assert(t, is.DeepEqual(v, vCopy))
38
+
39
+	_, err = service.Create(ctx, "v1", "d2")
40
+	assert.Check(t, IsNameConflict(err), err)
41
+	assert.Check(t, errdefs.IsConflict(err), err)
42
+
43
+	assert.Assert(t, service.Remove(ctx, "v1"))
44
+	_, err = service.Create(ctx, "v1", "d2")
45
+	assert.Assert(t, err)
46
+	_, err = service.Create(ctx, "v1", "d2")
47
+	assert.Assert(t, err)
48
+
49
+}
50
+
51
+func TestServiceList(t *testing.T) {
52
+	t.Parallel()
53
+
54
+	ds := volumedrivers.NewStore(nil)
55
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d1"), "d1"))
56
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d2"), "d2"))
57
+
58
+	service, cleanup := newTestService(t, ds)
59
+	defer cleanup()
60
+
61
+	ctx := context.Background()
62
+
63
+	_, err := service.Create(ctx, "v1", "d1")
64
+	assert.Assert(t, err)
65
+	_, err = service.Create(ctx, "v2", "d1")
66
+	assert.Assert(t, err)
67
+	_, err = service.Create(ctx, "v3", "d2")
68
+	assert.Assert(t, err)
69
+
70
+	ls, _, err := service.List(ctx, filters.NewArgs(filters.Arg("driver", "d1")))
71
+	assert.Assert(t, err)
72
+	assert.Check(t, is.Len(ls, 2))
73
+
74
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("driver", "d2")))
75
+	assert.Assert(t, err)
76
+	assert.Check(t, is.Len(ls, 1))
77
+
78
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("driver", "notexist")))
79
+	assert.Assert(t, err)
80
+	assert.Check(t, is.Len(ls, 0))
81
+
82
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("dangling", "true")))
83
+	assert.Assert(t, err)
84
+	assert.Check(t, is.Len(ls, 3))
85
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("dangling", "false")))
86
+	assert.Assert(t, err)
87
+	assert.Check(t, is.Len(ls, 0))
88
+
89
+	_, err = service.Get(ctx, "v1", opts.WithGetReference("foo"))
90
+	assert.Assert(t, err)
91
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("dangling", "true")))
92
+	assert.Assert(t, err)
93
+	assert.Check(t, is.Len(ls, 2))
94
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("dangling", "false")))
95
+	assert.Assert(t, err)
96
+	assert.Check(t, is.Len(ls, 1))
97
+
98
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("dangling", "false"), filters.Arg("driver", "d2")))
99
+	assert.Assert(t, err)
100
+	assert.Check(t, is.Len(ls, 0))
101
+	ls, _, err = service.List(ctx, filters.NewArgs(filters.Arg("dangling", "true"), filters.Arg("driver", "d2")))
102
+	assert.Assert(t, err)
103
+	assert.Check(t, is.Len(ls, 1))
104
+}
105
+
106
+func TestServiceRemove(t *testing.T) {
107
+	t.Parallel()
108
+
109
+	ds := volumedrivers.NewStore(nil)
110
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d1"), "d1"))
111
+
112
+	service, cleanup := newTestService(t, ds)
113
+	defer cleanup()
114
+	ctx := context.Background()
115
+
116
+	_, err := service.Create(ctx, "test", "d1")
117
+	assert.Assert(t, err)
118
+
119
+	assert.Assert(t, service.Remove(ctx, "test"))
120
+	assert.Assert(t, service.Remove(ctx, "test", opts.WithPurgeOnError(true)))
121
+}
122
+
123
+func TestServiceGet(t *testing.T) {
124
+	t.Parallel()
125
+
126
+	ds := volumedrivers.NewStore(nil)
127
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d1"), "d1"))
128
+
129
+	service, cleanup := newTestService(t, ds)
130
+	defer cleanup()
131
+	ctx := context.Background()
132
+
133
+	v, err := service.Get(ctx, "notexist")
134
+	assert.Assert(t, IsNotExist(err))
135
+	assert.Check(t, v == nil)
136
+
137
+	created, err := service.Create(ctx, "test", "d1")
138
+	assert.Assert(t, err)
139
+	assert.Assert(t, created != nil)
140
+
141
+	v, err = service.Get(ctx, "test")
142
+	assert.Assert(t, err)
143
+	assert.Assert(t, is.DeepEqual(created, v))
144
+
145
+	v, err = service.Get(ctx, "test", opts.WithGetResolveStatus)
146
+	assert.Assert(t, err)
147
+	assert.Assert(t, is.Len(v.Status, 1), v.Status)
148
+
149
+	v, err = service.Get(ctx, "test", opts.WithGetDriver("notarealdriver"))
150
+	assert.Assert(t, errdefs.IsConflict(err), err)
151
+	v, err = service.Get(ctx, "test", opts.WithGetDriver("d1"))
152
+	assert.Assert(t, err == nil)
153
+	assert.Assert(t, is.DeepEqual(created, v))
154
+
155
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("d2"), "d2"))
156
+	v, err = service.Get(ctx, "test", opts.WithGetDriver("d2"))
157
+	assert.Assert(t, errdefs.IsConflict(err), err)
158
+}
159
+
160
+func TestServicePrune(t *testing.T) {
161
+	t.Parallel()
162
+
163
+	ds := volumedrivers.NewStore(nil)
164
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver(volume.DefaultDriverName), volume.DefaultDriverName))
165
+	assert.Assert(t, ds.Register(testutils.NewFakeDriver("other"), "other"))
166
+
167
+	service, cleanup := newTestService(t, ds)
168
+	defer cleanup()
169
+	ctx := context.Background()
170
+
171
+	_, err := service.Create(ctx, "test", volume.DefaultDriverName)
172
+	assert.Assert(t, err)
173
+	_, err = service.Create(ctx, "test2", "other")
174
+	assert.Assert(t, err)
175
+
176
+	pr, err := service.Prune(ctx, filters.NewArgs(filters.Arg("label", "banana")))
177
+	assert.Assert(t, err)
178
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 0))
179
+
180
+	pr, err = service.Prune(ctx, filters.NewArgs())
181
+	assert.Assert(t, err)
182
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 1))
183
+	assert.Assert(t, is.Equal(pr.VolumesDeleted[0], "test"))
184
+
185
+	_, err = service.Get(ctx, "test")
186
+	assert.Assert(t, IsNotExist(err), err)
187
+
188
+	v, err := service.Get(ctx, "test2")
189
+	assert.Assert(t, err)
190
+	assert.Assert(t, is.Equal(v.Driver, "other"))
191
+
192
+	_, err = service.Create(ctx, "test", volume.DefaultDriverName)
193
+	assert.Assert(t, err)
194
+
195
+	pr, err = service.Prune(ctx, filters.NewArgs(filters.Arg("label!", "banana")))
196
+	assert.Assert(t, err)
197
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 1))
198
+	assert.Assert(t, is.Equal(pr.VolumesDeleted[0], "test"))
199
+	v, err = service.Get(ctx, "test2")
200
+	assert.Assert(t, err)
201
+	assert.Assert(t, is.Equal(v.Driver, "other"))
202
+
203
+	_, err = service.Create(ctx, "test", volume.DefaultDriverName, opts.WithCreateLabels(map[string]string{"banana": ""}))
204
+	assert.Assert(t, err)
205
+	pr, err = service.Prune(ctx, filters.NewArgs(filters.Arg("label!", "banana")))
206
+	assert.Assert(t, err)
207
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 0))
208
+
209
+	_, err = service.Create(ctx, "test3", volume.DefaultDriverName, opts.WithCreateLabels(map[string]string{"banana": "split"}))
210
+	assert.Assert(t, err)
211
+	pr, err = service.Prune(ctx, filters.NewArgs(filters.Arg("label!", "banana=split")))
212
+	assert.Assert(t, err)
213
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 1))
214
+	assert.Assert(t, is.Equal(pr.VolumesDeleted[0], "test"))
215
+
216
+	pr, err = service.Prune(ctx, filters.NewArgs(filters.Arg("label", "banana=split")))
217
+	assert.Assert(t, err)
218
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 1))
219
+	assert.Assert(t, is.Equal(pr.VolumesDeleted[0], "test3"))
220
+
221
+	v, err = service.Create(ctx, "test", volume.DefaultDriverName, opts.WithCreateReference(t.Name()))
222
+	assert.Assert(t, err)
223
+
224
+	pr, err = service.Prune(ctx, filters.NewArgs())
225
+	assert.Assert(t, err)
226
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 0))
227
+	assert.Assert(t, service.Release(ctx, v.Name, t.Name()))
228
+
229
+	pr, err = service.Prune(ctx, filters.NewArgs())
230
+	assert.Assert(t, err)
231
+	assert.Assert(t, is.Len(pr.VolumesDeleted, 1))
232
+	assert.Assert(t, is.Equal(pr.VolumesDeleted[0], "test"))
233
+}
234
+
235
+func newTestService(t *testing.T, ds *volumedrivers.Store) (*VolumesService, func()) {
236
+	t.Helper()
237
+
238
+	dir, err := ioutil.TempDir("", t.Name())
239
+	assert.Assert(t, err)
240
+
241
+	store, err := NewStore(dir, ds)
242
+	assert.Assert(t, err)
243
+	s := &VolumesService{vs: store, eventLogger: dummyEventLogger{}}
244
+	return s, func() {
245
+		assert.Check(t, s.Shutdown())
246
+		assert.Check(t, os.RemoveAll(dir))
247
+	}
248
+}
249
+
250
+type dummyEventLogger struct{}
251
+
252
+func (dummyEventLogger) LogVolumeEvent(_, _ string, _ map[string]string) {}
0 253
new file mode 100644
... ...
@@ -0,0 +1,858 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"context"
4
+	"fmt"
5
+	"net"
6
+	"os"
7
+	"path/filepath"
8
+	"runtime"
9
+	"sync"
10
+	"time"
11
+
12
+	"github.com/pkg/errors"
13
+
14
+	"github.com/boltdb/bolt"
15
+	"github.com/docker/docker/errdefs"
16
+	"github.com/docker/docker/pkg/locker"
17
+	"github.com/docker/docker/volume"
18
+	"github.com/docker/docker/volume/drivers"
19
+	volumemounts "github.com/docker/docker/volume/mounts"
20
+	"github.com/docker/docker/volume/service/opts"
21
+	"github.com/sirupsen/logrus"
22
+)
23
+
24
+const (
25
+	volumeDataDir = "volumes"
26
+)
27
+
28
+type volumeWrapper struct {
29
+	volume.Volume
30
+	labels  map[string]string
31
+	scope   string
32
+	options map[string]string
33
+}
34
+
35
+func (v volumeWrapper) Options() map[string]string {
36
+	if v.options == nil {
37
+		return nil
38
+	}
39
+	options := make(map[string]string, len(v.options))
40
+	for key, value := range v.options {
41
+		options[key] = value
42
+	}
43
+	return options
44
+}
45
+
46
+func (v volumeWrapper) Labels() map[string]string {
47
+	if v.labels == nil {
48
+		return nil
49
+	}
50
+
51
+	labels := make(map[string]string, len(v.labels))
52
+	for key, value := range v.labels {
53
+		labels[key] = value
54
+	}
55
+	return labels
56
+}
57
+
58
+func (v volumeWrapper) Scope() string {
59
+	return v.scope
60
+}
61
+
62
+func (v volumeWrapper) CachedPath() string {
63
+	if vv, ok := v.Volume.(interface {
64
+		CachedPath() string
65
+	}); ok {
66
+		return vv.CachedPath()
67
+	}
68
+	return v.Volume.Path()
69
+}
70
+
71
+// NewStore creates a new volume store at the given path
72
+func NewStore(rootPath string, drivers *drivers.Store) (*VolumeStore, error) {
73
+	vs := &VolumeStore{
74
+		locks:   &locker.Locker{},
75
+		names:   make(map[string]volume.Volume),
76
+		refs:    make(map[string]map[string]struct{}),
77
+		labels:  make(map[string]map[string]string),
78
+		options: make(map[string]map[string]string),
79
+		drivers: drivers,
80
+	}
81
+
82
+	if rootPath != "" {
83
+		// initialize metadata store
84
+		volPath := filepath.Join(rootPath, volumeDataDir)
85
+		if err := os.MkdirAll(volPath, 0750); err != nil {
86
+			return nil, err
87
+		}
88
+
89
+		var err error
90
+		vs.db, err = bolt.Open(filepath.Join(volPath, "metadata.db"), 0600, &bolt.Options{Timeout: 1 * time.Second})
91
+		if err != nil {
92
+			return nil, errors.Wrap(err, "error while opening volume store metadata database")
93
+		}
94
+
95
+		// initialize volumes bucket
96
+		if err := vs.db.Update(func(tx *bolt.Tx) error {
97
+			if _, err := tx.CreateBucketIfNotExists(volumeBucketName); err != nil {
98
+				return errors.Wrap(err, "error while setting up volume store metadata database")
99
+			}
100
+			return nil
101
+		}); err != nil {
102
+			return nil, err
103
+		}
104
+	}
105
+
106
+	vs.restore()
107
+
108
+	return vs, nil
109
+}
110
+
111
+func (s *VolumeStore) getNamed(name string) (volume.Volume, bool) {
112
+	s.globalLock.RLock()
113
+	v, exists := s.names[name]
114
+	s.globalLock.RUnlock()
115
+	return v, exists
116
+}
117
+
118
+func (s *VolumeStore) setNamed(v volume.Volume, ref string) {
119
+	name := v.Name()
120
+
121
+	s.globalLock.Lock()
122
+	s.names[name] = v
123
+	if len(ref) > 0 {
124
+		if s.refs[name] == nil {
125
+			s.refs[name] = make(map[string]struct{})
126
+		}
127
+		s.refs[name][ref] = struct{}{}
128
+	}
129
+	s.globalLock.Unlock()
130
+}
131
+
132
+// hasRef returns true if the given name has at least one ref.
133
+// Callers of this function are expected to hold the name lock.
134
+func (s *VolumeStore) hasRef(name string) bool {
135
+	s.globalLock.RLock()
136
+	l := len(s.refs[name])
137
+	s.globalLock.RUnlock()
138
+	return l > 0
139
+}
140
+
141
+// getRefs gets the list of refs for a given name
142
+// Callers of this function are expected to hold the name lock.
143
+func (s *VolumeStore) getRefs(name string) []string {
144
+	s.globalLock.RLock()
145
+	defer s.globalLock.RUnlock()
146
+
147
+	refs := make([]string, 0, len(s.refs[name]))
148
+	for r := range s.refs[name] {
149
+		refs = append(refs, r)
150
+	}
151
+
152
+	return refs
153
+}
154
+
155
+// purge allows the cleanup of internal data on docker in case
156
+// the internal data is out of sync with volumes driver plugins.
157
+func (s *VolumeStore) purge(ctx context.Context, name string) error {
158
+	s.globalLock.Lock()
159
+	defer s.globalLock.Unlock()
160
+
161
+	select {
162
+	case <-ctx.Done():
163
+		return ctx.Err()
164
+	default:
165
+	}
166
+
167
+	v, exists := s.names[name]
168
+	if exists {
169
+		driverName := v.DriverName()
170
+		if _, err := s.drivers.ReleaseDriver(driverName); err != nil {
171
+			logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver")
172
+		}
173
+	}
174
+	if err := s.removeMeta(name); err != nil {
175
+		logrus.Errorf("Error removing volume metadata for volume %q: %v", name, err)
176
+	}
177
+	delete(s.names, name)
178
+	delete(s.refs, name)
179
+	delete(s.labels, name)
180
+	delete(s.options, name)
181
+	return nil
182
+}
183
+
184
+// VolumeStore is a struct that stores the list of volumes available and keeps track of their usage counts
185
+type VolumeStore struct {
186
+	// locks ensures that only one action is being performed on a particular volume at a time without locking the entire store
187
+	// since actions on volumes can be quite slow, this ensures the store is free to handle requests for other volumes.
188
+	locks   *locker.Locker
189
+	drivers *drivers.Store
190
+	// globalLock is used to protect access to mutable structures used by the store object
191
+	globalLock sync.RWMutex
192
+	// names stores the volume name -> volume relationship.
193
+	// This is used for making lookups faster so we don't have to probe all drivers
194
+	names map[string]volume.Volume
195
+	// refs stores the volume name and the list of things referencing it
196
+	refs map[string]map[string]struct{}
197
+	// labels stores volume labels for each volume
198
+	labels map[string]map[string]string
199
+	// options stores volume options for each volume
200
+	options map[string]map[string]string
201
+	db      *bolt.DB
202
+}
203
+
204
+func filterByDriver(names []string) filterFunc {
205
+	return func(v volume.Volume) bool {
206
+		for _, name := range names {
207
+			if name == v.DriverName() {
208
+				return true
209
+			}
210
+		}
211
+		return false
212
+	}
213
+}
214
+
215
+func (s *VolumeStore) byReferenced(referenced bool) filterFunc {
216
+	return func(v volume.Volume) bool {
217
+		return s.hasRef(v.Name()) == referenced
218
+	}
219
+}
220
+
221
+func (s *VolumeStore) filter(ctx context.Context, vols *[]volume.Volume, by By) (warnings []string, err error) {
222
+	// note that this specifically does not support the `FromList` By type.
223
+	switch f := by.(type) {
224
+	case nil:
225
+		if *vols == nil {
226
+			var ls []volume.Volume
227
+			ls, warnings, err = s.list(ctx)
228
+			if err != nil {
229
+				return warnings, err
230
+			}
231
+			*vols = ls
232
+		}
233
+	case byDriver:
234
+		if *vols != nil {
235
+			filter(vols, filterByDriver([]string(f)))
236
+			return nil, nil
237
+		}
238
+		var ls []volume.Volume
239
+		ls, warnings, err = s.list(ctx, []string(f)...)
240
+		if err != nil {
241
+			return nil, err
242
+		}
243
+		*vols = ls
244
+	case ByReferenced:
245
+		// TODO(@cpuguy83): It would be nice to optimize this by looking at the list
246
+		// of referenced volumes, however the locking strategy makes this difficult
247
+		// without either providing inconsistent data or deadlocks.
248
+		if *vols == nil {
249
+			var ls []volume.Volume
250
+			ls, warnings, err = s.list(ctx)
251
+			if err != nil {
252
+				return nil, err
253
+			}
254
+			*vols = ls
255
+		}
256
+		filter(vols, s.byReferenced(bool(f)))
257
+	case andCombinator:
258
+		for _, by := range f {
259
+			w, err := s.filter(ctx, vols, by)
260
+			if err != nil {
261
+				return warnings, err
262
+			}
263
+			warnings = append(warnings, w...)
264
+		}
265
+	case orCombinator:
266
+		for _, by := range f {
267
+			switch by.(type) {
268
+			case byDriver:
269
+				var ls []volume.Volume
270
+				w, err := s.filter(ctx, &ls, by)
271
+				if err != nil {
272
+					return warnings, err
273
+				}
274
+				warnings = append(warnings, w...)
275
+			default:
276
+				ls, w, err := s.list(ctx)
277
+				if err != nil {
278
+					return warnings, err
279
+				}
280
+				warnings = append(warnings, w...)
281
+				w, err = s.filter(ctx, &ls, by)
282
+				if err != nil {
283
+					return warnings, err
284
+				}
285
+				warnings = append(warnings, w...)
286
+				*vols = append(*vols, ls...)
287
+			}
288
+		}
289
+		unique(vols)
290
+	case CustomFilter:
291
+		if *vols == nil {
292
+			var ls []volume.Volume
293
+			ls, warnings, err = s.list(ctx)
294
+			if err != nil {
295
+				return nil, err
296
+			}
297
+			*vols = ls
298
+		}
299
+		filter(vols, filterFunc(f))
300
+	default:
301
+		return nil, errdefs.InvalidParameter(errors.Errorf("unsupported filter: %T", f))
302
+	}
303
+	return warnings, nil
304
+}
305
+
306
+func unique(ls *[]volume.Volume) {
307
+	names := make(map[string]bool, len(*ls))
308
+	filter(ls, func(v volume.Volume) bool {
309
+		if names[v.Name()] {
310
+			return false
311
+		}
312
+		names[v.Name()] = true
313
+		return true
314
+	})
315
+}
316
+
317
+// Find lists volumes filtered by the past in filter.
318
+// If a driver returns a volume that has name which conflicts with another volume from a different driver,
319
+// the first volume is chosen and the conflicting volume is dropped.
320
+func (s *VolumeStore) Find(ctx context.Context, by By) (vols []volume.Volume, warnings []string, err error) {
321
+	logrus.WithField("ByType", fmt.Sprintf("%T", by)).WithField("ByValue", fmt.Sprintf("%+v", by)).Debug("VolumeStore.Find")
322
+	switch f := by.(type) {
323
+	case nil, orCombinator, andCombinator, byDriver, ByReferenced, CustomFilter:
324
+		warnings, err = s.filter(ctx, &vols, by)
325
+	case fromList:
326
+		warnings, err = s.filter(ctx, f.ls, f.by)
327
+	default:
328
+		// Really shouldn't be possible, but makes sure that any new By's are added to this check.
329
+		err = errdefs.InvalidParameter(errors.Errorf("unsupported filter type: %T", f))
330
+	}
331
+	if err != nil {
332
+		return nil, nil, &OpErr{Err: err, Op: "list"}
333
+	}
334
+
335
+	var out []volume.Volume
336
+
337
+	for _, v := range vols {
338
+		name := normalizeVolumeName(v.Name())
339
+
340
+		s.locks.Lock(name)
341
+		storedV, exists := s.getNamed(name)
342
+		// Note: it's not safe to populate the cache here because the volume may have been
343
+		// deleted before we acquire a lock on its name
344
+		if exists && storedV.DriverName() != v.DriverName() {
345
+			logrus.Warnf("Volume name %s already exists for driver %s, not including volume returned by %s", v.Name(), storedV.DriverName(), v.DriverName())
346
+			s.locks.Unlock(v.Name())
347
+			continue
348
+		}
349
+
350
+		out = append(out, v)
351
+		s.locks.Unlock(v.Name())
352
+	}
353
+	return out, warnings, nil
354
+}
355
+
356
+type filterFunc func(volume.Volume) bool
357
+
358
+func filter(vols *[]volume.Volume, fn filterFunc) {
359
+	var evict []int
360
+	for i, v := range *vols {
361
+		if !fn(v) {
362
+			evict = append(evict, i)
363
+		}
364
+	}
365
+
366
+	for n, i := range evict {
367
+		copy((*vols)[i-n:], (*vols)[i-n+1:])
368
+		(*vols)[len(*vols)-1] = nil
369
+		*vols = (*vols)[:len(*vols)-1]
370
+	}
371
+}
372
+
373
+// list goes through each volume driver and asks for its list of volumes.
374
+// TODO(@cpuguy83): plumb context through
375
+func (s *VolumeStore) list(ctx context.Context, driverNames ...string) ([]volume.Volume, []string, error) {
376
+	var (
377
+		ls       = []volume.Volume{} // do not return a nil value as this affects filtering
378
+		warnings []string
379
+	)
380
+
381
+	var dls []volume.Driver
382
+
383
+	all, err := s.drivers.GetAllDrivers()
384
+	if err != nil {
385
+		return nil, nil, err
386
+	}
387
+	if len(driverNames) == 0 {
388
+		dls = all
389
+	} else {
390
+		idx := make(map[string]bool, len(driverNames))
391
+		for _, name := range driverNames {
392
+			idx[name] = true
393
+		}
394
+		for _, d := range all {
395
+			if idx[d.Name()] {
396
+				dls = append(dls, d)
397
+			}
398
+		}
399
+	}
400
+
401
+	type vols struct {
402
+		vols       []volume.Volume
403
+		err        error
404
+		driverName string
405
+	}
406
+	chVols := make(chan vols, len(dls))
407
+
408
+	for _, vd := range dls {
409
+		go func(d volume.Driver) {
410
+			vs, err := d.List()
411
+			if err != nil {
412
+				chVols <- vols{driverName: d.Name(), err: &OpErr{Err: err, Name: d.Name(), Op: "list"}}
413
+				return
414
+			}
415
+			for i, v := range vs {
416
+				s.globalLock.RLock()
417
+				vs[i] = volumeWrapper{v, s.labels[v.Name()], d.Scope(), s.options[v.Name()]}
418
+				s.globalLock.RUnlock()
419
+			}
420
+
421
+			chVols <- vols{vols: vs}
422
+		}(vd)
423
+	}
424
+
425
+	badDrivers := make(map[string]struct{})
426
+	for i := 0; i < len(dls); i++ {
427
+		vs := <-chVols
428
+
429
+		if vs.err != nil {
430
+			warnings = append(warnings, vs.err.Error())
431
+			badDrivers[vs.driverName] = struct{}{}
432
+		}
433
+		ls = append(ls, vs.vols...)
434
+	}
435
+
436
+	if len(badDrivers) > 0 {
437
+		s.globalLock.RLock()
438
+		for _, v := range s.names {
439
+			if _, exists := badDrivers[v.DriverName()]; exists {
440
+				ls = append(ls, v)
441
+			}
442
+		}
443
+		s.globalLock.RUnlock()
444
+	}
445
+	return ls, warnings, nil
446
+}
447
+
448
+// Create creates a volume with the given name and driver
449
+// If the volume needs to be created with a reference to prevent race conditions
450
+// with volume cleanup, make sure to use the `CreateWithReference` option.
451
+func (s *VolumeStore) Create(ctx context.Context, name, driverName string, createOpts ...opts.CreateOption) (volume.Volume, error) {
452
+	var cfg opts.CreateConfig
453
+	for _, o := range createOpts {
454
+		o(&cfg)
455
+	}
456
+
457
+	name = normalizeVolumeName(name)
458
+	s.locks.Lock(name)
459
+	defer s.locks.Unlock(name)
460
+
461
+	select {
462
+	case <-ctx.Done():
463
+		return nil, ctx.Err()
464
+	default:
465
+	}
466
+
467
+	v, err := s.create(ctx, name, driverName, cfg.Options, cfg.Labels)
468
+	if err != nil {
469
+		if _, ok := err.(*OpErr); ok {
470
+			return nil, err
471
+		}
472
+		return nil, &OpErr{Err: err, Name: name, Op: "create"}
473
+	}
474
+
475
+	s.setNamed(v, cfg.Reference)
476
+	return v, nil
477
+}
478
+
479
+// checkConflict checks the local cache for name collisions with the passed in name,
480
+// for existing volumes with the same name but in a different driver.
481
+// This is used by `Create` as a best effort to prevent name collisions for volumes.
482
+// If a matching volume is found that is not a conflict that is returned so the caller
483
+// does not need to perform an additional lookup.
484
+// When no matching volume is found, both returns will be nil
485
+//
486
+// Note: This does not probe all the drivers for name collisions because v1 plugins
487
+// are very slow, particularly if the plugin is down, and cause other issues,
488
+// particularly around locking the store.
489
+// TODO(cpuguy83): With v2 plugins this shouldn't be a problem. Could also potentially
490
+// use a connect timeout for this kind of check to ensure we aren't blocking for a
491
+// long time.
492
+func (s *VolumeStore) checkConflict(ctx context.Context, name, driverName string) (volume.Volume, error) {
493
+	// check the local cache
494
+	v, _ := s.getNamed(name)
495
+	if v == nil {
496
+		return nil, nil
497
+	}
498
+
499
+	vDriverName := v.DriverName()
500
+	var conflict bool
501
+	if driverName != "" {
502
+		// Retrieve canonical driver name to avoid inconsistencies (for example
503
+		// "plugin" vs. "plugin:latest")
504
+		vd, err := s.drivers.GetDriver(driverName)
505
+		if err != nil {
506
+			return nil, err
507
+		}
508
+
509
+		if vDriverName != vd.Name() {
510
+			conflict = true
511
+		}
512
+	}
513
+
514
+	// let's check if the found volume ref
515
+	// is stale by checking with the driver if it still exists
516
+	exists, err := volumeExists(ctx, s.drivers, v)
517
+	if err != nil {
518
+		return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err)
519
+	}
520
+
521
+	if exists {
522
+		if conflict {
523
+			return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name)
524
+		}
525
+		return v, nil
526
+	}
527
+
528
+	if s.hasRef(v.Name()) {
529
+		// Containers are referencing this volume but it doesn't seem to exist anywhere.
530
+		// Return a conflict error here, the user can fix this with `docker volume rm -f`
531
+		return nil, errors.Wrapf(errNameConflict, "found references to volume '%s' in driver '%s' but the volume was not found in the driver -- you may need to remove containers referencing this volume or force remove the volume to re-create it", name, vDriverName)
532
+	}
533
+
534
+	// doesn't exist, so purge it from the cache
535
+	s.purge(ctx, name)
536
+	return nil, nil
537
+}
538
+
539
+// volumeExists returns if the volume is still present in the driver.
540
+// An error is returned if there was an issue communicating with the driver.
541
+func volumeExists(ctx context.Context, store *drivers.Store, v volume.Volume) (bool, error) {
542
+	exists, err := lookupVolume(ctx, store, v.DriverName(), v.Name())
543
+	if err != nil {
544
+		return false, err
545
+	}
546
+	return exists != nil, nil
547
+}
548
+
549
+// create asks the given driver to create a volume with the name/opts.
550
+// If a volume with the name is already known, it will ask the stored driver for the volume.
551
+// If the passed in driver name does not match the driver name which is stored
552
+//  for the given volume name, an error is returned after checking if the reference is stale.
553
+// If the reference is stale, it will be purged and this create can continue.
554
+// It is expected that callers of this function hold any necessary locks.
555
+func (s *VolumeStore) create(ctx context.Context, name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
556
+	// Validate the name in a platform-specific manner
557
+
558
+	// volume name validation is specific to the host os and not on container image
559
+	// windows/lcow should have an equivalent volumename validation logic so we create a parser for current host OS
560
+	parser := volumemounts.NewParser(runtime.GOOS)
561
+	err := parser.ValidateVolumeName(name)
562
+	if err != nil {
563
+		return nil, err
564
+	}
565
+
566
+	v, err := s.checkConflict(ctx, name, driverName)
567
+	if err != nil {
568
+		return nil, err
569
+	}
570
+
571
+	if v != nil {
572
+		// there is an existing volume, if we already have this stored locally, return it.
573
+		// TODO: there could be some inconsistent details such as labels here
574
+		if vv, _ := s.getNamed(v.Name()); vv != nil {
575
+			return vv, nil
576
+		}
577
+	}
578
+
579
+	// Since there isn't a specified driver name, let's see if any of the existing drivers have this volume name
580
+	if driverName == "" {
581
+		v, _ = s.getVolume(ctx, name, "")
582
+		if v != nil {
583
+			return v, nil
584
+		}
585
+	}
586
+
587
+	if driverName == "" {
588
+		driverName = volume.DefaultDriverName
589
+	}
590
+	vd, err := s.drivers.CreateDriver(driverName)
591
+	if err != nil {
592
+		return nil, &OpErr{Op: "create", Name: name, Err: err}
593
+	}
594
+
595
+	logrus.Debugf("Registering new volume reference: driver %q, name %q", vd.Name(), name)
596
+	if v, _ = vd.Get(name); v == nil {
597
+		v, err = vd.Create(name, opts)
598
+		if err != nil {
599
+			if _, err := s.drivers.ReleaseDriver(driverName); err != nil {
600
+				logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver")
601
+			}
602
+			return nil, err
603
+		}
604
+	}
605
+
606
+	s.globalLock.Lock()
607
+	s.labels[name] = labels
608
+	s.options[name] = opts
609
+	s.refs[name] = make(map[string]struct{})
610
+	s.globalLock.Unlock()
611
+
612
+	metadata := volumeMetadata{
613
+		Name:    name,
614
+		Driver:  vd.Name(),
615
+		Labels:  labels,
616
+		Options: opts,
617
+	}
618
+
619
+	if err := s.setMeta(name, metadata); err != nil {
620
+		return nil, err
621
+	}
622
+	return volumeWrapper{v, labels, vd.Scope(), opts}, nil
623
+}
624
+
625
+// Get looks if a volume with the given name exists and returns it if so
626
+func (s *VolumeStore) Get(ctx context.Context, name string, getOptions ...opts.GetOption) (volume.Volume, error) {
627
+	var cfg opts.GetConfig
628
+	for _, o := range getOptions {
629
+		o(&cfg)
630
+	}
631
+	name = normalizeVolumeName(name)
632
+	s.locks.Lock(name)
633
+	defer s.locks.Unlock(name)
634
+
635
+	v, err := s.getVolume(ctx, name, cfg.Driver)
636
+	if err != nil {
637
+		return nil, &OpErr{Err: err, Name: name, Op: "get"}
638
+	}
639
+	if cfg.Driver != "" && v.DriverName() != cfg.Driver {
640
+		return nil, &OpErr{Name: name, Op: "get", Err: errdefs.Conflict(errors.New("found volume driver does not match passed in driver"))}
641
+	}
642
+	s.setNamed(v, cfg.Reference)
643
+	return v, nil
644
+}
645
+
646
+// getVolume requests the volume, if the driver info is stored it just accesses that driver,
647
+// if the driver is unknown it probes all drivers until it finds the first volume with that name.
648
+// it is expected that callers of this function hold any necessary locks
649
+func (s *VolumeStore) getVolume(ctx context.Context, name, driverName string) (volume.Volume, error) {
650
+	var meta volumeMetadata
651
+	meta, err := s.getMeta(name)
652
+	if err != nil {
653
+		return nil, err
654
+	}
655
+
656
+	if driverName != "" {
657
+		if meta.Driver == "" {
658
+			meta.Driver = driverName
659
+		}
660
+		if driverName != meta.Driver {
661
+			return nil, errdefs.Conflict(errors.New("provided volume driver does not match stored driver"))
662
+		}
663
+	}
664
+
665
+	if driverName == "" {
666
+		driverName = meta.Driver
667
+	}
668
+	if driverName == "" {
669
+		s.globalLock.RLock()
670
+		select {
671
+		case <-ctx.Done():
672
+			s.globalLock.RUnlock()
673
+			return nil, ctx.Err()
674
+		default:
675
+		}
676
+		v, exists := s.names[name]
677
+		s.globalLock.RUnlock()
678
+		if exists {
679
+			meta.Driver = v.DriverName()
680
+			if err := s.setMeta(name, meta); err != nil {
681
+				return nil, err
682
+			}
683
+		}
684
+	}
685
+
686
+	if meta.Driver != "" {
687
+		vol, err := lookupVolume(ctx, s.drivers, meta.Driver, name)
688
+		if err != nil {
689
+			return nil, err
690
+		}
691
+		if vol == nil {
692
+			s.purge(ctx, name)
693
+			return nil, errNoSuchVolume
694
+		}
695
+
696
+		var scope string
697
+		vd, err := s.drivers.GetDriver(meta.Driver)
698
+		if err == nil {
699
+			scope = vd.Scope()
700
+		}
701
+		return volumeWrapper{vol, meta.Labels, scope, meta.Options}, nil
702
+	}
703
+
704
+	logrus.Debugf("Probing all drivers for volume with name: %s", name)
705
+	drivers, err := s.drivers.GetAllDrivers()
706
+	if err != nil {
707
+		return nil, err
708
+	}
709
+
710
+	for _, d := range drivers {
711
+		select {
712
+		case <-ctx.Done():
713
+			return nil, ctx.Err()
714
+		default:
715
+		}
716
+		v, err := d.Get(name)
717
+		if err != nil || v == nil {
718
+			continue
719
+		}
720
+		meta.Driver = v.DriverName()
721
+		if err := s.setMeta(name, meta); err != nil {
722
+			return nil, err
723
+		}
724
+		return volumeWrapper{v, meta.Labels, d.Scope(), meta.Options}, nil
725
+	}
726
+	return nil, errNoSuchVolume
727
+}
728
+
729
+// lookupVolume gets the specified volume from the specified driver.
730
+// This will only return errors related to communications with the driver.
731
+// If the driver returns an error that is not communication related the
732
+//   error is logged but not returned.
733
+// If the volume is not found it will return `nil, nil``
734
+// TODO(@cpuguy83): plumb through the context to lower level components
735
+func lookupVolume(ctx context.Context, store *drivers.Store, driverName, volumeName string) (volume.Volume, error) {
736
+	if driverName == "" {
737
+		driverName = volume.DefaultDriverName
738
+	}
739
+	vd, err := store.GetDriver(driverName)
740
+	if err != nil {
741
+		return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName)
742
+	}
743
+	v, err := vd.Get(volumeName)
744
+	if err != nil {
745
+		err = errors.Cause(err)
746
+		if _, ok := err.(net.Error); ok {
747
+			if v != nil {
748
+				volumeName = v.Name()
749
+				driverName = v.DriverName()
750
+			}
751
+			return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName)
752
+		}
753
+
754
+		// At this point, the error could be anything from the driver, such as "no such volume"
755
+		// Let's not check an error here, and instead check if the driver returned a volume
756
+		logrus.WithError(err).WithField("driver", driverName).WithField("volume", volumeName).Debug("Error while looking up volume")
757
+	}
758
+	return v, nil
759
+}
760
+
761
+// Remove removes the requested volume. A volume is not removed if it has any refs
762
+func (s *VolumeStore) Remove(ctx context.Context, v volume.Volume, rmOpts ...opts.RemoveOption) error {
763
+	var cfg opts.RemoveConfig
764
+	for _, o := range rmOpts {
765
+		o(&cfg)
766
+	}
767
+
768
+	name := v.Name()
769
+	s.locks.Lock(name)
770
+	defer s.locks.Unlock(name)
771
+
772
+	select {
773
+	case <-ctx.Done():
774
+		return ctx.Err()
775
+	default:
776
+	}
777
+
778
+	if s.hasRef(name) {
779
+		return &OpErr{Err: errVolumeInUse, Name: name, Op: "remove", Refs: s.getRefs(name)}
780
+	}
781
+
782
+	v, err := s.getVolume(ctx, name, v.DriverName())
783
+	if err != nil {
784
+		return err
785
+	}
786
+
787
+	vd, err := s.drivers.GetDriver(v.DriverName())
788
+	if err != nil {
789
+		return &OpErr{Err: err, Name: v.DriverName(), Op: "remove"}
790
+	}
791
+
792
+	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
793
+	vol := unwrapVolume(v)
794
+
795
+	err = vd.Remove(vol)
796
+	if err != nil {
797
+		err = &OpErr{Err: err, Name: name, Op: "remove"}
798
+	}
799
+
800
+	if err == nil || cfg.PurgeOnError {
801
+		if e := s.purge(ctx, name); e != nil && err == nil {
802
+			err = e
803
+		}
804
+	}
805
+	return err
806
+}
807
+
808
+// Release releases the specified reference to the volume
809
+func (s *VolumeStore) Release(ctx context.Context, name string, ref string) error {
810
+	s.locks.Lock(name)
811
+	defer s.locks.Unlock(name)
812
+	select {
813
+	case <-ctx.Done():
814
+		return ctx.Err()
815
+	default:
816
+	}
817
+
818
+	s.globalLock.Lock()
819
+	defer s.globalLock.Unlock()
820
+
821
+	select {
822
+	case <-ctx.Done():
823
+		return ctx.Err()
824
+	default:
825
+	}
826
+
827
+	if s.refs[name] != nil {
828
+		delete(s.refs[name], ref)
829
+	}
830
+	return nil
831
+}
832
+
833
+// CountReferences gives a count of all references for a given volume.
834
+func (s *VolumeStore) CountReferences(v volume.Volume) int {
835
+	name := normalizeVolumeName(v.Name())
836
+
837
+	s.locks.Lock(name)
838
+	defer s.locks.Unlock(name)
839
+	s.globalLock.Lock()
840
+	defer s.globalLock.Unlock()
841
+
842
+	return len(s.refs[name])
843
+}
844
+
845
+func unwrapVolume(v volume.Volume) volume.Volume {
846
+	if vol, ok := v.(volumeWrapper); ok {
847
+		return vol.Volume
848
+	}
849
+
850
+	return v
851
+}
852
+
853
+// Shutdown releases all resources used by the volume store
854
+// It does not make any changes to volumes, drivers, etc.
855
+func (s *VolumeStore) Shutdown() error {
856
+	return s.db.Close()
857
+}
0 858
new file mode 100644
... ...
@@ -0,0 +1,421 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import (
3
+	"context"
4
+	"errors"
5
+	"fmt"
6
+	"io/ioutil"
7
+	"net"
8
+	"os"
9
+	"strings"
10
+	"testing"
11
+
12
+	"github.com/docker/docker/volume"
13
+	volumedrivers "github.com/docker/docker/volume/drivers"
14
+	"github.com/docker/docker/volume/service/opts"
15
+	volumetestutils "github.com/docker/docker/volume/testutils"
16
+	"github.com/google/go-cmp/cmp"
17
+	"github.com/gotestyourself/gotestyourself/assert"
18
+	is "github.com/gotestyourself/gotestyourself/assert/cmp"
19
+)
20
+
21
+func TestCreate(t *testing.T) {
22
+	t.Parallel()
23
+
24
+	s, cleanup := setupTest(t)
25
+	defer cleanup()
26
+	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
27
+
28
+	ctx := context.Background()
29
+	v, err := s.Create(ctx, "fake1", "fake")
30
+	if err != nil {
31
+		t.Fatal(err)
32
+	}
33
+	if v.Name() != "fake1" {
34
+		t.Fatalf("Expected fake1 volume, got %v", v)
35
+	}
36
+	if l, _, _ := s.Find(ctx, nil); len(l) != 1 {
37
+		t.Fatalf("Expected 1 volume in the store, got %v: %v", len(l), l)
38
+	}
39
+
40
+	if _, err := s.Create(ctx, "none", "none"); err == nil {
41
+		t.Fatalf("Expected unknown driver error, got nil")
42
+	}
43
+
44
+	_, err = s.Create(ctx, "fakeerror", "fake", opts.WithCreateOptions(map[string]string{"error": "create error"}))
45
+	expected := &OpErr{Op: "create", Name: "fakeerror", Err: errors.New("create error")}
46
+	if err != nil && err.Error() != expected.Error() {
47
+		t.Fatalf("Expected create fakeError: create error, got %v", err)
48
+	}
49
+}
50
+
51
+func TestRemove(t *testing.T) {
52
+	t.Parallel()
53
+
54
+	s, cleanup := setupTest(t)
55
+	defer cleanup()
56
+
57
+	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
58
+	s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
59
+
60
+	ctx := context.Background()
61
+
62
+	// doing string compare here since this error comes directly from the driver
63
+	expected := "no such volume"
64
+	var v volume.Volume = volumetestutils.NoopVolume{}
65
+	if err := s.Remove(ctx, v); err == nil || !strings.Contains(err.Error(), expected) {
66
+		t.Fatalf("Expected error %q, got %v", expected, err)
67
+	}
68
+
69
+	v, err := s.Create(ctx, "fake1", "fake", opts.WithCreateReference("fake"))
70
+	if err != nil {
71
+		t.Fatal(err)
72
+	}
73
+
74
+	if err := s.Remove(ctx, v); !IsInUse(err) {
75
+		t.Fatalf("Expected ErrVolumeInUse error, got %v", err)
76
+	}
77
+	s.Release(ctx, v.Name(), "fake")
78
+	if err := s.Remove(ctx, v); err != nil {
79
+		t.Fatal(err)
80
+	}
81
+	if l, _, _ := s.Find(ctx, nil); len(l) != 0 {
82
+		t.Fatalf("Expected 0 volumes in the store, got %v, %v", len(l), l)
83
+	}
84
+}
85
+
86
+func TestList(t *testing.T) {
87
+	t.Parallel()
88
+
89
+	dir, err := ioutil.TempDir("", "test-list")
90
+	assert.NilError(t, err)
91
+	defer os.RemoveAll(dir)
92
+
93
+	drivers := volumedrivers.NewStore(nil)
94
+	drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
95
+	drivers.Register(volumetestutils.NewFakeDriver("fake2"), "fake2")
96
+
97
+	s, err := NewStore(dir, drivers)
98
+	assert.NilError(t, err)
99
+
100
+	ctx := context.Background()
101
+	if _, err := s.Create(ctx, "test", "fake"); err != nil {
102
+		t.Fatal(err)
103
+	}
104
+	if _, err := s.Create(ctx, "test2", "fake2"); err != nil {
105
+		t.Fatal(err)
106
+	}
107
+
108
+	ls, _, err := s.Find(ctx, nil)
109
+	if err != nil {
110
+		t.Fatal(err)
111
+	}
112
+	if len(ls) != 2 {
113
+		t.Fatalf("expected 2 volumes, got: %d", len(ls))
114
+	}
115
+	if err := s.Shutdown(); err != nil {
116
+		t.Fatal(err)
117
+	}
118
+
119
+	// and again with a new store
120
+	s, err = NewStore(dir, drivers)
121
+	if err != nil {
122
+		t.Fatal(err)
123
+	}
124
+	ls, _, err = s.Find(ctx, nil)
125
+	if err != nil {
126
+		t.Fatal(err)
127
+	}
128
+	if len(ls) != 2 {
129
+		t.Fatalf("expected 2 volumes, got: %d", len(ls))
130
+	}
131
+}
132
+
133
+func TestFindByDriver(t *testing.T) {
134
+	t.Parallel()
135
+	s, cleanup := setupTest(t)
136
+	defer cleanup()
137
+
138
+	assert.Assert(t, s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake"))
139
+	assert.Assert(t, s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop"))
140
+
141
+	ctx := context.Background()
142
+	_, err := s.Create(ctx, "fake1", "fake")
143
+	assert.NilError(t, err)
144
+
145
+	_, err = s.Create(ctx, "fake2", "fake")
146
+	assert.NilError(t, err)
147
+
148
+	_, err = s.Create(ctx, "fake3", "noop")
149
+	assert.NilError(t, err)
150
+
151
+	l, _, err := s.Find(ctx, ByDriver("fake"))
152
+	assert.NilError(t, err)
153
+	assert.Equal(t, len(l), 2)
154
+
155
+	l, _, err = s.Find(ctx, ByDriver("noop"))
156
+	assert.NilError(t, err)
157
+	assert.Equal(t, len(l), 1)
158
+
159
+	l, _, err = s.Find(ctx, ByDriver("nosuchdriver"))
160
+	assert.NilError(t, err)
161
+	assert.Equal(t, len(l), 0)
162
+}
163
+
164
+func TestFindByReferenced(t *testing.T) {
165
+	t.Parallel()
166
+	s, cleanup := setupTest(t)
167
+	defer cleanup()
168
+
169
+	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
170
+	s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
171
+
172
+	ctx := context.Background()
173
+	if _, err := s.Create(ctx, "fake1", "fake", opts.WithCreateReference("volReference")); err != nil {
174
+		t.Fatal(err)
175
+	}
176
+	if _, err := s.Create(ctx, "fake2", "fake"); err != nil {
177
+		t.Fatal(err)
178
+	}
179
+
180
+	dangling, _, err := s.Find(ctx, ByReferenced(false))
181
+	assert.Assert(t, err)
182
+	assert.Assert(t, len(dangling) == 1)
183
+	assert.Check(t, dangling[0].Name() == "fake2")
184
+
185
+	used, _, err := s.Find(ctx, ByReferenced(true))
186
+	assert.Assert(t, err)
187
+	assert.Assert(t, len(used) == 1)
188
+	assert.Check(t, used[0].Name() == "fake1")
189
+}
190
+
191
+func TestDerefMultipleOfSameRef(t *testing.T) {
192
+	t.Parallel()
193
+	s, cleanup := setupTest(t)
194
+	defer cleanup()
195
+	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
196
+
197
+	ctx := context.Background()
198
+	v, err := s.Create(ctx, "fake1", "fake", opts.WithCreateReference("volReference"))
199
+	if err != nil {
200
+		t.Fatal(err)
201
+	}
202
+
203
+	if _, err := s.Get(ctx, "fake1", opts.WithGetDriver("fake"), opts.WithGetReference("volReference")); err != nil {
204
+		t.Fatal(err)
205
+	}
206
+
207
+	s.Release(ctx, v.Name(), "volReference")
208
+	if err := s.Remove(ctx, v); err != nil {
209
+		t.Fatal(err)
210
+	}
211
+}
212
+
213
+func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) {
214
+	t.Parallel()
215
+	s, cleanup := setupTest(t)
216
+	defer cleanup()
217
+
218
+	vd := volumetestutils.NewFakeDriver("fake")
219
+	s.drivers.Register(vd, "fake")
220
+
221
+	// Create a volume in the driver directly
222
+	if _, err := vd.Create("foo", nil); err != nil {
223
+		t.Fatal(err)
224
+	}
225
+
226
+	ctx := context.Background()
227
+	v, err := s.Create(ctx, "foo", "fake", opts.WithCreateLabels(map[string]string{"hello": "world"}))
228
+	if err != nil {
229
+		t.Fatal(err)
230
+	}
231
+
232
+	switch dv := v.(type) {
233
+	case volume.DetailedVolume:
234
+		if dv.Labels()["hello"] != "world" {
235
+			t.Fatalf("labels don't match")
236
+		}
237
+	default:
238
+		t.Fatalf("got unexpected type: %T", v)
239
+	}
240
+}
241
+
242
+func TestDefererencePluginOnCreateError(t *testing.T) {
243
+	t.Parallel()
244
+
245
+	var (
246
+		l   net.Listener
247
+		err error
248
+	)
249
+
250
+	for i := 32768; l == nil && i < 40000; i++ {
251
+		l, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", i))
252
+	}
253
+	if l == nil {
254
+		t.Fatalf("could not create listener: %v", err)
255
+	}
256
+	defer l.Close()
257
+
258
+	s, cleanup := setupTest(t)
259
+	defer cleanup()
260
+
261
+	d := volumetestutils.NewFakeDriver("TestDefererencePluginOnCreateError")
262
+	p, err := volumetestutils.MakeFakePlugin(d, l)
263
+	if err != nil {
264
+		t.Fatal(err)
265
+	}
266
+
267
+	pg := volumetestutils.NewFakePluginGetter(p)
268
+	s.drivers = volumedrivers.NewStore(pg)
269
+
270
+	ctx := context.Background()
271
+	// create a good volume so we have a plugin reference
272
+	_, err = s.Create(ctx, "fake1", d.Name())
273
+	if err != nil {
274
+		t.Fatal(err)
275
+	}
276
+
277
+	// Now create another one expecting an error
278
+	_, err = s.Create(ctx, "fake2", d.Name(), opts.WithCreateOptions(map[string]string{"error": "some error"}))
279
+	if err == nil || !strings.Contains(err.Error(), "some error") {
280
+		t.Fatalf("expected an error on create: %v", err)
281
+	}
282
+
283
+	// There should be only 1 plugin reference
284
+	if refs := volumetestutils.FakeRefs(p); refs != 1 {
285
+		t.Fatalf("expected 1 plugin reference, got: %d", refs)
286
+	}
287
+}
288
+
289
+func TestRefDerefRemove(t *testing.T) {
290
+	t.Parallel()
291
+
292
+	driverName := "test-ref-deref-remove"
293
+	s, cleanup := setupTest(t)
294
+	defer cleanup()
295
+	s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
296
+
297
+	ctx := context.Background()
298
+	v, err := s.Create(ctx, "test", driverName, opts.WithCreateReference("test-ref"))
299
+	assert.NilError(t, err)
300
+
301
+	err = s.Remove(ctx, v)
302
+	assert.Assert(t, is.ErrorContains(err, ""))
303
+	assert.Equal(t, errVolumeInUse, err.(*OpErr).Err)
304
+
305
+	s.Release(ctx, v.Name(), "test-ref")
306
+	err = s.Remove(ctx, v)
307
+	assert.NilError(t, err)
308
+}
309
+
310
+func TestGet(t *testing.T) {
311
+	t.Parallel()
312
+
313
+	driverName := "test-get"
314
+	s, cleanup := setupTest(t)
315
+	defer cleanup()
316
+	s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
317
+
318
+	ctx := context.Background()
319
+	_, err := s.Get(ctx, "not-exist")
320
+	assert.Assert(t, is.ErrorContains(err, ""))
321
+	assert.Equal(t, errNoSuchVolume, err.(*OpErr).Err)
322
+
323
+	v1, err := s.Create(ctx, "test", driverName, opts.WithCreateLabels(map[string]string{"a": "1"}))
324
+	assert.NilError(t, err)
325
+
326
+	v2, err := s.Get(ctx, "test")
327
+	assert.NilError(t, err)
328
+	assert.DeepEqual(t, v1, v2, cmpVolume)
329
+
330
+	dv := v2.(volume.DetailedVolume)
331
+	assert.Equal(t, "1", dv.Labels()["a"])
332
+
333
+	err = s.Remove(ctx, v1)
334
+	assert.NilError(t, err)
335
+}
336
+
337
+func TestGetWithReference(t *testing.T) {
338
+	t.Parallel()
339
+
340
+	driverName := "test-get-with-ref"
341
+	s, cleanup := setupTest(t)
342
+	defer cleanup()
343
+	s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
344
+
345
+	ctx := context.Background()
346
+	_, err := s.Get(ctx, "not-exist", opts.WithGetDriver(driverName), opts.WithGetReference("test-ref"))
347
+	assert.Assert(t, is.ErrorContains(err, ""))
348
+
349
+	v1, err := s.Create(ctx, "test", driverName, opts.WithCreateLabels(map[string]string{"a": "1"}))
350
+	assert.NilError(t, err)
351
+
352
+	v2, err := s.Get(ctx, "test", opts.WithGetDriver(driverName), opts.WithGetReference("test-ref"))
353
+	assert.NilError(t, err)
354
+	assert.DeepEqual(t, v1, v2, cmpVolume)
355
+
356
+	err = s.Remove(ctx, v2)
357
+	assert.Assert(t, is.ErrorContains(err, ""))
358
+	assert.Equal(t, errVolumeInUse, err.(*OpErr).Err)
359
+
360
+	s.Release(ctx, v2.Name(), "test-ref")
361
+	err = s.Remove(ctx, v2)
362
+	assert.NilError(t, err)
363
+}
364
+
365
+var cmpVolume = cmp.AllowUnexported(volumetestutils.FakeVolume{}, volumeWrapper{})
366
+
367
+func setupTest(t *testing.T) (*VolumeStore, func()) {
368
+	t.Helper()
369
+
370
+	dirName := strings.Replace(t.Name(), string(os.PathSeparator), "_", -1)
371
+	dir, err := ioutil.TempDir("", dirName)
372
+	assert.NilError(t, err)
373
+
374
+	cleanup := func() {
375
+		t.Helper()
376
+		err := os.RemoveAll(dir)
377
+		assert.Check(t, err)
378
+	}
379
+
380
+	s, err := NewStore(dir, volumedrivers.NewStore(nil))
381
+	assert.Check(t, err)
382
+	return s, func() {
383
+		s.Shutdown()
384
+		cleanup()
385
+	}
386
+}
387
+
388
+func TestFilterFunc(t *testing.T) {
389
+	testDriver := volumetestutils.NewFakeDriver("test")
390
+	testVolume, err := testDriver.Create("test", nil)
391
+	assert.NilError(t, err)
392
+	testVolume2, err := testDriver.Create("test2", nil)
393
+	assert.NilError(t, err)
394
+	testVolume3, err := testDriver.Create("test3", nil)
395
+	assert.NilError(t, err)
396
+
397
+	for _, test := range []struct {
398
+		vols   []volume.Volume
399
+		fn     filterFunc
400
+		desc   string
401
+		expect []volume.Volume
402
+	}{
403
+		{desc: "test nil list", vols: nil, expect: nil, fn: func(volume.Volume) bool { return true }},
404
+		{desc: "test empty list", vols: []volume.Volume{}, expect: []volume.Volume{}, fn: func(volume.Volume) bool { return true }},
405
+		{desc: "test filter non-empty to empty", vols: []volume.Volume{testVolume}, expect: []volume.Volume{}, fn: func(volume.Volume) bool { return false }},
406
+		{desc: "test nothing to fitler non-empty list", vols: []volume.Volume{testVolume}, expect: []volume.Volume{testVolume}, fn: func(volume.Volume) bool { return true }},
407
+		{desc: "test filter some", vols: []volume.Volume{testVolume, testVolume2}, expect: []volume.Volume{testVolume}, fn: func(v volume.Volume) bool { return v.Name() == testVolume.Name() }},
408
+		{desc: "test filter middle", vols: []volume.Volume{testVolume, testVolume2, testVolume3}, expect: []volume.Volume{testVolume, testVolume3}, fn: func(v volume.Volume) bool { return v.Name() != testVolume2.Name() }},
409
+		{desc: "test filter middle and last", vols: []volume.Volume{testVolume, testVolume2, testVolume3}, expect: []volume.Volume{testVolume}, fn: func(v volume.Volume) bool { return v.Name() != testVolume2.Name() && v.Name() != testVolume3.Name() }},
410
+		{desc: "test filter first and last", vols: []volume.Volume{testVolume, testVolume2, testVolume3}, expect: []volume.Volume{testVolume2}, fn: func(v volume.Volume) bool { return v.Name() != testVolume.Name() && v.Name() != testVolume3.Name() }},
411
+	} {
412
+		t.Run(test.desc, func(t *testing.T) {
413
+			test := test
414
+			t.Parallel()
415
+
416
+			filter(&test.vols, test.fn)
417
+			assert.DeepEqual(t, test.vols, test.expect, cmpVolume)
418
+		})
419
+	}
420
+}
0 421
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+// +build linux freebsd darwin
1
+
2
+package service // import "github.com/docker/docker/volume/service"
3
+
4
+// normalizeVolumeName is a platform specific function to normalize the name
5
+// of a volume. This is a no-op on Unix-like platforms
6
+func normalizeVolumeName(name string) string {
7
+	return name
8
+}
0 9
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+package service // import "github.com/docker/docker/volume/service"
1
+
2
+import "strings"
3
+
4
+// normalizeVolumeName is a platform specific function to normalize the name
5
+// of a volume. On Windows, as NTFS is case insensitive, under
6
+// c:\ProgramData\Docker\Volumes\, the folders John and john would be synonymous.
7
+// Hence we can't allow the volume "John" and "john" to be created as separate
8
+// volumes.
9
+func normalizeVolumeName(name string) string {
10
+	return strings.ToLower(name)
11
+}
0 12
deleted file mode 100644
... ...
@@ -1,95 +0,0 @@
1
-package store // import "github.com/docker/docker/volume/store"
2
-
3
-import (
4
-	"encoding/json"
5
-
6
-	"github.com/boltdb/bolt"
7
-	"github.com/docker/docker/errdefs"
8
-	"github.com/pkg/errors"
9
-	"github.com/sirupsen/logrus"
10
-)
11
-
12
-var volumeBucketName = []byte("volumes")
13
-
14
-type volumeMetadata struct {
15
-	Name    string
16
-	Driver  string
17
-	Labels  map[string]string
18
-	Options map[string]string
19
-}
20
-
21
-func (s *VolumeStore) setMeta(name string, meta volumeMetadata) error {
22
-	return s.db.Update(func(tx *bolt.Tx) error {
23
-		return setMeta(tx, name, meta)
24
-	})
25
-}
26
-
27
-func setMeta(tx *bolt.Tx, name string, meta volumeMetadata) error {
28
-	metaJSON, err := json.Marshal(meta)
29
-	if err != nil {
30
-		return err
31
-	}
32
-	b, err := tx.CreateBucketIfNotExists(volumeBucketName)
33
-	if err != nil {
34
-		return errors.Wrap(err, "error creating volume bucket")
35
-	}
36
-	return errors.Wrap(b.Put([]byte(name), metaJSON), "error setting volume metadata")
37
-}
38
-
39
-func (s *VolumeStore) getMeta(name string) (volumeMetadata, error) {
40
-	var meta volumeMetadata
41
-	err := s.db.View(func(tx *bolt.Tx) error {
42
-		return getMeta(tx, name, &meta)
43
-	})
44
-	return meta, err
45
-}
46
-
47
-func getMeta(tx *bolt.Tx, name string, meta *volumeMetadata) error {
48
-	b := tx.Bucket(volumeBucketName)
49
-	if b == nil {
50
-		return errdefs.NotFound(errors.New("volume bucket does not exist"))
51
-	}
52
-	val := b.Get([]byte(name))
53
-	if len(val) == 0 {
54
-		return nil
55
-	}
56
-	if err := json.Unmarshal(val, meta); err != nil {
57
-		return errors.Wrap(err, "error unmarshaling volume metadata")
58
-	}
59
-	return nil
60
-}
61
-
62
-func (s *VolumeStore) removeMeta(name string) error {
63
-	return s.db.Update(func(tx *bolt.Tx) error {
64
-		return removeMeta(tx, name)
65
-	})
66
-}
67
-
68
-func removeMeta(tx *bolt.Tx, name string) error {
69
-	b := tx.Bucket(volumeBucketName)
70
-	return errors.Wrap(b.Delete([]byte(name)), "error removing volume metadata")
71
-}
72
-
73
-// listMeta is used during restore to get the list of volume metadata
74
-// from the on-disk database.
75
-// Any errors that occur are only logged.
76
-func listMeta(tx *bolt.Tx) []volumeMetadata {
77
-	var ls []volumeMetadata
78
-	b := tx.Bucket(volumeBucketName)
79
-	b.ForEach(func(k, v []byte) error {
80
-		if len(v) == 0 {
81
-			// don't try to unmarshal an empty value
82
-			return nil
83
-		}
84
-
85
-		var m volumeMetadata
86
-		if err := json.Unmarshal(v, &m); err != nil {
87
-			// Just log the error
88
-			logrus.Errorf("Error while reading volume metadata for volume %q: %v", string(k), err)
89
-			return nil
90
-		}
91
-		ls = append(ls, m)
92
-		return nil
93
-	})
94
-	return ls
95
-}
96 1
deleted file mode 100644
... ...
@@ -1,52 +0,0 @@
1
-package store
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"path/filepath"
7
-	"testing"
8
-	"time"
9
-
10
-	"github.com/boltdb/bolt"
11
-	"github.com/gotestyourself/gotestyourself/assert"
12
-	is "github.com/gotestyourself/gotestyourself/assert/cmp"
13
-)
14
-
15
-func TestSetGetMeta(t *testing.T) {
16
-	t.Parallel()
17
-
18
-	dir, err := ioutil.TempDir("", "test-set-get")
19
-	assert.NilError(t, err)
20
-	defer os.RemoveAll(dir)
21
-
22
-	db, err := bolt.Open(filepath.Join(dir, "db"), 0600, &bolt.Options{Timeout: 1 * time.Second})
23
-	assert.NilError(t, err)
24
-
25
-	store := &VolumeStore{db: db}
26
-
27
-	_, err = store.getMeta("test")
28
-	assert.Assert(t, is.ErrorContains(err, ""))
29
-
30
-	err = db.Update(func(tx *bolt.Tx) error {
31
-		_, err := tx.CreateBucket(volumeBucketName)
32
-		return err
33
-	})
34
-	assert.NilError(t, err)
35
-
36
-	meta, err := store.getMeta("test")
37
-	assert.NilError(t, err)
38
-	assert.DeepEqual(t, volumeMetadata{}, meta)
39
-
40
-	testMeta := volumeMetadata{
41
-		Name:    "test",
42
-		Driver:  "fake",
43
-		Labels:  map[string]string{"a": "1", "b": "2"},
44
-		Options: map[string]string{"foo": "bar"},
45
-	}
46
-	err = store.setMeta("test", testMeta)
47
-	assert.NilError(t, err)
48
-
49
-	meta, err = store.getMeta("test")
50
-	assert.NilError(t, err)
51
-	assert.DeepEqual(t, testMeta, meta)
52
-}
53 1
deleted file mode 100644
... ...
@@ -1,95 +0,0 @@
1
-package store // import "github.com/docker/docker/volume/store"
2
-
3
-import (
4
-	"strings"
5
-)
6
-
7
-const (
8
-	// errVolumeInUse is a typed error returned when trying to remove a volume that is currently in use by a container
9
-	errVolumeInUse conflictError = "volume is in use"
10
-	// errNoSuchVolume is a typed error returned if the requested volume doesn't exist in the volume store
11
-	errNoSuchVolume notFoundError = "no such volume"
12
-	// errNameConflict is a typed error returned on create when a volume exists with the given name, but for a different driver
13
-	errNameConflict conflictError = "volume name must be unique"
14
-)
15
-
16
-type conflictError string
17
-
18
-func (e conflictError) Error() string {
19
-	return string(e)
20
-}
21
-func (conflictError) Conflict() {}
22
-
23
-type notFoundError string
24
-
25
-func (e notFoundError) Error() string {
26
-	return string(e)
27
-}
28
-
29
-func (notFoundError) NotFound() {}
30
-
31
-// OpErr is the error type returned by functions in the store package. It describes
32
-// the operation, volume name, and error.
33
-type OpErr struct {
34
-	// Err is the error that occurred during the operation.
35
-	Err error
36
-	// Op is the operation which caused the error, such as "create", or "list".
37
-	Op string
38
-	// Name is the name of the resource being requested for this op, typically the volume name or the driver name.
39
-	Name string
40
-	// Refs is the list of references associated with the resource.
41
-	Refs []string
42
-}
43
-
44
-// Error satisfies the built-in error interface type.
45
-func (e *OpErr) Error() string {
46
-	if e == nil {
47
-		return "<nil>"
48
-	}
49
-	s := e.Op
50
-	if e.Name != "" {
51
-		s = s + " " + e.Name
52
-	}
53
-
54
-	s = s + ": " + e.Err.Error()
55
-	if len(e.Refs) > 0 {
56
-		s = s + " - " + "[" + strings.Join(e.Refs, ", ") + "]"
57
-	}
58
-	return s
59
-}
60
-
61
-// Cause returns the error the caused this error
62
-func (e *OpErr) Cause() error {
63
-	return e.Err
64
-}
65
-
66
-// IsInUse returns a boolean indicating whether the error indicates that a
67
-// volume is in use
68
-func IsInUse(err error) bool {
69
-	return isErr(err, errVolumeInUse)
70
-}
71
-
72
-// IsNotExist returns a boolean indicating whether the error indicates that the volume does not exist
73
-func IsNotExist(err error) bool {
74
-	return isErr(err, errNoSuchVolume)
75
-}
76
-
77
-// IsNameConflict returns a boolean indicating whether the error indicates that a
78
-// volume name is already taken
79
-func IsNameConflict(err error) bool {
80
-	return isErr(err, errNameConflict)
81
-}
82
-
83
-type causal interface {
84
-	Cause() error
85
-}
86
-
87
-func isErr(err error, expected error) bool {
88
-	switch pe := err.(type) {
89
-	case nil:
90
-		return false
91
-	case causal:
92
-		return isErr(pe.Cause(), expected)
93
-	}
94
-	return err == expected
95
-}
96 1
deleted file mode 100644
... ...
@@ -1,82 +0,0 @@
1
-package store // import "github.com/docker/docker/volume/store"
2
-
3
-import (
4
-	"sync"
5
-
6
-	"github.com/boltdb/bolt"
7
-	"github.com/docker/docker/volume"
8
-	"github.com/sirupsen/logrus"
9
-)
10
-
11
-// restore is called when a new volume store is created.
12
-// It's primary purpose is to ensure that all drivers' refcounts are set based
13
-// on known volumes after a restart.
14
-// This only attempts to track volumes that are actually stored in the on-disk db.
15
-// It does not probe the available drivers to find anything that may have been added
16
-// out of band.
17
-func (s *VolumeStore) restore() {
18
-	var ls []volumeMetadata
19
-	s.db.View(func(tx *bolt.Tx) error {
20
-		ls = listMeta(tx)
21
-		return nil
22
-	})
23
-
24
-	chRemove := make(chan *volumeMetadata, len(ls))
25
-	var wg sync.WaitGroup
26
-	for _, meta := range ls {
27
-		wg.Add(1)
28
-		// this is potentially a very slow operation, so do it in a goroutine
29
-		go func(meta volumeMetadata) {
30
-			defer wg.Done()
31
-
32
-			var v volume.Volume
33
-			var err error
34
-			if meta.Driver != "" {
35
-				v, err = lookupVolume(s.drivers, meta.Driver, meta.Name)
36
-				if err != nil && err != errNoSuchVolume {
37
-					logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume")
38
-					return
39
-				}
40
-				if v == nil {
41
-					// doesn't exist in the driver, remove it from the db
42
-					chRemove <- &meta
43
-					return
44
-				}
45
-			} else {
46
-				v, err = s.getVolume(meta.Name)
47
-				if err != nil {
48
-					if err == errNoSuchVolume {
49
-						chRemove <- &meta
50
-					}
51
-					return
52
-				}
53
-
54
-				meta.Driver = v.DriverName()
55
-				if err := s.setMeta(v.Name(), meta); err != nil {
56
-					logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", v.Name()).Warn("Error updating volume metadata on restore")
57
-				}
58
-			}
59
-
60
-			// increment driver refcount
61
-			s.drivers.CreateDriver(meta.Driver)
62
-
63
-			// cache the volume
64
-			s.globalLock.Lock()
65
-			s.options[v.Name()] = meta.Options
66
-			s.labels[v.Name()] = meta.Labels
67
-			s.names[v.Name()] = v
68
-			s.globalLock.Unlock()
69
-		}(meta)
70
-	}
71
-
72
-	wg.Wait()
73
-	close(chRemove)
74
-	s.db.Update(func(tx *bolt.Tx) error {
75
-		for meta := range chRemove {
76
-			if err := removeMeta(tx, meta.Name); err != nil {
77
-				logrus.WithField("volume", meta.Name).Warnf("Error removing stale entry from volume db: %v", err)
78
-			}
79
-		}
80
-		return nil
81
-	})
82
-}
83 1
deleted file mode 100644
... ...
@@ -1,55 +0,0 @@
1
-package store
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"testing"
7
-
8
-	"github.com/docker/docker/volume"
9
-	volumedrivers "github.com/docker/docker/volume/drivers"
10
-	volumetestutils "github.com/docker/docker/volume/testutils"
11
-	"github.com/gotestyourself/gotestyourself/assert"
12
-)
13
-
14
-func TestRestore(t *testing.T) {
15
-	t.Parallel()
16
-
17
-	dir, err := ioutil.TempDir("", "test-restore")
18
-	assert.NilError(t, err)
19
-	defer os.RemoveAll(dir)
20
-
21
-	drivers := volumedrivers.NewStore(nil)
22
-	driverName := "test-restore"
23
-	drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
24
-
25
-	s, err := New(dir, drivers)
26
-	assert.NilError(t, err)
27
-	defer s.Shutdown()
28
-
29
-	_, err = s.Create("test1", driverName, nil, nil)
30
-	assert.NilError(t, err)
31
-
32
-	testLabels := map[string]string{"a": "1"}
33
-	testOpts := map[string]string{"foo": "bar"}
34
-	_, err = s.Create("test2", driverName, testOpts, testLabels)
35
-	assert.NilError(t, err)
36
-
37
-	s.Shutdown()
38
-
39
-	s, err = New(dir, drivers)
40
-	assert.NilError(t, err)
41
-
42
-	v, err := s.Get("test1")
43
-	assert.NilError(t, err)
44
-
45
-	dv := v.(volume.DetailedVolume)
46
-	var nilMap map[string]string
47
-	assert.DeepEqual(t, nilMap, dv.Options())
48
-	assert.DeepEqual(t, nilMap, dv.Labels())
49
-
50
-	v, err = s.Get("test2")
51
-	assert.NilError(t, err)
52
-	dv = v.(volume.DetailedVolume)
53
-	assert.DeepEqual(t, testOpts, dv.Options())
54
-	assert.DeepEqual(t, testLabels, dv.Labels())
55
-}
56 1
deleted file mode 100644
... ...
@@ -1,707 +0,0 @@
1
-package store // import "github.com/docker/docker/volume/store"
2
-
3
-import (
4
-	"net"
5
-	"os"
6
-	"path/filepath"
7
-	"runtime"
8
-	"sync"
9
-	"time"
10
-
11
-	"github.com/pkg/errors"
12
-
13
-	"github.com/boltdb/bolt"
14
-	"github.com/docker/docker/pkg/locker"
15
-	"github.com/docker/docker/volume"
16
-	"github.com/docker/docker/volume/drivers"
17
-	volumemounts "github.com/docker/docker/volume/mounts"
18
-	"github.com/sirupsen/logrus"
19
-)
20
-
21
-const (
22
-	volumeDataDir = "volumes"
23
-)
24
-
25
-type volumeWrapper struct {
26
-	volume.Volume
27
-	labels  map[string]string
28
-	scope   string
29
-	options map[string]string
30
-}
31
-
32
-func (v volumeWrapper) Options() map[string]string {
33
-	if v.options == nil {
34
-		return nil
35
-	}
36
-	options := make(map[string]string, len(v.options))
37
-	for key, value := range v.options {
38
-		options[key] = value
39
-	}
40
-	return options
41
-}
42
-
43
-func (v volumeWrapper) Labels() map[string]string {
44
-	if v.labels == nil {
45
-		return nil
46
-	}
47
-
48
-	labels := make(map[string]string, len(v.labels))
49
-	for key, value := range v.labels {
50
-		labels[key] = value
51
-	}
52
-	return labels
53
-}
54
-
55
-func (v volumeWrapper) Scope() string {
56
-	return v.scope
57
-}
58
-
59
-func (v volumeWrapper) CachedPath() string {
60
-	if vv, ok := v.Volume.(interface {
61
-		CachedPath() string
62
-	}); ok {
63
-		return vv.CachedPath()
64
-	}
65
-	return v.Volume.Path()
66
-}
67
-
68
-// New initializes a VolumeStore to keep
69
-// reference counting of volumes in the system.
70
-func New(rootPath string, drivers *drivers.Store) (*VolumeStore, error) {
71
-	vs := &VolumeStore{
72
-		locks:   &locker.Locker{},
73
-		names:   make(map[string]volume.Volume),
74
-		refs:    make(map[string]map[string]struct{}),
75
-		labels:  make(map[string]map[string]string),
76
-		options: make(map[string]map[string]string),
77
-		drivers: drivers,
78
-	}
79
-
80
-	if rootPath != "" {
81
-		// initialize metadata store
82
-		volPath := filepath.Join(rootPath, volumeDataDir)
83
-		if err := os.MkdirAll(volPath, 0750); err != nil {
84
-			return nil, err
85
-		}
86
-
87
-		dbPath := filepath.Join(volPath, "metadata.db")
88
-
89
-		var err error
90
-		vs.db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
91
-		if err != nil {
92
-			return nil, errors.Wrap(err, "error while opening volume store metadata database")
93
-		}
94
-
95
-		// initialize volumes bucket
96
-		if err := vs.db.Update(func(tx *bolt.Tx) error {
97
-			if _, err := tx.CreateBucketIfNotExists(volumeBucketName); err != nil {
98
-				return errors.Wrap(err, "error while setting up volume store metadata database")
99
-			}
100
-			return nil
101
-		}); err != nil {
102
-			return nil, err
103
-		}
104
-	}
105
-
106
-	vs.restore()
107
-
108
-	return vs, nil
109
-}
110
-
111
-func (s *VolumeStore) getNamed(name string) (volume.Volume, bool) {
112
-	s.globalLock.RLock()
113
-	v, exists := s.names[name]
114
-	s.globalLock.RUnlock()
115
-	return v, exists
116
-}
117
-
118
-func (s *VolumeStore) setNamed(v volume.Volume, ref string) {
119
-	name := v.Name()
120
-
121
-	s.globalLock.Lock()
122
-	s.names[name] = v
123
-	if len(ref) > 0 {
124
-		if s.refs[name] == nil {
125
-			s.refs[name] = make(map[string]struct{})
126
-		}
127
-		s.refs[name][ref] = struct{}{}
128
-	}
129
-	s.globalLock.Unlock()
130
-}
131
-
132
-// hasRef returns true if the given name has at least one ref.
133
-// Callers of this function are expected to hold the name lock.
134
-func (s *VolumeStore) hasRef(name string) bool {
135
-	s.globalLock.RLock()
136
-	l := len(s.refs[name])
137
-	s.globalLock.RUnlock()
138
-	return l > 0
139
-}
140
-
141
-// getRefs gets the list of refs for a given name
142
-// Callers of this function are expected to hold the name lock.
143
-func (s *VolumeStore) getRefs(name string) []string {
144
-	s.globalLock.RLock()
145
-	defer s.globalLock.RUnlock()
146
-
147
-	refs := make([]string, 0, len(s.refs[name]))
148
-	for r := range s.refs[name] {
149
-		refs = append(refs, r)
150
-	}
151
-
152
-	return refs
153
-}
154
-
155
-// Purge allows the cleanup of internal data on docker in case
156
-// the internal data is out of sync with volumes driver plugins.
157
-func (s *VolumeStore) Purge(name string) {
158
-	s.globalLock.Lock()
159
-	v, exists := s.names[name]
160
-	if exists {
161
-		driverName := v.DriverName()
162
-		if _, err := s.drivers.ReleaseDriver(driverName); err != nil {
163
-			logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver")
164
-		}
165
-	}
166
-	if err := s.removeMeta(name); err != nil {
167
-		logrus.Errorf("Error removing volume metadata for volume %q: %v", name, err)
168
-	}
169
-	delete(s.names, name)
170
-	delete(s.refs, name)
171
-	delete(s.labels, name)
172
-	delete(s.options, name)
173
-	s.globalLock.Unlock()
174
-}
175
-
176
-// VolumeStore is a struct that stores the list of volumes available and keeps track of their usage counts
177
-type VolumeStore struct {
178
-	// locks ensures that only one action is being performed on a particular volume at a time without locking the entire store
179
-	// since actions on volumes can be quite slow, this ensures the store is free to handle requests for other volumes.
180
-	locks   *locker.Locker
181
-	drivers *drivers.Store
182
-	// globalLock is used to protect access to mutable structures used by the store object
183
-	globalLock sync.RWMutex
184
-	// names stores the volume name -> volume relationship.
185
-	// This is used for making lookups faster so we don't have to probe all drivers
186
-	names map[string]volume.Volume
187
-	// refs stores the volume name and the list of things referencing it
188
-	refs map[string]map[string]struct{}
189
-	// labels stores volume labels for each volume
190
-	labels map[string]map[string]string
191
-	// options stores volume options for each volume
192
-	options map[string]map[string]string
193
-	db      *bolt.DB
194
-}
195
-
196
-// List proxies to all registered volume drivers to get the full list of volumes
197
-// If a driver returns a volume that has name which conflicts with another volume from a different driver,
198
-// the first volume is chosen and the conflicting volume is dropped.
199
-func (s *VolumeStore) List() ([]volume.Volume, []string, error) {
200
-	vols, warnings, err := s.list()
201
-	if err != nil {
202
-		return nil, nil, &OpErr{Err: err, Op: "list"}
203
-	}
204
-	var out []volume.Volume
205
-
206
-	for _, v := range vols {
207
-		name := normalizeVolumeName(v.Name())
208
-
209
-		s.locks.Lock(name)
210
-		storedV, exists := s.getNamed(name)
211
-		// Note: it's not safe to populate the cache here because the volume may have been
212
-		// deleted before we acquire a lock on its name
213
-		if exists && storedV.DriverName() != v.DriverName() {
214
-			logrus.Warnf("Volume name %s already exists for driver %s, not including volume returned by %s", v.Name(), storedV.DriverName(), v.DriverName())
215
-			s.locks.Unlock(v.Name())
216
-			continue
217
-		}
218
-
219
-		out = append(out, v)
220
-		s.locks.Unlock(v.Name())
221
-	}
222
-	return out, warnings, nil
223
-}
224
-
225
-// list goes through each volume driver and asks for its list of volumes.
226
-func (s *VolumeStore) list() ([]volume.Volume, []string, error) {
227
-	var (
228
-		ls       []volume.Volume
229
-		warnings []string
230
-	)
231
-
232
-	drivers, err := s.drivers.GetAllDrivers()
233
-	if err != nil {
234
-		return nil, nil, err
235
-	}
236
-
237
-	type vols struct {
238
-		vols       []volume.Volume
239
-		err        error
240
-		driverName string
241
-	}
242
-	chVols := make(chan vols, len(drivers))
243
-
244
-	for _, vd := range drivers {
245
-		go func(d volume.Driver) {
246
-			vs, err := d.List()
247
-			if err != nil {
248
-				chVols <- vols{driverName: d.Name(), err: &OpErr{Err: err, Name: d.Name(), Op: "list"}}
249
-				return
250
-			}
251
-			for i, v := range vs {
252
-				s.globalLock.RLock()
253
-				vs[i] = volumeWrapper{v, s.labels[v.Name()], d.Scope(), s.options[v.Name()]}
254
-				s.globalLock.RUnlock()
255
-			}
256
-
257
-			chVols <- vols{vols: vs}
258
-		}(vd)
259
-	}
260
-
261
-	badDrivers := make(map[string]struct{})
262
-	for i := 0; i < len(drivers); i++ {
263
-		vs := <-chVols
264
-
265
-		if vs.err != nil {
266
-			warnings = append(warnings, vs.err.Error())
267
-			badDrivers[vs.driverName] = struct{}{}
268
-			logrus.Warn(vs.err)
269
-		}
270
-		ls = append(ls, vs.vols...)
271
-	}
272
-
273
-	if len(badDrivers) > 0 {
274
-		s.globalLock.RLock()
275
-		for _, v := range s.names {
276
-			if _, exists := badDrivers[v.DriverName()]; exists {
277
-				ls = append(ls, v)
278
-			}
279
-		}
280
-		s.globalLock.RUnlock()
281
-	}
282
-	return ls, warnings, nil
283
-}
284
-
285
-// CreateWithRef creates a volume with the given name and driver and stores the ref
286
-// This ensures there's no race between creating a volume and then storing a reference.
287
-func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts, labels map[string]string) (volume.Volume, error) {
288
-	name = normalizeVolumeName(name)
289
-	s.locks.Lock(name)
290
-	defer s.locks.Unlock(name)
291
-
292
-	v, err := s.create(name, driverName, opts, labels)
293
-	if err != nil {
294
-		if _, ok := err.(*OpErr); ok {
295
-			return nil, err
296
-		}
297
-		return nil, &OpErr{Err: err, Name: name, Op: "create"}
298
-	}
299
-
300
-	s.setNamed(v, ref)
301
-	return v, nil
302
-}
303
-
304
-// Create creates a volume with the given name and driver.
305
-// This is just like CreateWithRef() except we don't store a reference while holding the lock.
306
-func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
307
-	return s.CreateWithRef(name, driverName, "", opts, labels)
308
-}
309
-
310
-// checkConflict checks the local cache for name collisions with the passed in name,
311
-// for existing volumes with the same name but in a different driver.
312
-// This is used by `Create` as a best effort to prevent name collisions for volumes.
313
-// If a matching volume is found that is not a conflict that is returned so the caller
314
-// does not need to perform an additional lookup.
315
-// When no matching volume is found, both returns will be nil
316
-//
317
-// Note: This does not probe all the drivers for name collisions because v1 plugins
318
-// are very slow, particularly if the plugin is down, and cause other issues,
319
-// particularly around locking the store.
320
-// TODO(cpuguy83): With v2 plugins this shouldn't be a problem. Could also potentially
321
-// use a connect timeout for this kind of check to ensure we aren't blocking for a
322
-// long time.
323
-func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, error) {
324
-	// check the local cache
325
-	v, _ := s.getNamed(name)
326
-	if v == nil {
327
-		return nil, nil
328
-	}
329
-
330
-	vDriverName := v.DriverName()
331
-	var conflict bool
332
-	if driverName != "" {
333
-		// Retrieve canonical driver name to avoid inconsistencies (for example
334
-		// "plugin" vs. "plugin:latest")
335
-		vd, err := s.drivers.GetDriver(driverName)
336
-		if err != nil {
337
-			return nil, err
338
-		}
339
-
340
-		if vDriverName != vd.Name() {
341
-			conflict = true
342
-		}
343
-	}
344
-
345
-	// let's check if the found volume ref
346
-	// is stale by checking with the driver if it still exists
347
-	exists, err := volumeExists(s.drivers, v)
348
-	if err != nil {
349
-		return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err)
350
-	}
351
-
352
-	if exists {
353
-		if conflict {
354
-			return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name)
355
-		}
356
-		return v, nil
357
-	}
358
-
359
-	if s.hasRef(v.Name()) {
360
-		// Containers are referencing this volume but it doesn't seem to exist anywhere.
361
-		// Return a conflict error here, the user can fix this with `docker volume rm -f`
362
-		return nil, errors.Wrapf(errNameConflict, "found references to volume '%s' in driver '%s' but the volume was not found in the driver -- you may need to remove containers referencing this volume or force remove the volume to re-create it", name, vDriverName)
363
-	}
364
-
365
-	// doesn't exist, so purge it from the cache
366
-	s.Purge(name)
367
-	return nil, nil
368
-}
369
-
370
-// volumeExists returns if the volume is still present in the driver.
371
-// An error is returned if there was an issue communicating with the driver.
372
-func volumeExists(store *drivers.Store, v volume.Volume) (bool, error) {
373
-	exists, err := lookupVolume(store, v.DriverName(), v.Name())
374
-	if err != nil {
375
-		return false, err
376
-	}
377
-	return exists != nil, nil
378
-}
379
-
380
-// create asks the given driver to create a volume with the name/opts.
381
-// If a volume with the name is already known, it will ask the stored driver for the volume.
382
-// If the passed in driver name does not match the driver name which is stored
383
-//  for the given volume name, an error is returned after checking if the reference is stale.
384
-// If the reference is stale, it will be purged and this create can continue.
385
-// It is expected that callers of this function hold any necessary locks.
386
-func (s *VolumeStore) create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
387
-	// Validate the name in a platform-specific manner
388
-
389
-	// volume name validation is specific to the host os and not on container image
390
-	// windows/lcow should have an equivalent volumename validation logic so we create a parser for current host OS
391
-	parser := volumemounts.NewParser(runtime.GOOS)
392
-	err := parser.ValidateVolumeName(name)
393
-	if err != nil {
394
-		return nil, err
395
-	}
396
-
397
-	v, err := s.checkConflict(name, driverName)
398
-	if err != nil {
399
-		return nil, err
400
-	}
401
-
402
-	if v != nil {
403
-		// there is an existing volume, if we already have this stored locally, return it.
404
-		// TODO: there could be some inconsistent details such as labels here
405
-		if vv, _ := s.getNamed(v.Name()); vv != nil {
406
-			return vv, nil
407
-		}
408
-	}
409
-
410
-	// Since there isn't a specified driver name, let's see if any of the existing drivers have this volume name
411
-	if driverName == "" {
412
-		v, _ = s.getVolume(name)
413
-		if v != nil {
414
-			return v, nil
415
-		}
416
-	}
417
-
418
-	if driverName == "" {
419
-		driverName = volume.DefaultDriverName
420
-	}
421
-	vd, err := s.drivers.CreateDriver(driverName)
422
-	if err != nil {
423
-		return nil, &OpErr{Op: "create", Name: name, Err: err}
424
-	}
425
-
426
-	logrus.Debugf("Registering new volume reference: driver %q, name %q", vd.Name(), name)
427
-	if v, _ = vd.Get(name); v == nil {
428
-		v, err = vd.Create(name, opts)
429
-		if err != nil {
430
-			if _, err := s.drivers.ReleaseDriver(driverName); err != nil {
431
-				logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver")
432
-			}
433
-			return nil, err
434
-		}
435
-	}
436
-
437
-	s.globalLock.Lock()
438
-	s.labels[name] = labels
439
-	s.options[name] = opts
440
-	s.refs[name] = make(map[string]struct{})
441
-	s.globalLock.Unlock()
442
-
443
-	metadata := volumeMetadata{
444
-		Name:    name,
445
-		Driver:  vd.Name(),
446
-		Labels:  labels,
447
-		Options: opts,
448
-	}
449
-
450
-	if err := s.setMeta(name, metadata); err != nil {
451
-		return nil, err
452
-	}
453
-	return volumeWrapper{v, labels, vd.Scope(), opts}, nil
454
-}
455
-
456
-// GetWithRef gets a volume with the given name from the passed in driver and stores the ref
457
-// This is just like Get(), but we store the reference while holding the lock.
458
-// This makes sure there are no races between checking for the existence of a volume and adding a reference for it
459
-func (s *VolumeStore) GetWithRef(name, driverName, ref string) (volume.Volume, error) {
460
-	name = normalizeVolumeName(name)
461
-	s.locks.Lock(name)
462
-	defer s.locks.Unlock(name)
463
-
464
-	if driverName == "" {
465
-		driverName = volume.DefaultDriverName
466
-	}
467
-	vd, err := s.drivers.GetDriver(driverName)
468
-	if err != nil {
469
-		return nil, &OpErr{Err: err, Name: name, Op: "get"}
470
-	}
471
-
472
-	v, err := vd.Get(name)
473
-	if err != nil {
474
-		return nil, &OpErr{Err: err, Name: name, Op: "get"}
475
-	}
476
-
477
-	s.setNamed(v, ref)
478
-
479
-	s.globalLock.RLock()
480
-	defer s.globalLock.RUnlock()
481
-	return volumeWrapper{v, s.labels[name], vd.Scope(), s.options[name]}, nil
482
-}
483
-
484
-// Get looks if a volume with the given name exists and returns it if so
485
-func (s *VolumeStore) Get(name string) (volume.Volume, error) {
486
-	name = normalizeVolumeName(name)
487
-	s.locks.Lock(name)
488
-	defer s.locks.Unlock(name)
489
-
490
-	v, err := s.getVolume(name)
491
-	if err != nil {
492
-		return nil, &OpErr{Err: err, Name: name, Op: "get"}
493
-	}
494
-	s.setNamed(v, "")
495
-	return v, nil
496
-}
497
-
498
-// getVolume requests the volume, if the driver info is stored it just accesses that driver,
499
-// if the driver is unknown it probes all drivers until it finds the first volume with that name.
500
-// it is expected that callers of this function hold any necessary locks
501
-func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
502
-	var meta volumeMetadata
503
-	meta, err := s.getMeta(name)
504
-	if err != nil {
505
-		return nil, err
506
-	}
507
-
508
-	driverName := meta.Driver
509
-	if driverName == "" {
510
-		s.globalLock.RLock()
511
-		v, exists := s.names[name]
512
-		s.globalLock.RUnlock()
513
-		if exists {
514
-			meta.Driver = v.DriverName()
515
-			if err := s.setMeta(name, meta); err != nil {
516
-				return nil, err
517
-			}
518
-		}
519
-	}
520
-
521
-	if meta.Driver != "" {
522
-		vol, err := lookupVolume(s.drivers, meta.Driver, name)
523
-		if err != nil {
524
-			return nil, err
525
-		}
526
-		if vol == nil {
527
-			s.Purge(name)
528
-			return nil, errNoSuchVolume
529
-		}
530
-
531
-		var scope string
532
-		vd, err := s.drivers.GetDriver(meta.Driver)
533
-		if err == nil {
534
-			scope = vd.Scope()
535
-		}
536
-		return volumeWrapper{vol, meta.Labels, scope, meta.Options}, nil
537
-	}
538
-
539
-	logrus.Debugf("Probing all drivers for volume with name: %s", name)
540
-	drivers, err := s.drivers.GetAllDrivers()
541
-	if err != nil {
542
-		return nil, err
543
-	}
544
-
545
-	for _, d := range drivers {
546
-		v, err := d.Get(name)
547
-		if err != nil || v == nil {
548
-			continue
549
-		}
550
-		meta.Driver = v.DriverName()
551
-		if err := s.setMeta(name, meta); err != nil {
552
-			return nil, err
553
-		}
554
-		return volumeWrapper{v, meta.Labels, d.Scope(), meta.Options}, nil
555
-	}
556
-	return nil, errNoSuchVolume
557
-}
558
-
559
-// lookupVolume gets the specified volume from the specified driver.
560
-// This will only return errors related to communications with the driver.
561
-// If the driver returns an error that is not communication related the
562
-//   error is logged but not returned.
563
-// If the volume is not found it will return `nil, nil``
564
-func lookupVolume(store *drivers.Store, driverName, volumeName string) (volume.Volume, error) {
565
-	if driverName == "" {
566
-		driverName = volume.DefaultDriverName
567
-	}
568
-	vd, err := store.GetDriver(driverName)
569
-	if err != nil {
570
-		return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName)
571
-	}
572
-	v, err := vd.Get(volumeName)
573
-	if err != nil {
574
-		err = errors.Cause(err)
575
-		if _, ok := err.(net.Error); ok {
576
-			if v != nil {
577
-				volumeName = v.Name()
578
-				driverName = v.DriverName()
579
-			}
580
-			return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName)
581
-		}
582
-
583
-		// At this point, the error could be anything from the driver, such as "no such volume"
584
-		// Let's not check an error here, and instead check if the driver returned a volume
585
-		logrus.WithError(err).WithField("driver", driverName).WithField("volume", volumeName).Warnf("Error while looking up volume")
586
-	}
587
-	return v, nil
588
-}
589
-
590
-// Remove removes the requested volume. A volume is not removed if it has any refs
591
-func (s *VolumeStore) Remove(v volume.Volume) error {
592
-	name := normalizeVolumeName(v.Name())
593
-	s.locks.Lock(name)
594
-	defer s.locks.Unlock(name)
595
-
596
-	if s.hasRef(name) {
597
-		return &OpErr{Err: errVolumeInUse, Name: v.Name(), Op: "remove", Refs: s.getRefs(name)}
598
-	}
599
-
600
-	vd, err := s.drivers.GetDriver(v.DriverName())
601
-	if err != nil {
602
-		return &OpErr{Err: err, Name: v.DriverName(), Op: "remove"}
603
-	}
604
-
605
-	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
606
-	vol := unwrapVolume(v)
607
-	if err := vd.Remove(vol); err != nil {
608
-		return &OpErr{Err: err, Name: name, Op: "remove"}
609
-	}
610
-
611
-	s.Purge(name)
612
-	return nil
613
-}
614
-
615
-// Dereference removes the specified reference to the volume
616
-func (s *VolumeStore) Dereference(v volume.Volume, ref string) {
617
-	name := v.Name()
618
-
619
-	s.locks.Lock(name)
620
-	defer s.locks.Unlock(name)
621
-
622
-	s.globalLock.Lock()
623
-	defer s.globalLock.Unlock()
624
-
625
-	if s.refs[name] != nil {
626
-		delete(s.refs[name], ref)
627
-	}
628
-}
629
-
630
-// Refs gets the current list of refs for the given volume
631
-func (s *VolumeStore) Refs(v volume.Volume) []string {
632
-	name := v.Name()
633
-
634
-	s.locks.Lock(name)
635
-	defer s.locks.Unlock(name)
636
-
637
-	return s.getRefs(name)
638
-}
639
-
640
-// FilterByDriver returns the available volumes filtered by driver name
641
-func (s *VolumeStore) FilterByDriver(name string) ([]volume.Volume, error) {
642
-	vd, err := s.drivers.GetDriver(name)
643
-	if err != nil {
644
-		return nil, &OpErr{Err: err, Name: name, Op: "list"}
645
-	}
646
-	ls, err := vd.List()
647
-	if err != nil {
648
-		return nil, &OpErr{Err: err, Name: name, Op: "list"}
649
-	}
650
-	for i, v := range ls {
651
-		options := map[string]string{}
652
-		s.globalLock.RLock()
653
-		for key, value := range s.options[v.Name()] {
654
-			options[key] = value
655
-		}
656
-		ls[i] = volumeWrapper{v, s.labels[v.Name()], vd.Scope(), options}
657
-		s.globalLock.RUnlock()
658
-	}
659
-	return ls, nil
660
-}
661
-
662
-// FilterByUsed returns the available volumes filtered by if they are in use or not.
663
-// `used=true` returns only volumes that are being used, while `used=false` returns
664
-// only volumes that are not being used.
665
-func (s *VolumeStore) FilterByUsed(vols []volume.Volume, used bool) []volume.Volume {
666
-	return s.filter(vols, func(v volume.Volume) bool {
667
-		s.locks.Lock(v.Name())
668
-		hasRef := s.hasRef(v.Name())
669
-		s.locks.Unlock(v.Name())
670
-		return used == hasRef
671
-	})
672
-}
673
-
674
-// filterFunc defines a function to allow filter volumes in the store
675
-type filterFunc func(vol volume.Volume) bool
676
-
677
-// filter returns the available volumes filtered by a filterFunc function
678
-func (s *VolumeStore) filter(vols []volume.Volume, f filterFunc) []volume.Volume {
679
-	var ls []volume.Volume
680
-	for _, v := range vols {
681
-		if f(v) {
682
-			ls = append(ls, v)
683
-		}
684
-	}
685
-	return ls
686
-}
687
-
688
-func unwrapVolume(v volume.Volume) volume.Volume {
689
-	if vol, ok := v.(volumeWrapper); ok {
690
-		return vol.Volume
691
-	}
692
-
693
-	return v
694
-}
695
-
696
-// Shutdown releases all resources used by the volume store
697
-// It does not make any changes to volumes, drivers, etc.
698
-func (s *VolumeStore) Shutdown() error {
699
-	return s.db.Close()
700
-}
701
-
702
-// GetDriverList gets the list of volume drivers from the configured volume driver
703
-// store.
704
-// TODO(@cpuguy83): This should be factored out into a separate service.
705
-func (s *VolumeStore) GetDriverList() []string {
706
-	return s.drivers.GetDriverList()
707
-}
708 1
deleted file mode 100644
... ...
@@ -1,379 +0,0 @@
1
-package store // import "github.com/docker/docker/volume/store"
2
-
3
-import (
4
-	"errors"
5
-	"fmt"
6
-	"io/ioutil"
7
-	"net"
8
-	"os"
9
-	"strings"
10
-	"testing"
11
-
12
-	"github.com/docker/docker/volume"
13
-	volumedrivers "github.com/docker/docker/volume/drivers"
14
-	volumetestutils "github.com/docker/docker/volume/testutils"
15
-	"github.com/google/go-cmp/cmp"
16
-	"github.com/gotestyourself/gotestyourself/assert"
17
-	is "github.com/gotestyourself/gotestyourself/assert/cmp"
18
-)
19
-
20
-func TestCreate(t *testing.T) {
21
-	t.Parallel()
22
-
23
-	s, cleanup := setupTest(t)
24
-	defer cleanup()
25
-	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
26
-
27
-	v, err := s.Create("fake1", "fake", nil, nil)
28
-	if err != nil {
29
-		t.Fatal(err)
30
-	}
31
-	if v.Name() != "fake1" {
32
-		t.Fatalf("Expected fake1 volume, got %v", v)
33
-	}
34
-	if l, _, _ := s.List(); len(l) != 1 {
35
-		t.Fatalf("Expected 1 volume in the store, got %v: %v", len(l), l)
36
-	}
37
-
38
-	if _, err := s.Create("none", "none", nil, nil); err == nil {
39
-		t.Fatalf("Expected unknown driver error, got nil")
40
-	}
41
-
42
-	_, err = s.Create("fakeerror", "fake", map[string]string{"error": "create error"}, nil)
43
-	expected := &OpErr{Op: "create", Name: "fakeerror", Err: errors.New("create error")}
44
-	if err != nil && err.Error() != expected.Error() {
45
-		t.Fatalf("Expected create fakeError: create error, got %v", err)
46
-	}
47
-}
48
-
49
-func TestRemove(t *testing.T) {
50
-	t.Parallel()
51
-
52
-	s, cleanup := setupTest(t)
53
-	defer cleanup()
54
-
55
-	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
56
-	s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
57
-
58
-	// doing string compare here since this error comes directly from the driver
59
-	expected := "no such volume"
60
-	if err := s.Remove(volumetestutils.NoopVolume{}); err == nil || !strings.Contains(err.Error(), expected) {
61
-		t.Fatalf("Expected error %q, got %v", expected, err)
62
-	}
63
-
64
-	v, err := s.CreateWithRef("fake1", "fake", "fake", nil, nil)
65
-	if err != nil {
66
-		t.Fatal(err)
67
-	}
68
-
69
-	if err := s.Remove(v); !IsInUse(err) {
70
-		t.Fatalf("Expected ErrVolumeInUse error, got %v", err)
71
-	}
72
-	s.Dereference(v, "fake")
73
-	if err := s.Remove(v); err != nil {
74
-		t.Fatal(err)
75
-	}
76
-	if l, _, _ := s.List(); len(l) != 0 {
77
-		t.Fatalf("Expected 0 volumes in the store, got %v, %v", len(l), l)
78
-	}
79
-}
80
-
81
-func TestList(t *testing.T) {
82
-	t.Parallel()
83
-
84
-	dir, err := ioutil.TempDir("", "test-list")
85
-	assert.NilError(t, err)
86
-	defer os.RemoveAll(dir)
87
-
88
-	drivers := volumedrivers.NewStore(nil)
89
-	drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
90
-	drivers.Register(volumetestutils.NewFakeDriver("fake2"), "fake2")
91
-
92
-	s, err := New(dir, drivers)
93
-	assert.NilError(t, err)
94
-
95
-	if _, err := s.Create("test", "fake", nil, nil); err != nil {
96
-		t.Fatal(err)
97
-	}
98
-	if _, err := s.Create("test2", "fake2", nil, nil); err != nil {
99
-		t.Fatal(err)
100
-	}
101
-
102
-	ls, _, err := s.List()
103
-	if err != nil {
104
-		t.Fatal(err)
105
-	}
106
-	if len(ls) != 2 {
107
-		t.Fatalf("expected 2 volumes, got: %d", len(ls))
108
-	}
109
-	if err := s.Shutdown(); err != nil {
110
-		t.Fatal(err)
111
-	}
112
-
113
-	// and again with a new store
114
-	s, err = New(dir, drivers)
115
-	if err != nil {
116
-		t.Fatal(err)
117
-	}
118
-	ls, _, err = s.List()
119
-	if err != nil {
120
-		t.Fatal(err)
121
-	}
122
-	if len(ls) != 2 {
123
-		t.Fatalf("expected 2 volumes, got: %d", len(ls))
124
-	}
125
-}
126
-
127
-func TestFilterByDriver(t *testing.T) {
128
-	t.Parallel()
129
-	s, cleanup := setupTest(t)
130
-	defer cleanup()
131
-
132
-	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
133
-	s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
134
-
135
-	if _, err := s.Create("fake1", "fake", nil, nil); err != nil {
136
-		t.Fatal(err)
137
-	}
138
-	if _, err := s.Create("fake2", "fake", nil, nil); err != nil {
139
-		t.Fatal(err)
140
-	}
141
-	if _, err := s.Create("fake3", "noop", nil, nil); err != nil {
142
-		t.Fatal(err)
143
-	}
144
-
145
-	if l, _ := s.FilterByDriver("fake"); len(l) != 2 {
146
-		t.Fatalf("Expected 2 volumes, got %v, %v", len(l), l)
147
-	}
148
-
149
-	if l, _ := s.FilterByDriver("noop"); len(l) != 1 {
150
-		t.Fatalf("Expected 1 volume, got %v, %v", len(l), l)
151
-	}
152
-}
153
-
154
-func TestFilterByUsed(t *testing.T) {
155
-	t.Parallel()
156
-	s, cleanup := setupTest(t)
157
-	defer cleanup()
158
-
159
-	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
160
-	s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
161
-
162
-	if _, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil); err != nil {
163
-		t.Fatal(err)
164
-	}
165
-	if _, err := s.Create("fake2", "fake", nil, nil); err != nil {
166
-		t.Fatal(err)
167
-	}
168
-
169
-	vols, _, err := s.List()
170
-	if err != nil {
171
-		t.Fatal(err)
172
-	}
173
-
174
-	dangling := s.FilterByUsed(vols, false)
175
-	if len(dangling) != 1 {
176
-		t.Fatalf("expected 1 dangling volume, got %v", len(dangling))
177
-	}
178
-	if dangling[0].Name() != "fake2" {
179
-		t.Fatalf("expected dangling volume fake2, got %s", dangling[0].Name())
180
-	}
181
-
182
-	used := s.FilterByUsed(vols, true)
183
-	if len(used) != 1 {
184
-		t.Fatalf("expected 1 used volume, got %v", len(used))
185
-	}
186
-	if used[0].Name() != "fake1" {
187
-		t.Fatalf("expected used volume fake1, got %s", used[0].Name())
188
-	}
189
-}
190
-
191
-func TestDerefMultipleOfSameRef(t *testing.T) {
192
-	t.Parallel()
193
-	s, cleanup := setupTest(t)
194
-	defer cleanup()
195
-	s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
196
-
197
-	v, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil)
198
-	if err != nil {
199
-		t.Fatal(err)
200
-	}
201
-
202
-	if _, err := s.GetWithRef("fake1", "fake", "volReference"); err != nil {
203
-		t.Fatal(err)
204
-	}
205
-
206
-	s.Dereference(v, "volReference")
207
-	if err := s.Remove(v); err != nil {
208
-		t.Fatal(err)
209
-	}
210
-}
211
-
212
-func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) {
213
-	t.Parallel()
214
-	s, cleanup := setupTest(t)
215
-	defer cleanup()
216
-
217
-	vd := volumetestutils.NewFakeDriver("fake")
218
-	s.drivers.Register(vd, "fake")
219
-
220
-	// Create a volume in the driver directly
221
-	if _, err := vd.Create("foo", nil); err != nil {
222
-		t.Fatal(err)
223
-	}
224
-
225
-	v, err := s.Create("foo", "fake", nil, map[string]string{"hello": "world"})
226
-	if err != nil {
227
-		t.Fatal(err)
228
-	}
229
-
230
-	switch dv := v.(type) {
231
-	case volume.DetailedVolume:
232
-		if dv.Labels()["hello"] != "world" {
233
-			t.Fatalf("labels don't match")
234
-		}
235
-	default:
236
-		t.Fatalf("got unexpected type: %T", v)
237
-	}
238
-}
239
-
240
-func TestDefererencePluginOnCreateError(t *testing.T) {
241
-	t.Parallel()
242
-
243
-	var (
244
-		l   net.Listener
245
-		err error
246
-	)
247
-
248
-	for i := 32768; l == nil && i < 40000; i++ {
249
-		l, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", i))
250
-	}
251
-	if l == nil {
252
-		t.Fatalf("could not create listener: %v", err)
253
-	}
254
-	defer l.Close()
255
-
256
-	s, cleanup := setupTest(t)
257
-	defer cleanup()
258
-
259
-	d := volumetestutils.NewFakeDriver("TestDefererencePluginOnCreateError")
260
-	p, err := volumetestutils.MakeFakePlugin(d, l)
261
-	if err != nil {
262
-		t.Fatal(err)
263
-	}
264
-
265
-	pg := volumetestutils.NewFakePluginGetter(p)
266
-	s.drivers = volumedrivers.NewStore(pg)
267
-
268
-	// create a good volume so we have a plugin reference
269
-	_, err = s.Create("fake1", d.Name(), nil, nil)
270
-	if err != nil {
271
-		t.Fatal(err)
272
-	}
273
-
274
-	// Now create another one expecting an error
275
-	_, err = s.Create("fake2", d.Name(), map[string]string{"error": "some error"}, nil)
276
-	if err == nil || !strings.Contains(err.Error(), "some error") {
277
-		t.Fatalf("expected an error on create: %v", err)
278
-	}
279
-
280
-	// There should be only 1 plugin reference
281
-	if refs := volumetestutils.FakeRefs(p); refs != 1 {
282
-		t.Fatalf("expected 1 plugin reference, got: %d", refs)
283
-	}
284
-}
285
-
286
-func TestRefDerefRemove(t *testing.T) {
287
-	t.Parallel()
288
-
289
-	driverName := "test-ref-deref-remove"
290
-	s, cleanup := setupTest(t)
291
-	defer cleanup()
292
-	s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
293
-
294
-	v, err := s.CreateWithRef("test", driverName, "test-ref", nil, nil)
295
-	assert.NilError(t, err)
296
-
297
-	err = s.Remove(v)
298
-	assert.Assert(t, is.ErrorContains(err, ""))
299
-	assert.Equal(t, errVolumeInUse, err.(*OpErr).Err)
300
-
301
-	s.Dereference(v, "test-ref")
302
-	err = s.Remove(v)
303
-	assert.NilError(t, err)
304
-}
305
-
306
-func TestGet(t *testing.T) {
307
-	t.Parallel()
308
-
309
-	driverName := "test-get"
310
-	s, cleanup := setupTest(t)
311
-	defer cleanup()
312
-	s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
313
-
314
-	_, err := s.Get("not-exist")
315
-	assert.Assert(t, is.ErrorContains(err, ""))
316
-	assert.Equal(t, errNoSuchVolume, err.(*OpErr).Err)
317
-
318
-	v1, err := s.Create("test", driverName, nil, map[string]string{"a": "1"})
319
-	assert.NilError(t, err)
320
-
321
-	v2, err := s.Get("test")
322
-	assert.NilError(t, err)
323
-	assert.DeepEqual(t, v1, v2, cmpVolume)
324
-
325
-	dv := v2.(volume.DetailedVolume)
326
-	assert.Equal(t, "1", dv.Labels()["a"])
327
-
328
-	err = s.Remove(v1)
329
-	assert.NilError(t, err)
330
-}
331
-
332
-func TestGetWithRef(t *testing.T) {
333
-	t.Parallel()
334
-
335
-	driverName := "test-get-with-ref"
336
-	s, cleanup := setupTest(t)
337
-	defer cleanup()
338
-	s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
339
-
340
-	_, err := s.GetWithRef("not-exist", driverName, "test-ref")
341
-	assert.Assert(t, is.ErrorContains(err, ""))
342
-
343
-	v1, err := s.Create("test", driverName, nil, map[string]string{"a": "1"})
344
-	assert.NilError(t, err)
345
-
346
-	v2, err := s.GetWithRef("test", driverName, "test-ref")
347
-	assert.NilError(t, err)
348
-	assert.DeepEqual(t, v1, v2, cmpVolume)
349
-
350
-	err = s.Remove(v2)
351
-	assert.Assert(t, is.ErrorContains(err, ""))
352
-	assert.Equal(t, errVolumeInUse, err.(*OpErr).Err)
353
-
354
-	s.Dereference(v2, "test-ref")
355
-	err = s.Remove(v2)
356
-	assert.NilError(t, err)
357
-}
358
-
359
-var cmpVolume = cmp.AllowUnexported(volumetestutils.FakeVolume{}, volumeWrapper{})
360
-
361
-func setupTest(t *testing.T) (*VolumeStore, func()) {
362
-	t.Helper()
363
-
364
-	dirName := strings.Replace(t.Name(), string(os.PathSeparator), "_", -1)
365
-	dir, err := ioutil.TempDir("", dirName)
366
-	assert.NilError(t, err)
367
-
368
-	cleanup := func() {
369
-		err := os.RemoveAll(dir)
370
-		assert.Check(t, err)
371
-	}
372
-
373
-	s, err := New(dir, volumedrivers.NewStore(nil))
374
-	assert.Check(t, err)
375
-	return s, func() {
376
-		s.Shutdown()
377
-		cleanup()
378
-	}
379
-}
380 1
deleted file mode 100644
... ...
@@ -1,9 +0,0 @@
1
-// +build linux freebsd
2
-
3
-package store // import "github.com/docker/docker/volume/store"
4
-
5
-// normalizeVolumeName is a platform specific function to normalize the name
6
-// of a volume. This is a no-op on Unix-like platforms
7
-func normalizeVolumeName(name string) string {
8
-	return name
9
-}
10 1
deleted file mode 100644
... ...
@@ -1,12 +0,0 @@
1
-package store // import "github.com/docker/docker/volume/store"
2
-
3
-import "strings"
4
-
5
-// normalizeVolumeName is a platform specific function to normalize the name
6
-// of a volume. On Windows, as NTFS is case insensitive, under
7
-// c:\ProgramData\Docker\Volumes\, the folders John and john would be synonymous.
8
-// Hence we can't allow the volume "John" and "john" to be created as separate
9
-// volumes.
10
-func normalizeVolumeName(name string) string {
11
-	return strings.ToLower(name)
12
-}
... ...
@@ -64,7 +64,9 @@ func (FakeVolume) Mount(_ string) (string, error) { return "fake", nil }
64 64
 func (FakeVolume) Unmount(_ string) error { return nil }
65 65
 
66 66
 // Status provides low-level details about the volume
67
-func (FakeVolume) Status() map[string]interface{} { return nil }
67
+func (FakeVolume) Status() map[string]interface{} {
68
+	return map[string]interface{}{"datakey": "datavalue"}
69
+}
68 70
 
69 71
 // CreatedAt provides the time the volume (directory) was created at
70 72
 func (FakeVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }