Browse code

Merge pull request #26398 from tiborvass/plugin-fixes

plugins: container-rootfs-relative paths

Anusha Ragunathan authored on 2016/12/10 05:48:59
Showing 17 changed files
... ...
@@ -1383,6 +1383,7 @@ definitions:
1383 1383
           - Workdir
1384 1384
           - Network
1385 1385
           - Linux
1386
+          - PropagatedMount
1386 1387
           - Mounts
1387 1388
           - Env
1388 1389
           - Args
... ...
@@ -1447,6 +1448,9 @@ definitions:
1447 1447
                 type: "array"
1448 1448
                 items:
1449 1449
                   $ref: "#/definitions/PluginDevice"
1450
+          PropagatedMount:
1451
+            type: "string"
1452
+            x-nullable: false
1450 1453
           Mounts:
1451 1454
             type: "array"
1452 1455
             items:
... ...
@@ -71,6 +71,10 @@ type PluginConfig struct {
71 71
 	// Required: true
72 72
 	Network PluginConfigNetwork `json:"Network"`
73 73
 
74
+	// propagated mount
75
+	// Required: true
76
+	PropagatedMount string `json:"PropagatedMount"`
77
+
74 78
 	// user
75 79
 	User PluginConfigUser `json:"User,omitempty"`
76 80
 
... ...
@@ -111,6 +111,10 @@ Config provides the base accessible fields for working with V0 plugin format
111 111
 
112 112
 	  options of the mount.
113 113
 
114
+- **`propagatedMount`** *string*
115
+
116
+   path to be mounted as rshared, so that mounts under that path are visible to docker. This is useful for volume plugins.
117
+
114 118
 - **`env`** *PluginEnv array*
115 119
 
116 120
    env of the plugin, struct consisting of the following fields
... ...
@@ -22,6 +22,10 @@ beyond the lifetime of a single Engine host. See the
22 22
 
23 23
 ## Changelog
24 24
 
25
+### 1.13.0
26
+
27
+- If used as part of the v2 plugin architecture, mountpoints that are part of paths returned by plugin have to be mounted under the directory specified by PropagatedMount in the plugin configuration [#26398](https://github.com/docker/docker/pull/26398)
28
+
25 29
 ### 1.12.0
26 30
 
27 31
 - Add `Status` field to `VolumeDriver.Get` response ([#21006](https://github.com/docker/docker/pull/21006#))
... ...
@@ -88,6 +88,11 @@ func NewDaemon(c *check.C) *Daemon {
88 88
 	}
89 89
 }
90 90
 
91
+// RootDir returns the root directory of the daemon.
92
+func (d *Daemon) RootDir() string {
93
+	return d.root
94
+}
95
+
91 96
 func (d *Daemon) getClientConfig() (*clientConfig, error) {
92 97
 	var (
93 98
 		transport *http.Transport
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"syscall"
11 11
 
12 12
 	"github.com/docker/docker/pkg/integration/checker"
13
+	"github.com/docker/docker/pkg/mount"
13 14
 	"github.com/go-check/check"
14 15
 )
15 16
 
... ...
@@ -186,7 +187,6 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
186 186
 	testRequires(c, Network, IsAmd64)
187 187
 
188 188
 	volName := "plugin-volume"
189
-	volRoot := "/data"
190 189
 	destDir := "/tmp/data/"
191 190
 	destFile := "foo"
192 191
 
... ...
@@ -197,13 +197,25 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
197 197
 	if err != nil {
198 198
 		c.Fatalf("Could not install plugin: %v %s", err, out)
199 199
 	}
200
+	pluginID, err := s.d.Cmd("plugin", "inspect", "-f", "{{.Id}}", pName)
201
+	pluginID = strings.TrimSpace(pluginID)
202
+	if err != nil {
203
+		c.Fatalf("Could not retrieve plugin ID: %v %s", err, pluginID)
204
+	}
205
+	mountpointPrefix := filepath.Join(s.d.RootDir(), "plugins", pluginID, "rootfs")
200 206
 	defer func() {
201 207
 		if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
202 208
 			c.Fatalf("Could not disable plugin: %v %s", err, out)
203 209
 		}
210
+
204 211
 		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
205 212
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
206 213
 		}
214
+
215
+		exists, err := existsMountpointWithPrefix(mountpointPrefix)
216
+		c.Assert(err, checker.IsNil)
217
+		c.Assert(exists, checker.Equals, false)
218
+
207 219
 	}()
208 220
 
209 221
 	out, err = s.d.Cmd("volume", "create", "-d", pName, volName)
... ...
@@ -231,11 +243,24 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
231 231
 
232 232
 	out, err = s.d.Cmd("run", "--rm", "-v", volName+":"+destDir, "busybox", "touch", destDir+destFile)
233 233
 	c.Assert(err, checker.IsNil, check.Commentf(out))
234
-	path := filepath.Join(mountPoint, destFile)
234
+	path := filepath.Join(s.d.RootDir(), "plugins", pluginID, "rootfs", mountPoint, destFile)
235 235
 	_, err = os.Lstat(path)
236 236
 	c.Assert(err, checker.IsNil)
237 237
 
238
-	// tiborvass/no-remove is a volume plugin that persists data on disk at /data,
239
-	// even after the volume is removed. So perform an explicit filesystem cleanup.
240
-	os.RemoveAll(volRoot)
238
+	exists, err := existsMountpointWithPrefix(mountpointPrefix)
239
+	c.Assert(err, checker.IsNil)
240
+	c.Assert(exists, checker.Equals, true)
241
+}
242
+
243
+func existsMountpointWithPrefix(mountpointPrefix string) (bool, error) {
244
+	mounts, err := mount.GetMounts()
245
+	if err != nil {
246
+		return false, err
247
+	}
248
+	for _, mnt := range mounts {
249
+		if strings.HasPrefix(mnt.Mountpoint, mountpointPrefix) {
250
+			return true, nil
251
+		}
252
+	}
253
+	return false, nil
241 254
 }
... ...
@@ -11,8 +11,8 @@ import (
11 11
 )
12 12
 
13 13
 var (
14
-	pluginProcessName = "no-remove"
15
-	pName             = "tiborvass/no-remove"
14
+	pluginProcessName = "sample-volume-plugin"
15
+	pName             = "tiborvass/sample-volume-plugin"
16 16
 	pTag              = "latest"
17 17
 	pNameWithTag      = pName + ":" + pTag
18 18
 )
... ...
@@ -33,6 +33,7 @@ func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
33 33
 	c.Assert(err, checker.IsNil)
34 34
 
35 35
 	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
36
+	c.Assert(err, checker.NotNil)
36 37
 	c.Assert(out, checker.Contains, "is enabled")
37 38
 
38 39
 	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
... ...
@@ -15,6 +15,7 @@ const (
15 15
 type CompatPlugin interface {
16 16
 	Client() *plugins.Client
17 17
 	Name() string
18
+	BasePath() string
18 19
 	IsV1() bool
19 20
 }
20 21
 
... ...
@@ -78,6 +78,12 @@ type Plugin struct {
78 78
 	activateWait *sync.Cond
79 79
 }
80 80
 
81
+// BasePath returns the path to which all paths returned by the plugin are relative to.
82
+// For v1 plugins, this always returns the host's root directory.
83
+func (p *Plugin) BasePath() string {
84
+	return "/"
85
+}
86
+
81 87
 // Name returns the name of the plugin.
82 88
 func (p *Plugin) Name() string {
83 89
 	return p.name
... ...
@@ -255,7 +255,7 @@ func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.A
255 255
 		return err
256 256
 	}
257 257
 
258
-	rootfs, err := archive.Tar(filepath.Join(dest, "rootfs"), archive.Gzip)
258
+	rootfs, err := archive.Tar(p.Rootfs, archive.Gzip)
259 259
 	if err != nil {
260 260
 		return err
261 261
 	}
... ...
@@ -293,9 +293,13 @@ func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
293 293
 		}
294 294
 	}
295 295
 
296
+	id := p.GetID()
296 297
 	pm.pluginStore.Remove(p)
297
-	os.RemoveAll(filepath.Join(pm.libRoot, p.GetID()))
298
-	pm.pluginEventLogger(p.GetID(), name, "remove")
298
+	pluginDir := filepath.Join(pm.libRoot, id)
299
+	if err := os.RemoveAll(pluginDir); err != nil {
300
+		logrus.Warnf("unable to remove %q from plugin remove: %v", pluginDir, err)
301
+	}
302
+	pm.pluginEventLogger(id, name, "remove")
299 303
 	return nil
300 304
 }
301 305
 
... ...
@@ -177,7 +177,8 @@ func WritePullData(pd PullData, dest string, extract bool) error {
177 177
 	if err := json.Unmarshal(config, &p); err != nil {
178 178
 		return err
179 179
 	}
180
-	logrus.Debugf("%#v", p)
180
+	logrus.Debugf("plugin: %#v", p)
181
+
181 182
 	if err := os.MkdirAll(dest, 0700); err != nil {
182 183
 		return err
183 184
 	}
... ...
@@ -5,10 +5,12 @@ import (
5 5
 	"io"
6 6
 	"os"
7 7
 	"path/filepath"
8
+	"strings"
8 9
 	"sync"
9 10
 
10 11
 	"github.com/Sirupsen/logrus"
11 12
 	"github.com/docker/docker/libcontainerd"
13
+	"github.com/docker/docker/pkg/mount"
12 14
 	"github.com/docker/docker/plugin/store"
13 15
 	"github.com/docker/docker/plugin/v2"
14 16
 	"github.com/docker/docker/registry"
... ...
@@ -63,7 +65,7 @@ func Init(root string, ps *store.Store, remote libcontainerd.Remote, rs registry
63 63
 	root = filepath.Join(root, "plugins")
64 64
 	manager = &Manager{
65 65
 		libRoot:           root,
66
-		runRoot:           "/run/docker",
66
+		runRoot:           "/run/docker/plugins",
67 67
 		pluginStore:       ps,
68 68
 		registryService:   rs,
69 69
 		liveRestore:       liveRestore,
... ...
@@ -104,6 +106,13 @@ func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
104 104
 		pm.mu.RUnlock()
105 105
 
106 106
 		p.RemoveFromDisk()
107
+
108
+		if p.PropagatedMount != "" {
109
+			if err := mount.Unmount(p.PropagatedMount); err != nil {
110
+				logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
111
+			}
112
+		}
113
+
107 114
 		if restart {
108 115
 			pm.enable(p, c, true)
109 116
 		}
... ...
@@ -141,6 +150,24 @@ func (pm *Manager) reload() error {
141 141
 				return
142 142
 			}
143 143
 
144
+			if p.Rootfs != "" {
145
+				p.Rootfs = filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
146
+			}
147
+
148
+			// We should only enable rootfs propagation for certain plugin types that need it.
149
+			for _, typ := range p.PluginObj.Config.Interface.Types {
150
+				if typ.Capability == "volumedriver" && typ.Prefix == "docker" && strings.HasPrefix(typ.Version, "1.") {
151
+					if p.PluginObj.Config.PropagatedMount != "" {
152
+						// TODO: sanitize PropagatedMount and prevent breakout
153
+						p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
154
+						if err := os.MkdirAll(p.PropagatedMount, 0755); err != nil {
155
+							logrus.Errorf("failed to create PropagatedMount directory at %s: %v", p.PropagatedMount, err)
156
+							return
157
+						}
158
+					}
159
+				}
160
+			}
161
+
144 162
 			pm.pluginStore.Update(p)
145 163
 			requiresManualRestore := !pm.liveRestore && p.IsEnabled()
146 164
 
... ...
@@ -11,16 +11,18 @@ import (
11 11
 	"github.com/Sirupsen/logrus"
12 12
 	"github.com/docker/docker/libcontainerd"
13 13
 	"github.com/docker/docker/oci"
14
+	"github.com/docker/docker/pkg/mount"
14 15
 	"github.com/docker/docker/pkg/plugins"
15 16
 	"github.com/docker/docker/plugin/v2"
16 17
 	specs "github.com/opencontainers/runtime-spec/specs-go"
17 18
 )
18 19
 
19 20
 func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
21
+	p.Rootfs = filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
20 22
 	if p.IsEnabled() && !force {
21 23
 		return fmt.Errorf("plugin %s is already enabled", p.Name())
22 24
 	}
23
-	spec, err := p.InitSpec(oci.DefaultSpec(), pm.libRoot)
25
+	spec, err := p.InitSpec(oci.DefaultSpec())
24 26
 	if err != nil {
25 27
 		return err
26 28
 	}
... ...
@@ -32,6 +34,12 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
32 32
 	pm.cMap[p] = c
33 33
 	pm.mu.Unlock()
34 34
 
35
+	if p.PropagatedMount != "" {
36
+		if err := mount.MakeRShared(p.PropagatedMount); err != nil {
37
+			return err
38
+		}
39
+	}
40
+
35 41
 	if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec), attachToLog(p.GetID())); err != nil {
36 42
 		return err
37 43
 	}
... ...
@@ -2,6 +2,7 @@ package v2
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"errors"
5 6
 	"fmt"
6 7
 	"os"
7 8
 	"path/filepath"
... ...
@@ -22,7 +23,9 @@ type Plugin struct {
22 22
 	pClient           *plugins.Client
23 23
 	runtimeSourcePath string
24 24
 	refCount          int
25
-	libRoot           string
25
+	LibRoot           string // TODO: make private
26
+	PropagatedMount   string // TODO: make private
27
+	Rootfs            string // TODO: make private
26 28
 }
27 29
 
28 30
 const defaultPluginRuntimeDestination = "/run/docker/plugins"
... ...
@@ -45,7 +48,7 @@ func NewPlugin(name, id, runRoot, libRoot, tag string) *Plugin {
45 45
 	return &Plugin{
46 46
 		PluginObj:         newPluginObj(name, id, tag),
47 47
 		runtimeSourcePath: filepath.Join(runRoot, id),
48
-		libRoot:           libRoot,
48
+		LibRoot:           libRoot,
49 49
 	}
50 50
 }
51 51
 
... ...
@@ -63,6 +66,12 @@ func (p *Plugin) GetRuntimeSourcePath() string {
63 63
 	return p.runtimeSourcePath
64 64
 }
65 65
 
66
+// BasePath returns the path to which all paths returned by the plugin are relative to.
67
+// For Plugin objects this returns the host path of the plugin container's rootfs.
68
+func (p *Plugin) BasePath() string {
69
+	return p.Rootfs
70
+}
71
+
66 72
 // Client returns the plugin client.
67 73
 func (p *Plugin) Client() *plugins.Client {
68 74
 	p.mu.RLock()
... ...
@@ -112,7 +121,7 @@ func (p *Plugin) RemoveFromDisk() error {
112 112
 
113 113
 // InitPlugin populates the plugin object from the plugin config file.
114 114
 func (p *Plugin) InitPlugin() error {
115
-	dt, err := os.Open(filepath.Join(p.libRoot, p.PluginObj.ID, "config.json"))
115
+	dt, err := os.Open(filepath.Join(p.LibRoot, p.PluginObj.ID, "config.json"))
116 116
 	if err != nil {
117 117
 		return err
118 118
 	}
... ...
@@ -123,9 +132,7 @@ func (p *Plugin) InitPlugin() error {
123 123
 	}
124 124
 
125 125
 	p.PluginObj.Settings.Mounts = make([]types.PluginMount, len(p.PluginObj.Config.Mounts))
126
-	for i, mount := range p.PluginObj.Config.Mounts {
127
-		p.PluginObj.Settings.Mounts[i] = mount
128
-	}
126
+	copy(p.PluginObj.Settings.Mounts, p.PluginObj.Config.Mounts)
129 127
 	p.PluginObj.Settings.Env = make([]string, 0, len(p.PluginObj.Config.Env))
130 128
 	p.PluginObj.Settings.Devices = make([]types.PluginDevice, 0, len(p.PluginObj.Config.Linux.Devices))
131 129
 	copy(p.PluginObj.Settings.Devices, p.PluginObj.Config.Linux.Devices)
... ...
@@ -134,13 +141,14 @@ func (p *Plugin) InitPlugin() error {
134 134
 			p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
135 135
 		}
136 136
 	}
137
+	p.PluginObj.Settings.Args = make([]string, len(p.PluginObj.Config.Args.Value))
137 138
 	copy(p.PluginObj.Settings.Args, p.PluginObj.Config.Args.Value)
138 139
 
139 140
 	return p.writeSettings()
140 141
 }
141 142
 
142 143
 func (p *Plugin) writeSettings() error {
143
-	f, err := os.Create(filepath.Join(p.libRoot, p.PluginObj.ID, "plugin-settings.json"))
144
+	f, err := os.Create(filepath.Join(p.LibRoot, p.PluginObj.ID, "plugin-settings.json"))
144 145
 	if err != nil {
145 146
 		return err
146 147
 	}
... ...
@@ -287,18 +295,21 @@ func (p *Plugin) AddRefCount(count int) {
287 287
 }
288 288
 
289 289
 // InitSpec creates an OCI spec from the plugin's config.
290
-func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
291
-	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
290
+func (p *Plugin) InitSpec(s specs.Spec) (*specs.Spec, error) {
292 291
 	s.Root = specs.Root{
293
-		Path:     rootfs,
292
+		Path:     p.Rootfs,
294 293
 		Readonly: false, // TODO: all plugins should be readonly? settable in config?
295 294
 	}
296 295
 
297
-	userMounts := make(map[string]struct{}, len(p.PluginObj.Config.Mounts))
298
-	for _, m := range p.PluginObj.Config.Mounts {
296
+	userMounts := make(map[string]struct{}, len(p.PluginObj.Settings.Mounts))
297
+	for _, m := range p.PluginObj.Settings.Mounts {
299 298
 		userMounts[m.Destination] = struct{}{}
300 299
 	}
301 300
 
301
+	if err := os.MkdirAll(p.runtimeSourcePath, 0755); err != nil {
302
+		return nil, err
303
+	}
304
+
302 305
 	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
303 306
 		Source:      &p.runtimeSourcePath,
304 307
 		Destination: defaultPluginRuntimeDestination,
... ...
@@ -328,27 +339,16 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
328 328
 			})
329 329
 	}
330 330
 
331
-	for _, mount := range mounts {
331
+	for _, mnt := range mounts {
332 332
 		m := specs.Mount{
333
-			Destination: mount.Destination,
334
-			Type:        mount.Type,
335
-			Options:     mount.Options,
333
+			Destination: mnt.Destination,
334
+			Type:        mnt.Type,
335
+			Options:     mnt.Options,
336 336
 		}
337
-		// TODO: if nil, then it's required and user didn't set it
338
-		if mount.Source != nil {
339
-			m.Source = *mount.Source
340
-		}
341
-		if m.Source != "" && m.Type == "bind" {
342
-			fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks
343
-			if err != nil {
344
-				return nil, err
345
-			}
346
-			if fi.IsDir() {
347
-				if err := os.MkdirAll(m.Source, 0700); err != nil {
348
-					return nil, err
349
-				}
350
-			}
337
+		if mnt.Source == nil {
338
+			return nil, errors.New("mount source is not specified")
351 339
 		}
340
+		m.Source = *mnt.Source
352 341
 		s.Mounts = append(s.Mounts, m)
353 342
 	}
354 343
 
... ...
@@ -360,11 +360,16 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
360 360
 		}
361 361
 	}
362 362
 
363
+	if p.PluginObj.Config.PropagatedMount != "" {
364
+		p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
365
+		s.Linux.RootfsPropagation = "rshared"
366
+	}
367
+
363 368
 	if p.PluginObj.Config.Linux.DeviceCreation {
364 369
 		rwm := "rwm"
365 370
 		s.Linux.Resources.Devices = []specs.DeviceCgroup{{Allow: true, Access: &rwm}}
366 371
 	}
367
-	for _, dev := range p.PluginObj.Config.Linux.Devices {
372
+	for _, dev := range p.PluginObj.Settings.Devices {
368 373
 		path := *dev.Path
369 374
 		d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm")
370 375
 		if err != nil {
... ...
@@ -2,6 +2,7 @@ package volumedrivers
2 2
 
3 3
 import (
4 4
 	"errors"
5
+	"path/filepath"
5 6
 	"strings"
6 7
 
7 8
 	"github.com/Sirupsen/logrus"
... ...
@@ -14,6 +15,7 @@ var (
14 14
 
15 15
 type volumeDriverAdapter struct {
16 16
 	name         string
17
+	baseHostPath string
17 18
 	capabilities *volume.Capability
18 19
 	proxy        *volumeDriverProxy
19 20
 }
... ...
@@ -27,9 +29,10 @@ func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volum
27 27
 		return nil, err
28 28
 	}
29 29
 	return &volumeAdapter{
30
-		proxy:      a.proxy,
31
-		name:       name,
32
-		driverName: a.name,
30
+		proxy:        a.proxy,
31
+		name:         name,
32
+		driverName:   a.name,
33
+		baseHostPath: a.baseHostPath,
33 34
 	}, nil
34 35
 }
35 36
 
... ...
@@ -37,6 +40,13 @@ func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
37 37
 	return a.proxy.Remove(v.Name())
38 38
 }
39 39
 
40
+func hostPath(baseHostPath, path string) string {
41
+	if baseHostPath != "" {
42
+		path = filepath.Join(baseHostPath, path)
43
+	}
44
+	return path
45
+}
46
+
40 47
 func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
41 48
 	ls, err := a.proxy.List()
42 49
 	if err != nil {
... ...
@@ -46,10 +56,11 @@ func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
46 46
 	var out []volume.Volume
47 47
 	for _, vp := range ls {
48 48
 		out = append(out, &volumeAdapter{
49
-			proxy:      a.proxy,
50
-			name:       vp.Name,
51
-			driverName: a.name,
52
-			eMount:     vp.Mountpoint,
49
+			proxy:        a.proxy,
50
+			name:         vp.Name,
51
+			baseHostPath: a.baseHostPath,
52
+			driverName:   a.name,
53
+			eMount:       hostPath(a.baseHostPath, vp.Mountpoint),
53 54
 		})
54 55
 	}
55 56
 	return out, nil
... ...
@@ -67,11 +78,12 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
67 67
 	}
68 68
 
69 69
 	return &volumeAdapter{
70
-		proxy:      a.proxy,
71
-		name:       v.Name,
72
-		driverName: a.Name(),
73
-		eMount:     v.Mountpoint,
74
-		status:     v.Status,
70
+		proxy:        a.proxy,
71
+		name:         v.Name,
72
+		driverName:   a.Name(),
73
+		eMount:       v.Mountpoint,
74
+		status:       v.Status,
75
+		baseHostPath: a.baseHostPath,
75 76
 	}, nil
76 77
 }
77 78
 
... ...
@@ -108,11 +120,12 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
108 108
 }
109 109
 
110 110
 type volumeAdapter struct {
111
-	proxy      *volumeDriverProxy
112
-	name       string
113
-	driverName string
114
-	eMount     string // ephemeral host volume path
115
-	status     map[string]interface{}
111
+	proxy        *volumeDriverProxy
112
+	name         string
113
+	baseHostPath string
114
+	driverName   string
115
+	eMount       string // ephemeral host volume path
116
+	status       map[string]interface{}
116 117
 }
117 118
 
118 119
 type proxyVolume struct {
... ...
@@ -131,7 +144,8 @@ func (a *volumeAdapter) DriverName() string {
131 131
 
132 132
 func (a *volumeAdapter) Path() string {
133 133
 	if len(a.eMount) == 0 {
134
-		a.eMount, _ = a.proxy.Path(a.name)
134
+		mountpoint, _ := a.proxy.Path(a.name)
135
+		a.eMount = hostPath(a.baseHostPath, mountpoint)
135 136
 	}
136 137
 	return a.eMount
137 138
 }
... ...
@@ -141,8 +155,8 @@ func (a *volumeAdapter) CachedPath() string {
141 141
 }
142 142
 
143 143
 func (a *volumeAdapter) Mount(id string) (string, error) {
144
-	var err error
145
-	a.eMount, err = a.proxy.Mount(a.name, id)
144
+	mountpoint, err := a.proxy.Mount(a.name, id)
145
+	a.eMount = hostPath(a.baseHostPath, mountpoint)
146 146
 	return a.eMount, err
147 147
 }
148 148
 
... ...
@@ -22,9 +22,9 @@ var drivers = &driverExtpoint{
22 22
 const extName = "VolumeDriver"
23 23
 
24 24
 // NewVolumeDriver returns a driver has the given name mapped on the given client.
25
-func NewVolumeDriver(name string, c client) volume.Driver {
25
+func NewVolumeDriver(name string, baseHostPath string, c client) volume.Driver {
26 26
 	proxy := &volumeDriverProxy{c}
27
-	return &volumeDriverAdapter{name: name, proxy: proxy}
27
+	return &volumeDriverAdapter{name: name, baseHostPath: baseHostPath, proxy: proxy}
28 28
 }
29 29
 
30 30
 // volumeDriver defines the available functions that volume plugins must implement.
... ...
@@ -117,7 +117,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
117 117
 		return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
118 118
 	}
119 119
 
120
-	d := NewVolumeDriver(p.Name(), p.Client())
120
+	d := NewVolumeDriver(p.Name(), p.BasePath(), p.Client())
121 121
 	if err := validateDriver(d); err != nil {
122 122
 		return nil, err
123 123
 	}
... ...
@@ -199,7 +199,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
199 199
 			continue
200 200
 		}
201 201
 
202
-		ext = NewVolumeDriver(name, p.Client())
202
+		ext = NewVolumeDriver(name, p.BasePath(), p.Client())
203 203
 		if p.IsV1() {
204 204
 			drivers.extensions[name] = ext
205 205
 		}
... ...
@@ -4,6 +4,7 @@ package volumedrivers
4 4
 
5 5
 import (
6 6
 	"errors"
7
+
7 8
 	"github.com/docker/docker/volume"
8 9
 )
9 10