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