mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									24a8de2b60
								
							
						
					
					
						commit
						2b6bc294fc
					
				
					 12 changed files with 77 additions and 29 deletions
				
			
		| 
						 | 
				
			
			@ -12,6 +12,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/docker/docker/pkg/chrootarchive"
 | 
			
		||||
	"github.com/docker/docker/pkg/stringid"
 | 
			
		||||
	"github.com/docker/docker/pkg/symlink"
 | 
			
		||||
	"github.com/docker/docker/pkg/system"
 | 
			
		||||
	"github.com/docker/docker/utils"
 | 
			
		||||
| 
						 | 
				
			
			@ -181,11 +182,17 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path, err := v.Mount()
 | 
			
		||||
	id := stringid.GenerateNonCryptoID()
 | 
			
		||||
	path, err := v.Mount(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -328,9 +335,10 @@ func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog fun
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if volumeMount.Volume != nil {
 | 
			
		||||
			if err := volumeMount.Volume.Unmount(); err != nil {
 | 
			
		||||
			if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			volumeMount.ID = ""
 | 
			
		||||
 | 
			
		||||
			attributes := map[string]string{
 | 
			
		||||
				"driver":    volumeMount.Volume.DriverName(),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,7 +115,8 @@ Respond with a string error if an error occurred.
 | 
			
		|||
**Request**:
 | 
			
		||||
```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
 | 
			
		||||
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**:
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +165,8 @@ available, and/or a string error if an error occurred.
 | 
			
		|||
**Request**:
 | 
			
		||||
```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
 | 
			
		||||
this point.
 | 
			
		||||
 | 
			
		||||
`ID` is a unqiue ID for the caller that is requesting the mount.
 | 
			
		||||
 | 
			
		||||
**Response**:
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,7 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
			
		|||
	type pluginRequest struct {
 | 
			
		||||
		Name string
 | 
			
		||||
		Opts map[string]string
 | 
			
		||||
		ID   string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type pluginResp struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +205,11 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
			
		|||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := ioutil.WriteFile(filepath.Join(p, "mountID"), []byte(pr.ID), 0644); err != nil {
 | 
			
		||||
			send(w, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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(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), "")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,10 +42,17 @@ var templFuncs = template.FuncMap{
 | 
			
		|||
	"marshalType": marshalType,
 | 
			
		||||
	"isErr":       isErr,
 | 
			
		||||
	"lower":       strings.ToLower,
 | 
			
		||||
	"title":       strings.Title,
 | 
			
		||||
	"title":       title,
 | 
			
		||||
	"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(`
 | 
			
		||||
// generated code - DO NOT EDIT
 | 
			
		||||
{{ range $k, $v := .BuildTags }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,14 +101,14 @@ func (a *volumeAdapter) CachedPath() string {
 | 
			
		|||
	return a.eMount
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *volumeAdapter) Mount() (string, error) {
 | 
			
		||||
func (a *volumeAdapter) Mount(id string) (string, 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
		a.eMount = ""
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,9 +38,9 @@ type volumeDriver interface {
 | 
			
		|||
	// Get the mountpoint of the given volume
 | 
			
		||||
	Path(name string) (mountpoint string, err error)
 | 
			
		||||
	// 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(name string) (err error)
 | 
			
		||||
	Unmount(name, id string) (err error)
 | 
			
		||||
	// List lists all the volumes known to the driver
 | 
			
		||||
	List() (volumes list, err error)
 | 
			
		||||
	// Get retrieves the volume with the requested name
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,6 +97,7 @@ func (pp *volumeDriverProxy) Path(name string) (mountpoint string, err error) {
 | 
			
		|||
 | 
			
		||||
type volumeDriverProxyMountRequest struct {
 | 
			
		||||
	Name string
 | 
			
		||||
	ID   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type volumeDriverProxyMountResponse struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -104,13 +105,14 @@ type volumeDriverProxyMountResponse struct {
 | 
			
		|||
	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 (
 | 
			
		||||
		req volumeDriverProxyMountRequest
 | 
			
		||||
		ret volumeDriverProxyMountResponse
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	req.Name = name
 | 
			
		||||
	req.ID = id
 | 
			
		||||
	if err = pp.Call("VolumeDriver.Mount", req, &ret); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -126,19 +128,21 @@ func (pp *volumeDriverProxy) Mount(name string) (mountpoint string, err error) {
 | 
			
		|||
 | 
			
		||||
type volumeDriverProxyUnmountRequest struct {
 | 
			
		||||
	Name string
 | 
			
		||||
	ID   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type volumeDriverProxyUnmountResponse struct {
 | 
			
		||||
	Err string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pp *volumeDriverProxy) Unmount(name string) (err error) {
 | 
			
		||||
func (pp *volumeDriverProxy) Unmount(name string, id string) (err error) {
 | 
			
		||||
	var (
 | 
			
		||||
		req volumeDriverProxyUnmountRequest
 | 
			
		||||
		ret volumeDriverProxyUnmountResponse
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	req.Name = name
 | 
			
		||||
	req.ID = id
 | 
			
		||||
	if err = pp.Call("VolumeDriver.Unmount", req, &ret); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ func TestVolumeRequestError(t *testing.T) {
 | 
			
		|||
		t.Fatalf("Unexpected error: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = driver.Mount("volume")
 | 
			
		||||
	_, err = driver.Mount("volume", "123")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("Expected error, was nil")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ func TestVolumeRequestError(t *testing.T) {
 | 
			
		|||
		t.Fatalf("Unexpected error: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = driver.Unmount("volume")
 | 
			
		||||
	err = driver.Unmount("volume", "123")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("Expected error, was nil")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -287,7 +287,7 @@ func (v *localVolume) Path() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// 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()
 | 
			
		||||
	defer v.m.Unlock()
 | 
			
		||||
	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.
 | 
			
		||||
func (v *localVolume) Unmount() error {
 | 
			
		||||
func (v *localVolume) Unmount(id string) error {
 | 
			
		||||
	v.m.Lock()
 | 
			
		||||
	defer v.m.Unlock()
 | 
			
		||||
	if v.opts != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -181,12 +181,12 @@ func TestCreateWithOpts(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
	v := vol.(*localVolume)
 | 
			
		||||
 | 
			
		||||
	dir, err := v.Mount()
 | 
			
		||||
	dir, err := v.Mount("1234")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := v.Unmount(); err != nil {
 | 
			
		||||
		if err := v.Unmount("1234"); err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
| 
						 | 
				
			
			@ -225,14 +225,14 @@ func TestCreateWithOpts(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// test double mount
 | 
			
		||||
	if _, err := v.Mount(); err != nil {
 | 
			
		||||
	if _, err := v.Mount("1234"); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if v.active.count != 2 {
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
	if v.active.count != 1 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,10 +19,10 @@ func (NoopVolume) DriverName() string { return "noop" }
 | 
			
		|||
func (NoopVolume) Path() string { return "noop" }
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
func (NoopVolume) Unmount() error { return nil }
 | 
			
		||||
func (NoopVolume) Unmount(_ string) error { return nil }
 | 
			
		||||
 | 
			
		||||
// Status proivdes low-level details about the volume
 | 
			
		||||
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" }
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
func (FakeVolume) Unmount() error { return nil }
 | 
			
		||||
func (FakeVolume) Unmount(_ string) error { return nil }
 | 
			
		||||
 | 
			
		||||
// Status proivdes low-level details about the volume
 | 
			
		||||
func (FakeVolume) Status() map[string]interface{} { return nil }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,8 @@ import (
 | 
			
		|||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/pkg/stringid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DefaultDriverName is the driver name used for the driver
 | 
			
		||||
| 
						 | 
				
			
			@ -35,9 +37,9 @@ type Volume interface {
 | 
			
		|||
	Path() string
 | 
			
		||||
	// Mount mounts the volume and returns the absolute path to
 | 
			
		||||
	// 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() error
 | 
			
		||||
	Unmount(id string) error
 | 
			
		||||
	// Status returns low-level status information about a volume
 | 
			
		||||
	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
 | 
			
		||||
	// 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:"-"`
 | 
			
		||||
	// 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
 | 
			
		||||
// configured, or creating the source directory if supplied.
 | 
			
		||||
func (m *MountPoint) Setup() (string, error) {
 | 
			
		||||
	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 _, err := os.Stat(m.Source); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue