1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
12 "git.arvados.org/arvados.git/sdk/go/ctxlog"
13 "github.com/prometheus/client_golang/prometheus"
14 check "gopkg.in/check.v1"
17 type TrashWorkerTestData struct {
39 /* Delete block that does not exist in any of the keep volumes.
42 func (s *HandlerSuite) TestTrashWorkerIntegration_GetNonExistingLocator(c *check.C) {
43 s.cluster.Collections.BlobTrash = true
44 testData := TrashWorkerTestData{
45 Locator1: "5d41402abc4b2a76b9719d911017c592",
46 Block1: []byte("hello"),
48 Locator2: "5d41402abc4b2a76b9719d911017c592",
49 Block2: []byte("hello"),
53 DeleteLocator: "5d41402abc4b2a76b9719d911017c592",
55 ExpectLocator1: false,
56 ExpectLocator2: false,
58 s.performTrashWorkerTest(c, testData)
61 /* Delete a block that exists on volume 1 of the keep servers.
62 Expect the second locator in volume 2 to be unaffected.
64 func (s *HandlerSuite) TestTrashWorkerIntegration_LocatorInVolume1(c *check.C) {
65 s.cluster.Collections.BlobTrash = true
66 testData := TrashWorkerTestData{
75 DeleteLocator: TestHash, // first locator
77 ExpectLocator1: false,
80 s.performTrashWorkerTest(c, testData)
83 /* Delete a block that exists on volume 2 of the keep servers.
84 Expect the first locator in volume 1 to be unaffected.
86 func (s *HandlerSuite) TestTrashWorkerIntegration_LocatorInVolume2(c *check.C) {
87 s.cluster.Collections.BlobTrash = true
88 testData := TrashWorkerTestData{
97 DeleteLocator: TestHash2, // locator 2
100 ExpectLocator2: false,
102 s.performTrashWorkerTest(c, testData)
105 /* Delete a block with matching mtime for locator in both volumes.
106 Expect locator to be deleted from both volumes.
108 func (s *HandlerSuite) TestTrashWorkerIntegration_LocatorInBothVolumes(c *check.C) {
109 s.cluster.Collections.BlobTrash = true
110 testData := TrashWorkerTestData{
119 DeleteLocator: TestHash,
121 ExpectLocator1: false,
122 ExpectLocator2: false,
124 s.performTrashWorkerTest(c, testData)
127 /* Same locator with different Mtimes exists in both volumes.
128 Delete the second and expect the first to be still around.
130 func (s *HandlerSuite) TestTrashWorkerIntegration_MtimeMatchesForLocator1ButNotForLocator2(c *check.C) {
131 s.cluster.Collections.BlobTrash = true
132 testData := TrashWorkerTestData{
140 DifferentMtimes: true,
142 DeleteLocator: TestHash,
144 ExpectLocator1: true,
145 ExpectLocator2: false,
147 s.performTrashWorkerTest(c, testData)
150 // Delete a block that exists on both volumes with matching mtimes,
151 // but specify a MountUUID in the request so it only gets deleted from
153 func (s *HandlerSuite) TestTrashWorkerIntegration_SpecifyMountUUID(c *check.C) {
154 s.cluster.Collections.BlobTrash = true
155 testData := TrashWorkerTestData{
164 DeleteLocator: TestHash,
165 SpecifyMountUUID: true,
167 ExpectLocator1: true,
168 ExpectLocator2: true,
170 s.performTrashWorkerTest(c, testData)
173 /* Two different locators in volume 1.
175 Expect the other unaffected.
177 func (s *HandlerSuite) TestTrashWorkerIntegration_TwoDifferentLocatorsInVolume1(c *check.C) {
178 s.cluster.Collections.BlobTrash = true
179 testData := TrashWorkerTestData{
187 CreateInVolume1: true,
189 DeleteLocator: TestHash, // locator 1
191 ExpectLocator1: false,
192 ExpectLocator2: true,
194 s.performTrashWorkerTest(c, testData)
197 /* Allow default Trash Life time to be used. Thus, the newly created block
198 will not be deleted because its Mtime is within the trash life time.
200 func (s *HandlerSuite) TestTrashWorkerIntegration_SameLocatorInTwoVolumesWithDefaultTrashLifeTime(c *check.C) {
201 s.cluster.Collections.BlobTrash = true
202 testData := TrashWorkerTestData{
210 CreateInVolume1: true,
212 UseTrashLifeTime: true,
214 DeleteLocator: TestHash, // locator 1
216 // Since trash life time is in effect, block won't be deleted.
217 ExpectLocator1: true,
218 ExpectLocator2: true,
220 s.performTrashWorkerTest(c, testData)
223 /* Delete a block with matching mtime for locator in both volumes, but EnableDelete is false,
224 so block won't be deleted.
226 func (s *HandlerSuite) TestTrashWorkerIntegration_DisabledDelete(c *check.C) {
227 s.cluster.Collections.BlobTrash = false
228 testData := TrashWorkerTestData{
237 DeleteLocator: TestHash,
239 ExpectLocator1: true,
240 ExpectLocator2: true,
242 s.performTrashWorkerTest(c, testData)
245 /* Perform the test */
246 func (s *HandlerSuite) performTrashWorkerTest(c *check.C, testData TrashWorkerTestData) {
247 c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
248 // Replace the router's trashq -- which the worker goroutines
249 // started by setup() are now receiving from -- with a new
250 // one, so we can see what the handler sends to it.
251 trashq := NewWorkQueue()
252 s.handler.Handler.(*router).trashq = trashq
255 mounts := s.handler.volmgr.AllWritable()
256 if testData.CreateData {
257 mounts[0].Put(context.Background(), testData.Locator1, testData.Block1)
258 mounts[0].Put(context.Background(), testData.Locator1+".meta", []byte("metadata"))
260 if testData.CreateInVolume1 {
261 mounts[0].Put(context.Background(), testData.Locator2, testData.Block2)
262 mounts[0].Put(context.Background(), testData.Locator2+".meta", []byte("metadata"))
264 mounts[1].Put(context.Background(), testData.Locator2, testData.Block2)
265 mounts[1].Put(context.Background(), testData.Locator2+".meta", []byte("metadata"))
269 oldBlockTime := time.Now().Add(-s.cluster.Collections.BlobSigningTTL.Duration() - time.Minute)
271 // Create TrashRequest for the test
272 trashRequest := TrashRequest{
273 Locator: testData.DeleteLocator,
274 BlockMtime: oldBlockTime.UnixNano(),
276 if testData.SpecifyMountUUID {
277 trashRequest.MountUUID = s.handler.volmgr.Mounts()[0].UUID
280 // Run trash worker and put the trashRequest on trashq
281 trashList := list.New()
282 trashList.PushBack(trashRequest)
284 if !testData.UseTrashLifeTime {
285 // Trash worker would not delete block if its Mtime is
286 // within trash life time. Back-date the block to
287 // allow the deletion to succeed.
288 for _, mnt := range mounts {
289 mnt.Volume.(*MockVolume).Timestamps[testData.DeleteLocator] = oldBlockTime
290 if testData.DifferentMtimes {
291 oldBlockTime = oldBlockTime.Add(time.Second)
295 go RunTrashWorker(s.handler.volmgr, ctxlog.TestLogger(c), s.cluster, trashq)
297 // Install gate so all local operations block until we say go
298 gate := make(chan struct{})
299 for _, mnt := range mounts {
300 mnt.Volume.(*MockVolume).Gate = gate
303 assertStatusItem := func(k string, expect float64) {
304 if v := getStatusItem(s.handler, "TrashQueue", k); v != expect {
305 c.Errorf("Got %s %v, expected %v", k, v, expect)
309 assertStatusItem("InProgress", 0)
310 assertStatusItem("Queued", 0)
312 listLen := trashList.Len()
313 trashq.ReplaceQueue(trashList)
315 // Wait for worker to take request(s)
316 expectEqualWithin(c, time.Second, listLen, func() interface{} { return trashq.Status().InProgress })
318 // Ensure status.json also reports work is happening
319 assertStatusItem("InProgress", float64(1))
320 assertStatusItem("Queued", float64(listLen-1))
322 // Let worker proceed
325 // Wait for worker to finish
326 expectEqualWithin(c, time.Second, 0, func() interface{} { return trashq.Status().InProgress })
328 // Verify Locator1 to be un/deleted as expected
329 buf := make([]byte, BlockSize)
330 size, err := GetBlock(context.Background(), s.handler.volmgr, testData.Locator1, buf, nil)
331 if testData.ExpectLocator1 {
332 if size == 0 || err != nil {
333 c.Errorf("Expected Locator1 to be still present: %s", testData.Locator1)
336 if size > 0 || err == nil {
337 c.Errorf("Expected Locator1 to be deleted: %s", testData.Locator1)
341 // Verify Locator2 to be un/deleted as expected
342 if testData.Locator1 != testData.Locator2 {
343 size, err = GetBlock(context.Background(), s.handler.volmgr, testData.Locator2, buf, nil)
344 if testData.ExpectLocator2 {
345 if size == 0 || err != nil {
346 c.Errorf("Expected Locator2 to be still present: %s", testData.Locator2)
349 if size > 0 || err == nil {
350 c.Errorf("Expected Locator2 to be deleted: %s", testData.Locator2)
355 // The DifferentMtimes test puts the same locator in two
356 // different volumes, but only one copy has an Mtime matching
357 // the trash request.
358 if testData.DifferentMtimes {
360 for _, volume := range s.handler.volmgr.AllReadable() {
361 buf := make([]byte, BlockSize)
362 if _, err := volume.Get(context.Background(), testData.Locator1, buf); err == nil {
363 locatorFoundIn = locatorFoundIn + 1
366 c.Check(locatorFoundIn, check.Equals, 1)