d14d5a465f40a356fe389ece206695744e5d7d9c
[arvados.git] / services / keepstore / volume_generic_test.go
1 package main
2
3 import (
4         "bytes"
5         "os"
6         "regexp"
7         "sort"
8         "strings"
9         "testing"
10         "time"
11 )
12
13 // A TestableVolumeFactory returns a new TestableVolume. The factory
14 // function, and the TestableVolume it returns, can use "t" to write
15 // logs, fail the current test, etc.
16 type TestableVolumeFactory func(t *testing.T) TestableVolume
17
18 // DoGenericVolumeTests runs a set of tests that every TestableVolume
19 // is expected to pass. It calls factory to create a new TestableVolume
20 // for each test case, to avoid leaking state between tests.
21 func DoGenericVolumeTests(t *testing.T, factory TestableVolumeFactory) {
22         testGet(t, factory)
23         testGetNoSuchBlock(t, factory)
24
25         testCompareSameContent(t, factory)
26         testCompareWithDifferentContent(t, factory)
27         testCompareWithBadData(t, factory)
28
29         testPutBlockWithSameContent(t, factory)
30         testPutBlockWithDifferentContent(t, factory)
31         testPutMultipleBlocks(t, factory)
32
33         testPutAndTouch(t, factory)
34         testTouchNoSuchBlock(t, factory)
35
36         testMtimeNoSuchBlock(t, factory)
37
38         testIndexTo(t, factory)
39
40         testDeleteNewBlock(t, factory)
41         testDeleteOldBlock(t, factory)
42         testDeleteNoSuchBlock(t, factory)
43
44         testStatus(t, factory)
45
46         testString(t, factory)
47
48         testUpdateReadOnly(t, factory)
49
50         testGetConcurrent(t, factory)
51         testPutConcurrent(t, factory)
52 }
53
54 // Put a test block, get it and verify content
55 // Test should pass for both writable and read-only volumes
56 func testGet(t *testing.T, factory TestableVolumeFactory) {
57         v := factory(t)
58         defer v.Teardown()
59
60         v.PutRaw(TEST_HASH, TEST_BLOCK)
61
62         buf, err := v.Get(TEST_HASH)
63         if err != nil {
64                 t.Error(err)
65         }
66
67         bufs.Put(buf)
68
69         if bytes.Compare(buf, TEST_BLOCK) != 0 {
70                 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
71         }
72 }
73
74 // Invoke get on a block that does not exist in volume; should result in error
75 // Test should pass for both writable and read-only volumes
76 func testGetNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
77         v := factory(t)
78         defer v.Teardown()
79
80         if _, err := v.Get(TEST_HASH_2); err == nil {
81                 t.Errorf("Expected error while getting non-existing block %v", TEST_HASH_2)
82         }
83 }
84
85 // Put a test block and compare the locator with same content
86 // Test should pass for both writable and read-only volumes
87 func testCompareSameContent(t *testing.T, factory TestableVolumeFactory) {
88         v := factory(t)
89         defer v.Teardown()
90
91         v.PutRaw(TEST_HASH, TEST_BLOCK)
92
93         // Compare the block locator with same content
94         err := v.Compare(TEST_HASH, TEST_BLOCK)
95         if err != nil {
96                 t.Errorf("Got err %q, expected nil", err)
97         }
98 }
99
100 // Put a test block and compare the locator with a different content
101 // Expect error due to collision
102 // Test should pass for both writable and read-only volumes
103 func testCompareWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
104         v := factory(t)
105         defer v.Teardown()
106
107         v.PutRaw(TEST_HASH, TEST_BLOCK)
108
109         // Compare the block locator with different content; collision
110         err := v.Compare(TEST_HASH, []byte("baddata"))
111         if err == nil {
112                 t.Errorf("Expected error due to collision")
113         }
114 }
115
116 // Put a test block with bad data (hash does not match, but Put does not verify)
117 // Compare the locator with good data whose hash matches with locator
118 // Expect error due to corruption.
119 // Test should pass for both writable and read-only volumes
120 func testCompareWithBadData(t *testing.T, factory TestableVolumeFactory) {
121         v := factory(t)
122         defer v.Teardown()
123
124         v.PutRaw(TEST_HASH, []byte("baddata"))
125
126         err := v.Compare(TEST_HASH, TEST_BLOCK)
127         if err == nil {
128                 t.Errorf("Expected error due to corruption")
129         }
130 }
131
132 // Put a block and put again with same content
133 // Test is intended for only writable volumes
134 func testPutBlockWithSameContent(t *testing.T, factory TestableVolumeFactory) {
135         v := factory(t)
136         defer v.Teardown()
137
138         if v.Writable() == false {
139                 return
140         }
141
142         err := v.Put(TEST_HASH, TEST_BLOCK)
143         if err != nil {
144                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
145         }
146
147         err = v.Put(TEST_HASH, TEST_BLOCK)
148         if err != nil {
149                 t.Errorf("Got err putting block second time %q: %q, expected nil", TEST_BLOCK, err)
150         }
151 }
152
153 // Put a block and put again with different content
154 // Test is intended for only writable volumes
155 func testPutBlockWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
156         v := factory(t)
157         defer v.Teardown()
158
159         if v.Writable() == false {
160                 return
161         }
162
163         err := v.Put(TEST_HASH, TEST_BLOCK)
164         if err != nil {
165                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
166         }
167
168         putErr := v.Put(TEST_HASH, TEST_BLOCK_2)
169         buf, getErr := v.Get(TEST_HASH)
170         if putErr == nil {
171                 // Put must not return a nil error unless it has
172                 // overwritten the existing data.
173                 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
174                         t.Errorf("Put succeeded but Get returned %+v, expected %+v", buf, TEST_BLOCK_2)
175                 }
176         } else {
177                 // It is permissible for Put to fail, but it must
178                 // leave us with either the original data, the new
179                 // data, or nothing at all.
180                 if getErr == nil && bytes.Compare(buf, TEST_BLOCK) != 0 && bytes.Compare(buf, TEST_BLOCK_2) != 0 {
181                         t.Errorf("Put failed but Get returned %+v, which is neither %+v nor %+v", buf, TEST_BLOCK, TEST_BLOCK_2)
182                 }
183         }
184         if getErr == nil {
185                 bufs.Put(buf)
186         }
187 }
188
189 // Put and get multiple blocks
190 // Test is intended for only writable volumes
191 func testPutMultipleBlocks(t *testing.T, factory TestableVolumeFactory) {
192         v := factory(t)
193         defer v.Teardown()
194
195         if v.Writable() == false {
196                 return
197         }
198
199         err := v.Put(TEST_HASH, TEST_BLOCK)
200         if err != nil {
201                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
202         }
203
204         err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
205         if err != nil {
206                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_2, err)
207         }
208
209         err = v.Put(TEST_HASH_3, TEST_BLOCK_3)
210         if err != nil {
211                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_3, err)
212         }
213
214         data, err := v.Get(TEST_HASH)
215         if err != nil {
216                 t.Error(err)
217         } else if bytes.Compare(data, TEST_BLOCK) != 0 {
218                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK)
219         }
220         bufs.Put(data)
221
222         data, err = v.Get(TEST_HASH_2)
223         if err != nil {
224                 t.Error(err)
225         } else if bytes.Compare(data, TEST_BLOCK_2) != 0 {
226                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK_2)
227         }
228         bufs.Put(data)
229
230         data, err = v.Get(TEST_HASH_3)
231         if err != nil {
232                 t.Error(err)
233         } else if bytes.Compare(data, TEST_BLOCK_3) != 0 {
234                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK_3)
235         }
236         bufs.Put(data)
237 }
238
239 // testPutAndTouch
240 //   Test that when applying PUT to a block that already exists,
241 //   the block's modification time is updated.
242 // Test is intended for only writable volumes
243 func testPutAndTouch(t *testing.T, factory TestableVolumeFactory) {
244         v := factory(t)
245         defer v.Teardown()
246
247         if v.Writable() == false {
248                 return
249         }
250
251         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
252                 t.Error(err)
253         }
254
255         // We'll verify { t0 < threshold < t1 }, where t0 is the
256         // existing block's timestamp on disk before Put() and t1 is
257         // its timestamp after Put().
258         threshold := time.Now().Add(-time.Second)
259
260         // Set the stored block's mtime far enough in the past that we
261         // can see the difference between "timestamp didn't change"
262         // and "timestamp granularity is too low".
263         v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
264
265         // Make sure v.Mtime() agrees the above Utime really worked.
266         if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
267                 t.Errorf("Setting mtime failed: %v, %v", t0, err)
268         }
269
270         // Write the same block again.
271         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
272                 t.Error(err)
273         }
274
275         // Verify threshold < t1
276         if t1, err := v.Mtime(TEST_HASH); err != nil {
277                 t.Error(err)
278         } else if t1.Before(threshold) {
279                 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
280         }
281 }
282
283 // Touching a non-existing block should result in error.
284 // Test should pass for both writable and read-only volumes
285 func testTouchNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
286         v := factory(t)
287         defer v.Teardown()
288
289         if err := v.Touch(TEST_HASH); err == nil {
290                 t.Error("Expected error when attempted to touch a non-existing block")
291         }
292 }
293
294 // Invoking Mtime on a non-existing block should result in error.
295 // Test should pass for both writable and read-only volumes
296 func testMtimeNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
297         v := factory(t)
298         defer v.Teardown()
299
300         if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
301                 t.Error("Expected error when updating Mtime on a non-existing block")
302         }
303 }
304
305 // Put a few blocks and invoke IndexTo with:
306 // * no prefix
307 // * with a prefix
308 // * with no such prefix
309 // Test should pass for both writable and read-only volumes
310 func testIndexTo(t *testing.T, factory TestableVolumeFactory) {
311         v := factory(t)
312         defer v.Teardown()
313
314         v.PutRaw(TEST_HASH, TEST_BLOCK)
315         v.PutRaw(TEST_HASH_2, TEST_BLOCK_2)
316         v.PutRaw(TEST_HASH_3, TEST_BLOCK_3)
317
318         buf := new(bytes.Buffer)
319         v.IndexTo("", buf)
320         indexRows := strings.Split(string(buf.Bytes()), "\n")
321         sort.Strings(indexRows)
322         sortedIndex := strings.Join(indexRows, "\n")
323         m, err := regexp.MatchString(
324                 `^\n`+TEST_HASH+`\+\d+ \d+\n`+
325                         TEST_HASH_3+`\+\d+ \d+\n`+
326                         TEST_HASH_2+`\+\d+ \d+$`,
327                 sortedIndex)
328         if err != nil {
329                 t.Error(err)
330         } else if !m {
331                 t.Errorf("Got index %q for empty prefix", sortedIndex)
332         }
333
334         for _, prefix := range []string{"f", "f15", "f15ac"} {
335                 buf = new(bytes.Buffer)
336                 v.IndexTo(prefix, buf)
337
338                 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
339                 if err != nil {
340                         t.Error(err)
341                 } else if !m {
342                         t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
343                 }
344         }
345
346         for _, prefix := range []string{"zero", "zip", "zilch"} {
347                 buf = new(bytes.Buffer)
348                 v.IndexTo(prefix, buf)
349                 if err != nil {
350                         t.Errorf("Got error on IndexTo with no such prefix %v", err.Error())
351                 } else if buf.Len() != 0 {
352                         t.Errorf("Expected empty list for IndexTo with no such prefix %s", prefix)
353                 }
354         }
355 }
356
357 // Calling Delete() for a block immediately after writing it (not old enough)
358 // should neither delete the data nor return an error.
359 // Test is intended for only writable volumes
360 func testDeleteNewBlock(t *testing.T, factory TestableVolumeFactory) {
361         v := factory(t)
362         defer v.Teardown()
363
364         if v.Writable() == false {
365                 return
366         }
367
368         v.Put(TEST_HASH, TEST_BLOCK)
369
370         if err := v.Delete(TEST_HASH); err != nil {
371                 t.Error(err)
372         }
373         data, err := v.Get(TEST_HASH)
374         if err != nil {
375                 t.Error(err)
376         } else if bytes.Compare(data, TEST_BLOCK) != 0 {
377                 t.Error("Block still present, but content is incorrect: %+v != %+v", data, TEST_BLOCK)
378         }
379         bufs.Put(data)
380 }
381
382 // Calling Delete() for a block with a timestamp older than
383 // blob_signature_ttl seconds in the past should delete the data.
384 // Test is intended for only writable volumes
385 func testDeleteOldBlock(t *testing.T, factory TestableVolumeFactory) {
386         v := factory(t)
387         defer v.Teardown()
388
389         if v.Writable() == false {
390                 return
391         }
392
393         v.Put(TEST_HASH, TEST_BLOCK)
394         v.TouchWithDate(TEST_HASH, time.Now().Add(-2*blob_signature_ttl*time.Second))
395
396         if err := v.Delete(TEST_HASH); err != nil {
397                 t.Error(err)
398         }
399         if _, err := v.Get(TEST_HASH); err == nil || !os.IsNotExist(err) {
400                 t.Errorf("os.IsNotExist(%v) should have been true", err.Error())
401         }
402 }
403
404 // Calling Delete() for a block that does not exist should result in error.
405 // Test should pass for both writable and read-only volumes
406 func testDeleteNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
407         v := factory(t)
408         defer v.Teardown()
409
410         if err := v.Delete(TEST_HASH_2); err == nil {
411                 t.Errorf("Expected error when attempting to delete a non-existing block")
412         }
413 }
414
415 // Invoke Status and verify that VolumeStatus is returned
416 // Test should pass for both writable and read-only volumes
417 func testStatus(t *testing.T, factory TestableVolumeFactory) {
418         v := factory(t)
419         defer v.Teardown()
420
421         // Get node status and make a basic sanity check.
422         status := v.Status()
423         if status.DeviceNum == 0 {
424                 t.Errorf("uninitialized device_num in %v", status)
425         }
426
427         if status.BytesFree == 0 {
428                 t.Errorf("uninitialized bytes_free in %v", status)
429         }
430
431         if status.BytesUsed == 0 {
432                 t.Errorf("uninitialized bytes_used in %v", status)
433         }
434 }
435
436 // Invoke String for the volume; expect non-empty result
437 // Test should pass for both writable and read-only volumes
438 func testString(t *testing.T, factory TestableVolumeFactory) {
439         v := factory(t)
440         defer v.Teardown()
441
442         if id := v.String(); len(id) == 0 {
443                 t.Error("Got empty string for v.String()")
444         }
445 }
446
447 // Putting, updating, touching, and deleting blocks from a read-only volume result in error.
448 // Test is intended for only read-only volumes
449 func testUpdateReadOnly(t *testing.T, factory TestableVolumeFactory) {
450         v := factory(t)
451         defer v.Teardown()
452
453         if v.Writable() == true {
454                 return
455         }
456
457         v.PutRaw(TEST_HASH, TEST_BLOCK)
458
459         // Get from read-only volume should succeed
460         _, err := v.Get(TEST_HASH)
461         if err != nil {
462                 t.Errorf("got err %v, expected nil", err)
463         }
464
465         // Put a new block to read-only volume should result in error
466         err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
467         if err == nil {
468                 t.Errorf("Expected error when putting block in a read-only volume")
469         }
470         _, err = v.Get(TEST_HASH_2)
471         if err == nil {
472                 t.Errorf("Expected error when getting block whose put in read-only volume failed")
473         }
474
475         // Touch a block in read-only volume should result in error
476         err = v.Touch(TEST_HASH)
477         if err == nil {
478                 t.Errorf("Expected error when touching block in a read-only volume")
479         }
480
481         // Delete a block from a read-only volume should result in error
482         err = v.Delete(TEST_HASH)
483         if err == nil {
484                 t.Errorf("Expected error when deleting block from a read-only volume")
485         }
486
487         // Overwriting an existing block in read-only volume should result in error
488         err = v.Put(TEST_HASH, TEST_BLOCK)
489         if err == nil {
490                 t.Errorf("Expected error when putting block in a read-only volume")
491         }
492 }
493
494 // Launch concurrent Gets
495 // Test should pass for both writable and read-only volumes
496 func testGetConcurrent(t *testing.T, factory TestableVolumeFactory) {
497         v := factory(t)
498         defer v.Teardown()
499
500         v.PutRaw(TEST_HASH, TEST_BLOCK)
501         v.PutRaw(TEST_HASH_2, TEST_BLOCK_2)
502         v.PutRaw(TEST_HASH_3, TEST_BLOCK_3)
503
504         sem := make(chan int)
505         go func(sem chan int) {
506                 buf, err := v.Get(TEST_HASH)
507                 if err != nil {
508                         t.Errorf("err1: %v", err)
509                 }
510                 bufs.Put(buf)
511                 if bytes.Compare(buf, TEST_BLOCK) != 0 {
512                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
513                 }
514                 sem <- 1
515         }(sem)
516
517         go func(sem chan int) {
518                 buf, err := v.Get(TEST_HASH_2)
519                 if err != nil {
520                         t.Errorf("err2: %v", err)
521                 }
522                 bufs.Put(buf)
523                 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
524                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
525                 }
526                 sem <- 1
527         }(sem)
528
529         go func(sem chan int) {
530                 buf, err := v.Get(TEST_HASH_3)
531                 if err != nil {
532                         t.Errorf("err3: %v", err)
533                 }
534                 bufs.Put(buf)
535                 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
536                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
537                 }
538                 sem <- 1
539         }(sem)
540
541         // Wait for all goroutines to finish
542         for done := 0; done < 3; {
543                 done += <-sem
544         }
545 }
546
547 // Launch concurrent Puts
548 // Test is intended for only writable volumes
549 func testPutConcurrent(t *testing.T, factory TestableVolumeFactory) {
550         v := factory(t)
551         defer v.Teardown()
552
553         if v.Writable() == false {
554                 return
555         }
556
557         sem := make(chan int)
558         go func(sem chan int) {
559                 err := v.Put(TEST_HASH, TEST_BLOCK)
560                 if err != nil {
561                         t.Errorf("err1: %v", err)
562                 }
563                 sem <- 1
564         }(sem)
565
566         go func(sem chan int) {
567                 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
568                 if err != nil {
569                         t.Errorf("err2: %v", err)
570                 }
571                 sem <- 1
572         }(sem)
573
574         go func(sem chan int) {
575                 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
576                 if err != nil {
577                         t.Errorf("err3: %v", err)
578                 }
579                 sem <- 1
580         }(sem)
581
582         // Wait for all goroutines to finish
583         for done := 0; done < 3; {
584                 done += <-sem
585         }
586
587         // Double check that we actually wrote the blocks we expected to write.
588         buf, err := v.Get(TEST_HASH)
589         if err != nil {
590                 t.Errorf("Get #1: %v", err)
591         }
592         bufs.Put(buf)
593         if bytes.Compare(buf, TEST_BLOCK) != 0 {
594                 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
595         }
596
597         buf, err = v.Get(TEST_HASH_2)
598         if err != nil {
599                 t.Errorf("Get #2: %v", err)
600         }
601         bufs.Put(buf)
602         if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
603                 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
604         }
605
606         buf, err = v.Get(TEST_HASH_3)
607         if err != nil {
608                 t.Errorf("Get #3: %v", err)
609         }
610         bufs.Put(buf)
611         if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
612                 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
613         }
614 }