1// +build linux freebsd
2
3package graphtest // import "github.com/docker/docker/daemon/graphdriver/graphtest"
4
5import (
6	"bytes"
7	"io/ioutil"
8	"math/rand"
9	"os"
10	"path"
11	"reflect"
12	"testing"
13	"unsafe"
14
15	"github.com/docker/docker/daemon/graphdriver"
16	"github.com/docker/docker/pkg/stringid"
17	"github.com/docker/docker/quota"
18	units "github.com/docker/go-units"
19	"golang.org/x/sys/unix"
20	"gotest.tools/v3/assert"
21	is "gotest.tools/v3/assert/cmp"
22)
23
24var (
25	drv *Driver
26)
27
28// Driver conforms to graphdriver.Driver interface and
29// contains information such as root and reference count of the number of clients using it.
30// This helps in testing drivers added into the framework.
31type Driver struct {
32	graphdriver.Driver
33	root     string
34	refCount int
35}
36
37func newDriver(t testing.TB, name string, options []string) *Driver {
38	root, err := ioutil.TempDir("", "docker-graphtest-")
39	assert.NilError(t, err)
40
41	assert.NilError(t, os.MkdirAll(root, 0755))
42	d, err := graphdriver.GetDriver(name, nil, graphdriver.Options{DriverOptions: options, Root: root})
43	if err != nil {
44		t.Logf("graphdriver: %v\n", err)
45		if graphdriver.IsDriverNotSupported(err) {
46			t.Skipf("Driver %s not supported", name)
47		}
48		t.Fatal(err)
49	}
50	return &Driver{d, root, 1}
51}
52
53func cleanup(t testing.TB, d *Driver) {
54	if err := drv.Cleanup(); err != nil {
55		t.Fatal(err)
56	}
57	os.RemoveAll(d.root)
58}
59
60// GetDriver create a new driver with given name or return an existing driver with the name updating the reference count.
61func GetDriver(t testing.TB, name string, options ...string) graphdriver.Driver {
62	if drv == nil {
63		drv = newDriver(t, name, options)
64	} else {
65		drv.refCount++
66	}
67	return drv
68}
69
70// PutDriver removes the driver if it is no longer used and updates the reference count.
71func PutDriver(t testing.TB) {
72	if drv == nil {
73		t.Skip("No driver to put!")
74	}
75	drv.refCount--
76	if drv.refCount == 0 {
77		cleanup(t, drv)
78		drv = nil
79	}
80}
81
82// DriverTestCreateEmpty creates a new image and verifies it is empty and the right metadata
83func DriverTestCreateEmpty(t testing.TB, drivername string, driverOptions ...string) {
84	driver := GetDriver(t, drivername, driverOptions...)
85	defer PutDriver(t)
86
87	err := driver.Create("empty", "", nil)
88	assert.NilError(t, err)
89
90	defer func() {
91		assert.NilError(t, driver.Remove("empty"))
92	}()
93
94	if !driver.Exists("empty") {
95		t.Fatal("Newly created image doesn't exist")
96	}
97
98	dir, err := driver.Get("empty", "")
99	assert.NilError(t, err)
100
101	verifyFile(t, dir.Path(), 0755|os.ModeDir, 0, 0)
102
103	// Verify that the directory is empty
104	fis, err := readDir(dir, dir.Path())
105	assert.NilError(t, err)
106	assert.Check(t, is.Len(fis, 0))
107
108	driver.Put("empty")
109}
110
111// DriverTestCreateBase create a base driver and verify.
112func DriverTestCreateBase(t testing.TB, drivername string, driverOptions ...string) {
113	driver := GetDriver(t, drivername, driverOptions...)
114	defer PutDriver(t)
115
116	createBase(t, driver, "Base")
117	defer func() {
118		assert.NilError(t, driver.Remove("Base"))
119	}()
120	verifyBase(t, driver, "Base")
121}
122
123// DriverTestCreateSnap Create a driver and snap and verify.
124func DriverTestCreateSnap(t testing.TB, drivername string, driverOptions ...string) {
125	driver := GetDriver(t, drivername, driverOptions...)
126	defer PutDriver(t)
127
128	createBase(t, driver, "Base")
129	defer func() {
130		assert.NilError(t, driver.Remove("Base"))
131	}()
132
133	err := driver.Create("Snap", "Base", nil)
134	assert.NilError(t, err)
135	defer func() {
136		assert.NilError(t, driver.Remove("Snap"))
137	}()
138
139	verifyBase(t, driver, "Snap")
140}
141
142// DriverTestDeepLayerRead reads a file from a lower layer under a given number of layers
143func DriverTestDeepLayerRead(t testing.TB, layerCount int, drivername string, driverOptions ...string) {
144	driver := GetDriver(t, drivername, driverOptions...)
145	defer PutDriver(t)
146
147	base := stringid.GenerateRandomID()
148	if err := driver.Create(base, "", nil); err != nil {
149		t.Fatal(err)
150	}
151
152	content := []byte("test content")
153	if err := addFile(driver, base, "testfile.txt", content); err != nil {
154		t.Fatal(err)
155	}
156
157	topLayer, err := addManyLayers(driver, base, layerCount)
158	if err != nil {
159		t.Fatal(err)
160	}
161
162	err = checkManyLayers(driver, topLayer, layerCount)
163	if err != nil {
164		t.Fatal(err)
165	}
166
167	if err := checkFile(driver, topLayer, "testfile.txt", content); err != nil {
168		t.Fatal(err)
169	}
170}
171
172// DriverTestDiffApply tests diffing and applying produces the same layer
173func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverOptions ...string) {
174	driver := GetDriver(t, drivername, driverOptions...)
175	defer PutDriver(t)
176	base := stringid.GenerateRandomID()
177	upper := stringid.GenerateRandomID()
178	deleteFile := "file-remove.txt"
179	deleteFileContent := []byte("This file should get removed in upper!")
180	deleteDir := "var/lib"
181
182	if err := driver.Create(base, "", nil); err != nil {
183		t.Fatal(err)
184	}
185
186	if err := addManyFiles(driver, base, fileCount, 3); err != nil {
187		t.Fatal(err)
188	}
189
190	if err := addFile(driver, base, deleteFile, deleteFileContent); err != nil {
191		t.Fatal(err)
192	}
193
194	if err := addDirectory(driver, base, deleteDir); err != nil {
195		t.Fatal(err)
196	}
197
198	if err := driver.Create(upper, base, nil); err != nil {
199		t.Fatal(err)
200	}
201
202	if err := addManyFiles(driver, upper, fileCount, 6); err != nil {
203		t.Fatal(err)
204	}
205
206	if err := removeAll(driver, upper, deleteFile, deleteDir); err != nil {
207		t.Fatal(err)
208	}
209
210	diffSize, err := driver.DiffSize(upper, "")
211	if err != nil {
212		t.Fatal(err)
213	}
214
215	diff := stringid.GenerateRandomID()
216	if err := driver.Create(diff, base, nil); err != nil {
217		t.Fatal(err)
218	}
219
220	if err := checkManyFiles(driver, diff, fileCount, 3); err != nil {
221		t.Fatal(err)
222	}
223
224	if err := checkFile(driver, diff, deleteFile, deleteFileContent); err != nil {
225		t.Fatal(err)
226	}
227
228	arch, err := driver.Diff(upper, base)
229	if err != nil {
230		t.Fatal(err)
231	}
232
233	buf := bytes.NewBuffer(nil)
234	if _, err := buf.ReadFrom(arch); err != nil {
235		t.Fatal(err)
236	}
237	if err := arch.Close(); err != nil {
238		t.Fatal(err)
239	}
240
241	applyDiffSize, err := driver.ApplyDiff(diff, base, bytes.NewReader(buf.Bytes()))
242	if err != nil {
243		t.Fatal(err)
244	}
245
246	if applyDiffSize != diffSize {
247		t.Fatalf("Apply diff size different, got %d, expected %d", applyDiffSize, diffSize)
248	}
249
250	if err := checkManyFiles(driver, diff, fileCount, 6); err != nil {
251		t.Fatal(err)
252	}
253
254	if err := checkFileRemoved(driver, diff, deleteFile); err != nil {
255		t.Fatal(err)
256	}
257
258	if err := checkFileRemoved(driver, diff, deleteDir); err != nil {
259		t.Fatal(err)
260	}
261}
262
263// DriverTestChanges tests computed changes on a layer matches changes made
264func DriverTestChanges(t testing.TB, drivername string, driverOptions ...string) {
265	driver := GetDriver(t, drivername, driverOptions...)
266	defer PutDriver(t)
267	base := stringid.GenerateRandomID()
268	upper := stringid.GenerateRandomID()
269	if err := driver.Create(base, "", nil); err != nil {
270		t.Fatal(err)
271	}
272
273	if err := addManyFiles(driver, base, 20, 3); err != nil {
274		t.Fatal(err)
275	}
276
277	if err := driver.Create(upper, base, nil); err != nil {
278		t.Fatal(err)
279	}
280
281	expectedChanges, err := changeManyFiles(driver, upper, 20, 6)
282	if err != nil {
283		t.Fatal(err)
284	}
285
286	changes, err := driver.Changes(upper, base)
287	if err != nil {
288		t.Fatal(err)
289	}
290
291	if err = checkChanges(expectedChanges, changes); err != nil {
292		t.Fatal(err)
293	}
294}
295
296func writeRandomFile(path string, size uint64) error {
297	buf := make([]int64, size/8)
298
299	r := rand.NewSource(0)
300	for i := range buf {
301		buf[i] = r.Int63()
302	}
303
304	// Cast to []byte
305	header := *(*reflect.SliceHeader)(unsafe.Pointer(&buf))
306	header.Len *= 8
307	header.Cap *= 8
308	data := *(*[]byte)(unsafe.Pointer(&header))
309
310	return ioutil.WriteFile(path, data, 0700)
311}
312
313// DriverTestSetQuota Create a driver and test setting quota.
314func DriverTestSetQuota(t *testing.T, drivername string, required bool) {
315	driver := GetDriver(t, drivername)
316	defer PutDriver(t)
317
318	createBase(t, driver, "Base")
319	createOpts := &graphdriver.CreateOpts{}
320	createOpts.StorageOpt = make(map[string]string, 1)
321	createOpts.StorageOpt["size"] = "50M"
322	layerName := drivername + "Test"
323	if err := driver.CreateReadWrite(layerName, "Base", createOpts); err == quota.ErrQuotaNotSupported && !required {
324		t.Skipf("Quota not supported on underlying filesystem: %v", err)
325	} else if err != nil {
326		t.Fatal(err)
327	}
328
329	mountPath, err := driver.Get(layerName, "")
330	if err != nil {
331		t.Fatal(err)
332	}
333
334	quota := uint64(50 * units.MiB)
335
336	// Try to write a file smaller than quota, and ensure it works
337	err = writeRandomFile(path.Join(mountPath.Path(), "smallfile"), quota/2)
338	if err != nil {
339		t.Fatal(err)
340	}
341	defer os.Remove(path.Join(mountPath.Path(), "smallfile"))
342
343	// Try to write a file bigger than quota. We've already filled up half the quota, so hitting the limit should be easy
344	err = writeRandomFile(path.Join(mountPath.Path(), "bigfile"), quota)
345	if err == nil {
346		t.Fatalf("expected write to fail(), instead had success")
347	}
348	if pathError, ok := err.(*os.PathError); ok && pathError.Err != unix.EDQUOT && pathError.Err != unix.ENOSPC {
349		os.Remove(path.Join(mountPath.Path(), "bigfile"))
350		t.Fatalf("expect write() to fail with %v or %v, got %v", unix.EDQUOT, unix.ENOSPC, pathError.Err)
351	}
352}
353