Browse code

graphdriver: Add generic test framework for graph drivers

This adds daemon/graphdriver/graphtest/graphtest which has a few
generic tests for all graph drivers, and then uses these
from the btrs, devicemapper and vfs backends.

I've not yet added the aufs backend, because i can't test that here
atm. It should work though.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)

Alexander Larsson authored on 2014/04/30 21:43:51
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package btrfs
1
+
2
+import (
3
+	"github.com/dotcloud/docker/daemon/graphdriver/graphtest"
4
+	"testing"
5
+)
6
+
7
+// This avoids creating a new driver for each test if all tests are run
8
+// Make sure to put new tests between TestBtrfsSetup and TestBtrfsTeardown
9
+func TestBtrfsSetup(t *testing.T) {
10
+	graphtest.GetDriver(t, "btrfs")
11
+}
12
+
13
+func TestBtrfsCreateEmpty(t *testing.T) {
14
+	graphtest.DriverTestCreateEmpty(t, "btrfs")
15
+}
16
+
17
+func TestBtrfsCreateBase(t *testing.T) {
18
+	graphtest.DriverTestCreateBase(t, "btrfs")
19
+}
20
+
21
+func TestBtrfsCreateSnap(t *testing.T) {
22
+	graphtest.DriverTestCreateSnap(t, "btrfs")
23
+}
24
+
25
+func TestBtrfsTeardown(t *testing.T) {
26
+	graphtest.PutDriver(t)
27
+}
... ...
@@ -5,6 +5,7 @@ package devmapper
5 5
 import (
6 6
 	"fmt"
7 7
 	"github.com/dotcloud/docker/daemon/graphdriver"
8
+	"github.com/dotcloud/docker/daemon/graphdriver/graphtest"
8 9
 	"io/ioutil"
9 10
 	"path"
10 11
 	"runtime"
... ...
@@ -914,3 +915,25 @@ func assertMap(t *testing.T, m map[string]bool, keys ...string) {
914 914
 		t.Fatalf("Unexpected keys: %v", m)
915 915
 	}
916 916
 }
917
+
918
+// This avoids creating a new driver for each test if all tests are run
919
+// Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown
920
+func TestDevmapperSetup(t *testing.T) {
921
+	graphtest.GetDriver(t, "devicemapper")
922
+}
923
+
924
+func TestDevmapperCreateEmpty(t *testing.T) {
925
+	graphtest.DriverTestCreateEmpty(t, "devicemapper")
926
+}
927
+
928
+func TestDevmapperCreateBase(t *testing.T) {
929
+	graphtest.DriverTestCreateBase(t, "devicemapper")
930
+}
931
+
932
+func TestDevmapperCreateSnap(t *testing.T) {
933
+	graphtest.DriverTestCreateSnap(t, "devicemapper")
934
+}
935
+
936
+func TestDevmapperTeardown(t *testing.T) {
937
+	graphtest.PutDriver(t)
938
+}
917 939
new file mode 100644
... ...
@@ -0,0 +1,225 @@
0
+package graphtest
1
+
2
+import (
3
+	"github.com/dotcloud/docker/daemon/graphdriver"
4
+	"io/ioutil"
5
+	"os"
6
+	"path"
7
+	"syscall"
8
+	"testing"
9
+)
10
+
11
+var (
12
+	drv *Driver
13
+)
14
+
15
+type Driver struct {
16
+	graphdriver.Driver
17
+	root     string
18
+	refCount int
19
+}
20
+
21
+func newDriver(t *testing.T, name string) *Driver {
22
+	root, err := ioutil.TempDir("/var/tmp", "docker-graphtest-")
23
+	if err != nil {
24
+		t.Fatal(err)
25
+	}
26
+
27
+	if err := os.MkdirAll(root, 0755); err != nil {
28
+		t.Fatal(err)
29
+	}
30
+
31
+	d, err := graphdriver.GetDriver(name, root)
32
+	if err != nil {
33
+		t.Fatal(err)
34
+	}
35
+	return &Driver{d, root, 1}
36
+}
37
+
38
+func cleanup(t *testing.T, d *Driver) {
39
+	if err := drv.Cleanup(); err != nil {
40
+		t.Fatal(err)
41
+	}
42
+	os.RemoveAll(d.root)
43
+}
44
+
45
+func GetDriver(t *testing.T, name string) graphdriver.Driver {
46
+	if drv == nil {
47
+		drv = newDriver(t, name)
48
+	} else {
49
+		drv.refCount++
50
+	}
51
+	return drv
52
+}
53
+
54
+func PutDriver(t *testing.T) {
55
+	if drv == nil {
56
+		t.Fatal("No driver to put!")
57
+	}
58
+	drv.refCount--
59
+	if drv.refCount == 0 {
60
+		cleanup(t, drv)
61
+		drv = nil
62
+	}
63
+}
64
+
65
+func verifyFile(t *testing.T, path string, mode os.FileMode, uid, gid uint32) {
66
+	fi, err := os.Stat(path)
67
+	if err != nil {
68
+		t.Fatal(err)
69
+	}
70
+
71
+	if fi.Mode()&os.ModeType != mode&os.ModeType {
72
+		t.Fatalf("Expected %s type 0x%x, got 0x%x", path, mode&os.ModeType, fi.Mode()&os.ModeType)
73
+	}
74
+
75
+	if fi.Mode()&os.ModePerm != mode&os.ModePerm {
76
+		t.Fatalf("Expected %s mode %o, got %o", path, mode&os.ModePerm, fi.Mode()&os.ModePerm)
77
+	}
78
+
79
+	if fi.Mode()&os.ModeSticky != mode&os.ModeSticky {
80
+		t.Fatalf("Expected %s sticky 0x%x, got 0x%x", path, mode&os.ModeSticky, fi.Mode()&os.ModeSticky)
81
+	}
82
+
83
+	if fi.Mode()&os.ModeSetuid != mode&os.ModeSetuid {
84
+		t.Fatalf("Expected %s setuid 0x%x, got 0x%x", path, mode&os.ModeSetuid, fi.Mode()&os.ModeSetuid)
85
+	}
86
+
87
+	if fi.Mode()&os.ModeSetgid != mode&os.ModeSetgid {
88
+		t.Fatalf("Expected %s setgid 0x%x, got 0x%x", path, mode&os.ModeSetgid, fi.Mode()&os.ModeSetgid)
89
+	}
90
+
91
+	if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
92
+		if stat.Uid != uid {
93
+			t.Fatal("%s no owned by uid %d", path, uid)
94
+		}
95
+		if stat.Gid != gid {
96
+			t.Fatal("%s not owned by gid %d", path, gid)
97
+		}
98
+	}
99
+
100
+}
101
+
102
+// Creates an new image and verifies it is empty and the right metadata
103
+func DriverTestCreateEmpty(t *testing.T, drivername string) {
104
+	driver := GetDriver(t, drivername)
105
+	defer PutDriver(t)
106
+
107
+	if err := driver.Create("empty", ""); err != nil {
108
+		t.Fatal(err)
109
+	}
110
+
111
+	if !driver.Exists("empty") {
112
+		t.Fatal("Newly created image doesn't exist")
113
+	}
114
+
115
+	dir, err := driver.Get("empty", "")
116
+	if err != nil {
117
+		t.Fatal(err)
118
+	}
119
+
120
+	verifyFile(t, dir, 0755|os.ModeDir, 0, 0)
121
+
122
+	// Verify that the directory is empty
123
+	fis, err := ioutil.ReadDir(dir)
124
+	if err != nil {
125
+		t.Fatal(err)
126
+	}
127
+
128
+	if len(fis) != 0 {
129
+		t.Fatal("New directory not empty")
130
+	}
131
+
132
+	driver.Put("empty")
133
+
134
+	if err := driver.Remove("empty"); err != nil {
135
+		t.Fatal(err)
136
+	}
137
+
138
+}
139
+
140
+func createBase(t *testing.T, driver graphdriver.Driver, name string) {
141
+	// We need to be able to set any perms
142
+	oldmask := syscall.Umask(0)
143
+	defer syscall.Umask(oldmask)
144
+
145
+	if err := driver.Create(name, ""); err != nil {
146
+		t.Fatal(err)
147
+	}
148
+
149
+	dir, err := driver.Get(name, "")
150
+	if err != nil {
151
+		t.Fatal(err)
152
+	}
153
+	defer driver.Put(name)
154
+
155
+	subdir := path.Join(dir, "a subdir")
156
+	if err := os.Mkdir(subdir, 0705|os.ModeSticky); err != nil {
157
+		t.Fatal(err)
158
+	}
159
+	if err := os.Chown(subdir, 1, 2); err != nil {
160
+		t.Fatal(err)
161
+	}
162
+
163
+	file := path.Join(dir, "a file")
164
+	if err := ioutil.WriteFile(file, []byte("Some data"), 0222|os.ModeSetuid); err != nil {
165
+		t.Fatal(err)
166
+	}
167
+}
168
+
169
+func verifyBase(t *testing.T, driver graphdriver.Driver, name string) {
170
+	dir, err := driver.Get(name, "")
171
+	if err != nil {
172
+		t.Fatal(err)
173
+	}
174
+	defer driver.Put(name)
175
+
176
+	subdir := path.Join(dir, "a subdir")
177
+	verifyFile(t, subdir, 0705|os.ModeDir|os.ModeSticky, 1, 2)
178
+
179
+	file := path.Join(dir, "a file")
180
+	verifyFile(t, file, 0222|os.ModeSetuid, 0, 0)
181
+
182
+	fis, err := ioutil.ReadDir(dir)
183
+	if err != nil {
184
+		t.Fatal(err)
185
+	}
186
+
187
+	if len(fis) != 2 {
188
+		t.Fatal("Unexpected files in base image")
189
+	}
190
+
191
+}
192
+
193
+func DriverTestCreateBase(t *testing.T, drivername string) {
194
+	driver := GetDriver(t, drivername)
195
+	defer PutDriver(t)
196
+
197
+	createBase(t, driver, "Base")
198
+	verifyBase(t, driver, "Base")
199
+
200
+	if err := driver.Remove("Base"); err != nil {
201
+		t.Fatal(err)
202
+	}
203
+}
204
+
205
+func DriverTestCreateSnap(t *testing.T, drivername string) {
206
+	driver := GetDriver(t, drivername)
207
+	defer PutDriver(t)
208
+
209
+	createBase(t, driver, "Base")
210
+
211
+	if err := driver.Create("Snap", "Base"); err != nil {
212
+		t.Fatal(err)
213
+	}
214
+
215
+	verifyBase(t, driver, "Snap")
216
+
217
+	if err := driver.Remove("Snap"); err != nil {
218
+		t.Fatal(err)
219
+	}
220
+
221
+	if err := driver.Remove("Base"); err != nil {
222
+		t.Fatal(err)
223
+	}
224
+}
0 225
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package vfs
1
+
2
+import (
3
+	"github.com/dotcloud/docker/daemon/graphdriver/graphtest"
4
+	"testing"
5
+)
6
+
7
+// This avoids creating a new driver for each test if all tests are run
8
+// Make sure to put new tests between TestVfsSetup and TestVfsTeardown
9
+func TestVfsSetup(t *testing.T) {
10
+	graphtest.GetDriver(t, "vfs")
11
+}
12
+
13
+func TestVfsCreateEmpty(t *testing.T) {
14
+	graphtest.DriverTestCreateEmpty(t, "vfs")
15
+}
16
+
17
+func TestVfsCreateBase(t *testing.T) {
18
+	graphtest.DriverTestCreateBase(t, "vfs")
19
+}
20
+
21
+func TestVfsCreateSnap(t *testing.T) {
22
+	graphtest.DriverTestCreateSnap(t, "vfs")
23
+}
24
+
25
+func TestVfsTeardown(t *testing.T) {
26
+	graphtest.PutDriver(t)
27
+}