gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/host/contractmanager/storagefolderadd_test.go (about) 1 package contractmanager 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "sync" 10 "testing" 11 "time" 12 13 "gitlab.com/SiaPrime/SiaPrime/modules" 14 ) 15 16 // TestAddStorageFolder tries to add a storage folder to the contract manager, 17 // blocking until the add has completed. 18 func TestAddStorageFolder(t *testing.T) { 19 if testing.Short() { 20 t.SkipNow() 21 } 22 t.Parallel() 23 cmt, err := newContractManagerTester("TestAddStorageFolder") 24 if err != nil { 25 t.Fatal(err) 26 } 27 defer cmt.panicClose() 28 29 // Add a storage folder to the contract manager tester. 30 storageFolderDir := filepath.Join(cmt.persistDir, "storageFolderOne") 31 // Create the storage folder dir. 32 err = os.MkdirAll(storageFolderDir, 0700) 33 if err != nil { 34 t.Fatal(err) 35 } 36 err = cmt.cm.AddStorageFolder(storageFolderDir, modules.SectorSize*storageFolderGranularity*2) 37 if err != nil { 38 t.Fatal(err) 39 } 40 41 // Check that the storage folder has been added. 42 sfs := cmt.cm.StorageFolders() 43 if len(sfs) != 1 { 44 t.Fatal("There should be one storage folder reported") 45 } 46 // Check that the storage folder has the right path and size. 47 if sfs[0].Path != storageFolderDir { 48 t.Error("storage folder reported with wrong path") 49 } 50 if sfs[0].Capacity != modules.SectorSize*storageFolderGranularity*2 { 51 t.Error("storage folder reported with wrong sector size") 52 } 53 } 54 55 // dependencyLargeFolder is a mocked dependency that will return files which 56 // can only handle 1 MiB of data being written to them. 57 type dependencyLargeFolder struct { 58 modules.ProductionDependencies 59 } 60 61 // limitFile will return an error if a call to Write is made that will put the 62 // total throughput of the file over 1 MiB. 63 type limitFile struct { 64 throughput int64 65 mu sync.Mutex 66 *os.File 67 sync.Mutex 68 } 69 70 // createFile will return a file that will return an error if a write will put 71 // the total throughput of the file over 1 MiB. 72 func (*dependencyLargeFolder) CreateFile(s string) (modules.File, error) { 73 osFile, err := os.Create(s) 74 if err != nil { 75 return nil, err 76 } 77 78 lf := &limitFile{ 79 File: osFile, 80 } 81 return lf, nil 82 } 83 84 // Truncate returns an error if the operation will put the total throughput of 85 // the file over 8 MiB. 86 func (l *limitFile) Truncate(offset int64) error { 87 l.mu.Lock() 88 defer l.mu.Unlock() 89 // If the limit has already been reached, return an error. 90 if l.throughput >= 1<<20 { 91 return errors.New("limitFile throughput limit reached earlier") 92 } 93 94 fi, err := l.Stat() 95 if err != nil { 96 return errors.New("limitFile could not fetch fileinfo: " + err.Error()) 97 } 98 // No throughput if file is shrinking. 99 if fi.Size() > offset { 100 return l.File.Truncate(offset) 101 } 102 writeSize := offset - fi.Size() 103 104 // If the limit has not been reached, pass the call through to the 105 // underlying file. Limit counting is a little wonky because we assume the 106 // file being passed in has currently a size of zero. 107 if l.throughput+writeSize <= 1<<20 { 108 l.throughput += writeSize 109 return l.File.Truncate(offset) 110 } 111 112 // If the limit has been reached, return an error. 113 return errors.New("limitFile throughput limit reached before all input was written to disk") 114 } 115 116 // TestAddLargeStorageFolder tries to add a storage folder that is too large to 117 // fit on disk. This is represented by mocking a file that returns an error 118 // after more than 8 MiB have been written. 119 func TestAddLargeStorageFolder(t *testing.T) { 120 if testing.Short() { 121 t.SkipNow() 122 } 123 t.Parallel() 124 d := new(dependencyLargeFolder) 125 cmt, err := newMockedContractManagerTester(d, "TestAddLargeStorageFolder") 126 if err != nil { 127 t.Fatal(err) 128 } 129 defer cmt.panicClose() 130 131 // Add a storage folder to the contract manager tester. 132 storageFolderDir := filepath.Join(cmt.persistDir, "storageFolderOne") 133 // Create the storage folder dir. 134 err = os.MkdirAll(storageFolderDir, 0700) 135 if err != nil { 136 t.Fatal(err) 137 } 138 addErr := cmt.cm.AddStorageFolder(storageFolderDir, modules.SectorSize*storageFolderGranularity*16) // Total size must exceed the limit of the limitFile. 139 // Should be a storage folder error, but with all the context adding, I'm 140 // not sure how to check the error type. 141 if addErr == nil { 142 t.Fatal(err) 143 } 144 145 // Check that the storage folder has been added. 146 sfs := cmt.cm.StorageFolders() 147 if len(sfs) != 0 { 148 t.Fatal("Storage folder add should have failed.") 149 } 150 // Check that the storage folder is empty - because the operation failed, 151 // any files that got created should have been removed. 152 files, err := ioutil.ReadDir(storageFolderDir) 153 if err != nil { 154 t.Fatal(err) 155 } 156 if len(files) != 0 { 157 t.Log(addErr) 158 t.Error("there should not be any files in the storage folder because the AddStorageFolder operation failed.") 159 t.Error(len(files)) 160 for _, file := range files { 161 t.Error(file.Name()) 162 } 163 } 164 } 165 166 // TestAddStorageFolderConcurrent adds multiple storage folders concurrently to 167 // the contract manager. 168 func TestAddStorageFolderConcurrent(t *testing.T) { 169 if testing.Short() { 170 t.SkipNow() 171 } 172 t.Parallel() 173 cmt, err := newContractManagerTester("TestAddStorageFolderConcurrent") 174 if err != nil { 175 t.Fatal(err) 176 } 177 defer cmt.panicClose() 178 179 // Add a storage folder to the contract manager tester. 180 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 181 storageFolderTwo := filepath.Join(cmt.persistDir, "storageFolderTwo") 182 storageFolderThree := filepath.Join(cmt.persistDir, "storageFolderThree") 183 // Create the storage folder dir. 184 err = os.MkdirAll(storageFolderOne, 0700) 185 if err != nil { 186 t.Fatal(err) 187 } 188 err = os.MkdirAll(storageFolderTwo, 0700) 189 if err != nil { 190 t.Fatal(err) 191 } 192 err = os.MkdirAll(storageFolderThree, 0700) 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 // Launch three calls to add simultaneously and wait for all three to 198 // finish. 199 var wg sync.WaitGroup 200 wg.Add(3) 201 go func() { 202 defer wg.Done() 203 err := cmt.cm.AddStorageFolder(storageFolderOne, modules.SectorSize*storageFolderGranularity*8) 204 if err != nil { 205 t.Fatal(err) 206 } 207 }() 208 go func() { 209 defer wg.Done() 210 err := cmt.cm.AddStorageFolder(storageFolderTwo, modules.SectorSize*storageFolderGranularity*8) 211 if err != nil { 212 t.Fatal(err) 213 } 214 }() 215 go func() { 216 defer wg.Done() 217 err = cmt.cm.AddStorageFolder(storageFolderThree, modules.SectorSize*storageFolderGranularity*8) 218 if err != nil { 219 t.Fatal(err) 220 } 221 }() 222 wg.Wait() 223 224 // Check that the storage folder has been added. 225 sfs := cmt.cm.StorageFolders() 226 if len(sfs) != 3 { 227 t.Fatal("There should be one storage folder reported") 228 } 229 } 230 231 // dependencyBlockSFOne is a mocked dependency for os.Create that will return a 232 // file for storage folder one only which will block on a call to file.Truncate 233 // until a signal has been given that the block can be released. 234 type dependencyBlockSFOne struct { 235 blockLifted chan struct{} 236 writeCalled chan struct{} 237 modules.ProductionDependencies 238 } 239 240 // blockedFile is the file that gets returned by dependencyBlockSFOne to 241 // storageFolderOne. 242 type blockedFile struct { 243 blockLifted chan struct{} 244 writeCalled chan struct{} 245 *os.File 246 sync.Mutex 247 } 248 249 // Truncate will block until a signal is given that the block may be lifted. 250 // Truncate will signal when it has been called for the first time, so that the 251 // tester knows the function has reached a blocking point. 252 func (bf *blockedFile) Truncate(offset int64) error { 253 if !strings.Contains(bf.File.Name(), "storageFolderOne") || strings.Contains(bf.File.Name(), "siahostmetadata.dat") { 254 return bf.File.Truncate(offset) 255 } 256 close(bf.writeCalled) 257 <-bf.blockLifted 258 return bf.File.Truncate(offset) 259 } 260 261 // createFile will return a normal file to all callers except for 262 // storageFolderOne, which will have calls to file.Write blocked until a signal 263 // is given that the blocks may be released. 264 func (d *dependencyBlockSFOne) CreateFile(s string) (modules.File, error) { 265 // If storageFolderOne, return a file that will not write until the signal 266 // is sent that writing is okay. 267 if strings.Contains(s, "storageFolderOne") { 268 file, err := os.Create(s) 269 if err != nil { 270 return nil, err 271 } 272 bf := &blockedFile{ 273 blockLifted: d.blockLifted, 274 writeCalled: d.writeCalled, 275 File: file, 276 } 277 return bf, nil 278 } 279 280 // If not storageFolderOne, return a normal file. 281 return os.Create(s) 282 } 283 284 // TestAddStorageFolderBlocking adds multiple storage folders concurrently to 285 // the contract manager, blocking on the first one to make sure that the others 286 // are still allowed to complete. 287 func TestAddStorageFolderBlocking(t *testing.T) { 288 if testing.Short() { 289 t.SkipNow() 290 } 291 t.Parallel() 292 // Create the mocked dependencies that will block for the first storage 293 // folder. 294 d := &dependencyBlockSFOne{ 295 blockLifted: make(chan struct{}), 296 writeCalled: make(chan struct{}), 297 } 298 299 // Create a contract manager tester with the mocked dependencies. 300 cmt, err := newMockedContractManagerTester(d, "TestAddStorageFolderBlocking") 301 if err != nil { 302 t.Fatal(err) 303 } 304 defer cmt.panicClose() 305 306 // Add a storage folder to the contract manager tester. 307 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 308 storageFolderTwo := filepath.Join(cmt.persistDir, "storageFolderTwo") 309 storageFolderThree := filepath.Join(cmt.persistDir, "storageFolderThree") 310 // Create the storage folder dir. 311 err = os.MkdirAll(storageFolderOne, 0700) 312 if err != nil { 313 t.Fatal(err) 314 } 315 err = os.MkdirAll(storageFolderTwo, 0700) 316 if err != nil { 317 t.Fatal(err) 318 } 319 err = os.MkdirAll(storageFolderThree, 0700) 320 if err != nil { 321 t.Fatal(err) 322 } 323 324 // Spin off the first goroutine, and then wait until write has been called 325 // on the underlying file. 326 sfOneSize := modules.SectorSize * storageFolderGranularity * 8 327 go func() { 328 err := cmt.cm.AddStorageFolder(storageFolderOne, sfOneSize) 329 if err != nil { 330 t.Fatal(err) 331 } 332 }() 333 select { 334 case <-time.After(time.Second * 5): 335 t.Fatal("storage folder not written out") 336 case <-d.writeCalled: 337 } 338 339 // Check the status of the storage folder. At this point, the folder should 340 // be returned as an unfinished storage folder addition, with progress 341 // indicating that the storage folder is at 0 bytes progressed out of 342 // sfOneSize. 343 sfs := cmt.cm.StorageFolders() 344 if len(sfs) != 1 { 345 t.Fatal("there should be one storage folder reported") 346 } 347 if sfs[0].ProgressNumerator != 0 { 348 t.Error("storage folder is showing progress despite being blocked") 349 } 350 if sfs[0].ProgressDenominator != sfOneSize+sectorMetadataDiskSize*storageFolderGranularity*8 { 351 t.Error("storage folder is not showing that an action is in progress, though one is", sfs[0].ProgressDenominator, sfOneSize) 352 } 353 354 var wg sync.WaitGroup 355 wg.Add(2) 356 go func() { 357 defer wg.Done() 358 err := cmt.cm.AddStorageFolder(storageFolderTwo, modules.SectorSize*storageFolderGranularity*8) 359 if err != nil { 360 t.Fatal(err) 361 } 362 }() 363 go func() { 364 defer wg.Done() 365 err = cmt.cm.AddStorageFolder(storageFolderThree, modules.SectorSize*storageFolderGranularity*8) 366 if err != nil { 367 t.Fatal(err) 368 } 369 }() 370 wg.Wait() 371 close(d.blockLifted) 372 cmt.cm.tg.Flush() 373 374 // Check that the storage folder has been added. 375 sfs = cmt.cm.StorageFolders() 376 if len(sfs) != 3 { 377 t.Fatal("There should be one storage folder reported") 378 } 379 // All actions should have completed, so all storage folders should be 380 // reporting '0' in the progress denominator. 381 for _, sf := range sfs { 382 if sf.ProgressDenominator != 0 { 383 t.Error("ProgressDenominator is indicating that actions still remain") 384 } 385 } 386 } 387 388 // TestAddStorageFolderConsecutive adds multiple storage folders consecutively 389 // to the contract manager, blocking on the first one to make sure that the 390 // others are still allowed to complete. 391 func TestAddStorageFolderConsecutive(t *testing.T) { 392 if testing.Short() { 393 t.SkipNow() 394 } 395 t.Parallel() 396 // Create a contract manager tester with the mocked dependencies. 397 cmt, err := newContractManagerTester("TestAddStorageFolderConsecutive") 398 if err != nil { 399 t.Fatal(err) 400 } 401 defer cmt.panicClose() 402 403 // Add a storage folder to the contract manager tester. 404 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 405 storageFolderTwo := filepath.Join(cmt.persistDir, "storageFolderTwo") 406 storageFolderThree := filepath.Join(cmt.persistDir, "storageFolderThree") 407 // Create the storage folder dir. 408 err = os.MkdirAll(storageFolderOne, 0700) 409 if err != nil { 410 t.Fatal(err) 411 } 412 err = os.MkdirAll(storageFolderTwo, 0700) 413 if err != nil { 414 t.Fatal(err) 415 } 416 err = os.MkdirAll(storageFolderThree, 0700) 417 if err != nil { 418 t.Fatal(err) 419 } 420 421 // Spin off the first goroutine, and then wait until write has been called 422 // on the underlying file. 423 sfSize := modules.SectorSize * storageFolderGranularity * 8 424 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 425 if err != nil { 426 t.Fatal(err) 427 } 428 err = cmt.cm.AddStorageFolder(storageFolderTwo, sfSize) 429 if err != nil { 430 t.Fatal(err) 431 } 432 err = cmt.cm.AddStorageFolder(storageFolderThree, sfSize) 433 if err != nil { 434 t.Fatal(err) 435 } 436 437 // Check that the storage folder has been added. 438 sfs := cmt.cm.StorageFolders() 439 if len(sfs) != 3 { 440 t.Fatal("There should be one storage folder reported") 441 } 442 // All actions should have completed, so all storage folders should be 443 // reporting '0' in the progress denominator. 444 for _, sf := range sfs { 445 if sf.ProgressDenominator != 0 { 446 t.Error("ProgressDenominator is indicating that actions still remain") 447 } 448 } 449 } 450 451 // TestAddStorageFolderDoubleAdd concurrently adds two storage 452 // folders with the same path to the contract manager. 453 func TestAddStorageFolderDoubleAdd(t *testing.T) { 454 if testing.Short() { 455 t.SkipNow() 456 } 457 t.Parallel() 458 // Create a contract manager tester with the mocked dependencies. 459 cmt, err := newContractManagerTester("TestAddStorageFolderDoubleAdd") 460 if err != nil { 461 t.Fatal(err) 462 } 463 defer cmt.panicClose() 464 465 // Add a storage folder to the contract manager tester. 466 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 467 // Create the storage folder dir. 468 err = os.MkdirAll(storageFolderOne, 0700) 469 if err != nil { 470 t.Fatal(err) 471 } 472 473 // Call AddStorageFolder in three separate goroutines, where the same path 474 // is used in each. The errors are not checked because one of the storage 475 // folders will succeed, but it's uncertain which one. 476 sfSize := modules.SectorSize * storageFolderGranularity * 8 477 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 478 if err != nil { 479 t.Fatal(err) 480 } 481 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize*2) 482 if err != ErrRepeatFolder { 483 t.Fatal(err) 484 } 485 486 // Check that the storage folder has been added. 487 sfs := cmt.cm.StorageFolders() 488 if len(sfs) != 1 { 489 t.Fatal("There should be one storage folder reported") 490 } 491 // All actions should have completed, so all storage folders should be 492 // reporting '0' in the progress denominator 493 for _, sf := range sfs { 494 if sf.ProgressDenominator != 0 { 495 t.Error("ProgressDenominator is indicating that actions still remain") 496 } 497 } 498 } 499 500 // dependencyNoSyncLoop is a mocked dependency that will disable the sync loop. 501 type dependencyNoSyncLoop struct { 502 modules.ProductionDependencies 503 } 504 505 // disrupt will disrupt the threadedSyncLoop, causing the loop to terminate as 506 // soon as it is created. 507 func (*dependencyNoSyncLoop) Disrupt(s string) bool { 508 if s == "threadedSyncLoopStart" || s == "cleanWALFile" { 509 // Disrupt threadedSyncLoop. The sync loop will exit immediately 510 // instead of executing commits. Also disrupt the process that removes 511 // the WAL file following clean shutdown. 512 return true 513 } 514 return false 515 } 516 517 // TestAddStorageFolderDoubleAddNoCommit hijacks the sync loop in the contract 518 // manager such that the sync loop will not run automatically. Then, without 519 // doing an actual commit, the test will indicate to open functions that a 520 // commit has completed, allowing the next storage folder operation to happen. 521 // Because the changes were finalized but not committed, extra code coverage 522 // should be achieved, though the result of the storage folder being rejected 523 // should be the same. 524 func TestAddStorageFolderDoubleAddNoCommit(t *testing.T) { 525 if testing.Short() { 526 t.SkipNow() 527 } 528 t.Parallel() 529 d := new(dependencyNoSyncLoop) 530 cmt, err := newMockedContractManagerTester(d, "TestAddStorageFolderDoubleAddNoCommit") 531 if err != nil { 532 t.Fatal(err) 533 } 534 // The closing of this channel must happen after the call to panicClose. 535 closeFakeSyncChan := make(chan struct{}) 536 defer close(closeFakeSyncChan) 537 defer cmt.panicClose() 538 539 // The sync loop will never run, which means naively AddStorageFolder will 540 // never return. To get AddStorageFolder to return before the commit 541 // completes, spin up an alternate sync loop which only performs the 542 // signaling responsibilities of the commit function. 543 go func() { 544 for { 545 select { 546 case <-closeFakeSyncChan: 547 return 548 case <-time.After(time.Millisecond * 250): 549 // Signal that the commit operation has completed, even though 550 // it has not. 551 cmt.cm.wal.mu.Lock() 552 close(cmt.cm.wal.syncChan) 553 cmt.cm.wal.syncChan = make(chan struct{}) 554 cmt.cm.wal.mu.Unlock() 555 } 556 } 557 }() 558 559 // Add a storage folder to the contract manager tester. 560 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 561 // Create the storage folder dir. 562 err = os.MkdirAll(storageFolderOne, 0700) 563 if err != nil { 564 t.Fatal(err) 565 } 566 567 // Call AddStorageFolder in three separate goroutines, where the same path 568 // is used in each. The errors are not checked because one of the storage 569 // folders will succeed, but it's uncertain which one. 570 sfSize := modules.SectorSize * storageFolderGranularity * 8 571 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 572 if err != nil { 573 t.Fatal(err) 574 } 575 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize*2) 576 if err != ErrRepeatFolder { 577 t.Fatal(err) 578 } 579 580 // Check that the storage folder has been added. 581 sfs := cmt.cm.StorageFolders() 582 if len(sfs) != 1 { 583 t.Fatal("There should be one storage folder reported", len(sfs)) 584 } 585 // All actions should have completed, so all storage folders should be 586 // reporting '0' in the progress denominator 587 for _, sf := range sfs { 588 if sf.ProgressDenominator != 0 { 589 t.Error("ProgressDenominator is indicating that actions still remain") 590 } 591 } 592 } 593 594 // TestAddStorageFolderFailedCommit adds a storage folder without ever saving 595 // the settings. 596 func TestAddStorageFolderFailedCommit(t *testing.T) { 597 if testing.Short() { 598 t.SkipNow() 599 } 600 t.Parallel() 601 d := new(dependencyNoSettingsSave) 602 cmt, err := newMockedContractManagerTester(d, "TestAddStorageFolderFailedCommit") 603 if err != nil { 604 t.Fatal(err) 605 } 606 defer cmt.panicClose() 607 608 // Add a storage folder to the contract manager tester. 609 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 610 // Create the storage folder dir. 611 err = os.MkdirAll(storageFolderOne, 0700) 612 if err != nil { 613 t.Fatal(err) 614 } 615 sfSize := modules.SectorSize * storageFolderGranularity * 8 616 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 617 if err != nil { 618 t.Fatal(err) 619 } 620 d.mu.Lock() 621 d.triggered = true 622 d.mu.Unlock() 623 624 // Check that the storage folder has been added. 625 sfs := cmt.cm.StorageFolders() 626 if len(sfs) != 1 { 627 t.Fatal("There should be one storage folder reported") 628 } 629 // All actions should have completed, so all storage folders should be 630 // reporting '0' in the progress denominator 631 if sfs[0].ProgressDenominator != 0 { 632 t.Error("ProgressDenominator is indicating that actions still remain") 633 } 634 635 // Close the contract manager and replace it with a new contract manager. 636 // The new contract manager should have normal dependencies. 637 err = cmt.cm.Close() 638 if err != nil { 639 t.Fatal(err) 640 } 641 // Create the new contract manager using the same persist dir, so that it 642 // will see the uncommitted WAL. 643 cmt.cm, err = New(filepath.Join(cmt.persistDir, modules.ContractManagerDir)) 644 if err != nil { 645 t.Fatal(err) 646 } 647 // Check that the storage folder was properly recovered. 648 sfs = cmt.cm.StorageFolders() 649 if len(sfs) != 1 { 650 t.Fatal("There should be one storage folder reported", len(sfs)) 651 } 652 } 653 654 // dependencySFAddNoFinish is a mocked dependency that will prevent the 655 type dependencySFAddNoFinish struct { 656 modules.ProductionDependencies 657 } 658 659 // disrupt will disrupt the threadedSyncLoop, causing the loop to terminate as 660 // soon as it is created. 661 func (d *dependencySFAddNoFinish) Disrupt(s string) bool { 662 if s == "storageFolderAddFinish" { 663 return true 664 } 665 if s == "cleanWALFile" { 666 // Prevent the WAL file from being removed. 667 return true 668 } 669 return false 670 } 671 672 // TestAddStorageFolderUnfinishedCreate hijacks both the sync loop and the 673 // AddStorageFolder code to create a situation where the added storage folder 674 // is started but not seen through to conclusion, and no commit is run. 675 func TestAddStorageFolderUnfinishedCreate(t *testing.T) { 676 if testing.Short() { 677 t.SkipNow() 678 } 679 t.Parallel() 680 d := new(dependencySFAddNoFinish) 681 cmt, err := newMockedContractManagerTester(d, "TestAddStorageFolderUnfinishedCreate") 682 if err != nil { 683 t.Fatal(err) 684 } 685 defer cmt.panicClose() 686 687 // Add a storage folder to the contract manager tester. 688 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 689 // Create the storage folder dir. 690 err = os.MkdirAll(storageFolderOne, 0700) 691 if err != nil { 692 t.Fatal(err) 693 } 694 // Call AddStorageFolder, knowing that the changes will not be properly 695 // committed, and that the call itself will not actually complete. 696 sfSize := modules.SectorSize * storageFolderGranularity * 8 697 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 698 if err != nil { 699 t.Fatal(err) 700 } 701 702 // Check that the storage folder has been added. 703 sfs := cmt.cm.StorageFolders() 704 if len(sfs) != 1 { 705 t.Fatal("There should be one storage folder reported") 706 } 707 708 // Close the contract manager and replace it with a new contract manager. 709 // The new contract manager should have normal dependencies. 710 err = cmt.cm.Close() 711 if err != nil { 712 t.Fatal(err) 713 } 714 // Create the new contract manager using the same persist dir, so that it 715 // will see the uncommitted WAL. 716 cmt.cm, err = New(filepath.Join(cmt.persistDir, modules.ContractManagerDir)) 717 if err != nil { 718 t.Fatal(err) 719 } 720 // Check that the storage folder was properly removed - incomplete storage 721 // folder adds should be removed upon startup. 722 sfs = cmt.cm.StorageFolders() 723 if len(sfs) != 0 { 724 t.Error("Storage folder add should have failed.") 725 } 726 // Check that the storage folder is empty - because the operation failed, 727 // any files that got created should have been removed. 728 files, err := ioutil.ReadDir(storageFolderOne) 729 if err != nil { 730 t.Error(err) 731 } 732 if len(files) != 0 { 733 t.Error("there should not be any files in the storage folder because the AddStorageFolder operation failed:", len(files)) 734 t.Error(len(files)) 735 for _, file := range files { 736 t.Error(file.Name()) 737 } 738 } 739 } 740 741 // TestAddStorageFolderDoubleAddConcurrent concurrently adds two storage 742 // folders with the same path to the contract manager. 743 func TestAddStorageFolderDoubleAddConcurrent(t *testing.T) { 744 if testing.Short() { 745 t.SkipNow() 746 } 747 t.Parallel() 748 // Create a contract manager tester with the mocked dependencies. 749 cmt, err := newContractManagerTester("TestAddStorageFolderDoubleAddConcurrent") 750 if err != nil { 751 t.Fatal(err) 752 } 753 defer cmt.panicClose() 754 755 // Add a storage folder to the contract manager tester. 756 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 757 // Create the storage folder dir. 758 err = os.MkdirAll(storageFolderOne, 0700) 759 if err != nil { 760 t.Fatal(err) 761 } 762 763 // Call AddStorageFolder in three separate goroutines, where the same path 764 // is used in each. The errors are not checked because one of the storage 765 // folders will succeed, but it's uncertain which one. 766 var wg sync.WaitGroup 767 sfSize := modules.SectorSize * storageFolderGranularity * 8 768 wg.Add(3) 769 go func() { 770 _ = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 771 wg.Done() 772 }() 773 go func() { 774 _ = cmt.cm.AddStorageFolder(storageFolderOne, sfSize*2) 775 wg.Done() 776 }() 777 go func() { 778 _ = cmt.cm.AddStorageFolder(storageFolderOne, sfSize*3) 779 wg.Done() 780 }() 781 wg.Wait() 782 783 // Check that the storage folder has been added. 784 sfs := cmt.cm.StorageFolders() 785 if len(sfs) != 1 { 786 t.Fatal("There should be one storage folder reported") 787 } 788 // All actions should have completed, so all storage folders should be 789 // reporting '0' in the progress denominator. 790 for _, sf := range sfs { 791 if sf.ProgressDenominator != 0 { 792 t.Error("ProgressDenominator is indicating that actions still remain") 793 } 794 } 795 } 796 797 // TestAddStorageFolderReload adds a storage folder to the contract manager, 798 // and then reloads the contract manager to see if the storage folder is still 799 // there. 800 func TestAddStorageFolderReload(t *testing.T) { 801 if testing.Short() { 802 t.SkipNow() 803 } 804 t.Parallel() 805 // Create a contract manager tester with the mocked dependencies. 806 cmt, err := newContractManagerTester("TestAddStorageFolderReload") 807 if err != nil { 808 t.Fatal(err) 809 } 810 defer cmt.panicClose() 811 812 // Add a storage folder to the contract manager tester. 813 storageFolderOne := filepath.Join(cmt.persistDir, "storageFolderOne") 814 // Create the storage folder dir. 815 err = os.MkdirAll(storageFolderOne, 0700) 816 if err != nil { 817 t.Fatal(err) 818 } 819 sfSize := modules.SectorSize * storageFolderGranularity * 24 820 err = cmt.cm.AddStorageFolder(storageFolderOne, sfSize) 821 if err != nil { 822 t.Fatal(err) 823 } 824 825 // Check that the storage folder has been added. 826 sfs := cmt.cm.StorageFolders() 827 if len(sfs) != 1 { 828 t.Fatal("There should be one storage folder reported") 829 } 830 // Check that the size of the storage folder is correct. 831 if sfs[0].Capacity != sfSize { 832 t.Error("capacity reported by storage folder is not the capacity alloacted") 833 } 834 if sfs[0].CapacityRemaining != sfSize { 835 t.Error("capacity remaining reported by storage folder is not the capacity alloacted") 836 } 837 // All actions should have completed, so all storage folders should be 838 // reporting '0' in the progress denominator. 839 for _, sf := range sfs { 840 if sf.ProgressDenominator != 0 { 841 t.Error("ProgressDenominator is indicating that actions still remain") 842 } 843 } 844 845 // Close the contract manager and open a new one using the same 846 // persistence. 847 err = cmt.cm.Close() 848 if err != nil { 849 t.Fatal(err) 850 } 851 cmt.cm, err = New(filepath.Join(cmt.persistDir, modules.ContractManagerDir)) 852 if err != nil { 853 t.Fatal(err) 854 } 855 856 // Check that the storage folder has been added. 857 sfs = cmt.cm.StorageFolders() 858 if len(sfs) != 1 { 859 t.Fatal("There should be one storage folder reported", len(sfs)) 860 } 861 // Check that the size of the storage folder is correct. 862 if sfs[0].Capacity != sfSize { 863 t.Error("capacity reported by storage folder is not the capacity alloacted") 864 } 865 if sfs[0].CapacityRemaining != sfSize { 866 t.Error("capacity remaining reported by storage folder is not the capacity alloacted", sfs[0].Capacity, sfs[0].CapacityRemaining) 867 } 868 // Check that the storage folder as represented on disk has the correct 869 // size. 870 sectorLookupTableSize := int64(storageFolderGranularity * 24 * sectorMetadataDiskSize) 871 expectedSize := int64(sfSize) 872 fi, err := os.Stat(filepath.Join(storageFolderOne, sectorFile)) 873 if err != nil { 874 t.Fatal(err) 875 } 876 if fi.Size() != expectedSize { 877 t.Error("sector file had unexpected size", fi.Size(), expectedSize) 878 } 879 fi, err = os.Stat(filepath.Join(storageFolderOne, metadataFile)) 880 if err != nil { 881 t.Fatal(err) 882 } 883 if fi.Size() != sectorLookupTableSize { 884 t.Error("sector file had unexpected size", fi.Size(), sectorLookupTableSize) 885 } 886 }