Merge branch 'master' into wtsi-hgi-8087-arv-cli-request-body-from-file
[arvados.git] / services / keepstore / volume_generic_test.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/md5"
6         "fmt"
7         "os"
8         "regexp"
9         "sort"
10         "strings"
11         "time"
12
13         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
14 )
15
16 type TB interface {
17         Error(args ...interface{})
18         Errorf(format string, args ...interface{})
19         Fail()
20         FailNow()
21         Failed() bool
22         Fatal(args ...interface{})
23         Fatalf(format string, args ...interface{})
24         Log(args ...interface{})
25         Logf(format string, args ...interface{})
26 }
27
28 // A TestableVolumeFactory returns a new TestableVolume. The factory
29 // function, and the TestableVolume it returns, can use "t" to write
30 // logs, fail the current test, etc.
31 type TestableVolumeFactory func(t TB) TestableVolume
32
33 // DoGenericVolumeTests runs a set of tests that every TestableVolume
34 // is expected to pass. It calls factory to create a new TestableVolume
35 // for each test case, to avoid leaking state between tests.
36 func DoGenericVolumeTests(t TB, factory TestableVolumeFactory) {
37         testGet(t, factory)
38         testGetNoSuchBlock(t, factory)
39
40         testCompareNonexistent(t, factory)
41         testCompareSameContent(t, factory, TestHash, TestBlock)
42         testCompareSameContent(t, factory, EmptyHash, EmptyBlock)
43         testCompareWithCollision(t, factory, TestHash, TestBlock, []byte("baddata"))
44         testCompareWithCollision(t, factory, TestHash, TestBlock, EmptyBlock)
45         testCompareWithCollision(t, factory, EmptyHash, EmptyBlock, TestBlock)
46         testCompareWithCorruptStoredData(t, factory, TestHash, TestBlock, []byte("baddata"))
47         testCompareWithCorruptStoredData(t, factory, TestHash, TestBlock, EmptyBlock)
48         testCompareWithCorruptStoredData(t, factory, EmptyHash, EmptyBlock, []byte("baddata"))
49
50         testPutBlockWithSameContent(t, factory, TestHash, TestBlock)
51         testPutBlockWithSameContent(t, factory, EmptyHash, EmptyBlock)
52         testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], arvadostest.MD5CollisionData[1])
53         testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, EmptyBlock, arvadostest.MD5CollisionData[0])
54         testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], EmptyBlock)
55         testPutBlockWithDifferentContent(t, factory, EmptyHash, EmptyBlock, arvadostest.MD5CollisionData[0])
56         testPutMultipleBlocks(t, factory)
57
58         testPutAndTouch(t, factory)
59         testTouchNoSuchBlock(t, factory)
60
61         testMtimeNoSuchBlock(t, factory)
62
63         testIndexTo(t, factory)
64
65         testDeleteNewBlock(t, factory)
66         testDeleteOldBlock(t, factory)
67         testDeleteNoSuchBlock(t, factory)
68
69         testStatus(t, factory)
70
71         testString(t, factory)
72
73         testUpdateReadOnly(t, factory)
74
75         testGetConcurrent(t, factory)
76         testPutConcurrent(t, factory)
77
78         testPutFullBlock(t, factory)
79
80         testTrashUntrash(t, factory)
81         testTrashEmptyTrashUntrash(t, factory)
82 }
83
84 // Put a test block, get it and verify content
85 // Test should pass for both writable and read-only volumes
86 func testGet(t TB, factory TestableVolumeFactory) {
87         v := factory(t)
88         defer v.Teardown()
89
90         v.PutRaw(TestHash, TestBlock)
91
92         buf := make([]byte, BlockSize)
93         n, err := v.Get(TestHash, buf)
94         if err != nil {
95                 t.Fatal(err)
96         }
97
98         if bytes.Compare(buf[:n], TestBlock) != 0 {
99                 t.Errorf("expected %s, got %s", string(TestBlock), string(buf))
100         }
101 }
102
103 // Invoke get on a block that does not exist in volume; should result in error
104 // Test should pass for both writable and read-only volumes
105 func testGetNoSuchBlock(t TB, factory TestableVolumeFactory) {
106         v := factory(t)
107         defer v.Teardown()
108
109         buf := make([]byte, BlockSize)
110         if _, err := v.Get(TestHash2, buf); err == nil {
111                 t.Errorf("Expected error while getting non-existing block %v", TestHash2)
112         }
113 }
114
115 // Compare() should return os.ErrNotExist if the block does not exist.
116 // Otherwise, writing new data causes CompareAndTouch() to generate
117 // error logs even though everything is working fine.
118 func testCompareNonexistent(t TB, factory TestableVolumeFactory) {
119         v := factory(t)
120         defer v.Teardown()
121
122         err := v.Compare(TestHash, TestBlock)
123         if err != os.ErrNotExist {
124                 t.Errorf("Got err %T %q, expected os.ErrNotExist", err, err)
125         }
126 }
127
128 // Put a test block and compare the locator with same content
129 // Test should pass for both writable and read-only volumes
130 func testCompareSameContent(t TB, factory TestableVolumeFactory, testHash string, testData []byte) {
131         v := factory(t)
132         defer v.Teardown()
133
134         v.PutRaw(testHash, testData)
135
136         // Compare the block locator with same content
137         err := v.Compare(testHash, testData)
138         if err != nil {
139                 t.Errorf("Got err %q, expected nil", err)
140         }
141 }
142
143 // Test behavior of Compare() when stored data matches expected
144 // checksum but differs from new data we need to store. Requires
145 // testHash = md5(testDataA).
146 //
147 // Test should pass for both writable and read-only volumes
148 func testCompareWithCollision(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
149         v := factory(t)
150         defer v.Teardown()
151
152         v.PutRaw(testHash, testDataA)
153
154         // Compare the block locator with different content; collision
155         err := v.Compare(TestHash, testDataB)
156         if err == nil {
157                 t.Errorf("Got err nil, expected error due to collision")
158         }
159 }
160
161 // Test behavior of Compare() when stored data has become
162 // corrupted. Requires testHash = md5(testDataA) != md5(testDataB).
163 //
164 // Test should pass for both writable and read-only volumes
165 func testCompareWithCorruptStoredData(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
166         v := factory(t)
167         defer v.Teardown()
168
169         v.PutRaw(TestHash, testDataB)
170
171         err := v.Compare(testHash, testDataA)
172         if err == nil || err == CollisionError {
173                 t.Errorf("Got err %+v, expected non-collision error", err)
174         }
175 }
176
177 // Put a block and put again with same content
178 // Test is intended for only writable volumes
179 func testPutBlockWithSameContent(t TB, factory TestableVolumeFactory, testHash string, testData []byte) {
180         v := factory(t)
181         defer v.Teardown()
182
183         if v.Writable() == false {
184                 return
185         }
186
187         err := v.Put(testHash, testData)
188         if err != nil {
189                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
190         }
191
192         err = v.Put(testHash, testData)
193         if err != nil {
194                 t.Errorf("Got err putting block second time %q: %q, expected nil", TestBlock, err)
195         }
196 }
197
198 // Put a block and put again with different content
199 // Test is intended for only writable volumes
200 func testPutBlockWithDifferentContent(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
201         v := factory(t)
202         defer v.Teardown()
203
204         if v.Writable() == false {
205                 return
206         }
207
208         v.PutRaw(testHash, testDataA)
209
210         putErr := v.Put(testHash, testDataB)
211         buf := make([]byte, BlockSize)
212         n, getErr := v.Get(testHash, buf)
213         if putErr == nil {
214                 // Put must not return a nil error unless it has
215                 // overwritten the existing data.
216                 if bytes.Compare(buf[:n], testDataB) != 0 {
217                         t.Errorf("Put succeeded but Get returned %+q, expected %+q", buf[:n], testDataB)
218                 }
219         } else {
220                 // It is permissible for Put to fail, but it must
221                 // leave us with either the original data, the new
222                 // data, or nothing at all.
223                 if getErr == nil && bytes.Compare(buf[:n], testDataA) != 0 && bytes.Compare(buf[:n], testDataB) != 0 {
224                         t.Errorf("Put failed but Get returned %+q, which is neither %+q nor %+q", buf[:n], testDataA, testDataB)
225                 }
226         }
227 }
228
229 // Put and get multiple blocks
230 // Test is intended for only writable volumes
231 func testPutMultipleBlocks(t TB, factory TestableVolumeFactory) {
232         v := factory(t)
233         defer v.Teardown()
234
235         if v.Writable() == false {
236                 return
237         }
238
239         err := v.Put(TestHash, TestBlock)
240         if err != nil {
241                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
242         }
243
244         err = v.Put(TestHash2, TestBlock2)
245         if err != nil {
246                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock2, err)
247         }
248
249         err = v.Put(TestHash3, TestBlock3)
250         if err != nil {
251                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock3, err)
252         }
253
254         data := make([]byte, BlockSize)
255         n, err := v.Get(TestHash, data)
256         if err != nil {
257                 t.Error(err)
258         } else {
259                 if bytes.Compare(data[:n], TestBlock) != 0 {
260                         t.Errorf("Block present, but got %+q, expected %+q", data[:n], TestBlock)
261                 }
262         }
263
264         n, err = v.Get(TestHash2, data)
265         if err != nil {
266                 t.Error(err)
267         } else {
268                 if bytes.Compare(data[:n], TestBlock2) != 0 {
269                         t.Errorf("Block present, but got %+q, expected %+q", data[:n], TestBlock2)
270                 }
271         }
272
273         n, err = v.Get(TestHash3, data)
274         if err != nil {
275                 t.Error(err)
276         } else {
277                 if bytes.Compare(data[:n], TestBlock3) != 0 {
278                         t.Errorf("Block present, but to %+q, expected %+q", data[:n], TestBlock3)
279                 }
280         }
281 }
282
283 // testPutAndTouch
284 //   Test that when applying PUT to a block that already exists,
285 //   the block's modification time is updated.
286 // Test is intended for only writable volumes
287 func testPutAndTouch(t TB, factory TestableVolumeFactory) {
288         v := factory(t)
289         defer v.Teardown()
290
291         if v.Writable() == false {
292                 return
293         }
294
295         if err := v.Put(TestHash, TestBlock); err != nil {
296                 t.Error(err)
297         }
298
299         // We'll verify { t0 < threshold < t1 }, where t0 is the
300         // existing block's timestamp on disk before Put() and t1 is
301         // its timestamp after Put().
302         threshold := time.Now().Add(-time.Second)
303
304         // Set the stored block's mtime far enough in the past that we
305         // can see the difference between "timestamp didn't change"
306         // and "timestamp granularity is too low".
307         v.TouchWithDate(TestHash, time.Now().Add(-20*time.Second))
308
309         // Make sure v.Mtime() agrees the above Utime really worked.
310         if t0, err := v.Mtime(TestHash); err != nil || t0.IsZero() || !t0.Before(threshold) {
311                 t.Errorf("Setting mtime failed: %v, %v", t0, err)
312         }
313
314         // Write the same block again.
315         if err := v.Put(TestHash, TestBlock); err != nil {
316                 t.Error(err)
317         }
318
319         // Verify threshold < t1
320         if t1, err := v.Mtime(TestHash); err != nil {
321                 t.Error(err)
322         } else if t1.Before(threshold) {
323                 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
324         }
325 }
326
327 // Touching a non-existing block should result in error.
328 // Test should pass for both writable and read-only volumes
329 func testTouchNoSuchBlock(t TB, factory TestableVolumeFactory) {
330         v := factory(t)
331         defer v.Teardown()
332
333         if err := v.Touch(TestHash); err == nil {
334                 t.Error("Expected error when attempted to touch a non-existing block")
335         }
336 }
337
338 // Invoking Mtime on a non-existing block should result in error.
339 // Test should pass for both writable and read-only volumes
340 func testMtimeNoSuchBlock(t TB, factory TestableVolumeFactory) {
341         v := factory(t)
342         defer v.Teardown()
343
344         if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
345                 t.Error("Expected error when updating Mtime on a non-existing block")
346         }
347 }
348
349 // Put a few blocks and invoke IndexTo with:
350 // * no prefix
351 // * with a prefix
352 // * with no such prefix
353 // Test should pass for both writable and read-only volumes
354 func testIndexTo(t TB, factory TestableVolumeFactory) {
355         v := factory(t)
356         defer v.Teardown()
357
358         v.PutRaw(TestHash, TestBlock)
359         v.PutRaw(TestHash2, TestBlock2)
360         v.PutRaw(TestHash3, TestBlock3)
361
362         // Blocks whose names aren't Keep hashes should be omitted from
363         // index
364         v.PutRaw("fffffffffnotreallyahashfffffffff", nil)
365         v.PutRaw("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", nil)
366         v.PutRaw("f0000000000000000000000000000000f", nil)
367         v.PutRaw("f00", nil)
368
369         buf := new(bytes.Buffer)
370         v.IndexTo("", buf)
371         indexRows := strings.Split(string(buf.Bytes()), "\n")
372         sort.Strings(indexRows)
373         sortedIndex := strings.Join(indexRows, "\n")
374         m, err := regexp.MatchString(
375                 `^\n`+TestHash+`\+\d+ \d+\n`+
376                         TestHash3+`\+\d+ \d+\n`+
377                         TestHash2+`\+\d+ \d+$`,
378                 sortedIndex)
379         if err != nil {
380                 t.Error(err)
381         } else if !m {
382                 t.Errorf("Got index %q for empty prefix", sortedIndex)
383         }
384
385         for _, prefix := range []string{"f", "f15", "f15ac"} {
386                 buf = new(bytes.Buffer)
387                 v.IndexTo(prefix, buf)
388
389                 m, err := regexp.MatchString(`^`+TestHash2+`\+\d+ \d+\n$`, string(buf.Bytes()))
390                 if err != nil {
391                         t.Error(err)
392                 } else if !m {
393                         t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
394                 }
395         }
396
397         for _, prefix := range []string{"zero", "zip", "zilch"} {
398                 buf = new(bytes.Buffer)
399                 v.IndexTo(prefix, buf)
400                 if err != nil {
401                         t.Errorf("Got error on IndexTo with no such prefix %v", err.Error())
402                 } else if buf.Len() != 0 {
403                         t.Errorf("Expected empty list for IndexTo with no such prefix %s", prefix)
404                 }
405         }
406 }
407
408 // Calling Delete() for a block immediately after writing it (not old enough)
409 // should neither delete the data nor return an error.
410 // Test is intended for only writable volumes
411 func testDeleteNewBlock(t TB, factory TestableVolumeFactory) {
412         v := factory(t)
413         defer v.Teardown()
414         blobSignatureTTL = 300 * time.Second
415
416         if v.Writable() == false {
417                 return
418         }
419
420         v.Put(TestHash, TestBlock)
421
422         if err := v.Trash(TestHash); err != nil {
423                 t.Error(err)
424         }
425         data := make([]byte, BlockSize)
426         n, err := v.Get(TestHash, data)
427         if err != nil {
428                 t.Error(err)
429         } else if bytes.Compare(data[:n], TestBlock) != 0 {
430                 t.Errorf("Got data %+q, expected %+q", data[:n], TestBlock)
431         }
432 }
433
434 // Calling Delete() for a block with a timestamp older than
435 // blobSignatureTTL seconds in the past should delete the data.
436 // Test is intended for only writable volumes
437 func testDeleteOldBlock(t TB, factory TestableVolumeFactory) {
438         v := factory(t)
439         defer v.Teardown()
440         blobSignatureTTL = 300 * time.Second
441
442         if v.Writable() == false {
443                 return
444         }
445
446         v.Put(TestHash, TestBlock)
447         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
448
449         if err := v.Trash(TestHash); err != nil {
450                 t.Error(err)
451         }
452         data := make([]byte, BlockSize)
453         if _, err := v.Get(TestHash, data); err == nil || !os.IsNotExist(err) {
454                 t.Errorf("os.IsNotExist(%v) should have been true", err)
455         }
456
457         _, err := v.Mtime(TestHash)
458         if err == nil || !os.IsNotExist(err) {
459                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
460         }
461
462         err = v.Compare(TestHash, TestBlock)
463         if err == nil || !os.IsNotExist(err) {
464                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
465         }
466
467         indexBuf := new(bytes.Buffer)
468         v.IndexTo("", indexBuf)
469         if strings.Contains(string(indexBuf.Bytes()), TestHash) {
470                 t.Fatalf("Found trashed block in IndexTo")
471         }
472
473         err = v.Touch(TestHash)
474         if err == nil || !os.IsNotExist(err) {
475                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
476         }
477 }
478
479 // Calling Delete() for a block that does not exist should result in error.
480 // Test should pass for both writable and read-only volumes
481 func testDeleteNoSuchBlock(t TB, factory TestableVolumeFactory) {
482         v := factory(t)
483         defer v.Teardown()
484
485         if err := v.Trash(TestHash2); err == nil {
486                 t.Errorf("Expected error when attempting to delete a non-existing block")
487         }
488 }
489
490 // Invoke Status and verify that VolumeStatus is returned
491 // Test should pass for both writable and read-only volumes
492 func testStatus(t TB, factory TestableVolumeFactory) {
493         v := factory(t)
494         defer v.Teardown()
495
496         // Get node status and make a basic sanity check.
497         status := v.Status()
498         if status.DeviceNum == 0 {
499                 t.Errorf("uninitialized device_num in %v", status)
500         }
501
502         if status.BytesFree == 0 {
503                 t.Errorf("uninitialized bytes_free in %v", status)
504         }
505
506         if status.BytesUsed == 0 {
507                 t.Errorf("uninitialized bytes_used in %v", status)
508         }
509 }
510
511 // Invoke String for the volume; expect non-empty result
512 // Test should pass for both writable and read-only volumes
513 func testString(t TB, factory TestableVolumeFactory) {
514         v := factory(t)
515         defer v.Teardown()
516
517         if id := v.String(); len(id) == 0 {
518                 t.Error("Got empty string for v.String()")
519         }
520 }
521
522 // Putting, updating, touching, and deleting blocks from a read-only volume result in error.
523 // Test is intended for only read-only volumes
524 func testUpdateReadOnly(t TB, factory TestableVolumeFactory) {
525         v := factory(t)
526         defer v.Teardown()
527
528         if v.Writable() == true {
529                 return
530         }
531
532         v.PutRaw(TestHash, TestBlock)
533         buf := make([]byte, BlockSize)
534
535         // Get from read-only volume should succeed
536         _, err := v.Get(TestHash, buf)
537         if err != nil {
538                 t.Errorf("got err %v, expected nil", err)
539         }
540
541         // Put a new block to read-only volume should result in error
542         err = v.Put(TestHash2, TestBlock2)
543         if err == nil {
544                 t.Errorf("Expected error when putting block in a read-only volume")
545         }
546         _, err = v.Get(TestHash2, buf)
547         if err == nil {
548                 t.Errorf("Expected error when getting block whose put in read-only volume failed")
549         }
550
551         // Touch a block in read-only volume should result in error
552         err = v.Touch(TestHash)
553         if err == nil {
554                 t.Errorf("Expected error when touching block in a read-only volume")
555         }
556
557         // Delete a block from a read-only volume should result in error
558         err = v.Trash(TestHash)
559         if err == nil {
560                 t.Errorf("Expected error when deleting block from a read-only volume")
561         }
562
563         // Overwriting an existing block in read-only volume should result in error
564         err = v.Put(TestHash, TestBlock)
565         if err == nil {
566                 t.Errorf("Expected error when putting block in a read-only volume")
567         }
568 }
569
570 // Launch concurrent Gets
571 // Test should pass for both writable and read-only volumes
572 func testGetConcurrent(t TB, factory TestableVolumeFactory) {
573         v := factory(t)
574         defer v.Teardown()
575
576         v.PutRaw(TestHash, TestBlock)
577         v.PutRaw(TestHash2, TestBlock2)
578         v.PutRaw(TestHash3, TestBlock3)
579
580         sem := make(chan int)
581         go func() {
582                 buf := make([]byte, BlockSize)
583                 n, err := v.Get(TestHash, buf)
584                 if err != nil {
585                         t.Errorf("err1: %v", err)
586                 }
587                 if bytes.Compare(buf[:n], TestBlock) != 0 {
588                         t.Errorf("buf should be %s, is %s", string(TestBlock), string(buf[:n]))
589                 }
590                 sem <- 1
591         }()
592
593         go func() {
594                 buf := make([]byte, BlockSize)
595                 n, err := v.Get(TestHash2, buf)
596                 if err != nil {
597                         t.Errorf("err2: %v", err)
598                 }
599                 if bytes.Compare(buf[:n], TestBlock2) != 0 {
600                         t.Errorf("buf should be %s, is %s", string(TestBlock2), string(buf[:n]))
601                 }
602                 sem <- 1
603         }()
604
605         go func() {
606                 buf := make([]byte, BlockSize)
607                 n, err := v.Get(TestHash3, buf)
608                 if err != nil {
609                         t.Errorf("err3: %v", err)
610                 }
611                 if bytes.Compare(buf[:n], TestBlock3) != 0 {
612                         t.Errorf("buf should be %s, is %s", string(TestBlock3), string(buf[:n]))
613                 }
614                 sem <- 1
615         }()
616
617         // Wait for all goroutines to finish
618         for done := 0; done < 3; done++ {
619                 <-sem
620         }
621 }
622
623 // Launch concurrent Puts
624 // Test is intended for only writable volumes
625 func testPutConcurrent(t TB, factory TestableVolumeFactory) {
626         v := factory(t)
627         defer v.Teardown()
628
629         if v.Writable() == false {
630                 return
631         }
632
633         sem := make(chan int)
634         go func(sem chan int) {
635                 err := v.Put(TestHash, TestBlock)
636                 if err != nil {
637                         t.Errorf("err1: %v", err)
638                 }
639                 sem <- 1
640         }(sem)
641
642         go func(sem chan int) {
643                 err := v.Put(TestHash2, TestBlock2)
644                 if err != nil {
645                         t.Errorf("err2: %v", err)
646                 }
647                 sem <- 1
648         }(sem)
649
650         go func(sem chan int) {
651                 err := v.Put(TestHash3, TestBlock3)
652                 if err != nil {
653                         t.Errorf("err3: %v", err)
654                 }
655                 sem <- 1
656         }(sem)
657
658         // Wait for all goroutines to finish
659         for done := 0; done < 3; done++ {
660                 <-sem
661         }
662
663         // Double check that we actually wrote the blocks we expected to write.
664         buf := make([]byte, BlockSize)
665         n, err := v.Get(TestHash, buf)
666         if err != nil {
667                 t.Errorf("Get #1: %v", err)
668         }
669         if bytes.Compare(buf[:n], TestBlock) != 0 {
670                 t.Errorf("Get #1: expected %s, got %s", string(TestBlock), string(buf[:n]))
671         }
672
673         n, err = v.Get(TestHash2, buf)
674         if err != nil {
675                 t.Errorf("Get #2: %v", err)
676         }
677         if bytes.Compare(buf[:n], TestBlock2) != 0 {
678                 t.Errorf("Get #2: expected %s, got %s", string(TestBlock2), string(buf[:n]))
679         }
680
681         n, err = v.Get(TestHash3, buf)
682         if err != nil {
683                 t.Errorf("Get #3: %v", err)
684         }
685         if bytes.Compare(buf[:n], TestBlock3) != 0 {
686                 t.Errorf("Get #3: expected %s, got %s", string(TestBlock3), string(buf[:n]))
687         }
688 }
689
690 // Write and read back a full size block
691 func testPutFullBlock(t TB, factory TestableVolumeFactory) {
692         v := factory(t)
693         defer v.Teardown()
694
695         if !v.Writable() {
696                 return
697         }
698
699         wdata := make([]byte, BlockSize)
700         wdata[0] = 'a'
701         wdata[BlockSize-1] = 'z'
702         hash := fmt.Sprintf("%x", md5.Sum(wdata))
703         err := v.Put(hash, wdata)
704         if err != nil {
705                 t.Fatal(err)
706         }
707         buf := make([]byte, BlockSize)
708         n, err := v.Get(hash, buf)
709         if err != nil {
710                 t.Error(err)
711         }
712         if bytes.Compare(buf[:n], wdata) != 0 {
713                 t.Error("buf %+q != wdata %+q", buf[:n], wdata)
714         }
715 }
716
717 // With trashLifetime != 0, perform:
718 // Trash an old block - which either raises ErrNotImplemented or succeeds
719 // Untrash -  which either raises ErrNotImplemented or succeeds
720 // Get - which must succeed
721 func testTrashUntrash(t TB, factory TestableVolumeFactory) {
722         v := factory(t)
723         defer v.Teardown()
724         defer func() {
725                 trashLifetime = 0 * time.Second
726         }()
727
728         trashLifetime = 3600 * time.Second
729
730         // put block and backdate it
731         v.PutRaw(TestHash, TestBlock)
732         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
733
734         buf := make([]byte, BlockSize)
735         n, err := v.Get(TestHash, buf)
736         if err != nil {
737                 t.Fatal(err)
738         }
739         if bytes.Compare(buf[:n], TestBlock) != 0 {
740                 t.Errorf("Got data %+q, expected %+q", buf[:n], TestBlock)
741         }
742
743         // Trash
744         err = v.Trash(TestHash)
745         if v.Writable() == false {
746                 if err != MethodDisabledError {
747                         t.Fatal(err)
748                 }
749         } else if err != nil {
750                 if err != ErrNotImplemented {
751                         t.Fatal(err)
752                 }
753         } else {
754                 _, err = v.Get(TestHash, buf)
755                 if err == nil || !os.IsNotExist(err) {
756                         t.Errorf("os.IsNotExist(%v) should have been true", err)
757                 }
758
759                 // Untrash
760                 err = v.Untrash(TestHash)
761                 if err != nil {
762                         t.Fatal(err)
763                 }
764         }
765
766         // Get the block - after trash and untrash sequence
767         n, err = v.Get(TestHash, buf)
768         if err != nil {
769                 t.Fatal(err)
770         }
771         if bytes.Compare(buf[:n], TestBlock) != 0 {
772                 t.Errorf("Got data %+q, expected %+q", buf[:n], TestBlock)
773         }
774 }
775
776 func testTrashEmptyTrashUntrash(t TB, factory TestableVolumeFactory) {
777         v := factory(t)
778         defer v.Teardown()
779         defer func(orig time.Duration) {
780                 trashLifetime = orig
781         }(trashLifetime)
782
783         checkGet := func() error {
784                 buf := make([]byte, BlockSize)
785                 n, err := v.Get(TestHash, buf)
786                 if err != nil {
787                         return err
788                 }
789                 if bytes.Compare(buf[:n], TestBlock) != 0 {
790                         t.Fatalf("Got data %+q, expected %+q", buf[:n], TestBlock)
791                 }
792
793                 _, err = v.Mtime(TestHash)
794                 if err != nil {
795                         return err
796                 }
797
798                 err = v.Compare(TestHash, TestBlock)
799                 if err != nil {
800                         return err
801                 }
802
803                 indexBuf := new(bytes.Buffer)
804                 v.IndexTo("", indexBuf)
805                 if !strings.Contains(string(indexBuf.Bytes()), TestHash) {
806                         return os.ErrNotExist
807                 }
808
809                 return nil
810         }
811
812         // First set: EmptyTrash before reaching the trash deadline.
813
814         trashLifetime = 1 * time.Hour
815
816         v.PutRaw(TestHash, TestBlock)
817         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
818
819         err := checkGet()
820         if err != nil {
821                 t.Fatal(err)
822         }
823
824         // Trash the block
825         err = v.Trash(TestHash)
826         if err == MethodDisabledError || err == ErrNotImplemented {
827                 // Skip the trash tests for read-only volumes, and
828                 // volume types that don't support trashLifetime>0.
829                 return
830         }
831
832         err = checkGet()
833         if err == nil || !os.IsNotExist(err) {
834                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
835         }
836
837         err = v.Touch(TestHash)
838         if err == nil || !os.IsNotExist(err) {
839                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
840         }
841
842         v.EmptyTrash()
843
844         // Even after emptying the trash, we can untrash our block
845         // because the deadline hasn't been reached.
846         err = v.Untrash(TestHash)
847         if err != nil {
848                 t.Fatal(err)
849         }
850
851         err = checkGet()
852         if err != nil {
853                 t.Fatal(err)
854         }
855
856         err = v.Touch(TestHash)
857         if err != nil {
858                 t.Fatal(err)
859         }
860
861         // Because we Touch'ed, need to backdate again for next set of tests
862         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
863
864         // Untrash should fail if the only block in the trash has
865         // already been untrashed.
866         err = v.Untrash(TestHash)
867         if err == nil || !os.IsNotExist(err) {
868                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
869         }
870
871         // The failed Untrash should not interfere with our
872         // already-untrashed copy.
873         err = checkGet()
874         if err != nil {
875                 t.Fatal(err)
876         }
877
878         // Second set: EmptyTrash after the trash deadline has passed.
879
880         trashLifetime = 1 * time.Nanosecond
881
882         err = v.Trash(TestHash)
883         if err != nil {
884                 t.Fatal(err)
885         }
886         err = checkGet()
887         if err == nil || !os.IsNotExist(err) {
888                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
889         }
890
891         // Even though 1ns has passed, we can untrash because we
892         // haven't called EmptyTrash yet.
893         err = v.Untrash(TestHash)
894         if err != nil {
895                 t.Fatal(err)
896         }
897         err = checkGet()
898         if err != nil {
899                 t.Fatal(err)
900         }
901
902         // Trash it again, and this time call EmptyTrash so it really
903         // goes away.
904         // (In Azure volumes, un/trash changes Mtime, so first backdate again)
905         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
906         err = v.Trash(TestHash)
907         err = checkGet()
908         if err == nil || !os.IsNotExist(err) {
909                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
910         }
911         // EmptryTrash
912         v.EmptyTrash()
913
914         // Untrash won't find it
915         err = v.Untrash(TestHash)
916         if err == nil || !os.IsNotExist(err) {
917                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
918         }
919
920         // Get block won't find it
921         err = checkGet()
922         if err == nil || !os.IsNotExist(err) {
923                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
924         }
925
926         // Third set: If the same data block gets written again after
927         // being trashed, and then the trash gets emptied, the newer
928         // un-trashed copy doesn't get deleted along with it.
929
930         v.PutRaw(TestHash, TestBlock)
931         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
932
933         trashLifetime = time.Nanosecond
934         err = v.Trash(TestHash)
935         if err != nil {
936                 t.Fatal(err)
937         }
938         err = checkGet()
939         if err == nil || !os.IsNotExist(err) {
940                 t.Fatalf("os.IsNotExist(%v) should have been true", err)
941         }
942
943         v.PutRaw(TestHash, TestBlock)
944         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
945
946         // EmptyTrash should not delete the untrashed copy.
947         v.EmptyTrash()
948         err = checkGet()
949         if err != nil {
950                 t.Fatal(err)
951         }
952
953         // Fourth set: If the same data block gets trashed twice with
954         // different deadlines A and C, and then the trash is emptied
955         // at intermediate time B (A < B < C), it is still possible to
956         // untrash the block whose deadline is "C".
957
958         v.PutRaw(TestHash, TestBlock)
959         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
960
961         trashLifetime = time.Nanosecond
962         err = v.Trash(TestHash)
963         if err != nil {
964                 t.Fatal(err)
965         }
966
967         v.PutRaw(TestHash, TestBlock)
968         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
969
970         trashLifetime = time.Hour
971         err = v.Trash(TestHash)
972         if err != nil {
973                 t.Fatal(err)
974         }
975
976         // EmptyTrash should not prevent us from recovering the
977         // time.Hour ("C") trash
978         v.EmptyTrash()
979         err = v.Untrash(TestHash)
980         if err != nil {
981                 t.Fatal(err)
982         }
983         err = checkGet()
984         if err != nil {
985                 t.Fatal(err)
986         }
987 }