1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 type TrashWorkerTestData struct {
36 /* Delete block that does not exist in any of the keep volumes.
39 func TestTrashWorkerIntegration_GetNonExistingLocator(t *testing.T) {
40 theConfig.EnableDelete = true
41 testData := TrashWorkerTestData{
42 Locator1: "5d41402abc4b2a76b9719d911017c592",
43 Block1: []byte("hello"),
45 Locator2: "5d41402abc4b2a76b9719d911017c592",
46 Block2: []byte("hello"),
50 DeleteLocator: "5d41402abc4b2a76b9719d911017c592",
52 ExpectLocator1: false,
53 ExpectLocator2: false,
55 performTrashWorkerTest(testData, t)
58 /* Delete a block that exists on volume 1 of the keep servers.
59 Expect the second locator in volume 2 to be unaffected.
61 func TestTrashWorkerIntegration_LocatorInVolume1(t *testing.T) {
62 theConfig.EnableDelete = true
63 testData := TrashWorkerTestData{
72 DeleteLocator: TestHash, // first locator
74 ExpectLocator1: false,
77 performTrashWorkerTest(testData, t)
80 /* Delete a block that exists on volume 2 of the keep servers.
81 Expect the first locator in volume 1 to be unaffected.
83 func TestTrashWorkerIntegration_LocatorInVolume2(t *testing.T) {
84 theConfig.EnableDelete = true
85 testData := TrashWorkerTestData{
94 DeleteLocator: TestHash2, // locator 2
97 ExpectLocator2: false,
99 performTrashWorkerTest(testData, t)
102 /* Delete a block with matching mtime for locator in both volumes.
103 Expect locator to be deleted from both volumes.
105 func TestTrashWorkerIntegration_LocatorInBothVolumes(t *testing.T) {
106 theConfig.EnableDelete = true
107 testData := TrashWorkerTestData{
116 DeleteLocator: TestHash,
118 ExpectLocator1: false,
119 ExpectLocator2: false,
121 performTrashWorkerTest(testData, t)
124 /* Same locator with different Mtimes exists in both volumes.
125 Delete the second and expect the first to be still around.
127 func TestTrashWorkerIntegration_MtimeMatchesForLocator1ButNotForLocator2(t *testing.T) {
128 theConfig.EnableDelete = true
129 testData := TrashWorkerTestData{
137 DifferentMtimes: true,
139 DeleteLocator: TestHash,
141 ExpectLocator1: true,
142 ExpectLocator2: false,
144 performTrashWorkerTest(testData, t)
147 // Delete a block that exists on both volumes with matching mtimes,
148 // but specify a MountUUID in the request so it only gets deleted from
150 func TestTrashWorkerIntegration_SpecifyMountUUID(t *testing.T) {
151 theConfig.EnableDelete = true
152 testData := TrashWorkerTestData{
161 DeleteLocator: TestHash,
162 SpecifyMountUUID: true,
164 ExpectLocator1: true,
165 ExpectLocator2: true,
167 performTrashWorkerTest(testData, t)
170 /* Two different locators in volume 1.
172 Expect the other unaffected.
174 func TestTrashWorkerIntegration_TwoDifferentLocatorsInVolume1(t *testing.T) {
175 theConfig.EnableDelete = true
176 testData := TrashWorkerTestData{
184 CreateInVolume1: true,
186 DeleteLocator: TestHash, // locator 1
188 ExpectLocator1: false,
189 ExpectLocator2: true,
191 performTrashWorkerTest(testData, t)
194 /* Allow default Trash Life time to be used. Thus, the newly created block
195 will not be deleted because its Mtime is within the trash life time.
197 func TestTrashWorkerIntegration_SameLocatorInTwoVolumesWithDefaultTrashLifeTime(t *testing.T) {
198 theConfig.EnableDelete = true
199 testData := TrashWorkerTestData{
207 CreateInVolume1: true,
209 UseTrashLifeTime: true,
211 DeleteLocator: TestHash, // locator 1
213 // Since trash life time is in effect, block won't be deleted.
214 ExpectLocator1: true,
215 ExpectLocator2: true,
217 performTrashWorkerTest(testData, t)
220 /* Delete a block with matching mtime for locator in both volumes, but EnableDelete is false,
221 so block won't be deleted.
223 func TestTrashWorkerIntegration_DisabledDelete(t *testing.T) {
224 theConfig.EnableDelete = false
225 testData := TrashWorkerTestData{
234 DeleteLocator: TestHash,
236 ExpectLocator1: true,
237 ExpectLocator2: true,
239 performTrashWorkerTest(testData, t)
242 /* Perform the test */
243 func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
244 // Create Keep Volumes
245 KeepVM = MakeTestVolumeManager(2)
249 vols := KeepVM.AllWritable()
250 if testData.CreateData {
251 vols[0].Put(context.Background(), testData.Locator1, testData.Block1)
252 vols[0].Put(context.Background(), testData.Locator1+".meta", []byte("metadata"))
254 if testData.CreateInVolume1 {
255 vols[0].Put(context.Background(), testData.Locator2, testData.Block2)
256 vols[0].Put(context.Background(), testData.Locator2+".meta", []byte("metadata"))
258 vols[1].Put(context.Background(), testData.Locator2, testData.Block2)
259 vols[1].Put(context.Background(), testData.Locator2+".meta", []byte("metadata"))
263 oldBlockTime := time.Now().Add(-theConfig.BlobSignatureTTL.Duration() - time.Minute)
265 // Create TrashRequest for the test
266 trashRequest := TrashRequest{
267 Locator: testData.DeleteLocator,
268 BlockMtime: oldBlockTime.UnixNano(),
270 if testData.SpecifyMountUUID {
271 trashRequest.MountUUID = KeepVM.Mounts()[0].UUID
274 // Run trash worker and put the trashRequest on trashq
275 trashList := list.New()
276 trashList.PushBack(trashRequest)
277 trashq = NewWorkQueue()
280 if !testData.UseTrashLifeTime {
281 // Trash worker would not delete block if its Mtime is
282 // within trash life time. Back-date the block to
283 // allow the deletion to succeed.
284 for _, v := range vols {
285 v.(*MockVolume).Timestamps[testData.DeleteLocator] = oldBlockTime
286 if testData.DifferentMtimes {
287 oldBlockTime = oldBlockTime.Add(time.Second)
291 go RunTrashWorker(trashq)
293 // Install gate so all local operations block until we say go
294 gate := make(chan struct{})
295 for _, v := range vols {
296 v.(*MockVolume).Gate = gate
299 assertStatusItem := func(k string, expect float64) {
300 if v := getStatusItem("TrashQueue", k); v != expect {
301 t.Errorf("Got %s %v, expected %v", k, v, expect)
305 assertStatusItem("InProgress", 0)
306 assertStatusItem("Queued", 0)
308 listLen := trashList.Len()
309 trashq.ReplaceQueue(trashList)
311 // Wait for worker to take request(s)
312 expectEqualWithin(t, time.Second, listLen, func() interface{} { return trashq.Status().InProgress })
314 // Ensure status.json also reports work is happening
315 assertStatusItem("InProgress", float64(1))
316 assertStatusItem("Queued", float64(listLen-1))
318 // Let worker proceed
321 // Wait for worker to finish
322 expectEqualWithin(t, time.Second, 0, func() interface{} { return trashq.Status().InProgress })
324 // Verify Locator1 to be un/deleted as expected
325 buf := make([]byte, BlockSize)
326 size, err := GetBlock(context.Background(), testData.Locator1, buf, nil)
327 if testData.ExpectLocator1 {
328 if size == 0 || err != nil {
329 t.Errorf("Expected Locator1 to be still present: %s", testData.Locator1)
332 if size > 0 || err == nil {
333 t.Errorf("Expected Locator1 to be deleted: %s", testData.Locator1)
337 // Verify Locator2 to be un/deleted as expected
338 if testData.Locator1 != testData.Locator2 {
339 size, err = GetBlock(context.Background(), testData.Locator2, buf, nil)
340 if testData.ExpectLocator2 {
341 if size == 0 || err != nil {
342 t.Errorf("Expected Locator2 to be still present: %s", testData.Locator2)
345 if size > 0 || err == nil {
346 t.Errorf("Expected Locator2 to be deleted: %s", testData.Locator2)
351 // The DifferentMtimes test puts the same locator in two
352 // different volumes, but only one copy has an Mtime matching
353 // the trash request.
354 if testData.DifferentMtimes {
356 for _, volume := range KeepVM.AllReadable() {
357 buf := make([]byte, BlockSize)
358 if _, err := volume.Get(context.Background(), testData.Locator1, buf); err == nil {
359 locatorFoundIn = locatorFoundIn + 1
362 if locatorFoundIn != 1 {
363 t.Errorf("Found %d copies of %s, expected 1", locatorFoundIn, testData.Locator1)