Browse code

When calling volume driver Mount, send opaque ID

This generates an ID string for calls to Mount/Unmount, allowing drivers
to differentiate between two callers of `Mount` and `Unmount`.

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

Brian Goff authored on 2016/03/08 11:41:44
Showing 12 changed files
... ...
@@ -12,6 +12,7 @@ import (
12 12
 
13 13
 	"github.com/Sirupsen/logrus"
14 14
 	"github.com/docker/docker/pkg/chrootarchive"
15
+	"github.com/docker/docker/pkg/stringid"
15 16
 	"github.com/docker/docker/pkg/symlink"
16 17
 	"github.com/docker/docker/pkg/system"
17 18
 	"github.com/docker/docker/utils"
... ...
@@ -181,11 +182,17 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st
181 181
 		return err
182 182
 	}
183 183
 
184
-	path, err := v.Mount()
184
+	id := stringid.GenerateNonCryptoID()
185
+	path, err := v.Mount(id)
185 186
 	if err != nil {
186 187
 		return err
187 188
 	}
188
-	defer v.Unmount()
189
+
190
+	defer func() {
191
+		if err := v.Unmount(id); err != nil {
192
+			logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err)
193
+		}
194
+	}()
189 195
 	return copyExistingContents(rootfs, path)
190 196
 }
191 197
 
... ...
@@ -328,9 +335,10 @@ func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog fun
328 328
 		}
329 329
 
330 330
 		if volumeMount.Volume != nil {
331
-			if err := volumeMount.Volume.Unmount(); err != nil {
331
+			if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
332 332
 				return err
333 333
 			}
334
+			volumeMount.ID = ""
334 335
 
335 336
 			attributes := map[string]string{
336 337
 				"driver":    volumeMount.Volume.DriverName(),
... ...
@@ -115,7 +115,8 @@ Respond with a string error if an error occurred.
115 115
 **Request**:
116 116
 ```json
117 117
 {
118
-    "Name": "volume_name"
118
+    "Name": "volume_name",
119
+    "ID": "b87d7442095999a92b65b3d9691e697b61713829cc0ffd1bb72e4ccd51aa4d6c"
119 120
 }
120 121
 ```
121 122
 
... ...
@@ -124,6 +125,8 @@ name. This is called once per container start. If the same volume_name is reques
124 124
 more than once, the plugin may need to keep track of each new mount request and provision
125 125
 at the first mount request and deprovision at the last corresponding unmount request.
126 126
 
127
+`ID` is a unqiue ID for the caller that is requesting the mount.
128
+
127 129
 **Response**:
128 130
 ```json
129 131
 {
... ...
@@ -162,7 +165,8 @@ available, and/or a string error if an error occurred.
162 162
 **Request**:
163 163
 ```json
164 164
 {
165
-    "Name": "volume_name"
165
+    "Name": "volume_name",
166
+    "ID": "b87d7442095999a92b65b3d9691e697b61713829cc0ffd1bb72e4ccd51aa4d6c"
166 167
 }
167 168
 ```
168 169
 
... ...
@@ -170,6 +174,8 @@ Indication that Docker no longer is using the named volume. This is called once
170 170
 per container stop.  Plugin may deduce that it is safe to deprovision it at
171 171
 this point.
172 172
 
173
+`ID` is a unqiue ID for the caller that is requesting the mount.
174
+
173 175
 **Response**:
174 176
 ```json
175 177
 {
... ...
@@ -61,6 +61,7 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
61 61
 	type pluginRequest struct {
62 62
 		Name string
63 63
 		Opts map[string]string
64
+		ID   string
64 65
 	}
65 66
 
66 67
 	type pluginResp struct {
... ...
@@ -204,6 +205,11 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
204 204
 			return
205 205
 		}
206 206
 
207
+		if err := ioutil.WriteFile(filepath.Join(p, "mountID"), []byte(pr.ID), 0644); err != nil {
208
+			send(w, err)
209
+			return
210
+		}
211
+
207 212
 		send(w, &pluginResp{Mountpoint: p})
208 213
 	})
209 214
 
... ...
@@ -476,3 +482,12 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverPathCalls(c *check.C
476 476
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
477 477
 	c.Assert(s.ec.paths, checker.Equals, 1)
478 478
 }
479
+
480
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverMountID(c *check.C) {
481
+	err := s.d.StartWithBusybox()
482
+	c.Assert(err, checker.IsNil)
483
+
484
+	out, err := s.d.Cmd("run", "--rm", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", "busybox:latest", "cat", "/tmp/external-volume-test/test")
485
+	c.Assert(err, checker.IsNil, check.Commentf(out))
486
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
487
+}
... ...
@@ -42,10 +42,17 @@ var templFuncs = template.FuncMap{
42 42
 	"marshalType": marshalType,
43 43
 	"isErr":       isErr,
44 44
 	"lower":       strings.ToLower,
45
-	"title":       strings.Title,
45
+	"title":       title,
46 46
 	"tag":         buildTag,
47 47
 }
48 48
 
49
+func title(s string) string {
50
+	if strings.ToLower(s) == "id" {
51
+		return "ID"
52
+	}
53
+	return strings.Title(s)
54
+}
55
+
49 56
 var generatedTempl = template.Must(template.New("rpc_cient").Funcs(templFuncs).Parse(`
50 57
 // generated code - DO NOT EDIT
51 58
 {{ range $k, $v := .BuildTags }}
... ...
@@ -101,14 +101,14 @@ func (a *volumeAdapter) CachedPath() string {
101 101
 	return a.eMount
102 102
 }
103 103
 
104
-func (a *volumeAdapter) Mount() (string, error) {
104
+func (a *volumeAdapter) Mount(id string) (string, error) {
105 105
 	var err error
106
-	a.eMount, err = a.proxy.Mount(a.name)
106
+	a.eMount, err = a.proxy.Mount(a.name, id)
107 107
 	return a.eMount, err
108 108
 }
109 109
 
110
-func (a *volumeAdapter) Unmount() error {
111
-	err := a.proxy.Unmount(a.name)
110
+func (a *volumeAdapter) Unmount(id string) error {
111
+	err := a.proxy.Unmount(a.name, id)
112 112
 	if err == nil {
113 113
 		a.eMount = ""
114 114
 	}
... ...
@@ -38,9 +38,9 @@ type volumeDriver interface {
38 38
 	// Get the mountpoint of the given volume
39 39
 	Path(name string) (mountpoint string, err error)
40 40
 	// Mount the given volume and return the mountpoint
41
-	Mount(name string) (mountpoint string, err error)
41
+	Mount(name, id string) (mountpoint string, err error)
42 42
 	// Unmount the given volume
43
-	Unmount(name string) (err error)
43
+	Unmount(name, id string) (err error)
44 44
 	// List lists all the volumes known to the driver
45 45
 	List() (volumes list, err error)
46 46
 	// Get retrieves the volume with the requested name
... ...
@@ -97,6 +97,7 @@ func (pp *volumeDriverProxy) Path(name string) (mountpoint string, err error) {
97 97
 
98 98
 type volumeDriverProxyMountRequest struct {
99 99
 	Name string
100
+	ID   string
100 101
 }
101 102
 
102 103
 type volumeDriverProxyMountResponse struct {
... ...
@@ -104,13 +105,14 @@ type volumeDriverProxyMountResponse struct {
104 104
 	Err        string
105 105
 }
106 106
 
107
-func (pp *volumeDriverProxy) Mount(name string) (mountpoint string, err error) {
107
+func (pp *volumeDriverProxy) Mount(name string, id string) (mountpoint string, err error) {
108 108
 	var (
109 109
 		req volumeDriverProxyMountRequest
110 110
 		ret volumeDriverProxyMountResponse
111 111
 	)
112 112
 
113 113
 	req.Name = name
114
+	req.ID = id
114 115
 	if err = pp.Call("VolumeDriver.Mount", req, &ret); err != nil {
115 116
 		return
116 117
 	}
... ...
@@ -126,19 +128,21 @@ func (pp *volumeDriverProxy) Mount(name string) (mountpoint string, err error) {
126 126
 
127 127
 type volumeDriverProxyUnmountRequest struct {
128 128
 	Name string
129
+	ID   string
129 130
 }
130 131
 
131 132
 type volumeDriverProxyUnmountResponse struct {
132 133
 	Err string
133 134
 }
134 135
 
135
-func (pp *volumeDriverProxy) Unmount(name string) (err error) {
136
+func (pp *volumeDriverProxy) Unmount(name string, id string) (err error) {
136 137
 	var (
137 138
 		req volumeDriverProxyUnmountRequest
138 139
 		ret volumeDriverProxyUnmountResponse
139 140
 	)
140 141
 
141 142
 	req.Name = name
143
+	req.ID = id
142 144
 	if err = pp.Call("VolumeDriver.Unmount", req, &ret); err != nil {
143 145
 		return
144 146
 	}
... ...
@@ -68,7 +68,7 @@ func TestVolumeRequestError(t *testing.T) {
68 68
 		t.Fatalf("Unexpected error: %v\n", err)
69 69
 	}
70 70
 
71
-	_, err = driver.Mount("volume")
71
+	_, err = driver.Mount("volume", "123")
72 72
 	if err == nil {
73 73
 		t.Fatal("Expected error, was nil")
74 74
 	}
... ...
@@ -77,7 +77,7 @@ func TestVolumeRequestError(t *testing.T) {
77 77
 		t.Fatalf("Unexpected error: %v\n", err)
78 78
 	}
79 79
 
80
-	err = driver.Unmount("volume")
80
+	err = driver.Unmount("volume", "123")
81 81
 	if err == nil {
82 82
 		t.Fatal("Expected error, was nil")
83 83
 	}
... ...
@@ -287,7 +287,7 @@ func (v *localVolume) Path() string {
287 287
 }
288 288
 
289 289
 // Mount implements the localVolume interface, returning the data location.
290
-func (v *localVolume) Mount() (string, error) {
290
+func (v *localVolume) Mount(id string) (string, error) {
291 291
 	v.m.Lock()
292 292
 	defer v.m.Unlock()
293 293
 	if v.opts != nil {
... ...
@@ -303,7 +303,7 @@ func (v *localVolume) Mount() (string, error) {
303 303
 }
304 304
 
305 305
 // Umount is for satisfying the localVolume interface and does not do anything in this driver.
306
-func (v *localVolume) Unmount() error {
306
+func (v *localVolume) Unmount(id string) error {
307 307
 	v.m.Lock()
308 308
 	defer v.m.Unlock()
309 309
 	if v.opts != nil {
... ...
@@ -181,12 +181,12 @@ func TestCreateWithOpts(t *testing.T) {
181 181
 	}
182 182
 	v := vol.(*localVolume)
183 183
 
184
-	dir, err := v.Mount()
184
+	dir, err := v.Mount("1234")
185 185
 	if err != nil {
186 186
 		t.Fatal(err)
187 187
 	}
188 188
 	defer func() {
189
-		if err := v.Unmount(); err != nil {
189
+		if err := v.Unmount("1234"); err != nil {
190 190
 			t.Fatal(err)
191 191
 		}
192 192
 	}()
... ...
@@ -225,14 +225,14 @@ func TestCreateWithOpts(t *testing.T) {
225 225
 	}
226 226
 
227 227
 	// test double mount
228
-	if _, err := v.Mount(); err != nil {
228
+	if _, err := v.Mount("1234"); err != nil {
229 229
 		t.Fatal(err)
230 230
 	}
231 231
 	if v.active.count != 2 {
232 232
 		t.Fatalf("Expected active mount count to be 2, got %d", v.active.count)
233 233
 	}
234 234
 
235
-	if err := v.Unmount(); err != nil {
235
+	if err := v.Unmount("1234"); err != nil {
236 236
 		t.Fatal(err)
237 237
 	}
238 238
 	if v.active.count != 1 {
... ...
@@ -19,10 +19,10 @@ func (NoopVolume) DriverName() string { return "noop" }
19 19
 func (NoopVolume) Path() string { return "noop" }
20 20
 
21 21
 // Mount mounts the volume in the container
22
-func (NoopVolume) Mount() (string, error) { return "noop", nil }
22
+func (NoopVolume) Mount(_ string) (string, error) { return "noop", nil }
23 23
 
24 24
 // Unmount unmounts the volume from the container
25
-func (NoopVolume) Unmount() error { return nil }
25
+func (NoopVolume) Unmount(_ string) error { return nil }
26 26
 
27 27
 // Status proivdes low-level details about the volume
28 28
 func (NoopVolume) Status() map[string]interface{} { return nil }
... ...
@@ -48,10 +48,10 @@ func (f FakeVolume) DriverName() string { return f.driverName }
48 48
 func (FakeVolume) Path() string { return "fake" }
49 49
 
50 50
 // Mount mounts the volume in the container
51
-func (FakeVolume) Mount() (string, error) { return "fake", nil }
51
+func (FakeVolume) Mount(_ string) (string, error) { return "fake", nil }
52 52
 
53 53
 // Unmount unmounts the volume from the container
54
-func (FakeVolume) Unmount() error { return nil }
54
+func (FakeVolume) Unmount(_ string) error { return nil }
55 55
 
56 56
 // Status proivdes low-level details about the volume
57 57
 func (FakeVolume) Status() map[string]interface{} { return nil }
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"os"
6 6
 	"runtime"
7 7
 	"strings"
8
+
9
+	"github.com/docker/docker/pkg/stringid"
8 10
 )
9 11
 
10 12
 // DefaultDriverName is the driver name used for the driver
... ...
@@ -35,9 +37,9 @@ type Volume interface {
35 35
 	Path() string
36 36
 	// Mount mounts the volume and returns the absolute path to
37 37
 	// where it can be consumed.
38
-	Mount() (string, error)
38
+	Mount(id string) (string, error)
39 39
 	// Unmount unmounts the volume when it is no longer in use.
40
-	Unmount() error
40
+	Unmount(id string) error
41 41
 	// Status returns low-level status information about a volume
42 42
 	Status() map[string]interface{}
43 43
 }
... ...
@@ -64,13 +66,19 @@ type MountPoint struct {
64 64
 	// Use a pointer here so we can tell if the user set this value explicitly
65 65
 	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
66 66
 	CopyData bool `json:"-"`
67
+	// ID is the opaque ID used to pass to the volume driver.
68
+	// This should be set by calls to `Mount` and unset by calls to `Unmount`
69
+	ID string
67 70
 }
68 71
 
69 72
 // Setup sets up a mount point by either mounting the volume if it is
70 73
 // configured, or creating the source directory if supplied.
71 74
 func (m *MountPoint) Setup() (string, error) {
72 75
 	if m.Volume != nil {
73
-		return m.Volume.Mount()
76
+		if m.ID == "" {
77
+			m.ID = stringid.GenerateNonCryptoID()
78
+		}
79
+		return m.Volume.Mount(m.ID)
74 80
 	}
75 81
 	if len(m.Source) > 0 {
76 82
 		if _, err := os.Stat(m.Source); err != nil {