"strings"
"sync"
"time"
+
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+ "github.com/sirupsen/logrus"
+)
+
+var (
+ TestBlock = []byte("The quick brown fox jumps over the lazy dog.")
+ TestHash = "e4d909c290d0fb1ca068ffaddf22cbd0"
+ TestHashPutResp = "e4d909c290d0fb1ca068ffaddf22cbd0+44\n"
+
+ TestBlock2 = []byte("Pack my box with five dozen liquor jugs.")
+ TestHash2 = "f15ac516f788aec4f30932ffb6395c39"
+
+ TestBlock3 = []byte("Now is the time for all good men to come to the aid of their country.")
+ TestHash3 = "eed29bbffbc2dbe5e5ee0bb71888e61f"
+
+ // BadBlock is used to test collisions and corruption.
+ // It must not match any test hashes.
+ BadBlock = []byte("The magic words are squeamish ossifrage.")
+
+ EmptyHash = "d41d8cd98f00b204e9800998ecf8427e"
+ EmptyBlock = []byte("")
)
// A TestableVolume allows test suites to manipulate the state of an
// impractical to achieve with a sequence of normal Volume operations.
type TestableVolume interface {
Volume
+
// [Over]write content for a locator with the given data,
// bypassing all constraints like readonly and serialize.
PutRaw(locator string, data []byte)
+ // Returns the strings that a driver uses to record read/write operations.
+ ReadWriteOperationLabelValues() (r, w string)
+
// Specify the value Mtime() should return, until the next
// call to Touch, TouchWithDate, or Put.
TouchWithDate(locator string, lastPut time.Time)
Teardown()
}
+func init() {
+ driver["mock"] = newMockVolume
+}
+
// MockVolumes are test doubles for Volumes, used to test handlers.
type MockVolume struct {
Store map[string][]byte
Timestamps map[string]time.Time
// Bad volumes return an error for every operation.
- Bad bool
+ Bad bool
+ BadVolumeError error
// Touchable volumes' Touch() method succeeds for a locator
// that has been Put().
Touchable bool
- // Readonly volumes return an error for Put, Delete, and
- // Touch.
- Readonly bool
-
// Gate is a "starting gate", allowing test cases to pause
// volume operations long enough to inspect state. Every
// operation (except Status) starts by receiving from
// channel unblocks all operations. By default, Gate is a
// closed channel, so all operations proceed without
// blocking. See trash_worker_test.go for an example.
- Gate chan struct{}
+ Gate chan struct{} `json:"-"`
- called map[string]int
- mutex sync.Mutex
+ cluster *arvados.Cluster
+ volume arvados.Volume
+ logger logrus.FieldLogger
+ metrics *volumeMetricsVecs
+ called map[string]int
+ mutex sync.Mutex
}
-// CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
+// newMockVolume returns a non-Bad, non-Readonly, Touchable mock
// volume.
-func CreateMockVolume() *MockVolume {
+func newMockVolume(cluster *arvados.Cluster, volume arvados.Volume, logger logrus.FieldLogger, metrics *volumeMetricsVecs) (Volume, error) {
gate := make(chan struct{})
close(gate)
return &MockVolume{
Timestamps: make(map[string]time.Time),
Bad: false,
Touchable: true,
- Readonly: false,
called: map[string]int{},
Gate: gate,
- }
+ cluster: cluster,
+ volume: volume,
+ logger: logger,
+ metrics: metrics,
+ }, nil
}
// CallCount returns how many times the named method has been called.
v.gotCall("Compare")
<-v.Gate
if v.Bad {
- return errors.New("Bad volume")
+ return v.BadVolumeError
} else if block, ok := v.Store[loc]; ok {
if fmt.Sprintf("%x", md5.Sum(block)) != loc {
return DiskHashError
v.gotCall("Get")
<-v.Gate
if v.Bad {
- return 0, errors.New("Bad volume")
+ return 0, v.BadVolumeError
} else if block, ok := v.Store[loc]; ok {
copy(buf[:len(block)], block)
return len(block), nil
v.gotCall("Put")
<-v.Gate
if v.Bad {
- return errors.New("Bad volume")
+ return v.BadVolumeError
}
- if v.Readonly {
+ if v.volume.ReadOnly {
return MethodDisabledError
}
v.Store[loc] = block
func (v *MockVolume) Touch(loc string) error {
v.gotCall("Touch")
<-v.Gate
- if v.Readonly {
+ if v.volume.ReadOnly {
return MethodDisabledError
}
if v.Touchable {
var mtime time.Time
var err error
if v.Bad {
- err = errors.New("Bad volume")
+ err = v.BadVolumeError
} else if t, ok := v.Timestamps[loc]; ok {
mtime = t
} else {
func (v *MockVolume) Trash(loc string) error {
v.gotCall("Delete")
<-v.Gate
- if v.Readonly {
+ if v.volume.ReadOnly {
return MethodDisabledError
}
if _, ok := v.Store[loc]; ok {
- if time.Since(v.Timestamps[loc]) < time.Duration(theConfig.BlobSignatureTTL) {
+ if time.Since(v.Timestamps[loc]) < time.Duration(v.cluster.Collections.BlobSigningTTL) {
return nil
}
delete(v.Store, loc)
return os.ErrNotExist
}
-func (v *MockVolume) DeviceID() string {
+func (v *MockVolume) GetDeviceID() string {
return "mock-device-id"
}
-func (v *MockVolume) Type() string {
- return "Mock"
-}
-
-func (v *MockVolume) Start() error {
- return nil
-}
-
func (v *MockVolume) Untrash(loc string) error {
return nil
}
return "[MockVolume]"
}
-func (v *MockVolume) Writable() bool {
- return !v.Readonly
-}
-
-func (v *MockVolume) Replication() int {
- return 1
-}
-
func (v *MockVolume) EmptyTrash() {
}