зеркало из https://github.com/microsoft/docker.git
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:
Родитель
24a8de2b60
Коммит
2b6bc294fc
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче