Browse code

Merge pull request #32909 from cpuguy83/32907_volume_unmount_on_cp

Add refcount for MountPoint

Sebastiaan van Stijn authored on 2017/05/10 03:15:41
Showing 3 changed files
... ...
@@ -400,21 +400,20 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu
400 400
 func (container *Container) UnmountVolumes(volumeEventLog func(name, action string, attributes map[string]string)) error {
401 401
 	var errors []string
402 402
 	for _, volumeMount := range container.MountPoints {
403
-		// Check if the mountpoint has an ID, this is currently the best way to tell if it's actually mounted
404
-		// TODO(cpuguyh83): there should be a better way to handle this
405
-		if volumeMount.Volume != nil && volumeMount.ID != "" {
406
-			if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
407
-				errors = append(errors, err.Error())
408
-				continue
409
-			}
410
-			volumeMount.ID = ""
403
+		if volumeMount.Volume == nil {
404
+			continue
405
+		}
411 406
 
412
-			attributes := map[string]string{
413
-				"driver":    volumeMount.Volume.DriverName(),
414
-				"container": container.ID,
415
-			}
416
-			volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
407
+		if err := volumeMount.Cleanup(); err != nil {
408
+			errors = append(errors, err.Error())
409
+			continue
410
+		}
411
+
412
+		attributes := map[string]string{
413
+			"driver":    volumeMount.Volume.DriverName(),
414
+			"container": container.ID,
417 415
 		}
416
+		volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
418 417
 	}
419 418
 	if len(errors) > 0 {
420 419
 		return fmt.Errorf("error while unmounting volumes for container %s: %s", container.ID, strings.Join(errors, "; "))
... ...
@@ -616,3 +616,18 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c
616 616
 	out, _ = s.d.Cmd("run", "-w", "/foo", "-v", "testumount:/foo", "busybox", "true")
617 617
 	c.Assert(s.ec.unmounts, checker.Equals, 0, check.Commentf(out))
618 618
 }
619
+
620
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnCp(c *check.C) {
621
+	s.d.StartWithBusybox(c)
622
+	s.d.Cmd("volume", "create", "-d", "test-external-volume-driver", "--name=test")
623
+
624
+	out, _ := s.d.Cmd("run", "-d", "--name=test", "-v", "test:/foo", "busybox", "/bin/sh", "-c", "touch /test && top")
625
+	c.Assert(s.ec.mounts, checker.Equals, 1, check.Commentf(out))
626
+
627
+	out, _ = s.d.Cmd("cp", "test:/test", "/tmp/test")
628
+	c.Assert(s.ec.mounts, checker.Equals, 2, check.Commentf(out))
629
+	c.Assert(s.ec.unmounts, checker.Equals, 1, check.Commentf(out))
630
+
631
+	out, _ = s.d.Cmd("kill", "test")
632
+	c.Assert(s.ec.unmounts, checker.Equals, 2, check.Commentf(out))
633
+}
... ...
@@ -120,6 +120,28 @@ type MountPoint struct {
120 120
 
121 121
 	// Sepc is a copy of the API request that created this mount.
122 122
 	Spec mounttypes.Mount
123
+
124
+	// Track usage of this mountpoint
125
+	// Specicially needed for containers which are running and calls to `docker cp`
126
+	// because both these actions require mounting the volumes.
127
+	active int
128
+}
129
+
130
+// Cleanup frees resources used by the mountpoint
131
+func (m *MountPoint) Cleanup() error {
132
+	if m.Volume == nil || m.ID == "" {
133
+		return nil
134
+	}
135
+
136
+	if err := m.Volume.Unmount(m.ID); err != nil {
137
+		return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name())
138
+	}
139
+
140
+	m.active--
141
+	if m.active == 0 {
142
+		m.ID = ""
143
+	}
144
+	return nil
123 145
 }
124 146
 
125 147
 // Setup sets up a mount point by either mounting the volume if it is
... ...
@@ -147,12 +169,16 @@ func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (path string
147 147
 		if err != nil {
148 148
 			return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
149 149
 		}
150
+
150 151
 		m.ID = id
152
+		m.active++
151 153
 		return path, nil
152 154
 	}
155
+
153 156
 	if len(m.Source) == 0 {
154 157
 		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
155 158
 	}
159
+
156 160
 	// system.MkdirAll() produces an error if m.Source exists and is a file (not a directory),
157 161
 	if m.Type == mounttypes.TypeBind {
158 162
 		// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)