5562: Use static method. Fixes "TypeError: _socket_open() takes exactly 5 arguments...
[arvados.git] / services / keepstore / keepstore_test.go
index b153d6dce27309f3cb692ec0d6a84b724386891a..a6e29f4759e09892f16ec35127acc1adf4212f18 100644 (file)
@@ -52,15 +52,15 @@ func TestGetBlock(t *testing.T) {
 
        // Prepare two test Keep volumes. Our block is stored on the second volume.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
-       vols := KeepVM.Volumes()
+       vols := KeepVM.AllReadable()
        if err := vols[1].Put(TEST_HASH, TEST_BLOCK); err != nil {
                t.Error(err)
        }
 
        // Check that GetBlock returns success.
-       result, err := GetBlock(TEST_HASH)
+       result, err := GetBlock(TEST_HASH, false)
        if err != nil {
                t.Errorf("GetBlock error: %s", err)
        }
@@ -77,10 +77,10 @@ func TestGetBlockMissing(t *testing.T) {
 
        // Create two empty test Keep volumes.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
        // Check that GetBlock returns failure.
-       result, err := GetBlock(TEST_HASH)
+       result, err := GetBlock(TEST_HASH, false)
        if err != NotFoundError {
                t.Errorf("Expected NotFoundError, got %v", result)
        }
@@ -95,13 +95,13 @@ func TestGetBlockCorrupt(t *testing.T) {
 
        // Create two test Keep volumes and store a corrupt block in one.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
-       vols := KeepVM.Volumes()
+       vols := KeepVM.AllReadable()
        vols[0].Put(TEST_HASH, BAD_BLOCK)
 
        // Check that GetBlock returns failure.
-       result, err := GetBlock(TEST_HASH)
+       result, err := GetBlock(TEST_HASH, false)
        if err != DiskHashError {
                t.Errorf("Expected DiskHashError, got %v (buf: %v)", err, result)
        }
@@ -119,15 +119,15 @@ func TestPutBlockOK(t *testing.T) {
 
        // Create two test Keep volumes.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
        // Check that PutBlock stores the data as expected.
        if err := PutBlock(TEST_BLOCK, TEST_HASH); err != nil {
                t.Fatalf("PutBlock: %v", err)
        }
 
-       vols := KeepVM.Volumes()
-       result, err := vols[0].Get(TEST_HASH)
+       vols := KeepVM.AllReadable()
+       result, err := vols[1].Get(TEST_HASH)
        if err != nil {
                t.Fatalf("Volume #0 Get returned error: %v", err)
        }
@@ -146,9 +146,9 @@ func TestPutBlockOneVol(t *testing.T) {
 
        // Create two test Keep volumes, but cripple one of them.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
-       vols := KeepVM.Volumes()
+       vols := KeepVM.AllWritable()
        vols[0].(*MockVolume).Bad = true
 
        // Check that PutBlock stores the data as expected.
@@ -156,7 +156,7 @@ func TestPutBlockOneVol(t *testing.T) {
                t.Fatalf("PutBlock: %v", err)
        }
 
-       result, err := GetBlock(TEST_HASH)
+       result, err := GetBlock(TEST_HASH, false)
        if err != nil {
                t.Fatalf("GetBlock: %v", err)
        }
@@ -176,7 +176,7 @@ func TestPutBlockMD5Fail(t *testing.T) {
 
        // Create two test Keep volumes.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
        // Check that PutBlock returns the expected error when the hash does
        // not match the block.
@@ -185,7 +185,7 @@ func TestPutBlockMD5Fail(t *testing.T) {
        }
 
        // Confirm that GetBlock fails to return anything.
-       if result, err := GetBlock(TEST_HASH); err != NotFoundError {
+       if result, err := GetBlock(TEST_HASH, false); err != NotFoundError {
                t.Errorf("GetBlock succeeded after a corrupt block store (result = %s, err = %v)",
                        string(result), err)
        }
@@ -200,24 +200,24 @@ func TestPutBlockCorrupt(t *testing.T) {
 
        // Create two test Keep volumes.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
        // Store a corrupted block under TEST_HASH.
-       vols := KeepVM.Volumes()
+       vols := KeepVM.AllWritable()
        vols[0].Put(TEST_HASH, BAD_BLOCK)
        if err := PutBlock(TEST_BLOCK, TEST_HASH); err != nil {
                t.Errorf("PutBlock: %v", err)
        }
 
        // The block on disk should now match TEST_BLOCK.
-       if block, err := GetBlock(TEST_HASH); err != nil {
+       if block, err := GetBlock(TEST_HASH, false); err != nil {
                t.Errorf("GetBlock: %v", err)
        } else if bytes.Compare(block, TEST_BLOCK) != 0 {
                t.Errorf("GetBlock returned: '%s'", string(block))
        }
 }
 
-// PutBlockCollision
+// TestPutBlockCollision
 //     PutBlock returns a 400 Collision error when attempting to
 //     store a block that collides with another block on disk.
 //
@@ -231,7 +231,7 @@ func TestPutBlockCollision(t *testing.T) {
 
        // Prepare two test Keep volumes.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
        // Store one block, then attempt to store the other. Confirm that
        // PutBlock reported a CollisionError.
@@ -245,29 +245,64 @@ func TestPutBlockCollision(t *testing.T) {
        }
 }
 
-// ========================================
-// FindKeepVolumes tests.
-// ========================================
-
-// TestFindKeepVolumes
-//     Confirms that FindKeepVolumes finds tmpfs volumes with "/keep"
-//     directories at the top level.
+// TestPutBlockTouchFails
+//     When PutBlock is asked to PUT an existing block, but cannot
+//     modify the timestamp, it should write a second block.
 //
-func TestFindKeepVolumes(t *testing.T) {
-       var tempVols [2]string
-       var err error
+func TestPutBlockTouchFails(t *testing.T) {
+       defer teardown()
 
-       defer func() {
-               for _, path := range tempVols {
-                       os.RemoveAll(path)
-               }
-       }()
+       // Prepare two test Keep volumes.
+       KeepVM = MakeTestVolumeManager(2)
+       defer KeepVM.Close()
+       vols := KeepVM.AllWritable()
 
-       // Create two directories suitable for using as keep volumes.
+       // Store a block and then make the underlying volume bad,
+       // so a subsequent attempt to update the file timestamp
+       // will fail.
+       vols[0].Put(TEST_HASH, BAD_BLOCK)
+       old_mtime, err := vols[0].Mtime(TEST_HASH)
+       if err != nil {
+               t.Fatalf("vols[0].Mtime(%s): %s\n", TEST_HASH, err)
+       }
+
+       // vols[0].Touch will fail on the next call, so the volume
+       // manager will store a copy on vols[1] instead.
+       vols[0].(*MockVolume).Touchable = false
+       if err := PutBlock(TEST_BLOCK, TEST_HASH); err != nil {
+               t.Fatalf("PutBlock: %v", err)
+       }
+       vols[0].(*MockVolume).Touchable = true
+
+       // Now the mtime on the block on vols[0] should be unchanged, and
+       // there should be a copy of the block on vols[1].
+       new_mtime, err := vols[0].Mtime(TEST_HASH)
+       if err != nil {
+               t.Fatalf("vols[0].Mtime(%s): %s\n", TEST_HASH, err)
+       }
+       if !new_mtime.Equal(old_mtime) {
+               t.Errorf("mtime was changed on vols[0]:\nold_mtime = %v\nnew_mtime = %v\n",
+                       old_mtime, new_mtime)
+       }
+       result, err := vols[1].Get(TEST_HASH)
+       if err != nil {
+               t.Fatalf("vols[1]: %v", err)
+       }
+       if bytes.Compare(result, TEST_BLOCK) != 0 {
+               t.Errorf("new block does not match test block\nnew block = %v\n", result)
+       }
+}
+
+func TestDiscoverTmpfs(t *testing.T) {
+       var tempVols [4]string
+       var err error
+
+       // Create some directories suitable for using as keep volumes.
        for i := range tempVols {
                if tempVols[i], err = ioutil.TempDir("", "findvol"); err != nil {
                        t.Fatal(err)
                }
+               defer os.RemoveAll(tempVols[i])
                tempVols[i] = tempVols[i] + "/keep"
                if err = os.Mkdir(tempVols[i], 0755); err != nil {
                        t.Fatal(err)
@@ -275,53 +310,69 @@ func TestFindKeepVolumes(t *testing.T) {
        }
 
        // Set up a bogus PROC_MOUNTS file.
-       if f, err := ioutil.TempFile("", "keeptest"); err == nil {
-               for _, vol := range tempVols {
-                       fmt.Fprintf(f, "tmpfs %s tmpfs opts\n", path.Dir(vol))
+       f, err := ioutil.TempFile("", "keeptest")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.Remove(f.Name())
+       for i, vol := range tempVols {
+               // Add readonly mount points at odd indexes.
+               var opts string
+               switch i % 2 {
+               case 0:
+                       opts = "rw,nosuid,nodev,noexec"
+               case 1:
+                       opts = "nosuid,nodev,noexec,ro"
                }
-               f.Close()
-               PROC_MOUNTS = f.Name()
-
-               // Check that FindKeepVolumes finds the temp volumes.
-               resultVols := FindKeepVolumes()
-               if len(tempVols) != len(resultVols) {
-                       t.Fatalf("set up %d volumes, FindKeepVolumes found %d\n",
-                               len(tempVols), len(resultVols))
+               fmt.Fprintf(f, "tmpfs %s tmpfs %s 0 0\n", path.Dir(vol), opts)
+       }
+       f.Close()
+       PROC_MOUNTS = f.Name()
+
+       var resultVols volumeSet
+       added := resultVols.Discover()
+
+       if added != len(resultVols) {
+               t.Errorf("Discover returned %d, but added %d volumes",
+                       added, len(resultVols))
+       }
+       if added != len(tempVols) {
+               t.Errorf("Discover returned %d but we set up %d volumes",
+                       added, len(tempVols))
+       }
+       for i, tmpdir := range tempVols {
+               if tmpdir != resultVols[i].(*UnixVolume).root {
+                       t.Errorf("Discover returned %s, expected %s\n",
+                               resultVols[i].(*UnixVolume).root, tmpdir)
                }
-               for i := range tempVols {
-                       if tempVols[i] != resultVols[i] {
-                               t.Errorf("FindKeepVolumes returned %s, expected %s\n",
-                                       resultVols[i], tempVols[i])
-                       }
+               if expectReadonly := i % 2 == 1; expectReadonly != resultVols[i].(*UnixVolume).readonly {
+                       t.Errorf("Discover added %s with readonly=%v, should be %v",
+                               tmpdir, !expectReadonly, expectReadonly)
                }
-
-               os.Remove(f.Name())
        }
 }
 
-// TestFindKeepVolumesFail
-//     When no Keep volumes are present, FindKeepVolumes returns an empty slice.
-//
-func TestFindKeepVolumesFail(t *testing.T) {
+func TestDiscoverNone(t *testing.T) {
        defer teardown()
 
        // Set up a bogus PROC_MOUNTS file with no Keep vols.
-       if f, err := ioutil.TempFile("", "keeptest"); err == nil {
-               fmt.Fprintln(f, "rootfs / rootfs opts 0 0")
-               fmt.Fprintln(f, "sysfs /sys sysfs opts 0 0")
-               fmt.Fprintln(f, "proc /proc proc opts 0 0")
-               fmt.Fprintln(f, "udev /dev devtmpfs opts 0 0")
-               fmt.Fprintln(f, "devpts /dev/pts devpts opts 0 0")
-               f.Close()
-               PROC_MOUNTS = f.Name()
-
-               // Check that FindKeepVolumes returns an empty array.
-               resultVols := FindKeepVolumes()
-               if len(resultVols) != 0 {
-                       t.Fatalf("FindKeepVolumes returned %v", resultVols)
-               }
-
-               os.Remove(PROC_MOUNTS)
+       f, err := ioutil.TempFile("", "keeptest")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.Remove(f.Name())
+       fmt.Fprintln(f, "rootfs / rootfs opts 0 0")
+       fmt.Fprintln(f, "sysfs /sys sysfs opts 0 0")
+       fmt.Fprintln(f, "proc /proc proc opts 0 0")
+       fmt.Fprintln(f, "udev /dev devtmpfs opts 0 0")
+       fmt.Fprintln(f, "devpts /dev/pts devpts opts 0 0")
+       f.Close()
+       PROC_MOUNTS = f.Name()
+
+       var resultVols volumeSet
+       added := resultVols.Discover()
+       if added != 0 || len(resultVols) != 0 {
+               t.Fatalf("got %d, %v; expected 0, []", added, resultVols)
        }
 }
 
@@ -334,9 +385,9 @@ func TestIndex(t *testing.T) {
        // Include multiple blocks on different volumes, and
        // some metadata files.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
-       vols := KeepVM.Volumes()
+       vols := KeepVM.AllReadable()
        vols[0].Put(TEST_HASH, TEST_BLOCK)
        vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
        vols[0].Put(TEST_HASH_3, TEST_BLOCK_3)
@@ -372,9 +423,9 @@ func TestNodeStatus(t *testing.T) {
 
        // Set up test Keep volumes with some blocks.
        KeepVM = MakeTestVolumeManager(2)
-       defer func() { KeepVM.Quit() }()
+       defer KeepVM.Close()
 
-       vols := KeepVM.Volumes()
+       vols := KeepVM.AllReadable()
        vols[0].Put(TEST_HASH, TEST_BLOCK)
        vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
 
@@ -402,10 +453,8 @@ func TestNodeStatus(t *testing.T) {
 // Helper functions for unit tests.
 // ========================================
 
-// MakeTestVolumeManager
-//     Creates and returns a RRVolumeManager with the specified number
-//     of MockVolumes.
-//
+// MakeTestVolumeManager returns a RRVolumeManager with the specified
+// number of MockVolumes.
 func MakeTestVolumeManager(num_volumes int) VolumeManager {
        vols := make([]Volume, num_volumes)
        for i := range vols {
@@ -414,9 +463,7 @@ func MakeTestVolumeManager(num_volumes int) VolumeManager {
        return MakeRRVolumeManager(vols)
 }
 
-// teardown
-//     Cleanup to perform after each test.
-//
+// teardown cleans up after each test.
 func teardown() {
        data_manager_token = ""
        enforce_permissions = false