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