Ignore stat errors on volume rm.
| ... | ... |
@@ -155,7 +155,7 @@ func (daemon *Daemon) VolumeRm(name string) error {
|
| 155 | 155 |
if err == ErrVolumeInUse {
|
| 156 | 156 |
return fmt.Errorf("Conflict: %v", err)
|
| 157 | 157 |
} |
| 158 |
- return err |
|
| 158 |
+ return fmt.Errorf("Error while removing volume %s: %v", name, err)
|
|
| 159 | 159 |
} |
| 160 | 160 |
return nil |
| 161 | 161 |
} |
| ... | ... |
@@ -126,6 +126,7 @@ func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
|
| 126 | 126 |
func (r *Root) Remove(v volume.Volume) error {
|
| 127 | 127 |
r.m.Lock() |
| 128 | 128 |
defer r.m.Unlock() |
| 129 |
+ |
|
| 129 | 130 |
lv, ok := v.(*localVolume) |
| 130 | 131 |
if !ok {
|
| 131 | 132 |
return errors.New("unknown volume type")
|
| ... | ... |
@@ -133,18 +134,32 @@ func (r *Root) Remove(v volume.Volume) error {
|
| 133 | 133 |
|
| 134 | 134 |
realPath, err := filepath.EvalSymlinks(lv.path) |
| 135 | 135 |
if err != nil {
|
| 136 |
- return err |
|
| 136 |
+ if !os.IsNotExist(err) {
|
|
| 137 |
+ return err |
|
| 138 |
+ } |
|
| 139 |
+ realPath = filepath.Dir(lv.path) |
|
| 137 | 140 |
} |
| 141 |
+ |
|
| 138 | 142 |
if !r.scopedPath(realPath) {
|
| 139 |
- return fmt.Errorf("Unable to remove a directory of out the Docker root: %s", realPath)
|
|
| 143 |
+ return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath)
|
|
| 140 | 144 |
} |
| 141 | 145 |
|
| 142 |
- if err := os.RemoveAll(realPath); err != nil {
|
|
| 146 |
+ if err := removePath(realPath); err != nil {
|
|
| 143 | 147 |
return err |
| 144 | 148 |
} |
| 145 | 149 |
|
| 146 | 150 |
delete(r.volumes, lv.name) |
| 147 |
- return os.RemoveAll(filepath.Dir(lv.path)) |
|
| 151 |
+ return removePath(filepath.Dir(lv.path)) |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func removePath(path string) error {
|
|
| 155 |
+ if err := os.RemoveAll(path); err != nil {
|
|
| 156 |
+ if os.IsNotExist(err) {
|
|
| 157 |
+ return nil |
|
| 158 |
+ } |
|
| 159 |
+ return err |
|
| 160 |
+ } |
|
| 161 |
+ return nil |
|
| 148 | 162 |
} |
| 149 | 163 |
|
| 150 | 164 |
// 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) {
|
| 162 | 162 |
// is under Docker's root and the valid local paths. |
| 163 | 163 |
func (r *Root) scopedPath(realPath string) bool {
|
| 164 | 164 |
// Volumes path for Docker version >= 1.7 |
| 165 |
- if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) {
|
|
| 165 |
+ if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) && realPath != filepath.Join(r.scope, volumesPathName) {
|
|
| 166 | 166 |
return true |
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 | 169 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,81 @@ |
| 0 |
+package local |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io/ioutil" |
|
| 4 |
+ "os" |
|
| 5 |
+ "testing" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func TestRemove(t *testing.T) {
|
|
| 9 |
+ rootDir, err := ioutil.TempDir("", "local-volume-test")
|
|
| 10 |
+ if err != nil {
|
|
| 11 |
+ t.Fatal(err) |
|
| 12 |
+ } |
|
| 13 |
+ defer os.RemoveAll(rootDir) |
|
| 14 |
+ |
|
| 15 |
+ r, err := New(rootDir) |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ t.Fatal(err) |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ vol, err := r.Create("testing", nil)
|
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ t.Fatal(err) |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ if err := r.Remove(vol); err != nil {
|
|
| 26 |
+ t.Fatal(err) |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ vol, err = r.Create("testing2", nil)
|
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ t.Fatal(err) |
|
| 32 |
+ } |
|
| 33 |
+ if err := os.RemoveAll(vol.Path()); err != nil {
|
|
| 34 |
+ t.Fatal(err) |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ if err := r.Remove(vol); err != nil {
|
|
| 38 |
+ t.Fatal(err) |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ if _, err := os.Stat(vol.Path()); err != nil && !os.IsNotExist(err) {
|
|
| 42 |
+ t.Fatal("volume dir not removed")
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ if len(r.List()) != 0 {
|
|
| 46 |
+ t.Fatal("expected there to be no volumes")
|
|
| 47 |
+ } |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func TestInitializeWithVolumes(t *testing.T) {
|
|
| 51 |
+ rootDir, err := ioutil.TempDir("", "local-volume-test")
|
|
| 52 |
+ if err != nil {
|
|
| 53 |
+ t.Fatal(err) |
|
| 54 |
+ } |
|
| 55 |
+ defer os.RemoveAll(rootDir) |
|
| 56 |
+ |
|
| 57 |
+ r, err := New(rootDir) |
|
| 58 |
+ if err != nil {
|
|
| 59 |
+ t.Fatal(err) |
|
| 60 |
+ } |
|
| 61 |
+ |
|
| 62 |
+ vol, err := r.Create("testing", nil)
|
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ t.Fatal(err) |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ r, err = New(rootDir) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ t.Fatal(err) |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ v, err := r.Get(vol.Name()) |
|
| 73 |
+ if err != nil {
|
|
| 74 |
+ t.Fatal(err) |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ if v.Path() != vol.Path() {
|
|
| 78 |
+ t.Fatal("expected to re-initialize root with existing volumes")
|
|
| 79 |
+ } |
|
| 80 |
+} |