github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/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 td, err := ioutil.TempDir("", "tf") 193 if err != nil { 194 t.Fatalf("err: %s", err) 195 } 196 statePath := filepath.Join(td, DefaultStateFilename) 197 198 localState := &state.LocalState{Path: statePath} 199 if err := localState.WriteState(originalState); err != nil { 200 t.Fatal(err) 201 } 202 serial := localState.State().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 testFixturePath("refresh"), 228 } 229 if code := c.Run(args); code != 0 { 230 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 231 } 232 233 if !p.RefreshCalled { 234 t.Fatal("refresh should be called") 235 } 236 237 newState := testStateRead(t, statePath) 238 239 actual := newState.RootModule().Resources["test_instance.foo"].Primary 240 expected := p.RefreshReturn 241 if !reflect.DeepEqual(actual, expected) { 242 t.Logf("expected:\n%#v", expected) 243 t.Fatalf("bad:\n%#v", actual) 244 } 245 246 if newState.Serial <= serial { 247 t.Fatalf("serial not incremented during refresh. previous:%d, current:%d", serial, newState.Serial) 248 } 249 250 backupState := testStateRead(t, statePath+DefaultBackupExtension) 251 252 actual = backupState.RootModule().Resources["test_instance.foo"].Primary 253 expected = originalState.RootModule().Resources["test_instance.foo"].Primary 254 if !reflect.DeepEqual(actual, expected) { 255 t.Fatalf("bad: %#v", actual) 256 } 257 } 258 259 func TestRefresh_futureState(t *testing.T) { 260 cwd, err := os.Getwd() 261 if err != nil { 262 t.Fatalf("err: %s", err) 263 } 264 if err := os.Chdir(testFixturePath("refresh")); err != nil { 265 t.Fatalf("err: %s", err) 266 } 267 defer os.Chdir(cwd) 268 269 state := testState() 270 state.TFVersion = "99.99.99" 271 statePath := testStateFile(t, state) 272 273 p := testProvider() 274 ui := new(cli.MockUi) 275 c := &RefreshCommand{ 276 Meta: Meta{ 277 testingOverrides: metaOverridesForProvider(p), 278 Ui: ui, 279 }, 280 } 281 282 args := []string{ 283 "-state", statePath, 284 } 285 if code := c.Run(args); code == 0 { 286 t.Fatal("should fail") 287 } 288 289 if p.RefreshCalled { 290 t.Fatal("refresh should not be called") 291 } 292 293 f, err := os.Open(statePath) 294 if err != nil { 295 t.Fatalf("err: %s", err) 296 } 297 298 newState, err := terraform.ReadState(f) 299 f.Close() 300 if err != nil { 301 t.Fatalf("err: %s", err) 302 } 303 304 actual := strings.TrimSpace(newState.String()) 305 expected := strings.TrimSpace(state.String()) 306 if actual != expected { 307 t.Fatalf("bad:\n\n%s", actual) 308 } 309 } 310 311 func TestRefresh_pastState(t *testing.T) { 312 state := testState() 313 state.TFVersion = "0.1.0" 314 statePath := testStateFile(t, state) 315 316 p := testProvider() 317 ui := new(cli.MockUi) 318 c := &RefreshCommand{ 319 Meta: Meta{ 320 testingOverrides: metaOverridesForProvider(p), 321 Ui: ui, 322 }, 323 } 324 325 p.RefreshFn = nil 326 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 327 328 args := []string{ 329 "-state", statePath, 330 testFixturePath("refresh"), 331 } 332 if code := c.Run(args); code != 0 { 333 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 334 } 335 336 if !p.RefreshCalled { 337 t.Fatal("refresh should be called") 338 } 339 340 f, err := os.Open(statePath) 341 if err != nil { 342 t.Fatalf("err: %s", err) 343 } 344 345 newState, err := terraform.ReadState(f) 346 f.Close() 347 if err != nil { 348 t.Fatalf("err: %s", err) 349 } 350 351 actual := strings.TrimSpace(newState.String()) 352 expected := strings.TrimSpace(testRefreshStr) 353 if actual != expected { 354 t.Fatalf("bad:\n\n%s", actual) 355 } 356 357 if newState.TFVersion != version.Version { 358 t.Fatalf("bad:\n\n%s", newState.TFVersion) 359 } 360 } 361 362 func TestRefresh_outPath(t *testing.T) { 363 state := testState() 364 statePath := testStateFile(t, state) 365 366 // Output path 367 outf, err := ioutil.TempFile("", "tf") 368 if err != nil { 369 t.Fatalf("err: %s", err) 370 } 371 outPath := outf.Name() 372 outf.Close() 373 os.Remove(outPath) 374 375 p := testProvider() 376 ui := new(cli.MockUi) 377 c := &RefreshCommand{ 378 Meta: Meta{ 379 testingOverrides: metaOverridesForProvider(p), 380 Ui: ui, 381 }, 382 } 383 384 p.RefreshFn = nil 385 p.RefreshReturn = newInstanceState("yes") 386 387 args := []string{ 388 "-state", statePath, 389 "-state-out", outPath, 390 testFixturePath("refresh"), 391 } 392 if code := c.Run(args); code != 0 { 393 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 394 } 395 396 f, err := os.Open(statePath) 397 if err != nil { 398 t.Fatalf("err: %s", err) 399 } 400 401 newState, err := terraform.ReadState(f) 402 f.Close() 403 if err != nil { 404 t.Fatalf("err: %s", err) 405 } 406 407 if !reflect.DeepEqual(newState, state) { 408 t.Fatalf("bad: %#v", newState) 409 } 410 411 f, err = os.Open(outPath) 412 if err != nil { 413 t.Fatalf("err: %s", err) 414 } 415 416 newState, err = terraform.ReadState(f) 417 f.Close() 418 if err != nil { 419 t.Fatalf("err: %s", err) 420 } 421 422 actual := newState.RootModule().Resources["test_instance.foo"].Primary 423 expected := p.RefreshReturn 424 if !reflect.DeepEqual(actual, expected) { 425 t.Fatalf("bad: %#v", actual) 426 } 427 428 f, err = os.Open(outPath + DefaultBackupExtension) 429 if err != nil { 430 t.Fatalf("err: %s", err) 431 } 432 433 backupState, err := terraform.ReadState(f) 434 f.Close() 435 if err != nil { 436 t.Fatalf("err: %s", err) 437 } 438 439 actualStr := strings.TrimSpace(backupState.String()) 440 expectedStr := strings.TrimSpace(state.String()) 441 if actualStr != expectedStr { 442 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 443 } 444 } 445 446 func TestRefresh_var(t *testing.T) { 447 state := testState() 448 statePath := testStateFile(t, state) 449 450 p := testProvider() 451 ui := new(cli.MockUi) 452 c := &RefreshCommand{ 453 Meta: Meta{ 454 testingOverrides: metaOverridesForProvider(p), 455 Ui: ui, 456 }, 457 } 458 459 args := []string{ 460 "-var", "foo=bar", 461 "-state", statePath, 462 testFixturePath("refresh-var"), 463 } 464 if code := c.Run(args); code != 0 { 465 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 466 } 467 468 if !p.ConfigureCalled { 469 t.Fatal("configure should be called") 470 } 471 if p.ConfigureConfig.Config["value"].(string) != "bar" { 472 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 473 } 474 } 475 476 func TestRefresh_varFile(t *testing.T) { 477 state := testState() 478 statePath := testStateFile(t, state) 479 480 p := testProvider() 481 ui := new(cli.MockUi) 482 c := &RefreshCommand{ 483 Meta: Meta{ 484 testingOverrides: metaOverridesForProvider(p), 485 Ui: ui, 486 }, 487 } 488 489 varFilePath := testTempFile(t) 490 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 491 t.Fatalf("err: %s", err) 492 } 493 494 args := []string{ 495 "-var-file", varFilePath, 496 "-state", statePath, 497 testFixturePath("refresh-var"), 498 } 499 if code := c.Run(args); code != 0 { 500 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 501 } 502 503 if !p.ConfigureCalled { 504 t.Fatal("configure should be called") 505 } 506 if p.ConfigureConfig.Config["value"].(string) != "bar" { 507 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 508 } 509 } 510 511 func TestRefresh_varFileDefault(t *testing.T) { 512 state := testState() 513 statePath := testStateFile(t, state) 514 515 p := testProvider() 516 ui := new(cli.MockUi) 517 c := &RefreshCommand{ 518 Meta: Meta{ 519 testingOverrides: metaOverridesForProvider(p), 520 Ui: ui, 521 }, 522 } 523 524 varFileDir := testTempDir(t) 525 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 526 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 527 t.Fatalf("err: %s", err) 528 } 529 530 cwd, err := os.Getwd() 531 if err != nil { 532 t.Fatalf("err: %s", err) 533 } 534 if err := os.Chdir(varFileDir); err != nil { 535 t.Fatalf("err: %s", err) 536 } 537 defer os.Chdir(cwd) 538 539 args := []string{ 540 "-state", statePath, 541 testFixturePath("refresh-var"), 542 } 543 if code := c.Run(args); code != 0 { 544 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 545 } 546 547 if !p.ConfigureCalled { 548 t.Fatal("configure should be called") 549 } 550 if p.ConfigureConfig.Config["value"].(string) != "bar" { 551 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 552 } 553 } 554 555 func TestRefresh_varsUnset(t *testing.T) { 556 // Disable test mode so input would be asked 557 test = false 558 defer func() { test = true }() 559 560 defaultInputReader = bytes.NewBufferString("bar\n") 561 562 state := testState() 563 statePath := testStateFile(t, state) 564 565 p := testProvider() 566 ui := new(cli.MockUi) 567 c := &RefreshCommand{ 568 Meta: Meta{ 569 testingOverrides: metaOverridesForProvider(p), 570 Ui: ui, 571 }, 572 } 573 574 args := []string{ 575 "-state", statePath, 576 testFixturePath("refresh-unset-var"), 577 } 578 if code := c.Run(args); code != 0 { 579 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 580 } 581 } 582 583 func TestRefresh_backup(t *testing.T) { 584 state := testState() 585 statePath := testStateFile(t, state) 586 587 // Output path 588 outf, err := ioutil.TempFile("", "tf") 589 if err != nil { 590 t.Fatalf("err: %s", err) 591 } 592 outPath := outf.Name() 593 outf.Close() 594 os.Remove(outPath) 595 596 // Backup path 597 backupf, err := ioutil.TempFile("", "tf") 598 if err != nil { 599 t.Fatalf("err: %s", err) 600 } 601 backupPath := backupf.Name() 602 backupf.Close() 603 os.Remove(backupPath) 604 605 p := testProvider() 606 ui := new(cli.MockUi) 607 c := &RefreshCommand{ 608 Meta: Meta{ 609 testingOverrides: metaOverridesForProvider(p), 610 Ui: ui, 611 }, 612 } 613 614 p.RefreshFn = nil 615 p.RefreshReturn = newInstanceState("yes") 616 617 args := []string{ 618 "-state", statePath, 619 "-state-out", outPath, 620 "-backup", backupPath, 621 testFixturePath("refresh"), 622 } 623 if code := c.Run(args); code != 0 { 624 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 625 } 626 627 f, err := os.Open(statePath) 628 if err != nil { 629 t.Fatalf("err: %s", err) 630 } 631 632 newState, err := terraform.ReadState(f) 633 f.Close() 634 if err != nil { 635 t.Fatalf("err: %s", err) 636 } 637 638 if !reflect.DeepEqual(newState, state) { 639 t.Fatalf("bad: %#v", newState) 640 } 641 642 f, err = os.Open(outPath) 643 if err != nil { 644 t.Fatalf("err: %s", err) 645 } 646 647 newState, err = terraform.ReadState(f) 648 f.Close() 649 if err != nil { 650 t.Fatalf("err: %s", err) 651 } 652 653 actual := newState.RootModule().Resources["test_instance.foo"].Primary 654 expected := p.RefreshReturn 655 if !reflect.DeepEqual(actual, expected) { 656 t.Fatalf("bad: %#v", actual) 657 } 658 659 f, err = os.Open(backupPath) 660 if err != nil { 661 t.Fatalf("err: %s", err) 662 } 663 664 backupState, err := terraform.ReadState(f) 665 f.Close() 666 if err != nil { 667 t.Fatalf("err: %s", err) 668 } 669 670 actualStr := strings.TrimSpace(backupState.String()) 671 expectedStr := strings.TrimSpace(state.String()) 672 if actualStr != expectedStr { 673 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 674 } 675 } 676 677 func TestRefresh_disableBackup(t *testing.T) { 678 state := testState() 679 statePath := testStateFile(t, state) 680 681 // Output path 682 outf, err := ioutil.TempFile("", "tf") 683 if err != nil { 684 t.Fatalf("err: %s", err) 685 } 686 outPath := outf.Name() 687 outf.Close() 688 os.Remove(outPath) 689 690 p := testProvider() 691 ui := new(cli.MockUi) 692 c := &RefreshCommand{ 693 Meta: Meta{ 694 testingOverrides: metaOverridesForProvider(p), 695 Ui: ui, 696 }, 697 } 698 699 p.RefreshFn = nil 700 p.RefreshReturn = newInstanceState("yes") 701 702 args := []string{ 703 "-state", statePath, 704 "-state-out", outPath, 705 "-backup", "-", 706 testFixturePath("refresh"), 707 } 708 if code := c.Run(args); code != 0 { 709 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 710 } 711 712 f, err := os.Open(statePath) 713 if err != nil { 714 t.Fatalf("err: %s", err) 715 } 716 717 newState, err := terraform.ReadState(f) 718 f.Close() 719 if err != nil { 720 t.Fatalf("err: %s", err) 721 } 722 723 if !reflect.DeepEqual(newState, state) { 724 t.Fatalf("bad: %#v", newState) 725 } 726 727 f, err = os.Open(outPath) 728 if err != nil { 729 t.Fatalf("err: %s", err) 730 } 731 732 newState, err = terraform.ReadState(f) 733 f.Close() 734 if err != nil { 735 t.Fatalf("err: %s", err) 736 } 737 738 actual := newState.RootModule().Resources["test_instance.foo"].Primary 739 expected := p.RefreshReturn 740 if !reflect.DeepEqual(actual, expected) { 741 t.Fatalf("bad: %#v", actual) 742 } 743 744 // Ensure there is no backup 745 _, err = os.Stat(outPath + DefaultBackupExtension) 746 if err == nil || !os.IsNotExist(err) { 747 t.Fatalf("backup should not exist") 748 } 749 _, err = os.Stat("-") 750 if err == nil || !os.IsNotExist(err) { 751 t.Fatalf("backup should not exist") 752 } 753 } 754 755 func TestRefresh_displaysOutputs(t *testing.T) { 756 state := testState() 757 statePath := testStateFile(t, state) 758 759 p := testProvider() 760 ui := new(cli.MockUi) 761 c := &RefreshCommand{ 762 Meta: Meta{ 763 testingOverrides: metaOverridesForProvider(p), 764 Ui: ui, 765 }, 766 } 767 768 args := []string{ 769 "-state", statePath, 770 testFixturePath("refresh-output"), 771 } 772 if code := c.Run(args); code != 0 { 773 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 774 } 775 776 // Test that outputs were displayed 777 outputValue := "foo.example.com" 778 actual := ui.OutputWriter.String() 779 if !strings.Contains(actual, outputValue) { 780 t.Fatalf("Expected:\n%s\n\nTo include: %q", actual, outputValue) 781 } 782 } 783 784 // When creating an InstaneState for direct comparison to one contained in 785 // terraform.State, all fields must be initialized (duplicating the 786 // InstanceState.init() method) 787 func newInstanceState(id string) *terraform.InstanceState { 788 return &terraform.InstanceState{ 789 ID: id, 790 Attributes: make(map[string]string), 791 Ephemeral: terraform.EphemeralState{ 792 ConnInfo: make(map[string]string), 793 }, 794 Meta: make(map[string]interface{}), 795 } 796 } 797 798 const refreshVarFile = ` 799 foo = "bar" 800 ` 801 802 const testRefreshStr = ` 803 test_instance.foo: 804 ID = yes 805 provider = provider.test 806 ` 807 const testRefreshCwdStr = ` 808 test_instance.foo: 809 ID = yes 810 provider = provider.test 811 `