gitlab.com/jokerrs1/Sia@v1.3.2/node/api/host_test.go (about) 1 package api 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/url" 8 "os" 9 "path/filepath" 10 "reflect" 11 "strconv" 12 "testing" 13 "time" 14 15 "github.com/NebulousLabs/Sia/build" 16 "github.com/NebulousLabs/Sia/crypto" 17 "github.com/NebulousLabs/Sia/modules" 18 "github.com/NebulousLabs/Sia/modules/host/contractmanager" 19 "github.com/NebulousLabs/Sia/types" 20 ) 21 22 var ( 23 // Various folder sizes for testing host storage folder resizing. 24 // Must be provided as strings to the API call. 25 minFolderSizeString = strconv.FormatUint(modules.SectorSize*contractmanager.MinimumSectorsPerStorageFolder, 10) 26 maxFolderSizeString = strconv.FormatUint(modules.SectorSize*contractmanager.MaximumSectorsPerStorageFolder, 10) 27 tooSmallFolderString = strconv.FormatUint(modules.SectorSize*(contractmanager.MinimumSectorsPerStorageFolder-1), 10) 28 tooLargeFolderString = strconv.FormatUint(modules.SectorSize*(contractmanager.MaximumSectorsPerStorageFolder+1), 10) 29 mediumSizeFolderString = strconv.FormatUint(modules.SectorSize*contractmanager.MinimumSectorsPerStorageFolder*3, 10) 30 31 // Test cases for resizing a host's storage folder. 32 // Running all the invalid cases before the valid ones simplifies some 33 // logic in the tests that use resizeTests. 34 resizeTests = []struct { 35 sizeString string 36 size uint64 37 err error 38 }{ 39 // invalid sizes 40 {"", 0, io.EOF}, 41 {"0", 0, contractmanager.ErrSmallStorageFolder}, 42 {tooSmallFolderString, modules.SectorSize * (contractmanager.MinimumSectorsPerStorageFolder - 1), contractmanager.ErrSmallStorageFolder}, 43 {tooLargeFolderString, modules.SectorSize * (contractmanager.MaximumSectorsPerStorageFolder + 1), contractmanager.ErrLargeStorageFolder}, 44 45 // valid sizes 46 // 47 // TODO: Re-enable these when the host can support resizing into the 48 // same folder. 49 // 50 // {minFolderSizeString, contractmanager.MinimumSectorsPerStorageFolder * modules.SectorSize, nil}, 51 // {maxFolderSizeString, contractmanager.MaximumSectorsPerStorageFolder * modules.SectorSize, nil}, 52 // {mediumSizeFolderString, 3 * contractmanager.MinimumSectorsPerStorageFolder * modules.SectorSize, nil}, 53 } 54 ) 55 56 // TestEstimateWeight tests that /host/estimatescore works correctly. 57 func TestEstimateWeight(t *testing.T) { 58 if testing.Short() { 59 t.SkipNow() 60 } 61 t.Parallel() 62 63 st, err := createServerTester(t.Name()) 64 if err != nil { 65 t.Fatal(err) 66 } 67 defer st.server.panicClose() 68 69 // announce a host, create an allowance, upload some data. 70 if err := st.announceHost(); err != nil { 71 t.Fatal(err) 72 } 73 if err := st.acceptContracts(); err != nil { 74 t.Fatal(err) 75 } 76 if err := st.setHostStorage(); err != nil { 77 t.Fatal(err) 78 } 79 80 var eg HostEstimateScoreGET 81 if err := st.getAPI("/host/estimatescore", &eg); err != nil { 82 t.Fatal(err) 83 } 84 originalEstimate := eg.EstimatedScore 85 86 // verify that the estimate is being correctly updated by setting a massively 87 // increased min contract price and verifying that the score decreases. 88 is := st.host.InternalSettings() 89 is.MinContractPrice = is.MinContractPrice.Add(types.SiacoinPrecision.Mul64(9999999999)) 90 if err := st.host.SetInternalSettings(is); err != nil { 91 t.Fatal(err) 92 } 93 if err := st.getAPI("/host/estimatescore", &eg); err != nil { 94 t.Fatal(err) 95 } 96 if eg.EstimatedScore.Cmp(originalEstimate) != -1 { 97 t.Fatal("score estimate did not decrease after incrementing mincontractprice") 98 } 99 100 // add a few hosts to the hostdb and verify that the conversion rate is 101 // reflected correctly 102 st2, err := blankServerTester(t.Name() + "-st2") 103 if err != nil { 104 t.Fatal(err) 105 } 106 defer st2.panicClose() 107 st3, err := blankServerTester(t.Name() + "-st3") 108 if err != nil { 109 t.Fatal(err) 110 } 111 defer st3.panicClose() 112 st4, err := blankServerTester(t.Name() + "-st4") 113 if err != nil { 114 t.Fatal(err) 115 } 116 defer st4.panicClose() 117 sts := []*serverTester{st, st2, st3, st4} 118 err = fullyConnectNodes(sts) 119 if err != nil { 120 t.Fatal(err) 121 } 122 err = fundAllNodes(sts) 123 if err != nil { 124 t.Fatal(err) 125 } 126 for i, tester := range sts { 127 is = tester.host.InternalSettings() 128 is.MinContractPrice = types.SiacoinPrecision.Mul64(1000 + (1000 * uint64(i))) 129 err = tester.host.SetInternalSettings(is) 130 if err != nil { 131 t.Fatal(err) 132 } 133 } 134 err = announceAllHosts(sts) 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 tests := []struct { 140 price types.Currency 141 minConversionRate float64 142 }{ 143 {types.SiacoinPrecision, 100}, 144 {types.SiacoinPrecision.Mul64(50), 98}, 145 {types.SiacoinPrecision.Mul64(2500), 50}, 146 {types.SiacoinPrecision.Mul64(3000), 10}, 147 {types.SiacoinPrecision.Mul64(30000), 0.00001}, 148 } 149 for i, test := range tests { 150 err = st.getAPI(fmt.Sprintf("/host/estimatescore?mincontractprice=%v", test.price.String()), &eg) 151 if err != nil { 152 t.Fatal("test", i, "failed:", err) 153 } 154 if eg.ConversionRate < test.minConversionRate { 155 t.Fatalf("test %v: incorrect conversion rate: got %v wanted %v\n", i, eg.ConversionRate, test.minConversionRate) 156 } 157 } 158 } 159 160 // TestHostSettingsHandlerParsing verifies that providing invalid host settings 161 // doesn't reset the host's settings. 162 func TestHostSettingsHandlerParsing(t *testing.T) { 163 if testing.Short() { 164 t.SkipNow() 165 } 166 t.Parallel() 167 168 st, err := createServerTester(t.Name()) 169 if err != nil { 170 t.Fatal(err) 171 } 172 defer st.server.panicClose() 173 174 settings := st.host.InternalSettings() 175 settingsValues := url.Values{} 176 settingsValues.Set("maxdownloadbatchsize", "foo") 177 st.stdPostAPI("/host", settingsValues) 178 newSettings := st.host.InternalSettings() 179 if !reflect.DeepEqual(newSettings, settings) { 180 t.Fatal("invalid acceptingcontracts value changed host settings! got", newSettings, "wanted", settings) 181 } 182 } 183 184 // TestWorkingStatus tests that the host's WorkingStatus field is set 185 // correctly. 186 func TestWorkingStatus(t *testing.T) { 187 if testing.Short() { 188 t.SkipNow() 189 } 190 t.Parallel() 191 192 st, err := createServerTester(t.Name()) 193 if err != nil { 194 t.Fatal(err) 195 } 196 defer st.server.panicClose() 197 198 // announce a host, create an allowance, upload some data. 199 if err := st.announceHost(); err != nil { 200 t.Fatal(err) 201 } 202 if err := st.acceptContracts(); err != nil { 203 t.Fatal(err) 204 } 205 if err := st.setHostStorage(); err != nil { 206 t.Fatal(err) 207 } 208 209 // Set an allowance for the renter, allowing a contract to be formed. 210 allowanceValues := url.Values{} 211 allowanceValues.Set("funds", testFunds) 212 allowanceValues.Set("period", testPeriod) 213 allowanceValues.Set("renewwindow", testRenewWindow) 214 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 215 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 216 t.Fatal(err) 217 } 218 219 // Block until the allowance has finished forming contracts. 220 err = build.Retry(50, time.Millisecond*250, func() error { 221 var rc RenterContracts 222 err = st.getAPI("/renter/contracts", &rc) 223 if err != nil { 224 return errors.New("couldn't get renter stats") 225 } 226 if len(rc.Contracts) != 1 { 227 return errors.New("no contracts") 228 } 229 return nil 230 }) 231 if err != nil { 232 t.Fatal("allowance setting failed") 233 } 234 235 // Create a file. 236 path := filepath.Join(st.dir, "test.dat") 237 fileBytes := 1024 238 if err := createRandFile(path, fileBytes); err != nil { 239 t.Fatal(err) 240 } 241 242 // Upload to host. 243 uploadValues := url.Values{} 244 uploadValues.Set("source", path) 245 if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 246 t.Fatal(err) 247 } 248 249 // Only one piece will be uploaded (10% at current redundancy) 250 var rf RenterFiles 251 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 252 st.getAPI("/renter/files", &rf) 253 time.Sleep(50 * time.Millisecond) 254 } 255 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 256 t.Error(rf.Files[0].UploadProgress) 257 t.Fatal("uploading has failed") 258 } 259 260 err = retry(30, time.Second, func() error { 261 var hg HostGET 262 st.getAPI("/host", &hg) 263 264 if hg.WorkingStatus != modules.HostWorkingStatusWorking { 265 return errors.New("expected host to be working") 266 } 267 return nil 268 }) 269 if err != nil { 270 t.Fatal(err) 271 } 272 } 273 274 // TestConnectabilityStatus tests that the host's ConnectabilityStatus field is 275 // set correctly. 276 func TestConnectabilityStatus(t *testing.T) { 277 if testing.Short() { 278 t.SkipNow() 279 } 280 t.Parallel() 281 282 // create and announce a host 283 st, err := createServerTester(t.Name()) 284 if err != nil { 285 t.Fatal(err) 286 } 287 defer st.server.panicClose() 288 289 if err := st.announceHost(); err != nil { 290 t.Fatal(err) 291 } 292 293 err = retry(30, time.Second, func() error { 294 var hg HostGET 295 st.getAPI("/host", &hg) 296 297 if hg.ConnectabilityStatus != modules.HostConnectabilityStatusConnectable { 298 return errors.New("expected host to be connectable") 299 } 300 return nil 301 }) 302 if err != nil { 303 t.Fatal(err) 304 } 305 } 306 307 // TestStorageHandler tests that host storage is being reported correctly. 308 func TestStorageHandler(t *testing.T) { 309 if testing.Short() { 310 t.SkipNow() 311 } 312 t.Parallel() 313 st, err := createServerTester(t.Name()) 314 if err != nil { 315 t.Fatal(err) 316 } 317 defer st.server.panicClose() 318 319 // Announce the host and start accepting contracts. 320 if err := st.announceHost(); err != nil { 321 t.Fatal(err) 322 } 323 if err := st.acceptContracts(); err != nil { 324 t.Fatal(err) 325 } 326 if err := st.setHostStorage(); err != nil { 327 t.Fatal(err) 328 } 329 330 // Set an allowance for the renter, allowing a contract to be formed. 331 allowanceValues := url.Values{} 332 allowanceValues.Set("funds", testFunds) 333 allowanceValues.Set("period", testPeriod) 334 allowanceValues.Set("renewwindow", testRenewWindow) 335 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 336 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 337 t.Fatal(err) 338 } 339 340 // Block until the allowance has finished forming contracts. 341 err = build.Retry(50, time.Millisecond*250, func() error { 342 var rc RenterContracts 343 err = st.getAPI("/renter/contracts", &rc) 344 if err != nil { 345 return errors.New("couldn't get renter stats") 346 } 347 if len(rc.Contracts) != 1 { 348 return errors.New("no contracts") 349 } 350 return nil 351 }) 352 if err != nil { 353 t.Fatal("allowance setting failed") 354 } 355 356 // Create a file. 357 path := filepath.Join(st.dir, "test.dat") 358 fileBytes := 1024 359 if err := createRandFile(path, fileBytes); err != nil { 360 t.Fatal(err) 361 } 362 363 // Upload to host. 364 uploadValues := url.Values{} 365 uploadValues.Set("source", path) 366 if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 367 t.Fatal(err) 368 } 369 370 // Only one piece will be uploaded (10% at current redundancy) 371 var rf RenterFiles 372 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 373 st.getAPI("/renter/files", &rf) 374 time.Sleep(50 * time.Millisecond) 375 } 376 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 377 t.Error(rf.Files[0].UploadProgress) 378 t.Fatal("uploading has failed") 379 } 380 381 var sg StorageGET 382 if err := st.getAPI("/host/storage", &sg); err != nil { 383 t.Fatal(err) 384 } 385 386 // Uploading succeeded, so /host/storage should be reporting a successful 387 // write. 388 if sg.Folders[0].SuccessfulWrites != 1 { 389 t.Fatalf("expected 1 successful write, got %v", sg.Folders[0].SuccessfulWrites) 390 } 391 if used := sg.Folders[0].Capacity - sg.Folders[0].CapacityRemaining; used != modules.SectorSize { 392 t.Fatalf("expected used capacity to be the size of one sector (%v bytes), got %v bytes", modules.SectorSize, used) 393 } 394 } 395 396 // TestAddFolderNoPath tests that an API call to add a storage folder fails if 397 // no path was provided. 398 func TestAddFolderNoPath(t *testing.T) { 399 if testing.Short() { 400 t.SkipNow() 401 } 402 t.Parallel() 403 st, err := createServerTester(t.Name()) 404 if err != nil { 405 t.Fatal(err) 406 } 407 defer st.server.panicClose() 408 409 // Try adding a storage folder without setting "path" in the API call. 410 addValues := url.Values{} 411 addValues.Set("size", mediumSizeFolderString) 412 err = st.stdPostAPI("/host/storage/folders/add", addValues) 413 if err == nil { 414 t.Fatal(err) 415 } 416 417 // Setting the path to an empty string should trigger the same error. 418 addValues.Set("path", "") 419 err = st.stdPostAPI("/host/storage/folders/add", addValues) 420 if err == nil { 421 t.Fatal(err) 422 } 423 } 424 425 // TestAddFolderNoSize tests that an API call to add a storage folder fails if 426 // no path was provided. 427 func TestAddFolderNoSize(t *testing.T) { 428 if testing.Short() { 429 t.SkipNow() 430 } 431 t.Parallel() 432 st, err := createServerTester(t.Name()) 433 if err != nil { 434 t.Fatal(err) 435 } 436 defer st.server.panicClose() 437 438 // Try adding a storage folder without setting "size" in the API call. 439 addValues := url.Values{} 440 addValues.Set("path", st.dir) 441 err = st.stdPostAPI("/host/storage/folders/add", addValues) 442 if err == nil || err.Error() != io.EOF.Error() { 443 t.Fatalf("expected error to be %v, got %v", io.EOF, err) 444 } 445 } 446 447 // TestAddSameFolderTwice tests that an API call that attempts to add a 448 // host storage folder that's already been added is handled gracefully. 449 func TestAddSameFolderTwice(t *testing.T) { 450 if testing.Short() { 451 t.SkipNow() 452 } 453 t.Parallel() 454 st, err := createServerTester(t.Name()) 455 if err != nil { 456 t.Fatal(err) 457 } 458 defer st.server.panicClose() 459 460 // Make the call to add a storage folder twice. 461 addValues := url.Values{} 462 addValues.Set("path", st.dir) 463 addValues.Set("size", mediumSizeFolderString) 464 err = st.stdPostAPI("/host/storage/folders/add", addValues) 465 if err != nil { 466 t.Fatal(err) 467 } 468 err = st.stdPostAPI("/host/storage/folders/add", addValues) 469 if err == nil || err.Error() != contractmanager.ErrRepeatFolder.Error() { 470 t.Fatalf("expected err to be %v, got %v", err, contractmanager.ErrRepeatFolder) 471 } 472 } 473 474 // TestResizeEmptyStorageFolder tests that invalid and valid calls to resize 475 // an empty storage folder are properly handled. 476 func TestResizeEmptyStorageFolder(t *testing.T) { 477 if testing.Short() { 478 t.SkipNow() 479 } 480 t.Parallel() 481 st, err := createServerTester(t.Name()) 482 if err != nil { 483 t.Fatal(err) 484 } 485 defer st.server.panicClose() 486 487 // Announce the host and start accepting contracts. 488 if err := st.announceHost(); err != nil { 489 t.Fatal(err) 490 } 491 if err := st.acceptContracts(); err != nil { 492 t.Fatal(err) 493 } 494 if err := st.setHostStorage(); err != nil { 495 t.Fatal(err) 496 } 497 498 // Find out how large the host's initial storage folder is. 499 var sg StorageGET 500 if err := st.getAPI("/host/storage", &sg); err != nil { 501 t.Fatal(err) 502 } 503 defaultSize := sg.Folders[0].Capacity 504 // Convert defaultSize (uint64) to a string for the API call. 505 defaultSizeString := strconv.FormatUint(defaultSize, 10) 506 507 resizeValues := url.Values{} 508 resizeValues.Set("path", st.dir) 509 resizeValues.Set("newsize", defaultSizeString) 510 511 // Attempting to resize to the same size should return an error. 512 err = st.stdPostAPI("/host/storage/folders/resize", resizeValues) 513 if err == nil || err.Error() != contractmanager.ErrNoResize.Error() { 514 t.Fatalf("expected error %v, got %v", contractmanager.ErrNoResize, err) 515 } 516 517 // Try resizing to a bunch of sizes (invalid ones first, valid ones second). 518 // This ordering simplifies logic within the for loop. 519 for i, test := range resizeTests { 520 // Attempt to resize the host's storage folder. 521 resizeValues.Set("newsize", test.sizeString) 522 err = st.stdPostAPI("/host/storage/folders/resize", resizeValues) 523 if (err == nil && test.err != nil) || (err != nil && err.Error() != test.err.Error()) { 524 t.Fatalf("test %v: expected error to be %v, got %v", i, test.err, err) 525 } 526 527 // Find out if the resize call worked as expected. 528 if err := st.getAPI("/host/storage", &sg); err != nil { 529 t.Fatal(err) 530 } 531 // If the test size is valid, check that the folder has been resized 532 // properly. 533 if test.err == nil { 534 // Check that the folder's total capacity has been updated. 535 if got := sg.Folders[0].Capacity; got != test.size { 536 t.Fatalf("test %v: expected folder to be resized to %v; got %v instead", i, test.size, got) 537 } 538 // Check that the folder's remaining capacity has been updated. 539 if got := sg.Folders[0].CapacityRemaining; got != test.size { 540 t.Fatalf("folder should be empty, but capacity remaining (%v) != total capacity (%v)", got, test.size) 541 } 542 } else { 543 // If the test size is invalid, the folder should not have been 544 // resized. The invalid test cases are all run before the valid ones, 545 // so the folder size should still be defaultSize. 546 if got := sg.Folders[0].Capacity; got != defaultSize { 547 t.Fatalf("folder was resized to an invalid size (%v) in a test case that should have failed: %v", got, test) 548 } 549 } 550 } 551 } 552 553 // TestResizeNonemptyStorageFolder tests that invalid and valid calls to resize 554 // a storage folder with one sector filled are properly handled. 555 // Ideally, we would also test a very full storage folder (including the case 556 // where the host tries to resize to a size smaller than the amount of data 557 // in the folder), but that would be a very expensive test. 558 func TestResizeNonemptyStorageFolder(t *testing.T) { 559 if testing.Short() { 560 t.SkipNow() 561 } 562 t.Parallel() 563 st, err := createServerTester(t.Name()) 564 if err != nil { 565 t.Fatal(err) 566 } 567 defer st.server.panicClose() 568 569 // Announce the host and start accepting contracts. 570 if err := st.announceHost(); err != nil { 571 t.Fatal(err) 572 } 573 if err := st.acceptContracts(); err != nil { 574 t.Fatal(err) 575 } 576 if err := st.setHostStorage(); err != nil { 577 t.Fatal(err) 578 } 579 580 // Set an allowance for the renter, allowing a contract to be formed. 581 allowanceValues := url.Values{} 582 allowanceValues.Set("funds", testFunds) 583 allowanceValues.Set("period", testPeriod) 584 allowanceValues.Set("renewwindow", testRenewWindow) 585 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 586 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 587 t.Fatal(err) 588 } 589 590 // Block until the allowance has finished forming contracts. 591 err = build.Retry(50, time.Millisecond*250, func() error { 592 var rc RenterContracts 593 err = st.getAPI("/renter/contracts", &rc) 594 if err != nil { 595 return errors.New("couldn't get renter stats") 596 } 597 if len(rc.Contracts) != 1 { 598 return errors.New("no contracts") 599 } 600 return nil 601 }) 602 if err != nil { 603 t.Fatal("allowance setting failed") 604 } 605 606 // Create a file. 607 path := filepath.Join(st.dir, "test.dat") 608 fileBytes := 1024 609 if err := createRandFile(path, fileBytes); err != nil { 610 t.Fatal(err) 611 } 612 613 // Upload to host. 614 uploadValues := url.Values{} 615 uploadValues.Set("source", path) 616 if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 617 t.Fatal(err) 618 } 619 620 // Only one piece will be uploaded (10% at current redundancy) 621 var rf RenterFiles 622 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 623 st.getAPI("/renter/files", &rf) 624 time.Sleep(50 * time.Millisecond) 625 } 626 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 627 t.Error(rf.Files[0].UploadProgress) 628 t.Fatal("uploading has failed") 629 } 630 631 // Find out how large the host's initial storage folder is. 632 var sg StorageGET 633 if err := st.getAPI("/host/storage", &sg); err != nil { 634 t.Fatal(err) 635 } 636 defaultSize := sg.Folders[0].Capacity 637 // Convert defaultSize (uint64) to a string for the API call. 638 defaultSizeString := strconv.FormatUint(defaultSize, 10) 639 640 resizeValues := url.Values{} 641 resizeValues.Set("path", st.dir) 642 resizeValues.Set("newsize", defaultSizeString) 643 644 // Attempting to resize to the same size should return an error. 645 err = st.stdPostAPI("/host/storage/folders/resize", resizeValues) 646 if err == nil || err.Error() != contractmanager.ErrNoResize.Error() { 647 t.Fatalf("expected error %v, got %v", contractmanager.ErrNoResize, err) 648 } 649 650 // Try resizing to a bunch of sizes (invalid ones first, valid ones second). 651 // This ordering simplifies logic within the for loop. 652 for _, test := range resizeTests { 653 // Attempt to resize the host's storage folder. 654 resizeValues.Set("newsize", test.sizeString) 655 err = st.stdPostAPI("/host/storage/folders/resize", resizeValues) 656 if (err == nil && test.err != nil) || (err != nil && test.err == nil) || (err != nil && err.Error() != test.err.Error()) { 657 t.Fatalf("expected error to be %v, got %v", test.err, err) 658 } 659 660 // Find out if the resize call worked as expected. 661 if err := st.getAPI("/host/storage", &sg); err != nil { 662 t.Fatal(err) 663 } 664 // If the test size is valid, check that the folder has been resized 665 // properly. 666 if test.err == nil { 667 // Check that the folder's total capacity has been updated. 668 if sg.Folders[0].Capacity != test.size { 669 t.Fatalf("expected folder to be resized to %v; got %v instead", test.size, sg.Folders[0].Capacity) 670 } 671 // Since one sector has been uploaded, the available capacity 672 // should be one sector size smaller than the total capacity. 673 if used := test.size - sg.Folders[0].CapacityRemaining; used != modules.SectorSize { 674 t.Fatalf("used capacity (%v) != the size of 1 sector (%v)", used, modules.SectorSize) 675 } 676 } else { 677 // If the test size is invalid, the folder should not have been 678 // resized. The invalid test cases are all run before the valid 679 // ones, so the folder size should still be defaultSize. 680 if got := sg.Folders[0].Capacity; got != defaultSize { 681 t.Fatalf("folder was resized to an invalid size (%v) in a test case that should have failed: %v", got, test) 682 } 683 } 684 } 685 } 686 687 // TestResizeNonexistentFolder checks that an API call to resize a nonexistent 688 // folder triggers the appropriate error. 689 func TestResizeNonexistentFolder(t *testing.T) { 690 if testing.Short() { 691 t.SkipNow() 692 } 693 t.Parallel() 694 st, err := createServerTester(t.Name()) 695 if err != nil { 696 t.Fatal(err) 697 } 698 defer st.server.panicClose() 699 700 // No folder has been created yet at st.dir, so using it as the path for 701 // the resize call should trigger an error. 702 resizeValues := url.Values{} 703 resizeValues.Set("path", st.dir) 704 resizeValues.Set("newsize", mediumSizeFolderString) 705 err = st.stdPostAPI("/host/storage/folders/resize", resizeValues) 706 if err == nil || err.Error() != errStorageFolderNotFound.Error() { 707 t.Fatalf("expected error to be %v, got %v", errStorageFolderNotFound, err) 708 } 709 } 710 711 // TestStorageFolderUnavailable simulates the situation where a storage folder 712 // is not available to the host when the host starts, verifying that it sets 713 // FailedWrites and FailedReads correctly and eventually finds the storage 714 // folder when it is made available to the host again. 715 func TestStorageFolderUnavailable(t *testing.T) { 716 if testing.Short() || !build.VLONG { 717 t.SkipNow() 718 } 719 t.Parallel() 720 721 st, err := createServerTester(t.Name()) 722 if err != nil { 723 t.Fatal(err) 724 } 725 defer st.server.Close() 726 727 // add a storage folder 728 sfPath := build.TempDir(t.Name(), "storagefolder") 729 err = os.MkdirAll(sfPath, 0755) 730 if err != nil { 731 t.Fatal(err) 732 } 733 sfValues := url.Values{} 734 sfValues.Set("path", sfPath) 735 sfValues.Set("size", "1048576") 736 err = st.stdPostAPI("/host/storage/folders/add", sfValues) 737 if err != nil { 738 t.Fatal(err) 739 } 740 741 var sfs StorageGET 742 err = st.getAPI("/host/storage", &sfs) 743 if err != nil { 744 t.Fatal(err) 745 } 746 747 if sfs.Folders[0].FailedReads != 0 || sfs.Folders[0].FailedWrites != 0 { 748 t.Fatal("newly added folder has failed reads or writes") 749 } 750 751 // remove the folder on disk 752 st.server.Close() 753 sfPath2 := build.TempDir(t.Name(), "storagefolder-old") 754 err = os.Rename(sfPath, sfPath2) 755 if err != nil { 756 t.Fatal(err) 757 } 758 759 // reload the host 760 st, err = st.reloadedServerTester() 761 if err != nil { 762 t.Fatal(err) 763 } 764 defer st.server.Close() 765 766 err = st.getAPI("/host/storage", &sfs) 767 if err != nil { 768 t.Fatal(err) 769 } 770 if sfs.Folders[0].FailedWrites < 999 { 771 t.Fatal("storage folder should have lots of failed writes after being moved on disk") 772 } 773 if sfs.Folders[0].FailedReads < 999 { 774 t.Fatal("storage folder should have lots of failed reads after being moved on disk") 775 } 776 777 // try some actions on the dead storage folder 778 // resize 779 sfValues.Set("size", "2097152") 780 err = st.stdPostAPI("/host/storage/folders/resize", sfValues) 781 if err == nil { 782 t.Fatal("expected resize on unavailable storage folder to fail") 783 } 784 // remove 785 err = st.stdPostAPI("/host/storage/folders/remove", sfValues) 786 if err == nil { 787 t.Fatal("expected remove on unavailable storage folder to fail") 788 } 789 790 // move the folder back 791 err = os.Rename(sfPath2, sfPath) 792 if err != nil { 793 t.Fatal(err) 794 } 795 796 // wait for the contract manager to recheck the storage folder 797 // NOTE: this is a hard-coded constant based on the contractmanager's maxFolderRecheckInterval constant. 798 time.Sleep(time.Second * 10) 799 800 // verify the storage folder is reset to normal 801 err = st.getAPI("/host/storage", &sfs) 802 if err != nil { 803 t.Fatal(err) 804 } 805 if sfs.Folders[0].FailedWrites > 0 { 806 t.Fatal("storage folder should have no failed writes after being moved back") 807 } 808 if sfs.Folders[0].FailedReads > 0 { 809 t.Fatal("storage folder should have no failed reads after being moved back") 810 } 811 812 // reload the host and verify the storage folder is still good 813 st.server.Close() 814 st, err = st.reloadedServerTester() 815 if err != nil { 816 t.Fatal(err) 817 } 818 defer st.server.Close() 819 820 // storage folder should still be good 821 err = st.getAPI("/host/storage", &sfs) 822 if err != nil { 823 t.Fatal(err) 824 } 825 if sfs.Folders[0].FailedWrites > 0 { 826 t.Fatal("storage folder should have no failed writes after being moved back") 827 } 828 if sfs.Folders[0].FailedReads > 0 { 829 t.Fatal("storage folder should have no failed reads after being moved back") 830 } 831 } 832 833 // TestResizeFolderNoPath checks that an API call to resize a storage folder fails 834 // if no path was provided. 835 func TestResizeFolderNoPath(t *testing.T) { 836 if testing.Short() { 837 t.SkipNow() 838 } 839 t.Parallel() 840 st, err := createServerTester(t.Name()) 841 if err != nil { 842 t.Fatal(err) 843 } 844 defer st.server.panicClose() 845 846 // The call to resize should fail if no path has been provided. 847 resizeValues := url.Values{} 848 resizeValues.Set("newsize", mediumSizeFolderString) 849 err = st.stdPostAPI("/host/storage/folders/resize", resizeValues) 850 if err == nil || err.Error() != errNoPath.Error() { 851 t.Fatalf("expected error to be %v; got %v", errNoPath, err) 852 } 853 } 854 855 // TestRemoveEmptyStorageFolder checks that removing an empty storage folder 856 // succeeds -- even if the host is left with zero storage folders. 857 func TestRemoveEmptyStorageFolder(t *testing.T) { 858 if testing.Short() { 859 t.SkipNow() 860 } 861 t.Parallel() 862 st, err := createServerTester(t.Name()) 863 if err != nil { 864 t.Fatal(err) 865 } 866 defer st.server.panicClose() 867 868 // Set up a storage folder for the host. 869 if err := st.setHostStorage(); err != nil { 870 t.Fatal(err) 871 } 872 873 // Try to delete the host's empty storage folder. 874 removeValues := url.Values{} 875 removeValues.Set("path", st.dir) 876 if err = st.stdPostAPI("/host/storage/folders/remove", removeValues); err != nil { 877 t.Fatal(err) 878 } 879 } 880 881 // TestRemoveStorageFolderError checks that invalid calls to 882 // /host/storage/folders/remove fail with the appropriate error. 883 func TestRemoveStorageFolderError(t *testing.T) { 884 if testing.Short() { 885 t.SkipNow() 886 } 887 t.Parallel() 888 st, err := createServerTester(t.Name()) 889 if err != nil { 890 t.Fatal(err) 891 } 892 defer st.server.panicClose() 893 894 // Set up a storage folder for the host. 895 if err := st.setHostStorage(); err != nil { 896 t.Fatal(err) 897 } 898 899 // Try removing a nonexistent folder. 900 removeValues := url.Values{} 901 removeValues.Set("path", "/foo/bar") 902 err = st.stdPostAPI("/host/storage/folders/remove", removeValues) 903 if err == nil || err.Error() != errStorageFolderNotFound.Error() { 904 t.Fatalf("expected error %v, got %v", errStorageFolderNotFound, err) 905 } 906 907 // The folder path can't be an empty string. 908 removeValues.Set("path", "") 909 err = st.stdPostAPI("/host/storage/folders/remove", removeValues) 910 if err == nil || err.Error() != errNoPath.Error() { 911 t.Fatalf("expected error to be %v; got %v", errNoPath, err) 912 } 913 } 914 915 // TestRemoveStorageFolderForced checks that if a call to remove a storage 916 // folder will result in data loss, that call succeeds if and only if "force" 917 // has been set to "true". 918 func TestRemoveStorageFolderForced(t *testing.T) { 919 if testing.Short() { 920 t.SkipNow() 921 } 922 t.Parallel() 923 st, err := createServerTester(t.Name()) 924 if err != nil { 925 t.Fatal(err) 926 } 927 defer st.server.panicClose() 928 929 // Announce the host. 930 if err := st.announceHost(); err != nil { 931 t.Fatal(err) 932 } 933 if err := st.acceptContracts(); err != nil { 934 t.Fatal(err) 935 } 936 if err := st.setHostStorage(); err != nil { 937 t.Fatal(err) 938 } 939 940 // Set an allowance for the renter, allowing a contract to be formed. 941 allowanceValues := url.Values{} 942 allowanceValues.Set("funds", testFunds) 943 allowanceValues.Set("period", testPeriod) 944 allowanceValues.Set("renewwindow", testRenewWindow) 945 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 946 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 947 t.Fatal(err) 948 } 949 950 // Block until the allowance has finished forming contracts. 951 err = build.Retry(50, time.Millisecond*250, func() error { 952 var rc RenterContracts 953 err = st.getAPI("/renter/contracts", &rc) 954 if err != nil { 955 return errors.New("couldn't get renter stats") 956 } 957 if len(rc.Contracts) != 1 { 958 return errors.New("no contracts") 959 } 960 return nil 961 }) 962 if err != nil { 963 t.Fatal("allowance setting failed") 964 } 965 966 // Create a file for upload. 967 path := filepath.Join(st.dir, "test.dat") 968 if err := createRandFile(path, 512); err != nil { 969 t.Fatal(err) 970 } 971 // Upload to host. 972 uploadValues := url.Values{} 973 uploadValues.Set("source", path) 974 if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 975 t.Fatal(err) 976 } 977 978 // Only one piece will be uploaded (10% at current redundancy) 979 var rf RenterFiles 980 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 981 st.getAPI("/renter/files", &rf) 982 time.Sleep(50 * time.Millisecond) 983 } 984 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 985 t.Error(rf.Files[0].UploadProgress) 986 t.Fatal("uploading has failed") 987 } 988 989 // The host should not be able to remove its only folder without setting 990 // "force" to "true", since this will result in data loss (there are no 991 // other folders for the data to be redistributed to). 992 removeValues := url.Values{} 993 removeValues.Set("path", st.dir) 994 err = st.stdPostAPI("/host/storage/folders/remove", removeValues) 995 if err == nil || err.Error() != contractmanager.ErrPartialRelocation.Error() { 996 t.Fatalf("expected err to be %v; got %v", contractmanager.ErrPartialRelocation, err) 997 } 998 // Forced removal of the folder should succeed, though. 999 removeValues.Set("force", "true") 1000 err = st.stdPostAPI("/host/storage/folders/remove", removeValues) 1001 if err != nil { 1002 t.Fatal(err) 1003 } 1004 } 1005 1006 // TestDeleteSector tests the call to delete a storage sector from the host. 1007 func TestDeleteSector(t *testing.T) { 1008 t.Skip("broken because Merkle roots are no longer exposed") 1009 if testing.Short() { 1010 t.SkipNow() 1011 } 1012 t.Parallel() 1013 st, err := createServerTester(t.Name()) 1014 if err != nil { 1015 t.Fatal(err) 1016 } 1017 defer st.server.panicClose() 1018 1019 // Set up the host for forming contracts. 1020 if err := st.announceHost(); err != nil { 1021 t.Fatal(err) 1022 } 1023 if err := st.acceptContracts(); err != nil { 1024 t.Fatal(err) 1025 } 1026 if err := st.setHostStorage(); err != nil { 1027 t.Fatal(err) 1028 } 1029 1030 // Set an allowance for the renter, allowing contracts to formed. 1031 allowanceValues := url.Values{} 1032 allowanceValues.Set("funds", testFunds) 1033 allowanceValues.Set("period", testPeriod) 1034 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 1035 t.Fatal(err) 1036 } 1037 1038 // Block until the allowance has finished forming contracts. 1039 err = build.Retry(50, time.Millisecond*250, func() error { 1040 var rc RenterContracts 1041 err = st.getAPI("/renter/contracts", &rc) 1042 if err != nil { 1043 return errors.New("couldn't get renter stats") 1044 } 1045 if len(rc.Contracts) != 1 { 1046 return errors.New("no contracts") 1047 } 1048 return nil 1049 }) 1050 if err != nil { 1051 t.Fatal("allowance setting failed") 1052 } 1053 1054 // Create a file. 1055 path := filepath.Join(st.dir, "test.dat") 1056 if err := createRandFile(path, 1024); err != nil { 1057 t.Fatal(err) 1058 } 1059 1060 // Upload to host. 1061 uploadValues := url.Values{} 1062 uploadValues.Set("source", path) 1063 if err = st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 1064 t.Fatal(err) 1065 } 1066 1067 // Only one piece will be uploaded (10% at current redundancy) 1068 var rf RenterFiles 1069 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1070 st.getAPI("/renter/files", &rf) 1071 time.Sleep(50 * time.Millisecond) 1072 } 1073 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1074 t.Error(rf.Files[0].UploadProgress) 1075 t.Fatal("uploading has failed") 1076 } 1077 1078 // Get the Merkle root of the piece that was uploaded. 1079 contracts := st.renter.Contracts() 1080 if len(contracts) != 1 { 1081 t.Fatalf("expected exactly 1 contract to have been formed; got %v instead", len(contracts)) 1082 } 1083 // if len(contracts[0].MerkleRoots) < 1 { 1084 // t.Fatal("expected at least one merkle root") 1085 // } 1086 // sectorRoot := contracts[0].MerkleRoots[0].String() 1087 1088 // if err = st.stdPostAPI("/host/storage/sectors/delete/"+sectorRoot, url.Values{}); err != nil { 1089 // t.Fatal(err) 1090 // } 1091 } 1092 1093 // TestDeleteNonexistentSector checks that attempting to delete a storage 1094 // sector that doesn't exist will fail with the appropriate error. 1095 func TestDeleteNonexistentSector(t *testing.T) { 1096 if testing.Short() { 1097 t.SkipNow() 1098 } 1099 t.Parallel() 1100 st, err := createServerTester(t.Name()) 1101 if err != nil { 1102 t.Fatal(err) 1103 } 1104 defer st.server.panicClose() 1105 1106 // These calls to delete imaginary sectors should fail for a few reasons: 1107 // - the given sector root strings are invalid 1108 // - the renter hasn't uploaded anything 1109 // - the host has no storage folders yet 1110 // Right now, the calls fail for the first reason. This test will report if that behavior changes. 1111 badHash := crypto.HashObject("fake object").String() 1112 err = st.stdPostAPI("/host/storage/sectors/delete/"+badHash, url.Values{}) 1113 if err == nil || err.Error() != contractmanager.ErrSectorNotFound.Error() { 1114 t.Fatalf("expected error to be %v; got %v", contractmanager.ErrSectorNotFound, err) 1115 } 1116 wrongSize := "wrong size string" 1117 err = st.stdPostAPI("/host/storage/sectors/delete/"+wrongSize, url.Values{}) 1118 if err == nil || err.Error() != crypto.ErrHashWrongLen.Error() { 1119 t.Fatalf("expected error to be %v; got %v", crypto.ErrHashWrongLen, err) 1120 } 1121 }