github.com/weaviate/weaviate@v1.24.6/usecases/backup/restorer_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 "encoding/json" 17 "errors" 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/mock" 24 "github.com/weaviate/weaviate/entities/backup" 25 "github.com/weaviate/weaviate/entities/models" 26 "github.com/weaviate/weaviate/usecases/sharding" 27 ) 28 29 // ErrAny represent a random error 30 var ( 31 ErrAny = errors.New("any error") 32 any = mock.Anything 33 ) 34 35 func (r *restorer) Restore(ctx context.Context, 36 req *Request, 37 desc *backup.BackupDescriptor, 38 store nodeStore, 39 ) (*models.BackupRestoreResponse, error) { 40 status := string(backup.Started) 41 returnData := &models.BackupRestoreResponse{ 42 Classes: req.Classes, 43 ID: req.ID, 44 Backend: req.Backend, 45 Status: &status, 46 Path: store.HomeDir(), 47 } 48 if _, err := r.restore(ctx, req, desc, store); err != nil { 49 return nil, err 50 } 51 return returnData, nil 52 } 53 54 func (r *restorer) waitForCompletion(backend, id string, n, ms int) Status { 55 for i := 0; i < n; i++ { 56 delay := time.Millisecond * time.Duration(ms) 57 time.Sleep(delay) 58 status, err := r.status(backend, id) 59 if err != nil { 60 continue 61 } 62 if status.Status == backup.Success || status.Status == backup.Failed { 63 return status 64 } 65 } 66 return Status{} 67 } 68 69 func TestRestoreStatus(t *testing.T) { 70 t.Parallel() 71 var ( 72 backendType = "s3" 73 id = "1234" 74 m = createManager(nil, nil, nil, nil) 75 starTime = time.Now().UTC() 76 nodeHome = id + "/" + nodeName 77 path = "bucket/backups/" + nodeHome 78 ) 79 // initial state 80 _, err := m.restorer.status(backendType, id) 81 if err == nil || !strings.Contains(err.Error(), "not found") { 82 t.Errorf("must return an error if backup doesn't exist") 83 } 84 // active state 85 m.restorer.lastOp.reqStat = reqStat{ 86 Starttime: starTime, 87 ID: id, 88 Status: backup.Transferring, 89 Path: path, 90 } 91 st, err := m.restorer.status(backendType, id) 92 if err != nil { 93 t.Errorf("get active status: %v", err) 94 } 95 expected := Status{Path: path, StartedAt: starTime, Status: backup.Transferring} 96 if expected != st { 97 t.Errorf("get active status: got=%v want=%v", st, expected) 98 } 99 // cached status 100 m.restorer.lastOp.reset() 101 st.CompletedAt = starTime 102 m.restorer.restoreStatusMap.Store("s3/"+id, st) 103 st, err = m.restorer.status(backendType, id) 104 if err != nil { 105 t.Errorf("fetch status from map: %v", err) 106 } 107 expected.CompletedAt = starTime 108 if expected != st { 109 t.Errorf("fetch status from map got=%v want=%v", st, expected) 110 } 111 } 112 113 func TestRestoreRequestValidation(t *testing.T) { 114 var ( 115 cls = "MyClass" 116 backendName = "s3" 117 rawbytes = []byte("hello") 118 id = "1234" 119 timept = time.Now().UTC() 120 ctx = context.Background() 121 nodeHome = id + "/" + nodeName 122 path = "bucket/backups/" + nodeHome 123 req = &BackupRequest{ 124 Backend: backendName, 125 ID: id, 126 Include: []string{cls}, 127 Exclude: []string{}, 128 } 129 ) 130 meta := backup.BackupDescriptor{ 131 ID: id, 132 StartedAt: timept, 133 Version: "1", 134 ServerVersion: "1", 135 Status: string(backup.Success), 136 Classes: []backup.ClassDescriptor{{ 137 Name: cls, Schema: rawbytes, ShardingState: rawbytes, 138 }}, 139 } 140 141 t.Run("BackendFailure", func(t *testing.T) { // backend provider fails 142 backend := newFakeBackend() 143 m2 := createManager(nil, nil, backend, ErrAny) 144 _, err := m2.Restore(ctx, nil, &BackupRequest{ 145 Backend: backendName, 146 ID: id, 147 Include: []string{cls}, 148 Exclude: []string{}, 149 }) 150 assert.NotNil(t, err) 151 assert.Contains(t, err.Error(), backendName) 152 }) 153 154 t.Run("GetMetadataFile", func(t *testing.T) { 155 backend := newFakeBackend() 156 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, ErrAny) 157 backend.On("GetObject", ctx, req.ID, BackupFile).Return(nil, ErrAny) 158 159 backend.On("HomeDir", mock.Anything).Return(path) 160 m2 := createManager(nil, nil, backend, nil) 161 _, err := m2.Restore(ctx, nil, req) 162 if err == nil || !strings.Contains(err.Error(), "find") { 163 t.Errorf("must return an error if it fails to get meta data: %v", err) 164 } 165 // meta data not found 166 backend = newFakeBackend() 167 backend.On("HomeDir", mock.Anything).Return(path) 168 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, backup.ErrNotFound{}) 169 backend.On("GetObject", ctx, req.ID, BackupFile).Return(nil, backup.ErrNotFound{}) 170 171 m3 := createManager(nil, nil, backend, nil) 172 173 _, err = m3.Restore(ctx, nil, req) 174 if _, ok := err.(backup.ErrNotFound); !ok { 175 t.Errorf("must return an error if meta data doesn't exist: %v", err) 176 } 177 }) 178 179 t.Run("FailedBackup", func(t *testing.T) { 180 backend := newFakeBackend() 181 bytes := marshalMeta(backup.BackupDescriptor{ID: id, Status: string(backup.Failed)}) 182 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 183 backend.On("HomeDir", mock.Anything).Return(path) 184 m2 := createManager(nil, nil, backend, nil) 185 _, err := m2.Restore(ctx, nil, req) 186 assert.NotNil(t, err) 187 assert.Contains(t, err.Error(), backup.Failed) 188 assert.IsType(t, backup.ErrUnprocessable{}, err) 189 }) 190 191 t.Run("BackupWithHigherVersion", func(t *testing.T) { 192 var ( 193 backend = newFakeBackend() 194 meta = backup.BackupDescriptor{ 195 ID: id, 196 StartedAt: timept, 197 Version: "3.0", 198 ServerVersion: "1", 199 Status: string(backup.Success), 200 Classes: []backup.ClassDescriptor{{ 201 Name: cls, Schema: rawbytes, ShardingState: rawbytes, 202 }}, 203 } 204 bytes = marshalMeta(meta) 205 ) 206 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 207 backend.On("HomeDir", mock.Anything).Return(path) 208 m2 := createManager(nil, nil, backend, nil) 209 _, err := m2.Restore(ctx, nil, req) 210 assert.NotNil(t, err) 211 assert.Contains(t, err.Error(), errMsgHigherVersion) 212 assert.IsType(t, backup.ErrUnprocessable{}, err) 213 }) 214 215 t.Run("FailedOldBackup", func(t *testing.T) { 216 backend := newFakeBackend() 217 bytes := marshalMeta(backup.BackupDescriptor{ID: id, Status: string(backup.Failed)}) 218 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, ErrAny) 219 backend.On("GetObject", ctx, id, BackupFile).Return(bytes, nil) 220 221 backend.On("HomeDir", mock.Anything).Return(path) 222 m2 := createManager(nil, nil, backend, nil) 223 _, err := m2.Restore(ctx, nil, req) 224 assert.NotNil(t, err) 225 assert.Contains(t, err.Error(), backup.Failed) 226 assert.IsType(t, backup.ErrUnprocessable{}, err) 227 }) 228 229 t.Run("CorruptedBackupFile", func(t *testing.T) { 230 backend := newFakeBackend() 231 bytes := marshalMeta(backup.BackupDescriptor{ID: id, Status: string(backup.Success)}) 232 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 233 backend.On("HomeDir", mock.Anything).Return(path) 234 m2 := createManager(nil, nil, backend, nil) 235 _, err := m2.Restore(ctx, nil, req) 236 assert.NotNil(t, err) 237 assert.IsType(t, backup.ErrUnprocessable{}, err) 238 assert.Contains(t, err.Error(), "corrupted") 239 }) 240 241 t.Run("WrongBackupFile", func(t *testing.T) { 242 backend := newFakeBackend() 243 bytes := marshalMeta(backup.BackupDescriptor{ID: "123", Status: string(backup.Success)}) 244 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 245 backend.On("HomeDir", mock.Anything).Return(path) 246 m2 := createManager(nil, nil, backend, nil) 247 _, err := m2.Restore(ctx, nil, req) 248 assert.NotNil(t, err) 249 assert.IsType(t, backup.ErrUnprocessable{}, err) 250 assert.Contains(t, err.Error(), "wrong backup file") 251 }) 252 253 t.Run("UnknownClass", func(t *testing.T) { 254 backend := newFakeBackend() 255 bytes := marshalMeta(meta) 256 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 257 backend.On("HomeDir", mock.Anything).Return(path) 258 m2 := createManager(nil, nil, backend, nil) 259 _, err := m2.Restore(ctx, nil, &BackupRequest{ID: id, Include: []string{"unknown"}}) 260 assert.NotNil(t, err) 261 assert.Contains(t, err.Error(), "unknown") 262 }) 263 264 t.Run("EmptyResultClassList", func(t *testing.T) { // backup was successful but class list is empty 265 backend := newFakeBackend() 266 bytes := marshalMeta(meta) 267 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 268 backend.On("HomeDir", mock.Anything).Return(path) 269 m2 := createManager(nil, nil, backend, nil) 270 _, err := m2.Restore(ctx, nil, &BackupRequest{ID: id, Exclude: []string{cls}}) 271 assert.NotNil(t, err) 272 assert.Contains(t, err.Error(), "empty") 273 }) 274 } 275 276 func TestManagerRestoreBackup(t *testing.T) { 277 var ( 278 cls = "Article" 279 rawbytes = []byte("hello") 280 backendName = "gcs" 281 backupID = "1" 282 timept = time.Now().UTC() 283 ctx = context.Background() 284 nodeHome = backupID + "/" + nodeName 285 path = "bucket/backups/" + nodeHome 286 ) 287 288 rawShardingStateBytes, _ := json.Marshal(&sharding.State{ 289 IndexID: cls, 290 Physical: map[string]sharding.Physical{"cT9eTErXgmTX": { 291 Name: "cT9eTErXgmTX", 292 BelongsToNodes: []string{nodeName}, 293 }}, 294 }) 295 rawClassBytes, _ := json.Marshal(&models.Class{ 296 Class: cls, 297 }) 298 meta2 := backup.BackupDescriptor{ 299 ID: backupID, 300 StartedAt: timept, 301 Version: Version, 302 ServerVersion: "1", 303 Status: string(backup.Success), 304 305 Classes: []backup.ClassDescriptor{{ 306 Name: cls, 307 Schema: rawClassBytes, 308 ShardingState: rawShardingStateBytes, 309 Chunks: map[int32][]string{1: {"Shard1"}}, 310 Shards: []*backup.ShardDescriptor{ 311 { 312 Name: "Shard1", Node: "Node-1", 313 Chunk: 1, 314 }, 315 }, 316 }}, 317 } 318 meta1 := backup.BackupDescriptor{ 319 ID: backupID, 320 StartedAt: timept, 321 Version: version1, 322 ServerVersion: "1", 323 Status: string(backup.Success), 324 325 Classes: []backup.ClassDescriptor{{ 326 Name: cls, 327 Schema: rawClassBytes, 328 ShardingState: rawShardingStateBytes, 329 Chunks: map[int32][]string{1: {"Shard1"}}, 330 Shards: []*backup.ShardDescriptor{ 331 { 332 Name: "Shard1", Node: "Node-1", 333 Files: []string{"dir1/file1", "dir2/file2"}, 334 DocIDCounterPath: "counter.txt", 335 ShardVersionPath: "version.txt", 336 PropLengthTrackerPath: "prop.txt", 337 DocIDCounter: rawbytes, 338 Version: rawbytes, 339 PropLengthTracker: rawbytes, 340 Chunk: 1, 341 }, 342 }, 343 }}, 344 } 345 346 t.Run("ClassAlreadyExists", func(t *testing.T) { 347 var ( 348 req1 = BackupRequest{ 349 ID: backupID, 350 Include: []string{cls}, 351 Backend: backendName, 352 } 353 backend = newFakeBackend() 354 sourcer = &fakeSourcer{} 355 dataPath = t.TempDir() 356 ) 357 sourcer.On("ClassExists", cls).Return(true) 358 bytes := marshalMeta(meta2) 359 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 360 backend.On("HomeDir", mock.Anything).Return(path) 361 backend.On("SourceDataPath").Return(dataPath) 362 backend.On("Read", any, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 363 m := createManager(sourcer, nil, backend, nil) 364 resp1, err := m.Restore(ctx, nil, &req1) 365 assert.Nil(t, err) 366 status1 := string(backup.Started) 367 want1 := &models.BackupRestoreResponse{ 368 Backend: backendName, 369 Classes: req1.Include, 370 ID: backupID, 371 Status: &status1, 372 Path: path, 373 } 374 assert.Equal(t, resp1, want1) 375 lastStatus := m.restorer.waitForCompletion(req1.Backend, req1.ID, 10, 50) 376 assert.Nil(t, err) 377 assert.Equal(t, backup.Failed, lastStatus.Status) 378 }) 379 380 t.Run("AnotherBackupIsInProgress", func(t *testing.T) { 381 req1 := BackupRequest{ 382 ID: backupID, 383 Include: []string{cls}, 384 Backend: backendName, 385 } 386 backend := newFakeBackend() 387 sourcer := &fakeSourcer{} 388 sourcer.On("ClassExists", cls).Return(false) 389 bytes := marshalMeta(meta2) 390 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 391 backend.On("HomeDir", mock.Anything).Return(path) 392 // simulate work by delaying return of SourceDataPath() 393 backend.On("SourceDataPath").Return(t.TempDir()).After(time.Hour) 394 m2 := createManager(sourcer, nil, backend, nil) 395 _, err := m2.Restore(ctx, nil, &BackupRequest{ID: backupID}) 396 assert.Nil(t, err) 397 m := createManager(sourcer, nil, backend, nil) 398 resp1, err := m.Restore(ctx, nil, &req1) 399 assert.Nil(t, err) 400 status1 := string(backup.Started) 401 want1 := &models.BackupRestoreResponse{ 402 Backend: backendName, 403 Classes: req1.Include, 404 ID: backupID, 405 Status: &status1, 406 Path: path, 407 } 408 assert.Equal(t, resp1, want1) 409 // another caller 410 resp2, err := m.Restore(ctx, nil, &req1) 411 assert.NotNil(t, err) 412 assert.Contains(t, err.Error(), "already in progress") 413 assert.IsType(t, backup.ErrUnprocessable{}, err) 414 assert.Nil(t, resp2) 415 }) 416 417 t.Run("Success", func(t *testing.T) { 418 var ( 419 req1 = BackupRequest{ 420 ID: backupID, 421 Include: []string{cls}, 422 Backend: backendName, 423 } 424 backend = newFakeBackend() 425 sourcer = &fakeSourcer{} 426 dataPath = t.TempDir() 427 ) 428 sourcer.On("ClassExists", cls).Return(false) 429 bytes := marshalMeta(meta2) 430 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 431 backend.On("HomeDir", mock.Anything).Return(path) 432 backend.On("SourceDataPath").Return(dataPath) 433 backend.On("Read", any, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 434 m := createManager(sourcer, nil, backend, nil) 435 resp1, err := m.Restore(ctx, nil, &req1) 436 assert.Nil(t, err) 437 status1 := string(backup.Started) 438 want1 := &models.BackupRestoreResponse{ 439 Backend: backendName, 440 Classes: req1.Include, 441 ID: backupID, 442 Status: &status1, 443 Path: path, 444 } 445 assert.Equal(t, resp1, want1) 446 assert.Nil(t, err) 447 lastStatus := m.restorer.waitForCompletion(req1.Backend, req1.ID, 12, 50) 448 assert.Equal(t, backup.Success, lastStatus.Status) 449 }) 450 451 readSourceFile := func(t *testing.T, newVerion bool) { 452 req1 := BackupRequest{ 453 ID: backupID, 454 Include: []string{cls}, 455 Backend: backendName, 456 } 457 backend := newFakeBackend() 458 sourcer := &fakeSourcer{} 459 sourcer.On("ClassExists", cls).Return(false) 460 var bytes []byte 461 if newVerion { 462 bytes = marshalMeta(meta2) 463 backend.On("Read", any, nodeHome, mock.Anything, mock.Anything).Return(any, ErrAny) 464 } else { 465 bytes = marshalMeta(meta1) 466 backend.On("WriteToFile", any, nodeHome, mock.Anything, mock.Anything).Return(ErrAny) 467 } 468 469 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 470 backend.On("HomeDir", mock.Anything).Return(path) 471 backend.On("SourceDataPath").Return(t.TempDir()) 472 m := createManager(sourcer, nil, backend, nil) 473 resp1, err := m.Restore(ctx, nil, &req1) 474 assert.Nil(t, err) 475 status1 := string(backup.Started) 476 want1 := &models.BackupRestoreResponse{ 477 Backend: backendName, 478 Classes: req1.Include, 479 ID: backupID, 480 Status: &status1, 481 Path: path, 482 } 483 assert.Equal(t, resp1, want1) 484 lastStatus := m.restorer.waitForCompletion(req1.Backend, req1.ID, 10, 50) 485 486 assert.Nil(t, err) 487 assert.Equal(t, backup.Failed, lastStatus.Status) 488 // 489 } 490 t.Run("ReadSourceFile", func(t *testing.T) { 491 readSourceFile(t, true) 492 }) 493 t.Run("ReadSourceFileV1", func(t *testing.T) { 494 readSourceFile(t, false) 495 }) 496 497 t.Run("RestoreClassFails", func(t *testing.T) { 498 req1 := BackupRequest{ 499 ID: backupID, 500 Include: []string{cls}, 501 Backend: backendName, 502 } 503 backend := newFakeBackend() 504 sourcer := &fakeSourcer{} 505 schema := fakeSchemaManger{errRestoreClass: ErrAny, nodeName: nodeName} 506 sourcer.On("ClassExists", cls).Return(false) 507 bytes := marshalMeta(meta2) 508 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 509 backend.On("HomeDir", mock.Anything).Return(path) 510 backend.On("SourceDataPath").Return(t.TempDir()) 511 backend.On("Read", any, nodeHome, mock.Anything, mock.Anything).Return(any, nil) 512 m := createManager(sourcer, &schema, backend, nil) 513 resp1, err := m.Restore(ctx, nil, &req1) 514 assert.Nil(t, err) 515 status1 := string(backup.Started) 516 want1 := &models.BackupRestoreResponse{ 517 Backend: backendName, 518 Classes: req1.Include, 519 ID: backupID, 520 Status: &status1, 521 Path: path, 522 } 523 assert.Equal(t, resp1, want1) 524 lastStatus := m.restorer.waitForCompletion(req1.Backend, req1.ID, 10, 50) 525 526 assert.Nil(t, err) 527 assert.Equal(t, lastStatus.Status, backup.Failed) 528 }) 529 } 530 531 func TestManagerCoordinatedRestore(t *testing.T) { 532 var ( 533 backendName = "gcs" 534 rawbytes = []byte("hello") 535 timept = time.Now().UTC() 536 cls = "Class-A" 537 backupID = "2" 538 ctx = context.Background() 539 nodeHome = backupID + "/" + nodeName 540 path = "bucket/backups/" + nodeHome 541 req = Request{ 542 Method: OpRestore, 543 ID: backupID, 544 Classes: []string{cls}, 545 Backend: backendName, 546 Duration: time.Millisecond * 20, 547 } 548 ) 549 rawShardingStateBytes, _ := json.Marshal(&sharding.State{ 550 IndexID: cls, 551 Physical: map[string]sharding.Physical{"cT9eTErXgmTX": { 552 Name: "cT9eTErXgmTX", 553 BelongsToNodes: []string{nodeName}, 554 }}, 555 }) 556 rawClassBytes, _ := json.Marshal(&models.Class{ 557 Class: cls, 558 }) 559 560 metadata := backup.BackupDescriptor{ 561 ID: backupID, 562 StartedAt: timept, 563 Version: "1", 564 ServerVersion: "1", 565 Status: string(backup.Success), 566 Classes: []backup.ClassDescriptor{{ 567 Name: cls, 568 Schema: rawClassBytes, 569 ShardingState: rawShardingStateBytes, 570 Shards: []*backup.ShardDescriptor{ 571 { 572 Name: "Shard1", Node: "Node-1", 573 Files: []string{"dir1/file1", "dir2/file2"}, 574 DocIDCounterPath: "counter.txt", 575 ShardVersionPath: "version.txt", 576 PropLengthTrackerPath: "prop.txt", 577 DocIDCounter: rawbytes, 578 Version: rawbytes, 579 PropLengthTracker: rawbytes, 580 }, 581 }, 582 }}, 583 } 584 585 t.Run("GetMetadataFile", func(t *testing.T) { 586 backend := newFakeBackend() 587 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(nil, backup.ErrNotFound{}) 588 backend.On("GetObject", ctx, backupID, BackupFile).Return(nil, backup.ErrNotFound{}) 589 backend.On("HomeDir", mock.Anything).Return(path) 590 bm := createManager(nil, nil, backend, nil) 591 resp := bm.OnCanCommit(ctx, &req) 592 assert.Contains(t, resp.Err, errMetaNotFound.Error()) 593 assert.Equal(t, resp.Timeout, time.Duration(0)) 594 }) 595 596 t.Run("AnotherBackupIsInProgress", func(t *testing.T) { 597 backend := newFakeBackend() 598 sourcer := &fakeSourcer{} 599 sourcer.On("ClassExists", cls).Return(false) 600 bytes := marshalMeta(metadata) 601 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 602 backend.On("HomeDir", mock.Anything).Return(path) 603 // simulate work by delaying return of SourceDataPath() 604 backend.On("SourceDataPath").Return(t.TempDir()).After(time.Minute * 2) 605 m := createManager(sourcer, nil, backend, nil) 606 resp := m.OnCanCommit(ctx, &req) 607 assert.Equal(t, resp.Err, "") 608 resp = m.OnCanCommit(ctx, &req) 609 assert.Contains(t, resp.Err, "already in progress") 610 assert.Equal(t, time.Duration(0), resp.Timeout) 611 }) 612 613 t.Run("Success", func(t *testing.T) { 614 req := req 615 req.Duration = time.Hour 616 backend := newFakeBackend() 617 sourcer := &fakeSourcer{} 618 sourcer.On("ClassExists", cls).Return(false) 619 bytes := marshalMeta(metadata) 620 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 621 backend.On("HomeDir", mock.Anything).Return(path) 622 backend.On("SourceDataPath").Return(t.TempDir()) 623 backend.On("WriteToFile", any, nodeHome, mock.Anything, mock.Anything).Return(nil) 624 m := createManager(sourcer, nil, backend, nil) 625 resp1 := m.OnCanCommit(ctx, &req) 626 want1 := &CanCommitResponse{ 627 Method: OpRestore, 628 ID: req.ID, 629 Timeout: _TimeoutShardCommit, 630 } 631 assert.Equal(t, want1, resp1) 632 err := m.OnCommit(ctx, &StatusRequest{Method: OpRestore, ID: req.ID, Backend: req.Backend}) 633 assert.Nil(t, err) 634 lastStatus := m.restorer.waitForCompletion(req.Backend, req.ID, 12, 50) 635 assert.Nil(t, err) 636 assert.Equal(t, lastStatus.Status, backup.Success) 637 }) 638 639 t.Run("Abort", func(t *testing.T) { 640 req := req 641 req.Duration = time.Hour 642 backend := newFakeBackend() 643 sourcer := &fakeSourcer{} 644 sourcer.On("ClassExists", cls).Return(false) 645 bytes := marshalMeta(metadata) 646 backend.On("GetObject", ctx, nodeHome, BackupFile).Return(bytes, nil) 647 backend.On("HomeDir", mock.Anything).Return(path) 648 backend.On("SourceDataPath").Return(t.TempDir()) 649 backend.On("WriteToFile", any, nodeHome, mock.Anything, mock.Anything).Return(nil) 650 m := createManager(sourcer, nil, backend, nil) 651 resp1 := m.OnCanCommit(ctx, &req) 652 want1 := &CanCommitResponse{ 653 Method: OpRestore, 654 ID: req.ID, 655 Timeout: _TimeoutShardCommit, 656 } 657 assert.Equal(t, want1, resp1) 658 err := m.OnAbort(ctx, &AbortRequest{Method: OpRestore, ID: req.ID}) 659 assert.Nil(t, err) 660 lastStatus := m.restorer.waitForCompletion(req.Backend, req.ID, 10, 50) 661 662 assert.Nil(t, err) 663 assert.Equal(t, lastStatus.Status, backup.Failed) 664 }) 665 } 666 667 func TestRestoreOnStatus(t *testing.T) { 668 t.Parallel() 669 var ( 670 backendType = "s3" 671 id = "1234" 672 m = createManager(nil, nil, nil, nil) 673 ctx = context.Background() 674 starTime = time.Now().UTC() 675 nodeHome = id + "/" + nodeName 676 path = "bucket/backups/" + nodeHome 677 req = StatusRequest{ 678 Method: OpRestore, 679 ID: id, 680 Backend: backendType, 681 } 682 ) 683 // initial state 684 got := m.OnStatus(ctx, &req) 685 if !strings.Contains(got.Err, "not found") { 686 t.Errorf("must return an error if backup doesn't exist") 687 } 688 // active state 689 m.restorer.lastOp.reqStat = reqStat{ 690 Starttime: starTime, 691 ID: id, 692 Status: backup.Transferring, 693 Path: path, 694 } 695 got = m.OnStatus(ctx, &req) 696 expected := StatusResponse{Method: OpRestore, ID: req.ID, Status: backup.Transferring} 697 if expected != *got { 698 t.Errorf("get active status: got=%v want=%v", got, expected) 699 } 700 // cached status 701 m.restorer.lastOp.reset() 702 st := Status{Path: path, StartedAt: starTime, Status: backup.Transferring, CompletedAt: starTime} 703 m.restorer.restoreStatusMap.Store("s3/"+id, st) 704 got = m.OnStatus(ctx, &req) 705 if expected != *got { 706 t.Errorf("fetch status from map got=%v want=%v", st, expected) 707 } 708 } 709 710 func marshalMeta(m backup.BackupDescriptor) []byte { 711 bytes, _ := json.MarshalIndent(m, "", "") 712 return bytes 713 }