diff --git a/daemon/delete.go b/daemon/delete.go index f2116bd1d5..19ee408fe0 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -155,7 +155,7 @@ func (daemon *Daemon) VolumeRm(name string) error { if err == ErrVolumeInUse { return fmt.Errorf("Conflict: %v", err) } - return err + return fmt.Errorf("Error while removing volume %s: %v", name, err) } return nil } diff --git a/volume/local/local.go b/volume/local/local.go index dd60ad6828..a255d421a6 100644 --- a/volume/local/local.go +++ b/volume/local/local.go @@ -126,6 +126,7 @@ func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) { func (r *Root) Remove(v volume.Volume) error { r.m.Lock() defer r.m.Unlock() + lv, ok := v.(*localVolume) if !ok { return errors.New("unknown volume type") @@ -133,18 +134,32 @@ func (r *Root) Remove(v volume.Volume) error { realPath, err := filepath.EvalSymlinks(lv.path) if err != nil { - return err - } - if !r.scopedPath(realPath) { - return fmt.Errorf("Unable to remove a directory of out the Docker root: %s", realPath) + if !os.IsNotExist(err) { + return err + } + realPath = filepath.Dir(lv.path) } - if err := os.RemoveAll(realPath); err != nil { + if !r.scopedPath(realPath) { + return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath) + } + + if err := removePath(realPath); err != nil { return err } delete(r.volumes, lv.name) - return os.RemoveAll(filepath.Dir(lv.path)) + return removePath(filepath.Dir(lv.path)) +} + +func removePath(path string) error { + if err := os.RemoveAll(path); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + return nil } // Get looks up the volume for the given name and returns it if found @@ -162,7 +177,7 @@ func (r *Root) Get(name string) (volume.Volume, error) { // is under Docker's root and the valid local paths. func (r *Root) scopedPath(realPath string) bool { // Volumes path for Docker version >= 1.7 - if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) { + if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) && realPath != filepath.Join(r.scope, volumesPathName) { return true } diff --git a/volume/local/local_test.go b/volume/local/local_test.go new file mode 100644 index 0000000000..1e4e1b057b --- /dev/null +++ b/volume/local/local_test.go @@ -0,0 +1,81 @@ +package local + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestRemove(t *testing.T) { + rootDir, err := ioutil.TempDir("", "local-volume-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rootDir) + + r, err := New(rootDir) + if err != nil { + t.Fatal(err) + } + + vol, err := r.Create("testing", nil) + if err != nil { + t.Fatal(err) + } + + if err := r.Remove(vol); err != nil { + t.Fatal(err) + } + + vol, err = r.Create("testing2", nil) + if err != nil { + t.Fatal(err) + } + if err := os.RemoveAll(vol.Path()); err != nil { + t.Fatal(err) + } + + if err := r.Remove(vol); err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(vol.Path()); err != nil && !os.IsNotExist(err) { + t.Fatal("volume dir not removed") + } + + if len(r.List()) != 0 { + t.Fatal("expected there to be no volumes") + } +} + +func TestInitializeWithVolumes(t *testing.T) { + rootDir, err := ioutil.TempDir("", "local-volume-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rootDir) + + r, err := New(rootDir) + if err != nil { + t.Fatal(err) + } + + vol, err := r.Create("testing", nil) + if err != nil { + t.Fatal(err) + } + + r, err = New(rootDir) + if err != nil { + t.Fatal(err) + } + + v, err := r.Get(vol.Name()) + if err != nil { + t.Fatal(err) + } + + if v.Path() != vol.Path() { + t.Fatal("expected to re-initialize root with existing volumes") + } +}