1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
22 check "gopkg.in/check.v1"
25 type TestableUnixVolume struct {
30 func NewTestableUnixVolume(t TB, serialize bool, readonly bool) *TestableUnixVolume {
31 d, err := ioutil.TempDir("", "volume_test")
35 var locker sync.Locker
37 locker = &sync.Mutex{}
39 return &TestableUnixVolume{
40 UnixVolume: UnixVolume{
49 // PutRaw writes a Keep block directly into a UnixVolume, even if
50 // the volume is readonly.
51 func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
52 defer func(orig bool) {
56 err := v.Put(context.Background(), locator, data)
62 func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
63 err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
69 func (v *TestableUnixVolume) Teardown() {
70 if err := os.RemoveAll(v.Root); err != nil {
75 // serialize = false; readonly = false
76 func TestUnixVolumeWithGenericTests(t *testing.T) {
77 DoGenericVolumeTests(t, func(t TB) TestableVolume {
78 return NewTestableUnixVolume(t, false, false)
82 // serialize = false; readonly = true
83 func TestUnixVolumeWithGenericTestsReadOnly(t *testing.T) {
84 DoGenericVolumeTests(t, func(t TB) TestableVolume {
85 return NewTestableUnixVolume(t, false, true)
89 // serialize = true; readonly = false
90 func TestUnixVolumeWithGenericTestsSerialized(t *testing.T) {
91 DoGenericVolumeTests(t, func(t TB) TestableVolume {
92 return NewTestableUnixVolume(t, true, false)
96 // serialize = false; readonly = false
97 func TestUnixVolumeHandlersWithGenericVolumeTests(t *testing.T) {
98 DoHandlersWithGenericVolumeTests(t, func(t TB) (*RRVolumeManager, []TestableVolume) {
99 vols := make([]Volume, 2)
100 testableUnixVols := make([]TestableVolume, 2)
102 for i := range vols {
103 v := NewTestableUnixVolume(t, false, false)
105 testableUnixVols[i] = v
108 return MakeRRVolumeManager(vols), testableUnixVols
112 func TestReplicationDefault1(t *testing.T) {
117 if err := v.Start(); err != nil {
120 if got := v.Replication(); got != 1 {
121 t.Errorf("Replication() returned %d, expected 1 if no config given", got)
125 func TestGetNotFound(t *testing.T) {
126 v := NewTestableUnixVolume(t, false, false)
128 v.Put(context.Background(), TestHash, TestBlock)
130 buf := make([]byte, BlockSize)
131 n, err := v.Get(context.Background(), TestHash2, buf)
133 case os.IsNotExist(err):
136 t.Errorf("Read should have failed, returned %+q", buf[:n])
138 t.Errorf("Read expected ErrNotExist, got: %s", err)
142 func TestPut(t *testing.T) {
143 v := NewTestableUnixVolume(t, false, false)
146 err := v.Put(context.Background(), TestHash, TestBlock)
150 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
151 if buf, err := ioutil.ReadFile(p); err != nil {
153 } else if bytes.Compare(buf, TestBlock) != 0 {
154 t.Errorf("Write should have stored %s, did store %s",
155 string(TestBlock), string(buf))
159 func TestPutBadVolume(t *testing.T) {
160 v := NewTestableUnixVolume(t, false, false)
163 os.Chmod(v.Root, 000)
164 err := v.Put(context.Background(), TestHash, TestBlock)
166 t.Error("Write should have failed")
170 func TestUnixVolumeReadonly(t *testing.T) {
171 v := NewTestableUnixVolume(t, false, true)
174 v.PutRaw(TestHash, TestBlock)
176 buf := make([]byte, BlockSize)
177 _, err := v.Get(context.Background(), TestHash, buf)
179 t.Errorf("got err %v, expected nil", err)
182 err = v.Put(context.Background(), TestHash, TestBlock)
183 if err != MethodDisabledError {
184 t.Errorf("got err %v, expected MethodDisabledError", err)
187 err = v.Touch(TestHash)
188 if err != MethodDisabledError {
189 t.Errorf("got err %v, expected MethodDisabledError", err)
192 err = v.Trash(TestHash)
193 if err != MethodDisabledError {
194 t.Errorf("got err %v, expected MethodDisabledError", err)
198 func TestIsFull(t *testing.T) {
199 v := NewTestableUnixVolume(t, false, false)
202 fullPath := v.Root + "/full"
203 now := fmt.Sprintf("%d", time.Now().Unix())
204 os.Symlink(now, fullPath)
206 t.Errorf("%s: claims not to be full", v)
210 // Test with an expired /full link.
211 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
212 os.Symlink(expired, fullPath)
214 t.Errorf("%s: should no longer be full", v)
218 func TestNodeStatus(t *testing.T) {
219 v := NewTestableUnixVolume(t, false, false)
222 // Get node status and make a basic sanity check.
223 volinfo := v.Status()
224 if volinfo.MountPoint != v.Root {
225 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.Root)
227 if volinfo.DeviceNum == 0 {
228 t.Errorf("uninitialized device_num in %v", volinfo)
230 if volinfo.BytesFree == 0 {
231 t.Errorf("uninitialized bytes_free in %v", volinfo)
233 if volinfo.BytesUsed == 0 {
234 t.Errorf("uninitialized bytes_used in %v", volinfo)
238 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
239 v := NewTestableUnixVolume(t, false, false)
242 v.Put(context.Background(), TestHash, TestBlock)
243 mockErr := errors.New("Mock error")
244 err := v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
248 t.Errorf("Got %v, expected %v", err, mockErr)
252 func TestUnixVolumeGetFuncFileError(t *testing.T) {
253 v := NewTestableUnixVolume(t, false, false)
257 err := v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
262 t.Errorf("Expected error opening non-existent file")
265 t.Errorf("Worker func should not have been called")
269 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
270 v := NewTestableUnixVolume(t, false, false)
273 v.Put(context.Background(), TestHash, TestBlock)
275 mtx := NewMockMutex()
278 funcCalled := make(chan struct{})
279 go v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
280 funcCalled <- struct{}{}
284 case mtx.AllowLock <- struct{}{}:
286 t.Fatal("Function was called before mutex was acquired")
287 case <-time.After(5 * time.Second):
288 t.Fatal("Timed out before mutex was acquired")
292 case mtx.AllowUnlock <- struct{}{}:
293 t.Fatal("Mutex was released before function was called")
294 case <-time.After(5 * time.Second):
295 t.Fatal("Timed out waiting for funcCalled")
298 case mtx.AllowUnlock <- struct{}{}:
299 case <-time.After(5 * time.Second):
300 t.Fatal("Timed out waiting for getFunc() to release mutex")
304 func TestUnixVolumeCompare(t *testing.T) {
305 v := NewTestableUnixVolume(t, false, false)
308 v.Put(context.Background(), TestHash, TestBlock)
309 err := v.Compare(context.Background(), TestHash, TestBlock)
311 t.Errorf("Got err %q, expected nil", err)
314 err = v.Compare(context.Background(), TestHash, []byte("baddata"))
315 if err != CollisionError {
316 t.Errorf("Got err %q, expected %q", err, CollisionError)
319 v.Put(context.Background(), TestHash, []byte("baddata"))
320 err = v.Compare(context.Background(), TestHash, TestBlock)
321 if err != DiskHashError {
322 t.Errorf("Got err %q, expected %q", err, DiskHashError)
325 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
327 err = v.Compare(context.Background(), TestHash, TestBlock)
328 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
329 t.Errorf("Got err %q, expected %q", err, "permission denied")
333 func TestUnixVolumeContextCancelPut(t *testing.T) {
334 v := NewTestableUnixVolume(t, true, false)
337 ctx, cancel := context.WithCancel(context.Background())
339 time.Sleep(50 * time.Millisecond)
341 time.Sleep(50 * time.Millisecond)
344 err := v.Put(ctx, TestHash, TestBlock)
345 if err != context.Canceled {
346 t.Errorf("Put() returned %s -- expected short read / canceled", err)
350 func TestUnixVolumeContextCancelGet(t *testing.T) {
351 v := NewTestableUnixVolume(t, false, false)
353 bpath := v.blockPath(TestHash)
354 v.PutRaw(TestHash, TestBlock)
356 err := syscall.Mkfifo(bpath, 0600)
358 t.Fatalf("Mkfifo %s: %s", bpath, err)
360 defer os.Remove(bpath)
361 ctx, cancel := context.WithCancel(context.Background())
363 time.Sleep(50 * time.Millisecond)
366 buf := make([]byte, len(TestBlock))
367 n, err := v.Get(ctx, TestHash, buf)
368 if n == len(TestBlock) || err != context.Canceled {
369 t.Errorf("Get() returned %d, %s -- expected short read / canceled", n, err)
373 var _ = check.Suite(&UnixVolumeSuite{})
375 type UnixVolumeSuite struct {
376 volume *TestableUnixVolume
379 func (s *UnixVolumeSuite) TearDownTest(c *check.C) {
385 func (s *UnixVolumeSuite) TestStats(c *check.C) {
386 s.volume = NewTestableUnixVolume(c, false, false)
387 stats := func() string {
388 buf, err := json.Marshal(s.volume.InternalStats())
389 c.Check(err, check.IsNil)
393 c.Check(stats(), check.Matches, `.*"StatOps":0,.*`)
394 c.Check(stats(), check.Matches, `.*"Errors":0,.*`)
396 loc := "acbd18db4cc2f85cedef654fccc4a4d8"
397 _, err := s.volume.Get(context.Background(), loc, make([]byte, 3))
398 c.Check(err, check.NotNil)
399 c.Check(stats(), check.Matches, `.*"StatOps":[^0],.*`)
400 c.Check(stats(), check.Matches, `.*"Errors":[^0],.*`)
401 c.Check(stats(), check.Matches, `.*"\*os\.PathError":[^0].*`)
402 c.Check(stats(), check.Matches, `.*"InBytes":0,.*`)
403 c.Check(stats(), check.Matches, `.*"OpenOps":0,.*`)
404 c.Check(stats(), check.Matches, `.*"CreateOps":0,.*`)
406 err = s.volume.Put(context.Background(), loc, []byte("foo"))
407 c.Check(err, check.IsNil)
408 c.Check(stats(), check.Matches, `.*"OutBytes":3,.*`)
409 c.Check(stats(), check.Matches, `.*"CreateOps":1,.*`)
410 c.Check(stats(), check.Matches, `.*"OpenOps":0,.*`)
411 c.Check(stats(), check.Matches, `.*"UtimesOps":0,.*`)
413 err = s.volume.Touch(loc)
414 c.Check(err, check.IsNil)
415 c.Check(stats(), check.Matches, `.*"FlockOps":1,.*`)
416 c.Check(stats(), check.Matches, `.*"OpenOps":1,.*`)
417 c.Check(stats(), check.Matches, `.*"UtimesOps":1,.*`)
419 _, err = s.volume.Get(context.Background(), loc, make([]byte, 3))
420 c.Check(err, check.IsNil)
421 err = s.volume.Compare(context.Background(), loc, []byte("foo"))
422 c.Check(err, check.IsNil)
423 c.Check(stats(), check.Matches, `.*"InBytes":6,.*`)
424 c.Check(stats(), check.Matches, `.*"OpenOps":3,.*`)
426 err = s.volume.Trash(loc)
427 c.Check(err, check.IsNil)
428 c.Check(stats(), check.Matches, `.*"FlockOps":2,.*`)