github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/command/refresh_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/hashicorp/terraform/helper/copy" 13 "github.com/hashicorp/terraform/terraform" 14 "github.com/mitchellh/cli" 15 ) 16 17 func TestRefresh(t *testing.T) { 18 state := testState() 19 statePath := testStateFile(t, state) 20 21 p := testProvider() 22 ui := new(cli.MockUi) 23 c := &RefreshCommand{ 24 Meta: Meta{ 25 ContextOpts: testCtxConfig(p), 26 Ui: ui, 27 }, 28 } 29 30 p.RefreshFn = nil 31 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 32 33 args := []string{ 34 "-state", statePath, 35 testFixturePath("refresh"), 36 } 37 if code := c.Run(args); code != 0 { 38 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 39 } 40 41 if !p.RefreshCalled { 42 t.Fatal("refresh should be called") 43 } 44 45 f, err := os.Open(statePath) 46 if err != nil { 47 t.Fatalf("err: %s", err) 48 } 49 50 newState, err := terraform.ReadState(f) 51 f.Close() 52 if err != nil { 53 t.Fatalf("err: %s", err) 54 } 55 56 actual := strings.TrimSpace(newState.String()) 57 expected := strings.TrimSpace(testRefreshStr) 58 if actual != expected { 59 t.Fatalf("bad:\n\n%s", actual) 60 } 61 } 62 63 func TestRefresh_empty(t *testing.T) { 64 // Create a temporary working directory that is empty 65 td := tempDir(t) 66 copy.CopyDir(testFixturePath("refresh-empty"), td) 67 defer os.RemoveAll(td) 68 defer testChdir(t, td)() 69 70 p := testProvider() 71 ui := new(cli.MockUi) 72 c := &RefreshCommand{ 73 Meta: Meta{ 74 ContextOpts: testCtxConfig(p), 75 Ui: ui, 76 }, 77 } 78 79 p.RefreshFn = nil 80 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 81 82 args := []string{ 83 td, 84 } 85 if code := c.Run(args); code != 0 { 86 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 87 } 88 89 if p.RefreshCalled { 90 t.Fatal("refresh should not be called") 91 } 92 } 93 94 func TestRefresh_lockedState(t *testing.T) { 95 state := testState() 96 statePath := testStateFile(t, state) 97 98 unlock, err := testLockState("./testdata", statePath) 99 if err != nil { 100 t.Fatal(err) 101 } 102 defer unlock() 103 104 p := testProvider() 105 ui := new(cli.MockUi) 106 c := &RefreshCommand{ 107 Meta: Meta{ 108 ContextOpts: testCtxConfig(p), 109 Ui: ui, 110 }, 111 } 112 113 p.RefreshFn = nil 114 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 115 116 args := []string{ 117 "-state", statePath, 118 testFixturePath("refresh"), 119 } 120 121 if code := c.Run(args); code == 0 { 122 t.Fatal("expected error") 123 } 124 125 output := ui.ErrorWriter.String() 126 if !strings.Contains(output, "lock") { 127 t.Fatal("command output does not look like a lock error:", output) 128 } 129 } 130 131 func TestRefresh_cwd(t *testing.T) { 132 cwd, err := os.Getwd() 133 if err != nil { 134 t.Fatalf("err: %s", err) 135 } 136 if err := os.Chdir(testFixturePath("refresh")); err != nil { 137 t.Fatalf("err: %s", err) 138 } 139 defer os.Chdir(cwd) 140 141 state := testState() 142 statePath := testStateFile(t, state) 143 144 p := testProvider() 145 ui := new(cli.MockUi) 146 c := &RefreshCommand{ 147 Meta: Meta{ 148 ContextOpts: testCtxConfig(p), 149 Ui: ui, 150 }, 151 } 152 153 p.RefreshFn = nil 154 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 155 156 args := []string{ 157 "-state", statePath, 158 } 159 if code := c.Run(args); code != 0 { 160 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 161 } 162 163 if !p.RefreshCalled { 164 t.Fatal("refresh should be called") 165 } 166 167 f, err := os.Open(statePath) 168 if err != nil { 169 t.Fatalf("err: %s", err) 170 } 171 172 newState, err := terraform.ReadState(f) 173 f.Close() 174 if err != nil { 175 t.Fatalf("err: %s", err) 176 } 177 178 actual := strings.TrimSpace(newState.String()) 179 expected := strings.TrimSpace(testRefreshCwdStr) 180 if actual != expected { 181 t.Fatalf("bad:\n\n%s", actual) 182 } 183 } 184 185 func TestRefresh_defaultState(t *testing.T) { 186 originalState := testState() 187 188 // Write the state file in a temporary directory with the 189 // default filename. 190 td, err := ioutil.TempDir("", "tf") 191 if err != nil { 192 t.Fatalf("err: %s", err) 193 } 194 statePath := filepath.Join(td, DefaultStateFilename) 195 196 f, err := os.Create(statePath) 197 if err != nil { 198 t.Fatalf("err: %s", err) 199 } 200 err = terraform.WriteState(originalState, f) 201 f.Close() 202 if err != nil { 203 t.Fatalf("err: %s", err) 204 } 205 206 // Change to that directory 207 cwd, err := os.Getwd() 208 if err != nil { 209 t.Fatalf("err: %s", err) 210 } 211 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 212 t.Fatalf("err: %s", err) 213 } 214 defer os.Chdir(cwd) 215 216 p := testProvider() 217 ui := new(cli.MockUi) 218 c := &RefreshCommand{ 219 Meta: Meta{ 220 ContextOpts: testCtxConfig(p), 221 Ui: ui, 222 }, 223 } 224 225 p.RefreshFn = nil 226 p.RefreshReturn = newInstanceState("yes") 227 228 args := []string{ 229 testFixturePath("refresh"), 230 } 231 if code := c.Run(args); code != 0 { 232 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 233 } 234 235 if !p.RefreshCalled { 236 t.Fatal("refresh should be called") 237 } 238 239 f, err = os.Open(statePath) 240 if err != nil { 241 t.Fatalf("err: %s", err) 242 } 243 244 newState, err := terraform.ReadState(f) 245 f.Close() 246 if err != nil { 247 t.Fatalf("err: %s", err) 248 } 249 250 actual := newState.RootModule().Resources["test_instance.foo"].Primary 251 expected := p.RefreshReturn 252 if !reflect.DeepEqual(actual, expected) { 253 t.Logf("expected:\n%#v", expected) 254 t.Fatalf("bad:\n%#v", actual) 255 } 256 257 f, err = os.Open(statePath + DefaultBackupExtension) 258 if err != nil { 259 t.Fatalf("err: %s", err) 260 } 261 262 backupState, err := terraform.ReadState(f) 263 f.Close() 264 if err != nil { 265 t.Fatalf("err: %s", err) 266 } 267 268 actual = backupState.RootModule().Resources["test_instance.foo"].Primary 269 expected = originalState.RootModule().Resources["test_instance.foo"].Primary 270 if !reflect.DeepEqual(actual, expected) { 271 t.Fatalf("bad: %#v", actual) 272 } 273 } 274 275 func TestRefresh_futureState(t *testing.T) { 276 cwd, err := os.Getwd() 277 if err != nil { 278 t.Fatalf("err: %s", err) 279 } 280 if err := os.Chdir(testFixturePath("refresh")); err != nil { 281 t.Fatalf("err: %s", err) 282 } 283 defer os.Chdir(cwd) 284 285 state := testState() 286 state.TFVersion = "99.99.99" 287 statePath := testStateFile(t, state) 288 289 p := testProvider() 290 ui := new(cli.MockUi) 291 c := &RefreshCommand{ 292 Meta: Meta{ 293 ContextOpts: testCtxConfig(p), 294 Ui: ui, 295 }, 296 } 297 298 args := []string{ 299 "-state", statePath, 300 } 301 if code := c.Run(args); code == 0 { 302 t.Fatal("should fail") 303 } 304 305 if p.RefreshCalled { 306 t.Fatal("refresh should not be called") 307 } 308 309 f, err := os.Open(statePath) 310 if err != nil { 311 t.Fatalf("err: %s", err) 312 } 313 314 newState, err := terraform.ReadState(f) 315 f.Close() 316 if err != nil { 317 t.Fatalf("err: %s", err) 318 } 319 320 actual := strings.TrimSpace(newState.String()) 321 expected := strings.TrimSpace(state.String()) 322 if actual != expected { 323 t.Fatalf("bad:\n\n%s", actual) 324 } 325 } 326 327 func TestRefresh_pastState(t *testing.T) { 328 state := testState() 329 state.TFVersion = "0.1.0" 330 statePath := testStateFile(t, state) 331 332 p := testProvider() 333 ui := new(cli.MockUi) 334 c := &RefreshCommand{ 335 Meta: Meta{ 336 ContextOpts: testCtxConfig(p), 337 Ui: ui, 338 }, 339 } 340 341 p.RefreshFn = nil 342 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 343 344 args := []string{ 345 "-state", statePath, 346 testFixturePath("refresh"), 347 } 348 if code := c.Run(args); code != 0 { 349 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 350 } 351 352 if !p.RefreshCalled { 353 t.Fatal("refresh should be called") 354 } 355 356 f, err := os.Open(statePath) 357 if err != nil { 358 t.Fatalf("err: %s", err) 359 } 360 361 newState, err := terraform.ReadState(f) 362 f.Close() 363 if err != nil { 364 t.Fatalf("err: %s", err) 365 } 366 367 actual := strings.TrimSpace(newState.String()) 368 expected := strings.TrimSpace(testRefreshStr) 369 if actual != expected { 370 t.Fatalf("bad:\n\n%s", actual) 371 } 372 373 if newState.TFVersion != terraform.Version { 374 t.Fatalf("bad:\n\n%s", newState.TFVersion) 375 } 376 } 377 378 func TestRefresh_outPath(t *testing.T) { 379 state := testState() 380 statePath := testStateFile(t, state) 381 382 // Output path 383 outf, err := ioutil.TempFile("", "tf") 384 if err != nil { 385 t.Fatalf("err: %s", err) 386 } 387 outPath := outf.Name() 388 outf.Close() 389 os.Remove(outPath) 390 391 p := testProvider() 392 ui := new(cli.MockUi) 393 c := &RefreshCommand{ 394 Meta: Meta{ 395 ContextOpts: testCtxConfig(p), 396 Ui: ui, 397 }, 398 } 399 400 p.RefreshFn = nil 401 p.RefreshReturn = newInstanceState("yes") 402 403 args := []string{ 404 "-state", statePath, 405 "-state-out", outPath, 406 testFixturePath("refresh"), 407 } 408 if code := c.Run(args); code != 0 { 409 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 410 } 411 412 f, err := os.Open(statePath) 413 if err != nil { 414 t.Fatalf("err: %s", err) 415 } 416 417 newState, err := terraform.ReadState(f) 418 f.Close() 419 if err != nil { 420 t.Fatalf("err: %s", err) 421 } 422 423 if !reflect.DeepEqual(newState, state) { 424 t.Fatalf("bad: %#v", newState) 425 } 426 427 f, err = os.Open(outPath) 428 if err != nil { 429 t.Fatalf("err: %s", err) 430 } 431 432 newState, err = terraform.ReadState(f) 433 f.Close() 434 if err != nil { 435 t.Fatalf("err: %s", err) 436 } 437 438 actual := newState.RootModule().Resources["test_instance.foo"].Primary 439 expected := p.RefreshReturn 440 if !reflect.DeepEqual(actual, expected) { 441 t.Fatalf("bad: %#v", actual) 442 } 443 444 f, err = os.Open(outPath + DefaultBackupExtension) 445 if err != nil { 446 t.Fatalf("err: %s", err) 447 } 448 449 backupState, err := terraform.ReadState(f) 450 f.Close() 451 if err != nil { 452 t.Fatalf("err: %s", err) 453 } 454 455 actualStr := strings.TrimSpace(backupState.String()) 456 expectedStr := strings.TrimSpace(state.String()) 457 if actualStr != expectedStr { 458 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 459 } 460 } 461 462 func TestRefresh_var(t *testing.T) { 463 state := testState() 464 statePath := testStateFile(t, state) 465 466 p := testProvider() 467 ui := new(cli.MockUi) 468 c := &RefreshCommand{ 469 Meta: Meta{ 470 ContextOpts: testCtxConfig(p), 471 Ui: ui, 472 }, 473 } 474 475 args := []string{ 476 "-var", "foo=bar", 477 "-state", statePath, 478 testFixturePath("refresh-var"), 479 } 480 if code := c.Run(args); code != 0 { 481 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 482 } 483 484 if !p.ConfigureCalled { 485 t.Fatal("configure should be called") 486 } 487 if p.ConfigureConfig.Config["value"].(string) != "bar" { 488 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 489 } 490 } 491 492 func TestRefresh_varFile(t *testing.T) { 493 state := testState() 494 statePath := testStateFile(t, state) 495 496 p := testProvider() 497 ui := new(cli.MockUi) 498 c := &RefreshCommand{ 499 Meta: Meta{ 500 ContextOpts: testCtxConfig(p), 501 Ui: ui, 502 }, 503 } 504 505 varFilePath := testTempFile(t) 506 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 507 t.Fatalf("err: %s", err) 508 } 509 510 args := []string{ 511 "-var-file", varFilePath, 512 "-state", statePath, 513 testFixturePath("refresh-var"), 514 } 515 if code := c.Run(args); code != 0 { 516 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 517 } 518 519 if !p.ConfigureCalled { 520 t.Fatal("configure should be called") 521 } 522 if p.ConfigureConfig.Config["value"].(string) != "bar" { 523 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 524 } 525 } 526 527 func TestRefresh_varFileDefault(t *testing.T) { 528 state := testState() 529 statePath := testStateFile(t, state) 530 531 p := testProvider() 532 ui := new(cli.MockUi) 533 c := &RefreshCommand{ 534 Meta: Meta{ 535 ContextOpts: testCtxConfig(p), 536 Ui: ui, 537 }, 538 } 539 540 varFileDir := testTempDir(t) 541 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 542 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 543 t.Fatalf("err: %s", err) 544 } 545 546 cwd, err := os.Getwd() 547 if err != nil { 548 t.Fatalf("err: %s", err) 549 } 550 if err := os.Chdir(varFileDir); err != nil { 551 t.Fatalf("err: %s", err) 552 } 553 defer os.Chdir(cwd) 554 555 args := []string{ 556 "-state", statePath, 557 testFixturePath("refresh-var"), 558 } 559 if code := c.Run(args); code != 0 { 560 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 561 } 562 563 if !p.ConfigureCalled { 564 t.Fatal("configure should be called") 565 } 566 if p.ConfigureConfig.Config["value"].(string) != "bar" { 567 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 568 } 569 } 570 571 func TestRefresh_varsUnset(t *testing.T) { 572 // Disable test mode so input would be asked 573 test = false 574 defer func() { test = true }() 575 576 defaultInputReader = bytes.NewBufferString("bar\n") 577 578 state := testState() 579 statePath := testStateFile(t, state) 580 581 p := testProvider() 582 ui := new(cli.MockUi) 583 c := &RefreshCommand{ 584 Meta: Meta{ 585 ContextOpts: testCtxConfig(p), 586 Ui: ui, 587 }, 588 } 589 590 args := []string{ 591 "-state", statePath, 592 testFixturePath("refresh-unset-var"), 593 } 594 if code := c.Run(args); code != 0 { 595 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 596 } 597 } 598 599 func TestRefresh_backup(t *testing.T) { 600 state := testState() 601 statePath := testStateFile(t, state) 602 603 // Output path 604 outf, err := ioutil.TempFile("", "tf") 605 if err != nil { 606 t.Fatalf("err: %s", err) 607 } 608 outPath := outf.Name() 609 outf.Close() 610 os.Remove(outPath) 611 612 // Backup path 613 backupf, err := ioutil.TempFile("", "tf") 614 if err != nil { 615 t.Fatalf("err: %s", err) 616 } 617 backupPath := backupf.Name() 618 backupf.Close() 619 os.Remove(backupPath) 620 621 p := testProvider() 622 ui := new(cli.MockUi) 623 c := &RefreshCommand{ 624 Meta: Meta{ 625 ContextOpts: testCtxConfig(p), 626 Ui: ui, 627 }, 628 } 629 630 p.RefreshFn = nil 631 p.RefreshReturn = newInstanceState("yes") 632 633 args := []string{ 634 "-state", statePath, 635 "-state-out", outPath, 636 "-backup", backupPath, 637 testFixturePath("refresh"), 638 } 639 if code := c.Run(args); code != 0 { 640 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 641 } 642 643 f, err := os.Open(statePath) 644 if err != nil { 645 t.Fatalf("err: %s", err) 646 } 647 648 newState, err := terraform.ReadState(f) 649 f.Close() 650 if err != nil { 651 t.Fatalf("err: %s", err) 652 } 653 654 if !reflect.DeepEqual(newState, state) { 655 t.Fatalf("bad: %#v", newState) 656 } 657 658 f, err = os.Open(outPath) 659 if err != nil { 660 t.Fatalf("err: %s", err) 661 } 662 663 newState, err = terraform.ReadState(f) 664 f.Close() 665 if err != nil { 666 t.Fatalf("err: %s", err) 667 } 668 669 actual := newState.RootModule().Resources["test_instance.foo"].Primary 670 expected := p.RefreshReturn 671 if !reflect.DeepEqual(actual, expected) { 672 t.Fatalf("bad: %#v", actual) 673 } 674 675 f, err = os.Open(backupPath) 676 if err != nil { 677 t.Fatalf("err: %s", err) 678 } 679 680 backupState, err := terraform.ReadState(f) 681 f.Close() 682 if err != nil { 683 t.Fatalf("err: %s", err) 684 } 685 686 actualStr := strings.TrimSpace(backupState.String()) 687 expectedStr := strings.TrimSpace(state.String()) 688 if actualStr != expectedStr { 689 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 690 } 691 } 692 693 func TestRefresh_disableBackup(t *testing.T) { 694 state := testState() 695 statePath := testStateFile(t, state) 696 697 // Output path 698 outf, err := ioutil.TempFile("", "tf") 699 if err != nil { 700 t.Fatalf("err: %s", err) 701 } 702 outPath := outf.Name() 703 outf.Close() 704 os.Remove(outPath) 705 706 p := testProvider() 707 ui := new(cli.MockUi) 708 c := &RefreshCommand{ 709 Meta: Meta{ 710 ContextOpts: testCtxConfig(p), 711 Ui: ui, 712 }, 713 } 714 715 p.RefreshFn = nil 716 p.RefreshReturn = newInstanceState("yes") 717 718 args := []string{ 719 "-state", statePath, 720 "-state-out", outPath, 721 "-backup", "-", 722 testFixturePath("refresh"), 723 } 724 if code := c.Run(args); code != 0 { 725 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 726 } 727 728 f, err := os.Open(statePath) 729 if err != nil { 730 t.Fatalf("err: %s", err) 731 } 732 733 newState, err := terraform.ReadState(f) 734 f.Close() 735 if err != nil { 736 t.Fatalf("err: %s", err) 737 } 738 739 if !reflect.DeepEqual(newState, state) { 740 t.Fatalf("bad: %#v", newState) 741 } 742 743 f, err = os.Open(outPath) 744 if err != nil { 745 t.Fatalf("err: %s", err) 746 } 747 748 newState, err = terraform.ReadState(f) 749 f.Close() 750 if err != nil { 751 t.Fatalf("err: %s", err) 752 } 753 754 actual := newState.RootModule().Resources["test_instance.foo"].Primary 755 expected := p.RefreshReturn 756 if !reflect.DeepEqual(actual, expected) { 757 t.Fatalf("bad: %#v", actual) 758 } 759 760 // Ensure there is no backup 761 _, err = os.Stat(outPath + DefaultBackupExtension) 762 if err == nil || !os.IsNotExist(err) { 763 t.Fatalf("backup should not exist") 764 } 765 _, err = os.Stat("-") 766 if err == nil || !os.IsNotExist(err) { 767 t.Fatalf("backup should not exist") 768 } 769 } 770 771 func TestRefresh_displaysOutputs(t *testing.T) { 772 state := testState() 773 statePath := testStateFile(t, state) 774 775 p := testProvider() 776 ui := new(cli.MockUi) 777 c := &RefreshCommand{ 778 Meta: Meta{ 779 ContextOpts: testCtxConfig(p), 780 Ui: ui, 781 }, 782 } 783 784 args := []string{ 785 "-state", statePath, 786 testFixturePath("refresh-output"), 787 } 788 if code := c.Run(args); code != 0 { 789 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 790 } 791 792 // Test that outputs were displayed 793 outputValue := "foo.example.com" 794 actual := ui.OutputWriter.String() 795 if !strings.Contains(actual, outputValue) { 796 t.Fatalf("Expected:\n%s\n\nTo include: %q", actual, outputValue) 797 } 798 } 799 800 // When creating an InstaneState for direct comparison to one contained in 801 // terraform.State, all fields must be initialized (duplicating the 802 // InstanceState.init() method) 803 func newInstanceState(id string) *terraform.InstanceState { 804 return &terraform.InstanceState{ 805 ID: id, 806 Attributes: make(map[string]string), 807 Ephemeral: terraform.EphemeralState{ 808 ConnInfo: make(map[string]string), 809 }, 810 Meta: make(map[string]interface{}), 811 } 812 } 813 814 const refreshVarFile = ` 815 foo = "bar" 816 ` 817 818 const testRefreshStr = ` 819 test_instance.foo: 820 ID = yes 821 ` 822 const testRefreshCwdStr = ` 823 test_instance.foo: 824 ID = yes 825 `