github.com/weaviate/weaviate@v1.24.6/usecases/backup/backupper_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package backup 13 14 import ( 15 "context" 16 "errors" 17 "os" 18 "path/filepath" 19 "testing" 20 "time" 21 22 "github.com/sirupsen/logrus/hooks/test" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/mock" 25 "github.com/weaviate/weaviate/entities/backup" 26 "github.com/weaviate/weaviate/entities/models" 27 "github.com/weaviate/weaviate/entities/modulecapabilities" 28 ) 29 30 const ( 31 nodeName = "Node-1" 32 ) 33 34 var errNotFound = backup.NewErrNotFound(errors.New("not found")) 35 36 func (r *backupper) waitForCompletion(n, ms int) backup.Status { 37 for i := 0; i < n; i++ { 38 time.Sleep(time.Millisecond * time.Duration(ms)) 39 if i < 1 { 40 continue 41 } 42 if x := r.lastOp.get(); x.Status != "" { 43 return x.Status 44 } 45 } 46 return "" 47 } 48 49 func TestBackupStatus(t *testing.T) { 50 t.Parallel() 51 var ( 52 backendName = "s3" 53 id = "1234" 54 ctx = context.Background() 55 starTime = time.Date(2022, 1, 1, 1, 0, 0, 0, time.UTC) 56 nodeHome = id + "/" + nodeName 57 path = "bucket/backups/" + nodeHome 58 rawstatus = string(backup.Transferring) 59 want = &models.BackupCreateStatusResponse{ 60 ID: id, 61 Path: path, 62 Status: &rawstatus, 63 Backend: backendName, 64 } 65 ) 66 67 t.Run("ActiveState", func(t *testing.T) { 68 m := createManager(nil, nil, nil, nil) 69 m.backupper.lastOp.reqStat = reqStat{ 70 Starttime: starTime, 71 ID: id, 72 Status: backup.Transferring, 73 Path: path, 74 } 75 st, err := m.backupper.Status(ctx, backendName, id) 76 assert.Nil(t, err) 77 assert.Equal(t, want, st) 78 }) 79 80 t.Run("GetBackupProvider", func(t *testing.T) { 81 m := createManager(nil, nil, nil, ErrAny) 82 _, err := m.backupper.Status(ctx, backendName, id) 83 assert.NotNil(t, err) 84 }) 85 86 t.Run("MetadataNotFound", func(t *testing.T) { 87 backend := &fakeBackend{} 88 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, ErrAny) 89 backend.On("GetObject", ctx, id, BackupFile).Return(nil, ErrAny) 90 m := createManager(nil, nil, backend, nil) 91 _, err := m.backupper.Status(ctx, backendName, id) 92 assert.NotNil(t, err) 93 nerr := backup.ErrNotFound{} 94 if !errors.As(err, &nerr) { 95 t.Errorf("error want=%v got=%v", nerr, err) 96 } 97 }) 98 99 t.Run("ReadFromMetadata", func(t *testing.T) { 100 backend := &fakeBackend{} 101 bytes := marshalMeta(backup.BackupDescriptor{Status: string(backup.Transferring)}) 102 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 103 backend.On("HomeDir", mock.Anything).Return(path) 104 m := createManager(nil, nil, backend, nil) 105 got, err := m.backupper.Status(ctx, backendName, id) 106 assert.Nil(t, err) 107 assert.Equal(t, want, got) 108 }) 109 110 t.Run("ReadFromMetadataError", func(t *testing.T) { 111 backend := &fakeBackend{} 112 st := string(backup.Failed) 113 bytes := marshalMeta(backup.BackupDescriptor{Status: st, Error: "error1"}) 114 want = &models.BackupCreateStatusResponse{ 115 ID: id, 116 Path: path, 117 Status: &st, 118 Backend: backendName, 119 } 120 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 121 backend.On("HomeDir", mock.Anything).Return(path) 122 m := createManager(nil, nil, backend, nil) 123 _, err := m.backupper.Status(ctx, backendName, id) 124 assert.NotNil(t, err) 125 assert.ErrorContains(t, err, "error1") 126 }) 127 } 128 129 func TestBackupOnStatus(t *testing.T) { 130 t.Parallel() 131 var ( 132 backendName = "s3" 133 id = "1234" 134 ctx = context.Background() 135 starTime = time.Date(2022, 1, 1, 1, 0, 0, 0, time.UTC) 136 nodeHome = id + "/" + nodeName 137 path = "bucket/backups/" + nodeHome 138 req = StatusRequest{ 139 Method: OpCreate, 140 ID: id, 141 Backend: backendName, 142 } 143 ) 144 145 t.Run("ActiveState", func(t *testing.T) { 146 m := createManager(nil, nil, nil, nil) 147 m.backupper.lastOp.reqStat = reqStat{ 148 Starttime: starTime, 149 ID: id, 150 Status: backup.Transferring, 151 Path: path, 152 } 153 want := &StatusResponse{ 154 Method: OpCreate, 155 ID: id, 156 Status: backup.Transferring, 157 } 158 st := m.OnStatus(ctx, &req) 159 assert.Equal(t, want, st) 160 }) 161 162 t.Run("GetBackupProvider", func(t *testing.T) { 163 want := &StatusResponse{ 164 Method: OpCreate, 165 ID: id, 166 Status: backup.Failed, 167 } 168 m := createManager(nil, nil, nil, ErrAny) 169 got := m.OnStatus(ctx, &req) 170 assert.Contains(t, got.Err, req.Backend) 171 want.Err = got.Err 172 assert.Equal(t, want, got) 173 }) 174 175 t.Run("MetadataNotFound", func(t *testing.T) { 176 want := &StatusResponse{ 177 Method: OpCreate, 178 ID: id, 179 Status: backup.Failed, 180 } 181 backend := &fakeBackend{} 182 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, ErrAny) 183 backend.On("GetObject", ctx, id, BackupFile).Return(nil, ErrAny) 184 185 m := createManager(nil, nil, backend, nil) 186 got := m.OnStatus(ctx, &req) 187 assert.Contains(t, got.Err, errMetaNotFound.Error()) 188 want.Err = got.Err 189 assert.Equal(t, want, got) 190 }) 191 192 t.Run("ReadFromMetadata", func(t *testing.T) { 193 want := &StatusResponse{ 194 Method: OpCreate, 195 ID: id, 196 Status: backup.Success, 197 } 198 backend := &fakeBackend{} 199 bytes := marshalMeta(backup.BackupDescriptor{Status: string(backup.Success)}) 200 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 201 backend.On("HomeDir", mock.Anything).Return(path) 202 m := createManager(nil, nil, backend, nil) 203 got := m.OnStatus(ctx, &req) 204 assert.Equal(t, want, got) 205 }) 206 } 207 208 func TestManagerCreateBackup(t *testing.T) { 209 t.Parallel() 210 var ( 211 cls = "Class-A" 212 cls2 = "Class-B" 213 backendName = "gcs" 214 backupID = "1" 215 ctx = context.Background() 216 nodeHome = backupID + "/" + nodeName 217 path = "bucket/backups/" + nodeHome 218 req = BackupRequest{ 219 ID: backupID, 220 Include: []string{cls}, 221 Backend: backendName, 222 } 223 ) 224 225 t.Run("AnotherBackupIsInProgress", func(t *testing.T) { 226 req1 := BackupRequest{ 227 ID: backupID, 228 Include: []string{cls}, 229 Backend: backendName, 230 } 231 232 sourcer := &fakeSourcer{} 233 // first 234 sourcer.On("Backupable", ctx, req1.Include).Return(nil) 235 sourcer.On("CreateBackup", ctx, any).Return(nil, nil) 236 sourcer.On("ReleaseBackup", ctx, any).Return(nil) 237 var ch <-chan backup.ClassDescriptor 238 sourcer.On("BackupDescriptors", any, any, any).Return(ch) // just block 239 240 backend := &fakeBackend{} 241 // second 242 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, backup.ErrNotFound{}) 243 backend.On("GetObject", ctx, backupID, BackupFile).Return(nil, backup.ErrNotFound{}) 244 backend.On("HomeDir", any).Return(path) 245 sourcer.On("Backupable", any, req1.Include).Return(nil) 246 backend.On("Initialize", ctx, nodeHome).Return(nil) 247 sourcer.On("CreateBackup", ctx, any).Return(nil, ErrAny) 248 sourcer.On("ReleaseBackup", ctx, any).Return(nil) 249 m := createManager(sourcer, nil, backend, nil) 250 resp1, err := m.Backup(ctx, nil, &req1) 251 assert.Nil(t, err) 252 status1 := string(backup.Started) 253 want1 := &models.BackupCreateResponse{ 254 Backend: backendName, 255 Classes: req1.Include, 256 ID: backupID, 257 Status: &status1, 258 Path: path, 259 } 260 assert.Equal(t, resp1, want1) 261 resp2, err := m.Backup(ctx, nil, &req1) 262 assert.NotNil(t, err) 263 assert.Contains(t, err.Error(), "already in progress") 264 assert.IsType(t, backup.ErrUnprocessable{}, err) 265 assert.Nil(t, resp2) 266 }) 267 268 t.Run("InitMetadata", func(t *testing.T) { 269 classes := []string{cls} 270 271 sourcer := &fakeSourcer{} 272 sourcer.On("Backupable", ctx, classes).Return(nil) 273 backend := &fakeBackend{} 274 backend.On("HomeDir", mock.Anything).Return(path) 275 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 276 backend.On("GetObject", ctx, backupID, BackupFile).Return(nil, errNotFound) 277 278 backend.On("Initialize", ctx, nodeHome).Return(errors.New("init meta failed")) 279 bm := createManager(sourcer, nil, backend, nil) 280 281 meta, err := bm.Backup(ctx, nil, &BackupRequest{ 282 Backend: backendName, 283 ID: backupID, 284 Include: classes, 285 }) 286 287 assert.Nil(t, meta) 288 assert.NotNil(t, err) 289 assert.Contains(t, err.Error(), "init") 290 assert.IsType(t, backup.ErrUnprocessable{}, err) 291 }) 292 293 t.Run("Success", func(t *testing.T) { 294 var ( 295 classes = []string{cls} 296 sourcePath = t.TempDir() 297 sourcer = &fakeSourcer{} 298 backend = newFakeBackend() 299 ) 300 sourcer.On("Backupable", ctx, classes).Return(nil) 301 ch := fakeBackupDescriptor(genClassDescriptions(t, sourcePath, cls, cls2)...) 302 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch) 303 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 304 backend.On("HomeDir", mock.Anything).Return(path) 305 backend.On("SourceDataPath").Return(sourcePath) 306 307 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 308 backend.On("GetObject", ctx, backupID, BackupFile).Return(nil, errNotFound) 309 310 backend.On("Initialize", ctx, nodeHome).Return(nil) 311 backend.On("PutObject", any, nodeHome, BackupFile, mock.Anything).Return(nil).Twice() 312 backend.On("Write", any, nodeHome, any, any).Return(any, nil) 313 m := createManager(sourcer, nil, backend, nil) 314 315 resp, err := m.Backup(ctx, nil, &req) 316 317 assert.Nil(t, err) 318 status1 := string(backup.Started) 319 want1 := &models.BackupCreateResponse{ 320 Backend: backendName, 321 Classes: req.Include, 322 ID: backupID, 323 Status: &status1, 324 Path: path, 325 } 326 assert.Equal(t, resp, want1) 327 m.backupper.waitForCompletion(10, 50) 328 assert.Equal(t, string(backup.Success), backend.meta.Status) 329 assert.Equal(t, backend.meta.Error, "") 330 }) 331 332 t.Run("PutFile", func(t *testing.T) { 333 var ( 334 classes = []string{cls} 335 sourcePath = t.TempDir() 336 sourcer = &fakeSourcer{} 337 backend = newFakeBackend() 338 ) 339 sourcer.On("Backupable", ctx, classes).Return(nil) 340 ch := fakeBackupDescriptor(genClassDescriptions(t, sourcePath, cls, cls2)...) 341 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch) 342 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 343 344 backend.On("HomeDir", mock.Anything).Return(path) 345 backend.On("SourceDataPath").Return(sourcePath) 346 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 347 backend.On("GetObject", ctx, backupID, BackupFile).Return(nil, errNotFound) 348 349 backend.On("Initialize", ctx, nodeHome).Return(nil) 350 351 backend.On("Write", any, nodeHome, any, any).Return(any, ErrAny).Once() 352 backend.On("PutObject", any, nodeHome, BackupFile, any).Return(nil).Once() 353 m := createManager(sourcer, nil, backend, nil) 354 355 resp, err := m.Backup(ctx, nil, &req) 356 357 assert.Nil(t, err) 358 status1 := string(backup.Started) 359 want1 := &models.BackupCreateResponse{ 360 Backend: backendName, 361 Classes: req.Include, 362 ID: backupID, 363 Status: &status1, 364 Path: path, 365 } 366 assert.Equal(t, resp, want1) 367 m.backupper.waitForCompletion(10, 50) 368 369 assert.Equal(t, string(backup.Transferring), backend.meta.Status) 370 assert.Contains(t, backend.meta.Error, "pipe") 371 }) 372 373 t.Run("ClassDescriptor", func(t *testing.T) { 374 var ( 375 classes = []string{cls} 376 sourcer = &fakeSourcer{} 377 sourcePath = t.TempDir() 378 errNotFound = errNotFound 379 backend = newFakeBackend() 380 ) 381 382 sourcer.On("Backupable", ctx, classes).Return(nil) 383 cs := genClassDescriptions(t, sourcePath, cls, cls2) 384 cs[1].Error = ErrAny 385 ch := fakeBackupDescriptor(cs...) 386 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch) 387 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 388 389 backend.On("HomeDir", mock.Anything).Return(path) 390 backend.On("SourceDataPath").Return(sourcePath) 391 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 392 backend.On("GetObject", ctx, backupID, BackupFile).Return(nil, errNotFound) 393 394 backend.On("Initialize", ctx, nodeHome).Return(nil) 395 backend.On("Write", mock.Anything, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 396 backend.On("PutObject", mock.Anything, nodeHome, BackupFile, mock.Anything).Return(nil).Once() 397 m := createManager(sourcer, nil, backend, nil) 398 399 resp, err := m.Backup(ctx, nil, &req) 400 401 assert.Nil(t, err) 402 status1 := string(backup.Started) 403 want1 := &models.BackupCreateResponse{ 404 Backend: backendName, 405 Classes: req.Include, 406 ID: backupID, 407 Status: &status1, 408 Path: path, 409 } 410 assert.Equal(t, resp, want1) 411 m.backupper.waitForCompletion(10, 50) 412 assert.Nil(t, err) 413 assert.Equal(t, backend.meta.Status, string(backup.Transferring)) 414 assert.Equal(t, backend.meta.Error, ErrAny.Error()) 415 }) 416 } 417 418 func TestManagerCoordinatedBackup(t *testing.T) { 419 t.Parallel() 420 var ( 421 cls = "Class-A" 422 cls2 = "Class-B" 423 backendName = "gcs" 424 backupID = "1" 425 ctx = context.Background() 426 nodeHome = backupID + "/" + nodeName 427 path = "bucket/backups/" + nodeHome 428 req = Request{ 429 Method: OpCreate, 430 ID: backupID, 431 Classes: []string{cls, cls2}, 432 Backend: backendName, 433 Duration: time.Millisecond * 20, 434 } 435 any = mock.Anything 436 ) 437 438 t.Run("BackendUnregistered", func(t *testing.T) { 439 backendError := errors.New("I do not exist") 440 bm := createManager(nil, nil, nil, backendError) 441 ret := bm.OnCanCommit(ctx, &req) 442 assert.Contains(t, ret.Err, backendName) 443 }) 444 445 t.Run("ClassNotBackupable", func(t *testing.T) { 446 backend := &fakeBackend{} 447 backend.On("HomeDir", mock.Anything).Return(path) 448 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 449 sourcer := &fakeSourcer{} 450 sourcer.On("Backupable", ctx, req.Classes).Return(ErrAny) 451 bm := createManager(sourcer, nil, backend, nil) 452 453 resp := bm.OnCanCommit(ctx, &req) 454 assert.Contains(t, resp.Err, ErrAny.Error()) 455 assert.Equal(t, resp.Timeout, time.Duration(0)) 456 }) 457 458 t.Run("InitializeBackend", func(t *testing.T) { 459 backend := &fakeBackend{} 460 backend.On("HomeDir", mock.Anything).Return(path) 461 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 462 sourcer := &fakeSourcer{} 463 sourcer.On("Backupable", ctx, req.Classes).Return(nil) 464 backend.On("Initialize", ctx, nodeHome).Return(errors.New("init meta failed")) 465 bm := createManager(sourcer, nil, backend, nil) 466 467 resp := bm.OnCanCommit(ctx, &req) 468 assert.Contains(t, resp.Err, "init") 469 assert.Equal(t, resp.Timeout, time.Duration(0)) 470 }) 471 472 t.Run("AnotherBackupIsInProgress", func(t *testing.T) { 473 // first 474 sourcer := &fakeSourcer{} 475 sourcer.On("Backupable", ctx, req.Classes).Return(nil) 476 sourcer.On("CreateBackup", mock.Anything, mock.Anything).Return(nil, nil) 477 sourcer.On("ReleaseBackup", mock.Anything, mock.Anything).Return(nil) 478 var ch <-chan backup.ClassDescriptor 479 sourcer.On("BackupDescriptors", any, any, any).Return(ch) 480 481 backend := &fakeBackend{} 482 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, backup.ErrNotFound{}) 483 backend.On("HomeDir", mock.Anything).Return(path) 484 backend.On("Initialize", ctx, mock.Anything).Return(nil) 485 m := createManager(sourcer, nil, backend, nil) 486 // second 487 resp1 := m.OnCanCommit(ctx, &req) 488 want1 := &CanCommitResponse{ 489 Method: OpCreate, 490 ID: req.ID, 491 Timeout: req.Duration, 492 } 493 assert.Equal(t, resp1, want1) 494 resp := m.OnCanCommit(ctx, &req) 495 assert.Contains(t, resp.Err, "already in progress") 496 assert.Equal(t, resp.Timeout, time.Duration(0)) 497 }) 498 499 t.Run("Success", func(t *testing.T) { 500 var ( 501 sourcePath = t.TempDir() 502 sourcer = &fakeSourcer{} 503 backend = newFakeBackend() 504 ) 505 506 sourcer.On("Backupable", ctx, req.Classes).Return(nil) 507 ch := fakeBackupDescriptor(genClassDescriptions(t, sourcePath, cls, cls2)...) 508 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch) 509 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 510 511 backend.On("HomeDir", mock.Anything).Return(path) 512 backend.On("SourceDataPath").Return(sourcePath) 513 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 514 backend.On("Initialize", ctx, nodeHome).Return(nil) 515 backend.On("PutObject", mock.Anything, nodeHome, BackupFile, mock.Anything).Return(nil).Once() 516 backend.On("Write", mock.Anything, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 517 m := createManager(sourcer, nil, backend, nil) 518 519 req := req 520 req.Duration = time.Hour 521 got := m.OnCanCommit(ctx, &req) 522 want := &CanCommitResponse{OpCreate, req.ID, _TimeoutShardCommit, ""} 523 assert.Equal(t, got, want) 524 525 err := m.OnCommit(ctx, &StatusRequest{OpCreate, req.ID, backendName}) 526 assert.Nil(t, err) 527 m.backupper.waitForCompletion(20, 50) 528 assert.Equal(t, string(backup.Success), backend.meta.Status) 529 assert.Equal(t, "", backend.meta.Error) 530 }) 531 532 t.Run("AbortBeforeCommit", func(t *testing.T) { 533 var ( 534 sourcePath = t.TempDir() 535 sourcer = &fakeSourcer{} 536 backend = newFakeBackend() 537 ) 538 539 sourcer.On("Backupable", ctx, req.Classes).Return(nil) 540 ch := fakeBackupDescriptor(genClassDescriptions(t, sourcePath, cls, cls2)...) 541 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch) 542 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 543 544 backend.On("HomeDir", mock.Anything).Return(path) 545 backend.On("SourceDataPath").Return(sourcePath) 546 547 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 548 backend.On("Initialize", ctx, nodeHome).Return(nil) 549 backend.On("PutObject", mock.Anything, nodeHome, BackupFile, mock.Anything).Return(nil).Once() 550 backend.On("Write", mock.Anything, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 551 m := createManager(sourcer, nil, backend, nil) 552 553 req := req 554 req.Duration = time.Hour 555 got := m.OnCanCommit(ctx, &req) 556 want := &CanCommitResponse{OpCreate, req.ID, _TimeoutShardCommit, ""} 557 assert.Equal(t, got, want) 558 559 err := m.OnAbort(ctx, &AbortRequest{OpCreate, req.ID, backendName}) 560 assert.Nil(t, err) 561 m.backupper.waitForCompletion(20, 50) 562 assert.Contains(t, m.backupper.lastAsyncError.Error(), "abort") 563 }) 564 565 t.Run("AbortCommit", func(t *testing.T) { 566 var ( 567 sourcePath = t.TempDir() 568 sourcer = &fakeSourcer{} 569 backend = newFakeBackend() 570 m = createManager(sourcer, nil, backend, nil) 571 ) 572 573 sourcer.On("Backupable", ctx, req.Classes).Return(nil) 574 ch := fakeBackupDescriptor(genClassDescriptions(t, sourcePath, cls, cls2)...) 575 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch).RunFn = func(a mock.Arguments) { 576 m.OnAbort(ctx, &AbortRequest{OpCreate, req.ID, backendName}) 577 // give the abort request time to propagate 578 time.Sleep(10 * time.Millisecond) 579 } 580 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 581 // backend 582 backend.On("HomeDir", mock.Anything).Return(path) 583 backend.On("SourceDataPath").Return(sourcePath) 584 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 585 backend.On("Initialize", ctx, nodeHome).Return(nil) 586 backend.On("PutObject", mock.Anything, nodeHome, BackupFile, mock.Anything).Return(nil).Once() 587 backend.On("Write", mock.Anything, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 588 589 req := req 590 req.Duration = time.Hour 591 got := m.OnCanCommit(ctx, &req) 592 want := &CanCommitResponse{OpCreate, req.ID, _TimeoutShardCommit, ""} 593 assert.Equal(t, got, want) 594 595 err := m.OnCommit(ctx, &StatusRequest{OpCreate, req.ID, backendName}) 596 assert.Nil(t, err) 597 m.backupper.waitForCompletion(20, 50) 598 errMsg := context.Canceled.Error() 599 assert.Equal(t, string(backup.Transferring), backend.meta.Status) 600 assert.Equal(t, errMsg, backend.meta.Error) 601 assert.Contains(t, m.backupper.lastAsyncError.Error(), errMsg) 602 }) 603 604 t.Run("ExpirationTimeout", func(t *testing.T) { 605 var ( 606 sourcePath = t.TempDir() 607 sourcer = &fakeSourcer{} 608 backend = newFakeBackend() 609 ) 610 611 sourcer.On("Backupable", ctx, req.Classes).Return(nil) 612 ch := fakeBackupDescriptor(genClassDescriptions(t, sourcePath, cls, cls2)...) 613 sourcer.On("BackupDescriptors", any, backupID, mock.Anything).Return(ch) 614 sourcer.On("ReleaseBackup", ctx, backupID, mock.Anything).Return(nil) 615 616 backend.On("HomeDir", mock.Anything).Return(path) 617 backend.On("SourceDataPath").Return(sourcePath) 618 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, errNotFound) 619 backend.On("Initialize", ctx, nodeHome).Return(nil) 620 backend.On("PutObject", mock.Anything, nodeHome, BackupFile, mock.Anything).Return(nil).Once() 621 backend.On("Write", mock.Anything, backupID, mock.Anything, mock.Anything).Return(any, nil) 622 m := createManager(sourcer, nil, backend, nil) 623 624 req := req 625 req.Duration = time.Millisecond * 10 626 got := m.OnCanCommit(ctx, &req) 627 want := &CanCommitResponse{OpCreate, req.ID, req.Duration, ""} 628 assert.Equal(t, got, want) 629 630 m.backupper.waitForCompletion(20, 50) 631 assert.Contains(t, m.backupper.lastAsyncError.Error(), "timed out") 632 }) 633 } 634 635 func genClassDescriptions(t *testing.T, sourcePath string, classes ...string) []backup.ClassDescriptor { 636 ret := make([]backup.ClassDescriptor, len(classes)) 637 rawbytes := []byte("raw") 638 subDir := filepath.Join(sourcePath, "dir1") 639 if err := os.MkdirAll(subDir, os.ModePerm); err != nil { 640 t.Fatalf("create test subdirectory %s: %v", subDir, err) 641 } 642 files := []string{"dir1/file1", "dir1/file2", "counter.txt", "version.txt", "prop.txt"} 643 for _, p := range files { 644 p = filepath.Join(sourcePath, p) 645 if err := os.WriteFile(p, rawbytes, os.ModePerm); err != nil { 646 t.Fatalf("create test file %s: %v", p, err) 647 } 648 } 649 650 for i, cls := range classes { 651 ret[i] = backup.ClassDescriptor{ 652 Name: cls, Schema: rawbytes, ShardingState: rawbytes, 653 Shards: []*backup.ShardDescriptor{ 654 { 655 Name: "Shard1", Node: "Node-1", 656 Files: files[0:2], 657 DocIDCounterPath: files[2], 658 ShardVersionPath: files[3], 659 PropLengthTrackerPath: files[4], 660 DocIDCounter: rawbytes, 661 Version: rawbytes, 662 PropLengthTracker: rawbytes, 663 }, 664 }, 665 } 666 } 667 return ret 668 } 669 670 func fakeBackupDescriptor(descs ...backup.ClassDescriptor) <-chan backup.ClassDescriptor { 671 ch := make(chan backup.ClassDescriptor, len(descs)) 672 go func() { 673 for _, cls := range descs { 674 ch <- cls 675 } 676 close(ch) 677 }() 678 679 return ch 680 } 681 682 func createManager(sourcer Sourcer, schema schemaManger, backend modulecapabilities.BackupBackend, backendErr error) *Handler { 683 backends := &fakeBackupBackendProvider{backend, backendErr} 684 if sourcer == nil { 685 sourcer = &fakeSourcer{} 686 } 687 if schema == nil { 688 schema = &fakeSchemaManger{nodeName: nodeName} 689 } 690 691 logger, _ := test.NewNullLogger() 692 return NewHandler(logger, &fakeAuthorizer{}, schema, sourcer, backends) 693 }