mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Propagate unmount events to the external volume drivers.
Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
		
							parent
							
								
									a867c1b70a
								
							
						
					
					
						commit
						d592778f4a
					
				
					 8 changed files with 217 additions and 57 deletions
				
			
		| 
						 | 
					@ -346,6 +346,8 @@ func (container *Container) cleanup() {
 | 
				
			||||||
	for _, eConfig := range container.execCommands.s {
 | 
						for _, eConfig := range container.execCommands.s {
 | 
				
			||||||
		container.daemon.unregisterExecCommand(eConfig)
 | 
							container.daemon.unregisterExecCommand(eConfig)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						container.UnmountVolumes(true)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (container *Container) KillSig(sig int) error {
 | 
					func (container *Container) KillSig(sig int) error {
 | 
				
			||||||
| 
						 | 
					@ -469,6 +471,7 @@ func (container *Container) Stop(seconds int) error {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -564,16 +567,10 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
 | 
				
			||||||
	if err := container.Mount(); err != nil {
 | 
						if err := container.Mount(); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var paths []string
 | 
					 | 
				
			||||||
	unmount := func() {
 | 
					 | 
				
			||||||
		for _, p := range paths {
 | 
					 | 
				
			||||||
			syscall.Unmount(p, 0)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			// unmount any volumes
 | 
								// unmount any volumes
 | 
				
			||||||
			unmount()
 | 
								container.UnmountVolumes(true)
 | 
				
			||||||
			// unmount the container's rootfs
 | 
								// unmount the container's rootfs
 | 
				
			||||||
			container.Unmount()
 | 
								container.Unmount()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -587,7 +584,6 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		paths = append(paths, dest)
 | 
					 | 
				
			||||||
		if err := mount.Mount(m.Source, dest, "bind", "rbind,ro"); err != nil {
 | 
							if err := mount.Mount(m.Source, dest, "bind", "rbind,ro"); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -618,7 +614,7 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ioutils.NewReadCloserWrapper(archive, func() error {
 | 
						return ioutils.NewReadCloserWrapper(archive, func() error {
 | 
				
			||||||
			err := archive.Close()
 | 
								err := archive.Close()
 | 
				
			||||||
			unmount()
 | 
								container.UnmountVolumes(true)
 | 
				
			||||||
			container.Unmount()
 | 
								container.Unmount()
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}),
 | 
							}),
 | 
				
			||||||
| 
						 | 
					@ -1090,3 +1086,48 @@ func (container *Container) shouldRestart() bool {
 | 
				
			||||||
	return container.hostConfig.RestartPolicy.Name == "always" ||
 | 
						return container.hostConfig.RestartPolicy.Name == "always" ||
 | 
				
			||||||
		(container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
 | 
							(container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (container *Container) UnmountVolumes(forceSyscall bool) error {
 | 
				
			||||||
 | 
						for _, m := range container.MountPoints {
 | 
				
			||||||
 | 
							dest, err := container.GetResourcePath(m.Destination)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if forceSyscall {
 | 
				
			||||||
 | 
								syscall.Unmount(dest, 0)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if m.Volume != nil {
 | 
				
			||||||
 | 
								if err := m.Volume.Unmount(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (container *Container) copyImagePathContent(v volume.Volume, destination string) error {
 | 
				
			||||||
 | 
						rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, destination), container.basefs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = ioutil.ReadDir(rootfs); err != nil {
 | 
				
			||||||
 | 
							if os.IsNotExist(err) {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path, err := v.Mount()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := copyExistingContents(rootfs, path); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return v.Unmount()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ import (
 | 
				
			||||||
	"github.com/docker/docker/image"
 | 
						"github.com/docker/docker/image"
 | 
				
			||||||
	"github.com/docker/docker/pkg/parsers"
 | 
						"github.com/docker/docker/pkg/parsers"
 | 
				
			||||||
	"github.com/docker/docker/pkg/stringid"
 | 
						"github.com/docker/docker/pkg/stringid"
 | 
				
			||||||
	"github.com/docker/docker/pkg/symlink"
 | 
					 | 
				
			||||||
	"github.com/docker/docker/runconfig"
 | 
						"github.com/docker/docker/runconfig"
 | 
				
			||||||
	"github.com/docker/libcontainer/label"
 | 
						"github.com/docker/libcontainer/label"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -120,21 +119,20 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if stat, err := os.Stat(path); err == nil && !stat.IsDir() {
 | 
					
 | 
				
			||||||
 | 
							stat, err := os.Stat(path)
 | 
				
			||||||
 | 
							if err == nil && !stat.IsDir() {
 | 
				
			||||||
			return nil, nil, fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
 | 
								return nil, nil, fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		v, err := createVolume(name, config.VolumeDriver)
 | 
							v, err := createVolume(name, config.VolumeDriver)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, destination), container.basefs)
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err := container.copyImagePathContent(v, destination); err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if path, err = v.Mount(); err != nil {
 | 
					 | 
				
			||||||
			return nil, nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		copyExistingContents(rootfs, path)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		container.addMountPointWithVolume(destination, v, true)
 | 
							container.addMountPointWithVolume(destination, v, true)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,7 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		container.LogEvent("destroy")
 | 
							container.LogEvent("destroy")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if config.RemoveVolume {
 | 
							if config.RemoveVolume {
 | 
				
			||||||
			container.removeMountPoints()
 | 
								container.removeMountPoints()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -440,14 +440,6 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Regression test for #4830
 | 
					 | 
				
			||||||
func (s *DockerSuite) TestRunWithRelativePath(c *check.C) {
 | 
					 | 
				
			||||||
	runCmd := exec.Command(dockerBinary, "run", "-v", "tmp:/other-tmp", "busybox", "true")
 | 
					 | 
				
			||||||
	if _, _, _, err := runCommandWithStdoutStderr(runCmd); err == nil {
 | 
					 | 
				
			||||||
		c.Fatalf("relative path should result in an error")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *DockerSuite) TestRunVolumesMountedAsReadonly(c *check.C) {
 | 
					func (s *DockerSuite) TestRunVolumesMountedAsReadonly(c *check.C) {
 | 
				
			||||||
	cmd := exec.Command(dockerBinary, "run", "-v", "/test:/test:ro", "busybox", "touch", "/test/somefile")
 | 
						cmd := exec.Command(dockerBinary, "run", "-v", "/test:/test:ro", "busybox", "touch", "/test/somefile")
 | 
				
			||||||
	if code, err := runCommand(cmd); err == nil || code == 0 {
 | 
						if code, err := runCommand(cmd); err == nil || code == 0 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/httptest"
 | 
						"net/http/httptest"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,25 +17,40 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	check.Suite(&ExternalVolumeSuite{
 | 
						check.Suite(&DockerExternalVolumeSuite{
 | 
				
			||||||
		ds: &DockerSuite{},
 | 
							ds: &DockerSuite{},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ExternalVolumeSuite struct {
 | 
					type eventCounter struct {
 | 
				
			||||||
 | 
						activations int
 | 
				
			||||||
 | 
						creations   int
 | 
				
			||||||
 | 
						removals    int
 | 
				
			||||||
 | 
						mounts      int
 | 
				
			||||||
 | 
						unmounts    int
 | 
				
			||||||
 | 
						paths       int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DockerExternalVolumeSuite struct {
 | 
				
			||||||
	server *httptest.Server
 | 
						server *httptest.Server
 | 
				
			||||||
	ds     *DockerSuite
 | 
						ds     *DockerSuite
 | 
				
			||||||
 | 
						d      *Daemon
 | 
				
			||||||
 | 
						ec     *eventCounter
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ExternalVolumeSuite) SetUpTest(c *check.C) {
 | 
					func (s *DockerExternalVolumeSuite) SetUpTest(c *check.C) {
 | 
				
			||||||
 | 
						s.d = NewDaemon(c)
 | 
				
			||||||
	s.ds.SetUpTest(c)
 | 
						s.ds.SetUpTest(c)
 | 
				
			||||||
 | 
						s.ec = &eventCounter{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ExternalVolumeSuite) TearDownTest(c *check.C) {
 | 
					func (s *DockerExternalVolumeSuite) TearDownTest(c *check.C) {
 | 
				
			||||||
 | 
						s.d.Stop()
 | 
				
			||||||
	s.ds.TearDownTest(c)
 | 
						s.ds.TearDownTest(c)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
					func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
				
			||||||
	mux := http.NewServeMux()
 | 
						mux := http.NewServeMux()
 | 
				
			||||||
	s.server = httptest.NewServer(mux)
 | 
						s.server = httptest.NewServer(mux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,26 +58,30 @@ func (s *ExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
				
			||||||
		name string
 | 
							name string
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hostVolumePath := func(name string) string {
 | 
					 | 
				
			||||||
		return fmt.Sprintf("/var/lib/docker/volumes/%s", name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
 | 
						mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							s.ec.activations++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
 | 
							w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
 | 
				
			||||||
		fmt.Fprintln(w, `{"Implements": ["VolumeDriver"]}`)
 | 
							fmt.Fprintln(w, `{"Implements": ["VolumeDriver"]}`)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
 | 
						mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							s.ec.creations++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
 | 
							w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
 | 
				
			||||||
		fmt.Fprintln(w, `{}`)
 | 
							fmt.Fprintln(w, `{}`)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mux.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
 | 
						mux.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							s.ec.removals++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
 | 
							w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
 | 
				
			||||||
		fmt.Fprintln(w, `{}`)
 | 
							fmt.Fprintln(w, `{}`)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mux.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
 | 
						mux.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							s.ec.paths++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var pr pluginRequest
 | 
							var pr pluginRequest
 | 
				
			||||||
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
 | 
							if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
 | 
				
			||||||
			http.Error(w, err.Error(), 500)
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
| 
						 | 
					@ -76,6 +94,8 @@ func (s *ExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mux.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
 | 
						mux.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							s.ec.mounts++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var pr pluginRequest
 | 
							var pr pluginRequest
 | 
				
			||||||
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
 | 
							if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
 | 
				
			||||||
			http.Error(w, err.Error(), 500)
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
| 
						 | 
					@ -94,7 +114,9 @@ func (s *ExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
				
			||||||
		fmt.Fprintln(w, fmt.Sprintf("{\"Mountpoint\": \"%s\"}", p))
 | 
							fmt.Fprintln(w, fmt.Sprintf("{\"Mountpoint\": \"%s\"}", p))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mux.HandleFunc("/VolumeDriver.Umount", func(w http.ResponseWriter, r *http.Request) {
 | 
						mux.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							s.ec.unmounts++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var pr pluginRequest
 | 
							var pr pluginRequest
 | 
				
			||||||
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
 | 
							if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
 | 
				
			||||||
			http.Error(w, err.Error(), 500)
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
| 
						 | 
					@ -118,7 +140,7 @@ func (s *ExternalVolumeSuite) SetUpSuite(c *check.C) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ExternalVolumeSuite) TearDownSuite(c *check.C) {
 | 
					func (s *DockerExternalVolumeSuite) TearDownSuite(c *check.C) {
 | 
				
			||||||
	s.server.Close()
 | 
						s.server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
 | 
						if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -126,14 +148,102 @@ func (s *ExternalVolumeSuite) TearDownSuite(c *check.C) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ExternalVolumeSuite) TestStartExternalVolumeDriver(c *check.C) {
 | 
					func (s *DockerExternalVolumeSuite) TestStartExternalNamedVolumeDriver(c *check.C) {
 | 
				
			||||||
	runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", "busybox:latest", "cat", "/tmp/external-volume-test/test")
 | 
						if err := s.d.StartWithBusybox(); err != nil {
 | 
				
			||||||
	out, stderr, exitCode, err := runCommandWithStdoutStderr(runCmd)
 | 
							c.Fatal(err)
 | 
				
			||||||
	if err != nil && exitCode != 0 {
 | 
						}
 | 
				
			||||||
		c.Fatal(out, stderr, err)
 | 
					
 | 
				
			||||||
 | 
						out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", "busybox:latest", "cat", "/tmp/external-volume-test/test")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !strings.Contains(out, s.server.URL) {
 | 
						if !strings.Contains(out, s.server.URL) {
 | 
				
			||||||
		c.Fatalf("External volume mount failed. Output: %s\n", out)
 | 
							c.Fatalf("External volume mount failed. Output: %s\n", out)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := hostVolumePath("external-volume-test")
 | 
				
			||||||
 | 
						_, err = os.Lstat(p)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							c.Fatalf("Expected error checking volume path in host: %s\n", p)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							c.Fatalf("Expected volume path in host to not exist: %s, %v\n", p, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Assert(s.ec.activations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.creations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.removals, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.mounts, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.unmounts, check.Equals, 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DockerExternalVolumeSuite) TestStartExternalVolumeUnnamedDriver(c *check.C) {
 | 
				
			||||||
 | 
						if err := s.d.StartWithBusybox(); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", "busybox:latest", "cat", "/tmp/external-volume-test/test")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !strings.Contains(out, s.server.URL) {
 | 
				
			||||||
 | 
							c.Fatalf("External volume mount failed. Output: %s\n", out)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Assert(s.ec.activations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.creations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.removals, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.mounts, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.unmounts, check.Equals, 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s DockerExternalVolumeSuite) TestStartExternalVolumeDriverVolumesFrom(c *check.C) {
 | 
				
			||||||
 | 
						if err := s.d.StartWithBusybox(); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := s.d.Cmd("run", "-d", "--name", "vol-test1", "-v", "/foo", "--volume-driver", "test-external-volume-driver", "busybox:latest"); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := s.d.Cmd("run", "--rm", "--volumes-from", "vol-test1", "--name", "vol-test2", "busybox", "ls", "/tmp"); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := s.d.Cmd("rm", "-f", "vol-test1"); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Assert(s.ec.activations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.creations, check.Equals, 2)
 | 
				
			||||||
 | 
						c.Assert(s.ec.removals, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.mounts, check.Equals, 2)
 | 
				
			||||||
 | 
						c.Assert(s.ec.unmounts, check.Equals, 2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s DockerExternalVolumeSuite) TestStartExternalVolumeDriverDeleteContainer(c *check.C) {
 | 
				
			||||||
 | 
						if err := s.d.StartWithBusybox(); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := s.d.Cmd("run", "-d", "--name", "vol-test1", "-v", "/foo", "--volume-driver", "test-external-volume-driver", "busybox:latest"); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := s.d.Cmd("rm", "-fv", "vol-test1"); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Assert(s.ec.activations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.creations, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.removals, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.mounts, check.Equals, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.unmounts, check.Equals, 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func hostVolumePath(name string) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("/var/lib/docker/volumes/%s", name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,10 @@ func (a *volumeDriverAdapter) Create(name string) (volume.Volume, error) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &volumeAdapter{a.proxy, name, a.name}, nil
 | 
						return &volumeAdapter{
 | 
				
			||||||
 | 
							proxy:      a.proxy,
 | 
				
			||||||
 | 
							name:       name,
 | 
				
			||||||
 | 
							driverName: a.name}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
 | 
					func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
 | 
				
			||||||
| 
						 | 
					@ -27,6 +30,7 @@ type volumeAdapter struct {
 | 
				
			||||||
	proxy      *volumeDriverProxy
 | 
						proxy      *volumeDriverProxy
 | 
				
			||||||
	name       string
 | 
						name       string
 | 
				
			||||||
	driverName string
 | 
						driverName string
 | 
				
			||||||
 | 
						eMount     string // ephemeral host volume path
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *volumeAdapter) Name() string {
 | 
					func (a *volumeAdapter) Name() string {
 | 
				
			||||||
| 
						 | 
					@ -38,12 +42,17 @@ func (a *volumeAdapter) DriverName() string {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *volumeAdapter) Path() string {
 | 
					func (a *volumeAdapter) Path() string {
 | 
				
			||||||
 | 
						if len(a.eMount) > 0 {
 | 
				
			||||||
 | 
							return a.eMount
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	m, _ := a.proxy.Path(a.name)
 | 
						m, _ := a.proxy.Path(a.name)
 | 
				
			||||||
	return m
 | 
						return m
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *volumeAdapter) Mount() (string, error) {
 | 
					func (a *volumeAdapter) Mount() (string, error) {
 | 
				
			||||||
	return a.proxy.Mount(a.name)
 | 
						var err error
 | 
				
			||||||
 | 
						a.eMount, err = a.proxy.Mount(a.name)
 | 
				
			||||||
 | 
						return a.eMount, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *volumeAdapter) Unmount() error {
 | 
					func (a *volumeAdapter) Unmount() error {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
package volumedrivers
 | 
					package volumedrivers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"github.com/docker/docker/pkg/plugins"
 | 
						"github.com/docker/docker/pkg/plugins"
 | 
				
			||||||
	"github.com/docker/docker/volume"
 | 
						"github.com/docker/docker/volume"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -52,9 +52,9 @@ func Lookup(name string) (volume.Driver, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pl, err := plugins.Get(name, "VolumeDriver")
 | 
						pl, err := plugins.Get(name, "VolumeDriver")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logrus.Errorf("Error: %v", err)
 | 
							return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	d := NewVolumeDriver(name, pl.Client)
 | 
						d := NewVolumeDriver(name, pl.Client)
 | 
				
			||||||
	drivers.extensions[name] = d
 | 
						drivers.extensions[name] = d
 | 
				
			||||||
	return d, nil
 | 
						return d, nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
package volumedrivers
 | 
					package volumedrivers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// currently created by hand. generation tool would generate this like:
 | 
					// currently created by hand. generation tool would generate this like:
 | 
				
			||||||
// $ rpc-gen volume/drivers/api.go VolumeDriver > volume/drivers/proxy.go
 | 
					// $ rpc-gen volume/drivers/api.go VolumeDriver > volume/drivers/proxy.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,9 +23,9 @@ func (pp *volumeDriverProxy) Create(name string) error {
 | 
				
			||||||
	var ret volumeDriverResponse
 | 
						var ret volumeDriverResponse
 | 
				
			||||||
	err := pp.c.Call("VolumeDriver.Create", args, &ret)
 | 
						err := pp.c.Call("VolumeDriver.Create", args, &ret)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return pp.fmtError(name, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret.Err
 | 
						return pp.fmtError(name, ret.Err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pp *volumeDriverProxy) Remove(name string) error {
 | 
					func (pp *volumeDriverProxy) Remove(name string) error {
 | 
				
			||||||
| 
						 | 
					@ -31,27 +33,27 @@ func (pp *volumeDriverProxy) Remove(name string) error {
 | 
				
			||||||
	var ret volumeDriverResponse
 | 
						var ret volumeDriverResponse
 | 
				
			||||||
	err := pp.c.Call("VolumeDriver.Remove", args, &ret)
 | 
						err := pp.c.Call("VolumeDriver.Remove", args, &ret)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return pp.fmtError(name, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret.Err
 | 
						return pp.fmtError(name, ret.Err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pp *volumeDriverProxy) Path(name string) (string, error) {
 | 
					func (pp *volumeDriverProxy) Path(name string) (string, error) {
 | 
				
			||||||
	args := volumeDriverRequest{name}
 | 
						args := volumeDriverRequest{name}
 | 
				
			||||||
	var ret volumeDriverResponse
 | 
						var ret volumeDriverResponse
 | 
				
			||||||
	if err := pp.c.Call("VolumeDriver.Path", args, &ret); err != nil {
 | 
						if err := pp.c.Call("VolumeDriver.Path", args, &ret); err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", pp.fmtError(name, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret.Mountpoint, ret.Err
 | 
						return ret.Mountpoint, pp.fmtError(name, ret.Err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pp *volumeDriverProxy) Mount(name string) (string, error) {
 | 
					func (pp *volumeDriverProxy) Mount(name string) (string, error) {
 | 
				
			||||||
	args := volumeDriverRequest{name}
 | 
						args := volumeDriverRequest{name}
 | 
				
			||||||
	var ret volumeDriverResponse
 | 
						var ret volumeDriverResponse
 | 
				
			||||||
	if err := pp.c.Call("VolumeDriver.Mount", args, &ret); err != nil {
 | 
						if err := pp.c.Call("VolumeDriver.Mount", args, &ret); err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", pp.fmtError(name, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret.Mountpoint, ret.Err
 | 
						return ret.Mountpoint, pp.fmtError(name, ret.Err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pp *volumeDriverProxy) Unmount(name string) error {
 | 
					func (pp *volumeDriverProxy) Unmount(name string) error {
 | 
				
			||||||
| 
						 | 
					@ -59,7 +61,14 @@ func (pp *volumeDriverProxy) Unmount(name string) error {
 | 
				
			||||||
	var ret volumeDriverResponse
 | 
						var ret volumeDriverResponse
 | 
				
			||||||
	err := pp.c.Call("VolumeDriver.Unmount", args, &ret)
 | 
						err := pp.c.Call("VolumeDriver.Unmount", args, &ret)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return pp.fmtError(name, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret.Err
 | 
						return pp.fmtError(name, ret.Err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pp *volumeDriverProxy) fmtError(name string, err error) error {
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Errorf("External volume driver request failed for %s: %v", name, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue