Browse code

Merge pull request #34372 from cpuguy83/more_error_handling_for_pluginrm

Ignore exist/not-exist errors on plugin remove

Vincent Demeester authored on 2018/01/26 13:45:17
Showing 2 changed files
... ...
@@ -639,14 +639,10 @@ func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
639 639
 		return errors.Wrap(err, "error unmounting plugin data")
640 640
 	}
641 641
 
642
-	removeDir := pluginDir + "-removing"
643
-	if err := os.Rename(pluginDir, removeDir); err != nil {
644
-		return errors.Wrap(err, "error performing atomic remove of plugin dir")
642
+	if err := atomicRemoveAll(pluginDir); err != nil {
643
+		return err
645 644
 	}
646 645
 
647
-	if err := system.EnsureRemoveAll(removeDir); err != nil {
648
-		return errors.Wrap(err, "error removing plugin dir")
649
-	}
650 646
 	pm.config.Store.Remove(p)
651 647
 	pm.config.LogPluginEvent(id, name, "remove")
652 648
 	pm.publisher.Publish(EventRemove{Plugin: p.PluginObj})
... ...
@@ -835,3 +831,35 @@ func splitConfigRootFSFromTar(in io.ReadCloser, config *[]byte) io.ReadCloser {
835 835
 	}()
836 836
 	return pr
837 837
 }
838
+
839
+func atomicRemoveAll(dir string) error {
840
+	renamed := dir + "-removing"
841
+
842
+	err := os.Rename(dir, renamed)
843
+	switch {
844
+	case os.IsNotExist(err), err == nil:
845
+		// even if `dir` doesn't exist, we can still try and remove `renamed`
846
+	case os.IsExist(err):
847
+		// Some previous remove failed, check if the origin dir exists
848
+		if e := system.EnsureRemoveAll(renamed); e != nil {
849
+			return errors.Wrap(err, "rename target already exists and could not be removed")
850
+		}
851
+		if _, err := os.Stat(dir); os.IsNotExist(err) {
852
+			// origin doesn't exist, nothing left to do
853
+			return nil
854
+		}
855
+
856
+		// attempt to rename again
857
+		if err := os.Rename(dir, renamed); err != nil {
858
+			return errors.Wrap(err, "failed to rename dir for atomic removal")
859
+		}
860
+	default:
861
+		return errors.Wrap(err, "failed to rename dir for atomic removal")
862
+	}
863
+
864
+	if err := system.EnsureRemoveAll(renamed); err != nil {
865
+		os.Rename(renamed, dir)
866
+		return err
867
+	}
868
+	return nil
869
+}
838 870
new file mode 100644
... ...
@@ -0,0 +1,81 @@
0
+package plugin
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"path/filepath"
6
+	"testing"
7
+)
8
+
9
+func TestAtomicRemoveAllNormal(t *testing.T) {
10
+	dir, err := ioutil.TempDir("", "atomic-remove-with-normal")
11
+	if err != nil {
12
+		t.Fatal(err)
13
+	}
14
+	defer os.RemoveAll(dir) // just try to make sure this gets cleaned up
15
+
16
+	if err := atomicRemoveAll(dir); err != nil {
17
+		t.Fatal(err)
18
+	}
19
+
20
+	if _, err := os.Stat(dir); !os.IsNotExist(err) {
21
+		t.Fatalf("dir should be gone: %v", err)
22
+	}
23
+	if _, err := os.Stat(dir + "-removing"); !os.IsNotExist(err) {
24
+		t.Fatalf("dir should be gone: %v", err)
25
+	}
26
+}
27
+
28
+func TestAtomicRemoveAllAlreadyExists(t *testing.T) {
29
+	dir, err := ioutil.TempDir("", "atomic-remove-already-exists")
30
+	if err != nil {
31
+		t.Fatal(err)
32
+	}
33
+	defer os.RemoveAll(dir) // just try to make sure this gets cleaned up
34
+
35
+	if err := os.MkdirAll(dir+"-removing", 0755); err != nil {
36
+		t.Fatal(err)
37
+	}
38
+	defer os.RemoveAll(dir + "-removing")
39
+
40
+	if err := atomicRemoveAll(dir); err != nil {
41
+		t.Fatal(err)
42
+	}
43
+
44
+	if _, err := os.Stat(dir); !os.IsNotExist(err) {
45
+		t.Fatalf("dir should be gone: %v", err)
46
+	}
47
+	if _, err := os.Stat(dir + "-removing"); !os.IsNotExist(err) {
48
+		t.Fatalf("dir should be gone: %v", err)
49
+	}
50
+}
51
+
52
+func TestAtomicRemoveAllNotExist(t *testing.T) {
53
+	if err := atomicRemoveAll("/not-exist"); err != nil {
54
+		t.Fatal(err)
55
+	}
56
+
57
+	dir, err := ioutil.TempDir("", "atomic-remove-already-exists")
58
+	if err != nil {
59
+		t.Fatal(err)
60
+	}
61
+	defer os.RemoveAll(dir) // just try to make sure this gets cleaned up
62
+
63
+	// create the removing dir, but not the "real" one
64
+	foo := filepath.Join(dir, "foo")
65
+	removing := dir + "-removing"
66
+	if err := os.MkdirAll(removing, 0755); err != nil {
67
+		t.Fatal(err)
68
+	}
69
+
70
+	if err := atomicRemoveAll(dir); err != nil {
71
+		t.Fatal(err)
72
+	}
73
+
74
+	if _, err := os.Stat(foo); !os.IsNotExist(err) {
75
+		t.Fatalf("dir should be gone: %v", err)
76
+	}
77
+	if _, err := os.Stat(removing); !os.IsNotExist(err) {
78
+		t.Fatalf("dir should be gone: %v", err)
79
+	}
80
+}