When there is an error unmounting a local volume, it is still possible
to call `Remove()` on the volume causing removal of the mounted
resources which is generally not desirable.
This ensures that resources are unmounted before attempting removal.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
... | ... |
@@ -88,8 +88,8 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) { |
88 | 88 |
|
89 | 89 |
s.d.Restart(c) |
90 | 90 |
|
91 |
- if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil { |
|
92 |
- c.Fatal(err) |
|
91 |
+ if out, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil { |
|
92 |
+ c.Fatal(err, out) |
|
93 | 93 |
} |
94 | 94 |
|
95 | 95 |
if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil { |
... | ... |
@@ -218,6 +218,14 @@ func (r *Root) Remove(v volume.Volume) error { |
218 | 218 |
return fmt.Errorf("unknown volume type %T", v) |
219 | 219 |
} |
220 | 220 |
|
221 |
+ if lv.active.count > 0 { |
|
222 |
+ return fmt.Errorf("volume has active mounts") |
|
223 |
+ } |
|
224 |
+ |
|
225 |
+ if err := lv.unmount(); err != nil { |
|
226 |
+ return err |
|
227 |
+ } |
|
228 |
+ |
|
221 | 229 |
realPath, err := filepath.EvalSymlinks(lv.path) |
222 | 230 |
if err != nil { |
223 | 231 |
if !os.IsNotExist(err) { |
... | ... |
@@ -306,6 +314,7 @@ func (v *localVolume) Path() string { |
306 | 306 |
} |
307 | 307 |
|
308 | 308 |
// Mount implements the localVolume interface, returning the data location. |
309 |
+// If there are any provided mount options, the resources will be mounted at this point |
|
309 | 310 |
func (v *localVolume) Mount(id string) (string, error) { |
310 | 311 |
v.m.Lock() |
311 | 312 |
defer v.m.Unlock() |
... | ... |
@@ -321,19 +330,35 @@ func (v *localVolume) Mount(id string) (string, error) { |
321 | 321 |
return v.path, nil |
322 | 322 |
} |
323 | 323 |
|
324 |
-// Umount is for satisfying the localVolume interface and does not do anything in this driver. |
|
324 |
+// Unmount dereferences the id, and if it is the last reference will unmount any resources |
|
325 |
+// that were previously mounted. |
|
325 | 326 |
func (v *localVolume) Unmount(id string) error { |
326 | 327 |
v.m.Lock() |
327 | 328 |
defer v.m.Unlock() |
329 |
+ |
|
330 |
+ // Always decrement the count, even if the unmount fails |
|
331 |
+ // Essentially docker doesn't care if this fails, it will send an error, but |
|
332 |
+ // ultimately there's nothing that can be done. If we don't decrement the count |
|
333 |
+ // this volume can never be removed until a daemon restart occurs. |
|
328 | 334 |
if v.opts != nil { |
329 | 335 |
v.active.count-- |
330 |
- if v.active.count == 0 { |
|
331 |
- if err := mount.Unmount(v.path); err != nil { |
|
332 |
- v.active.count++ |
|
336 |
+ } |
|
337 |
+ |
|
338 |
+ if v.active.count > 0 { |
|
339 |
+ return nil |
|
340 |
+ } |
|
341 |
+ |
|
342 |
+ return v.unmount() |
|
343 |
+} |
|
344 |
+ |
|
345 |
+func (v *localVolume) unmount() error { |
|
346 |
+ if v.opts != nil { |
|
347 |
+ if err := mount.Unmount(v.path); err != nil { |
|
348 |
+ if mounted, mErr := mount.Mounted(v.path); mounted || mErr != nil { |
|
333 | 349 |
return errors.Wrapf(err, "error while unmounting volume path '%s'", v.path) |
334 | 350 |
} |
335 |
- v.active.mounted = false |
|
336 | 351 |
} |
352 |
+ v.active.mounted = false |
|
337 | 353 |
} |
338 | 354 |
return nil |
339 | 355 |
} |