1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "github.com/prometheus/client_golang/prometheus"
22 // A TestableVolume allows test suites to manipulate the state of an
23 // underlying Volume, in order to test behavior in cases that are
24 // impractical to achieve with a sequence of normal Volume operations.
25 type TestableVolume interface {
28 // Get prometheus metrics
29 GetMetricsVecs() (opsCounters, errCounters, ioBytes *prometheus.CounterVec)
31 // [Over]write content for a locator with the given data,
32 // bypassing all constraints like readonly and serialize.
33 PutRaw(locator string, data []byte)
35 // Specify the value Mtime() should return, until the next
36 // call to Touch, TouchWithDate, or Put.
37 TouchWithDate(locator string, lastPut time.Time)
39 // Clean up, delete temporary files.
43 // MockVolumes are test doubles for Volumes, used to test handlers.
44 type MockVolume struct {
45 Store map[string][]byte
46 Timestamps map[string]time.Time
48 // Bad volumes return an error for every operation.
52 // Touchable volumes' Touch() method succeeds for a locator
53 // that has been Put().
56 // Readonly volumes return an error for Put, Delete, and
60 // Gate is a "starting gate", allowing test cases to pause
61 // volume operations long enough to inspect state. Every
62 // operation (except Status) starts by receiving from
63 // Gate. Sending one value unblocks one operation; closing the
64 // channel unblocks all operations. By default, Gate is a
65 // closed channel, so all operations proceed without
66 // blocking. See trash_worker_test.go for an example.
73 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
75 func CreateMockVolume() *MockVolume {
76 gate := make(chan struct{})
79 Store: make(map[string][]byte),
80 Timestamps: make(map[string]time.Time),
84 called: map[string]int{},
89 // CallCount returns how many times the named method has been called.
90 func (v *MockVolume) CallCount(method string) int {
92 defer v.mutex.Unlock()
93 c, ok := v.called[method]
100 func (v *MockVolume) gotCall(method string) {
102 defer v.mutex.Unlock()
103 if _, ok := v.called[method]; !ok {
110 func (v *MockVolume) Compare(ctx context.Context, loc string, buf []byte) error {
114 return v.BadVolumeError
115 } else if block, ok := v.Store[loc]; ok {
116 if fmt.Sprintf("%x", md5.Sum(block)) != loc {
119 if bytes.Compare(buf, block) != 0 {
120 return CollisionError
128 func (v *MockVolume) Get(ctx context.Context, loc string, buf []byte) (int, error) {
132 return 0, v.BadVolumeError
133 } else if block, ok := v.Store[loc]; ok {
134 copy(buf[:len(block)], block)
135 return len(block), nil
137 return 0, os.ErrNotExist
140 func (v *MockVolume) Put(ctx context.Context, loc string, block []byte) error {
144 return v.BadVolumeError
147 return MethodDisabledError
153 func (v *MockVolume) Touch(loc string) error {
157 return MethodDisabledError
160 v.Timestamps[loc] = time.Now()
163 return errors.New("Touch failed")
166 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
172 err = v.BadVolumeError
173 } else if t, ok := v.Timestamps[loc]; ok {
181 func (v *MockVolume) IndexTo(prefix string, w io.Writer) error {
184 for loc, block := range v.Store {
185 if !IsValidLocator(loc) || !strings.HasPrefix(loc, prefix) {
188 _, err := fmt.Fprintf(w, "%s+%d %d\n",
189 loc, len(block), 123456789)
197 func (v *MockVolume) Trash(loc string) error {
201 return MethodDisabledError
203 if _, ok := v.Store[loc]; ok {
204 if time.Since(v.Timestamps[loc]) < time.Duration(theConfig.BlobSignatureTTL) {
210 return os.ErrNotExist
213 func (v *MockVolume) DeviceID() string {
214 return "mock-device-id"
217 func (v *MockVolume) Type() string {
221 func (v *MockVolume) Start(vm *volumeMetricsVecs) error {
225 func (v *MockVolume) Untrash(loc string) error {
229 func (v *MockVolume) Status() *VolumeStatus {
231 for _, block := range v.Store {
232 used = used + uint64(len(block))
234 return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
237 func (v *MockVolume) String() string {
238 return "[MockVolume]"
241 func (v *MockVolume) Writable() bool {
245 func (v *MockVolume) Replication() int {
249 func (v *MockVolume) EmptyTrash() {
252 func (v *MockVolume) GetStorageClasses() []string {