Add refcount for MountPoint
Sebastiaan van Stijn authored on 2017/05/10 03:15:41... | ... |
@@ -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) |