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