瀏覽代碼

Merge pull request #33120 from cpuguy83/backport_lazy-unmount-volume

[17.03.x] Backport lazy unmount volume
Victor Vieux 8 年之前
父節點
當前提交
4843bdced5

+ 2 - 2
integration-cli/docker_cli_daemon_test.go

@@ -94,8 +94,8 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
 		c.Fatal(err)
 	}
 
-	if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
-		c.Fatal(err)
+	if out, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
+		c.Fatal(err, out)
 	}
 
 	if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil {

+ 30 - 5
volume/local/local.go

@@ -218,6 +218,14 @@ func (r *Root) Remove(v volume.Volume) error {
 		return fmt.Errorf("unknown volume type %T", v)
 	}
 
+	if lv.active.count > 0 {
+		return fmt.Errorf("volume has active mounts")
+	}
+
+	if err := lv.unmount(); err != nil {
+		return err
+	}
+
 	realPath, err := filepath.EvalSymlinks(lv.path)
 	if err != nil {
 		if !os.IsNotExist(err) {
@@ -306,6 +314,7 @@ func (v *localVolume) Path() string {
 }
 
 // Mount implements the localVolume interface, returning the data location.
+// If there are any provided mount options, the resources will be mounted at this point
 func (v *localVolume) Mount(id string) (string, error) {
 	v.m.Lock()
 	defer v.m.Unlock()
@@ -321,19 +330,35 @@ func (v *localVolume) Mount(id string) (string, error) {
 	return v.path, nil
 }
 
-// Umount is for satisfying the localVolume interface and does not do anything in this driver.
+// Unmount dereferences the id, and if it is the last reference will unmount any resources
+// that were previously mounted.
 func (v *localVolume) Unmount(id string) error {
 	v.m.Lock()
 	defer v.m.Unlock()
+
+	// Always decrement the count, even if the unmount fails
+	// Essentially docker doesn't care if this fails, it will send an error, but
+	// ultimately there's nothing that can be done. If we don't decrement the count
+	// this volume can never be removed until a daemon restart occurs.
 	if v.opts != nil {
 		v.active.count--
-		if v.active.count == 0 {
-			if err := mount.Unmount(v.path); err != nil {
-				v.active.count++
+	}
+
+	if v.active.count > 0 {
+		return nil
+	}
+
+	return v.unmount()
+}
+
+func (v *localVolume) unmount() error {
+	if v.opts != nil {
+		if err := unmount(v.path); err != nil {
+			if mounted, mErr := mount.Mounted(v.path); mounted || mErr != nil {
 				return errors.Wrapf(err, "error while unmounting volume path '%s'", v.path)
 			}
-			v.active.mounted = false
 		}
+		v.active.mounted = false
 	}
 	return nil
 }

+ 7 - 0
volume/local/unmount_linux.go

@@ -0,0 +1,7 @@
+package local
+
+import "golang.org/x/sys/unix"
+
+func unmount(path string) error {
+	return unix.Unmount(path, unix.MNT_DETACH)
+}

+ 9 - 0
volume/local/unmount_unix.go

@@ -0,0 +1,9 @@
+// +build !linux,!windows
+
+package local
+
+import "golang.org/x/sys/unix"
+
+func unmount(path string) error {
+	return unix.Unmount(path, 0)
+}

+ 5 - 0
volume/local/unmount_windows.go

@@ -0,0 +1,5 @@
+package local
+
+func unmount(_ string) error {
+	return nil
+}