Browse Source

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 9 years ago
parent
commit
2b6bc294fc

+ 11 - 3
container/container_unix.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/chrootarchive"
+	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
@@ -181,11 +182,17 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st
 		return err
 		return err
 	}
 	}
 
 
-	path, err := v.Mount()
+	id := stringid.GenerateNonCryptoID()
+	path, err := v.Mount(id)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	defer v.Unmount()
+
+	defer func() {
+		if err := v.Unmount(id); err != nil {
+			logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err)
+		}
+	}()
 	return copyExistingContents(rootfs, path)
 	return copyExistingContents(rootfs, path)
 }
 }
 
 
@@ -328,9 +335,10 @@ func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog fun
 		}
 		}
 
 
 		if volumeMount.Volume != nil {
 		if volumeMount.Volume != nil {
-			if err := volumeMount.Volume.Unmount(); err != nil {
+			if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
 				return err
 				return err
 			}
 			}
+			volumeMount.ID = ""
 
 
 			attributes := map[string]string{
 			attributes := map[string]string{
 				"driver":    volumeMount.Volume.DriverName(),
 				"driver":    volumeMount.Volume.DriverName(),

+ 8 - 2
docs/extend/plugins_volume.md

@@ -115,7 +115,8 @@ Respond with a string error if an error occurred.
 **Request**:
 **Request**:
 ```json
 ```json
 {
 {
-    "Name": "volume_name"
+    "Name": "volume_name",
+    "ID": "b87d7442095999a92b65b3d9691e697b61713829cc0ffd1bb72e4ccd51aa4d6c"
 }
 }
 ```
 ```
 
 
@@ -124,6 +125,8 @@ name. This is called once per container start. If the same volume_name is reques
 more than once, the plugin may need to keep track of each new mount request and provision
 more than once, the plugin may need to keep track of each new mount request and provision
 at the first mount request and deprovision at the last corresponding unmount request.
 at the first mount request and deprovision at the last corresponding unmount request.
 
 
+`ID` is a unqiue ID for the caller that is requesting the mount.
+
 **Response**:
 **Response**:
 ```json
 ```json
 {
 {
@@ -162,7 +165,8 @@ available, and/or a string error if an error occurred.
 **Request**:
 **Request**:
 ```json
 ```json
 {
 {
-    "Name": "volume_name"
+    "Name": "volume_name",
+    "ID": "b87d7442095999a92b65b3d9691e697b61713829cc0ffd1bb72e4ccd51aa4d6c"
 }
 }
 ```
 ```
 
 
@@ -170,6 +174,8 @@ Indication that Docker no longer is using the named volume. This is called once
 per container stop.  Plugin may deduce that it is safe to deprovision it at
 per container stop.  Plugin may deduce that it is safe to deprovision it at
 this point.
 this point.
 
 
+`ID` is a unqiue ID for the caller that is requesting the mount.
+
 **Response**:
 **Response**:
 ```json
 ```json
 {
 {

+ 15 - 0
integration-cli/docker_cli_external_volume_driver_unix_test.go

@@ -61,6 +61,7 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
 	type pluginRequest struct {
 	type pluginRequest struct {
 		Name string
 		Name string
 		Opts map[string]string
 		Opts map[string]string
+		ID   string
 	}
 	}
 
 
 	type pluginResp struct {
 	type pluginResp struct {
@@ -204,6 +205,11 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
 			return
 			return
 		}
 		}
 
 
+		if err := ioutil.WriteFile(filepath.Join(p, "mountID"), []byte(pr.ID), 0644); err != nil {
+			send(w, err)
+			return
+		}
+
 		send(w, &pluginResp{Mountpoint: p})
 		send(w, &pluginResp{Mountpoint: p})
 	})
 	})
 
 
@@ -476,3 +482,12 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverPathCalls(c *check.C
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 	c.Assert(s.ec.paths, checker.Equals, 1)
 	c.Assert(s.ec.paths, checker.Equals, 1)
 }
 }
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverMountID(c *check.C) {
+	err := s.d.StartWithBusybox()
+	c.Assert(err, checker.IsNil)
+
+	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")
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+}

+ 8 - 1
pkg/plugins/pluginrpc-gen/template.go

@@ -42,10 +42,17 @@ var templFuncs = template.FuncMap{
 	"marshalType": marshalType,
 	"marshalType": marshalType,
 	"isErr":       isErr,
 	"isErr":       isErr,
 	"lower":       strings.ToLower,
 	"lower":       strings.ToLower,
-	"title":       strings.Title,
+	"title":       title,
 	"tag":         buildTag,
 	"tag":         buildTag,
 }
 }
 
 
+func title(s string) string {
+	if strings.ToLower(s) == "id" {
+		return "ID"
+	}
+	return strings.Title(s)
+}
+
 var generatedTempl = template.Must(template.New("rpc_cient").Funcs(templFuncs).Parse(`
 var generatedTempl = template.Must(template.New("rpc_cient").Funcs(templFuncs).Parse(`
 // generated code - DO NOT EDIT
 // generated code - DO NOT EDIT
 {{ range $k, $v := .BuildTags }}
 {{ range $k, $v := .BuildTags }}

+ 4 - 4
volume/drivers/adapter.go

@@ -101,14 +101,14 @@ func (a *volumeAdapter) CachedPath() string {
 	return a.eMount
 	return a.eMount
 }
 }
 
 
-func (a *volumeAdapter) Mount() (string, error) {
+func (a *volumeAdapter) Mount(id string) (string, error) {
 	var err error
 	var err error
-	a.eMount, err = a.proxy.Mount(a.name)
+	a.eMount, err = a.proxy.Mount(a.name, id)
 	return a.eMount, err
 	return a.eMount, err
 }
 }
 
 
-func (a *volumeAdapter) Unmount() error {
-	err := a.proxy.Unmount(a.name)
+func (a *volumeAdapter) Unmount(id string) error {
+	err := a.proxy.Unmount(a.name, id)
 	if err == nil {
 	if err == nil {
 		a.eMount = ""
 		a.eMount = ""
 	}
 	}

+ 2 - 2
volume/drivers/extpoint.go

@@ -38,9 +38,9 @@ type volumeDriver interface {
 	// Get the mountpoint of the given volume
 	// Get the mountpoint of the given volume
 	Path(name string) (mountpoint string, err error)
 	Path(name string) (mountpoint string, err error)
 	// Mount the given volume and return the mountpoint
 	// Mount the given volume and return the mountpoint
-	Mount(name string) (mountpoint string, err error)
+	Mount(name, id string) (mountpoint string, err error)
 	// Unmount the given volume
 	// Unmount the given volume
-	Unmount(name string) (err error)
+	Unmount(name, id string) (err error)
 	// List lists all the volumes known to the driver
 	// List lists all the volumes known to the driver
 	List() (volumes list, err error)
 	List() (volumes list, err error)
 	// Get retrieves the volume with the requested name
 	// Get retrieves the volume with the requested name

+ 6 - 2
volume/drivers/proxy.go

@@ -97,6 +97,7 @@ func (pp *volumeDriverProxy) Path(name string) (mountpoint string, err error) {
 
 
 type volumeDriverProxyMountRequest struct {
 type volumeDriverProxyMountRequest struct {
 	Name string
 	Name string
+	ID   string
 }
 }
 
 
 type volumeDriverProxyMountResponse struct {
 type volumeDriverProxyMountResponse struct {
@@ -104,13 +105,14 @@ type volumeDriverProxyMountResponse struct {
 	Err        string
 	Err        string
 }
 }
 
 
-func (pp *volumeDriverProxy) Mount(name string) (mountpoint string, err error) {
+func (pp *volumeDriverProxy) Mount(name string, id string) (mountpoint string, err error) {
 	var (
 	var (
 		req volumeDriverProxyMountRequest
 		req volumeDriverProxyMountRequest
 		ret volumeDriverProxyMountResponse
 		ret volumeDriverProxyMountResponse
 	)
 	)
 
 
 	req.Name = name
 	req.Name = name
+	req.ID = id
 	if err = pp.Call("VolumeDriver.Mount", req, &ret); err != nil {
 	if err = pp.Call("VolumeDriver.Mount", req, &ret); err != nil {
 		return
 		return
 	}
 	}
@@ -126,19 +128,21 @@ func (pp *volumeDriverProxy) Mount(name string) (mountpoint string, err error) {
 
 
 type volumeDriverProxyUnmountRequest struct {
 type volumeDriverProxyUnmountRequest struct {
 	Name string
 	Name string
+	ID   string
 }
 }
 
 
 type volumeDriverProxyUnmountResponse struct {
 type volumeDriverProxyUnmountResponse struct {
 	Err string
 	Err string
 }
 }
 
 
-func (pp *volumeDriverProxy) Unmount(name string) (err error) {
+func (pp *volumeDriverProxy) Unmount(name string, id string) (err error) {
 	var (
 	var (
 		req volumeDriverProxyUnmountRequest
 		req volumeDriverProxyUnmountRequest
 		ret volumeDriverProxyUnmountResponse
 		ret volumeDriverProxyUnmountResponse
 	)
 	)
 
 
 	req.Name = name
 	req.Name = name
+	req.ID = id
 	if err = pp.Call("VolumeDriver.Unmount", req, &ret); err != nil {
 	if err = pp.Call("VolumeDriver.Unmount", req, &ret); err != nil {
 		return
 		return
 	}
 	}

+ 2 - 2
volume/drivers/proxy_test.go

@@ -68,7 +68,7 @@ func TestVolumeRequestError(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	}
 
 
-	_, err = driver.Mount("volume")
+	_, err = driver.Mount("volume", "123")
 	if err == nil {
 	if err == nil {
 		t.Fatal("Expected error, was nil")
 		t.Fatal("Expected error, was nil")
 	}
 	}
@@ -77,7 +77,7 @@ func TestVolumeRequestError(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	}
 
 
-	err = driver.Unmount("volume")
+	err = driver.Unmount("volume", "123")
 	if err == nil {
 	if err == nil {
 		t.Fatal("Expected error, was nil")
 		t.Fatal("Expected error, was nil")
 	}
 	}

+ 2 - 2
volume/local/local.go

@@ -287,7 +287,7 @@ func (v *localVolume) Path() string {
 }
 }
 
 
 // Mount implements the localVolume interface, returning the data location.
 // Mount implements the localVolume interface, returning the data location.
-func (v *localVolume) Mount() (string, error) {
+func (v *localVolume) Mount(id string) (string, error) {
 	v.m.Lock()
 	v.m.Lock()
 	defer v.m.Unlock()
 	defer v.m.Unlock()
 	if v.opts != nil {
 	if v.opts != nil {
@@ -303,7 +303,7 @@ func (v *localVolume) Mount() (string, error) {
 }
 }
 
 
 // Umount is for satisfying the localVolume interface and does not do anything in this driver.
 // Umount is for satisfying the localVolume interface and does not do anything in this driver.
-func (v *localVolume) Unmount() error {
+func (v *localVolume) Unmount(id string) error {
 	v.m.Lock()
 	v.m.Lock()
 	defer v.m.Unlock()
 	defer v.m.Unlock()
 	if v.opts != nil {
 	if v.opts != nil {

+ 4 - 4
volume/local/local_test.go

@@ -181,12 +181,12 @@ func TestCreateWithOpts(t *testing.T) {
 	}
 	}
 	v := vol.(*localVolume)
 	v := vol.(*localVolume)
 
 
-	dir, err := v.Mount()
+	dir, err := v.Mount("1234")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	defer func() {
 	defer func() {
-		if err := v.Unmount(); err != nil {
+		if err := v.Unmount("1234"); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	}()
 	}()
@@ -225,14 +225,14 @@ func TestCreateWithOpts(t *testing.T) {
 	}
 	}
 
 
 	// test double mount
 	// test double mount
-	if _, err := v.Mount(); err != nil {
+	if _, err := v.Mount("1234"); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if v.active.count != 2 {
 	if v.active.count != 2 {
 		t.Fatalf("Expected active mount count to be 2, got %d", v.active.count)
 		t.Fatalf("Expected active mount count to be 2, got %d", v.active.count)
 	}
 	}
 
 
-	if err := v.Unmount(); err != nil {
+	if err := v.Unmount("1234"); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if v.active.count != 1 {
 	if v.active.count != 1 {

+ 4 - 4
volume/testutils/testutils.go

@@ -19,10 +19,10 @@ func (NoopVolume) DriverName() string { return "noop" }
 func (NoopVolume) Path() string { return "noop" }
 func (NoopVolume) Path() string { return "noop" }
 
 
 // Mount mounts the volume in the container
 // Mount mounts the volume in the container
-func (NoopVolume) Mount() (string, error) { return "noop", nil }
+func (NoopVolume) Mount(_ string) (string, error) { return "noop", nil }
 
 
 // Unmount unmounts the volume from the container
 // Unmount unmounts the volume from the container
-func (NoopVolume) Unmount() error { return nil }
+func (NoopVolume) Unmount(_ string) error { return nil }
 
 
 // Status proivdes low-level details about the volume
 // Status proivdes low-level details about the volume
 func (NoopVolume) Status() map[string]interface{} { return nil }
 func (NoopVolume) Status() map[string]interface{} { return nil }
@@ -48,10 +48,10 @@ func (f FakeVolume) DriverName() string { return f.driverName }
 func (FakeVolume) Path() string { return "fake" }
 func (FakeVolume) Path() string { return "fake" }
 
 
 // Mount mounts the volume in the container
 // Mount mounts the volume in the container
-func (FakeVolume) Mount() (string, error) { return "fake", nil }
+func (FakeVolume) Mount(_ string) (string, error) { return "fake", nil }
 
 
 // Unmount unmounts the volume from the container
 // Unmount unmounts the volume from the container
-func (FakeVolume) Unmount() error { return nil }
+func (FakeVolume) Unmount(_ string) error { return nil }
 
 
 // Status proivdes low-level details about the volume
 // Status proivdes low-level details about the volume
 func (FakeVolume) Status() map[string]interface{} { return nil }
 func (FakeVolume) Status() map[string]interface{} { return nil }

+ 11 - 3
volume/volume.go

@@ -5,6 +5,8 @@ import (
 	"os"
 	"os"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
+
+	"github.com/docker/docker/pkg/stringid"
 )
 )
 
 
 // DefaultDriverName is the driver name used for the driver
 // DefaultDriverName is the driver name used for the driver
@@ -35,9 +37,9 @@ type Volume interface {
 	Path() string
 	Path() string
 	// Mount mounts the volume and returns the absolute path to
 	// Mount mounts the volume and returns the absolute path to
 	// where it can be consumed.
 	// where it can be consumed.
-	Mount() (string, error)
+	Mount(id string) (string, error)
 	// Unmount unmounts the volume when it is no longer in use.
 	// Unmount unmounts the volume when it is no longer in use.
-	Unmount() error
+	Unmount(id string) error
 	// Status returns low-level status information about a volume
 	// Status returns low-level status information about a volume
 	Status() map[string]interface{}
 	Status() map[string]interface{}
 }
 }
@@ -64,13 +66,19 @@ type MountPoint struct {
 	// Use a pointer here so we can tell if the user set this value explicitly
 	// Use a pointer here so we can tell if the user set this value explicitly
 	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
 	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
 	CopyData bool `json:"-"`
 	CopyData bool `json:"-"`
+	// ID is the opaque ID used to pass to the volume driver.
+	// This should be set by calls to `Mount` and unset by calls to `Unmount`
+	ID string
 }
 }
 
 
 // Setup sets up a mount point by either mounting the volume if it is
 // Setup sets up a mount point by either mounting the volume if it is
 // configured, or creating the source directory if supplied.
 // configured, or creating the source directory if supplied.
 func (m *MountPoint) Setup() (string, error) {
 func (m *MountPoint) Setup() (string, error) {
 	if m.Volume != nil {
 	if m.Volume != nil {
-		return m.Volume.Mount()
+		if m.ID == "" {
+			m.ID = stringid.GenerateNonCryptoID()
+		}
+		return m.Volume.Mount(m.ID)
 	}
 	}
 	if len(m.Source) > 0 {
 	if len(m.Source) > 0 {
 		if _, err := os.Stat(m.Source); err != nil {
 		if _, err := os.Stat(m.Source); err != nil {