github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/command/apply_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "net/url" 10 "os" 11 "path/filepath" 12 "reflect" 13 "strings" 14 "sync" 15 "testing" 16 "time" 17 18 "github.com/hashicorp/terraform/remote" 19 "github.com/hashicorp/terraform/terraform" 20 "github.com/mitchellh/cli" 21 ) 22 23 func TestApply(t *testing.T) { 24 statePath := testTempFile(t) 25 26 p := testProvider() 27 ui := new(cli.MockUi) 28 c := &ApplyCommand{ 29 Meta: Meta{ 30 ContextOpts: testCtxConfig(p), 31 Ui: ui, 32 }, 33 } 34 35 args := []string{ 36 "-state", statePath, 37 testFixturePath("apply"), 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 _, err := os.Stat(statePath); err != nil { 44 t.Fatalf("err: %s", err) 45 } 46 47 f, err := os.Open(statePath) 48 if err != nil { 49 t.Fatalf("err: %s", err) 50 } 51 defer f.Close() 52 53 state, err := terraform.ReadState(f) 54 if err != nil { 55 t.Fatalf("err: %s", err) 56 } 57 if state == nil { 58 t.Fatal("state should not be nil") 59 } 60 } 61 62 func TestApply_configInvalid(t *testing.T) { 63 p := testProvider() 64 ui := new(cli.MockUi) 65 c := &ApplyCommand{ 66 Meta: Meta{ 67 ContextOpts: testCtxConfig(p), 68 Ui: ui, 69 }, 70 } 71 72 args := []string{ 73 "-state", testTempFile(t), 74 testFixturePath("apply-config-invalid"), 75 } 76 if code := c.Run(args); code != 1 { 77 t.Fatalf("bad: \n%s", ui.OutputWriter.String()) 78 } 79 } 80 81 func TestApply_defaultState(t *testing.T) { 82 td, err := ioutil.TempDir("", "tf") 83 if err != nil { 84 t.Fatalf("err: %s", err) 85 } 86 statePath := filepath.Join(td, DefaultStateFilename) 87 88 // Change to the temporary directory 89 cwd, err := os.Getwd() 90 if err != nil { 91 t.Fatalf("err: %s", err) 92 } 93 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 94 t.Fatalf("err: %s", err) 95 } 96 defer os.Chdir(cwd) 97 98 p := testProvider() 99 ui := new(cli.MockUi) 100 c := &ApplyCommand{ 101 Meta: Meta{ 102 ContextOpts: testCtxConfig(p), 103 Ui: ui, 104 }, 105 } 106 107 args := []string{ 108 testFixturePath("apply"), 109 } 110 if code := c.Run(args); code != 0 { 111 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 112 } 113 114 if _, err := os.Stat(statePath); err != nil { 115 t.Fatalf("err: %s", err) 116 } 117 118 f, err := os.Open(statePath) 119 if err != nil { 120 t.Fatalf("err: %s", err) 121 } 122 defer f.Close() 123 124 state, err := terraform.ReadState(f) 125 if err != nil { 126 t.Fatalf("err: %s", err) 127 } 128 if state == nil { 129 t.Fatal("state should not be nil") 130 } 131 } 132 133 func TestApply_error(t *testing.T) { 134 statePath := testTempFile(t) 135 136 p := testProvider() 137 ui := new(cli.MockUi) 138 c := &ApplyCommand{ 139 Meta: Meta{ 140 ContextOpts: testCtxConfig(p), 141 Ui: ui, 142 }, 143 } 144 145 var lock sync.Mutex 146 errored := false 147 p.ApplyFn = func( 148 info *terraform.InstanceInfo, 149 s *terraform.InstanceState, 150 d *terraform.InstanceDiff) (*terraform.InstanceState, error) { 151 lock.Lock() 152 defer lock.Unlock() 153 154 if !errored { 155 errored = true 156 return nil, fmt.Errorf("error") 157 } 158 159 return &terraform.InstanceState{ID: "foo"}, nil 160 } 161 p.DiffFn = func( 162 *terraform.InstanceInfo, 163 *terraform.InstanceState, 164 *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 165 return &terraform.InstanceDiff{ 166 Attributes: map[string]*terraform.ResourceAttrDiff{ 167 "ami": &terraform.ResourceAttrDiff{ 168 New: "bar", 169 }, 170 }, 171 }, nil 172 } 173 174 args := []string{ 175 "-state", statePath, 176 testFixturePath("apply-error"), 177 } 178 if code := c.Run(args); code != 1 { 179 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 180 } 181 182 if _, err := os.Stat(statePath); err != nil { 183 t.Fatalf("err: %s", err) 184 } 185 186 f, err := os.Open(statePath) 187 if err != nil { 188 t.Fatalf("err: %s", err) 189 } 190 defer f.Close() 191 192 state, err := terraform.ReadState(f) 193 if err != nil { 194 t.Fatalf("err: %s", err) 195 } 196 if state == nil { 197 t.Fatal("state should not be nil") 198 } 199 if len(state.RootModule().Resources) == 0 { 200 t.Fatal("no resources in state") 201 } 202 } 203 204 func TestApply_init(t *testing.T) { 205 // Change to the temporary directory 206 cwd, err := os.Getwd() 207 if err != nil { 208 t.Fatalf("err: %s", err) 209 } 210 dir := tempDir(t) 211 if err := os.MkdirAll(dir, 0755); err != nil { 212 t.Fatalf("err: %s", err) 213 } 214 if err := os.Chdir(dir); err != nil { 215 t.Fatalf("err: %s", err) 216 } 217 defer os.Chdir(cwd) 218 219 // Create the test fixtures 220 statePath := testTempFile(t) 221 ln := testHttpServer(t) 222 defer ln.Close() 223 224 // Initialize the command 225 p := testProvider() 226 ui := new(cli.MockUi) 227 c := &ApplyCommand{ 228 Meta: Meta{ 229 ContextOpts: testCtxConfig(p), 230 Ui: ui, 231 }, 232 } 233 234 // Build the URL to the init 235 var u url.URL 236 u.Scheme = "http" 237 u.Host = ln.Addr().String() 238 u.Path = "/header" 239 240 args := []string{ 241 "-state", statePath, 242 u.String(), 243 } 244 if code := c.Run(args); code != 0 { 245 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 246 } 247 248 if _, err := os.Stat("hello.tf"); err != nil { 249 t.Fatalf("err: %s", err) 250 } 251 252 if _, err := os.Stat(statePath); err != nil { 253 t.Fatalf("err: %s", err) 254 } 255 256 f, err := os.Open(statePath) 257 if err != nil { 258 t.Fatalf("err: %s", err) 259 } 260 defer f.Close() 261 262 state, err := terraform.ReadState(f) 263 if err != nil { 264 t.Fatalf("err: %s", err) 265 } 266 if state == nil { 267 t.Fatal("state should not be nil") 268 } 269 } 270 271 func TestApply_noArgs(t *testing.T) { 272 cwd, err := os.Getwd() 273 if err != nil { 274 t.Fatalf("err: %s", err) 275 } 276 if err := os.Chdir(testFixturePath("plan")); err != nil { 277 t.Fatalf("err: %s", err) 278 } 279 defer os.Chdir(cwd) 280 281 statePath := testTempFile(t) 282 283 p := testProvider() 284 ui := new(cli.MockUi) 285 c := &ApplyCommand{ 286 Meta: Meta{ 287 ContextOpts: testCtxConfig(p), 288 Ui: ui, 289 }, 290 } 291 292 args := []string{ 293 "-state", statePath, 294 } 295 if code := c.Run(args); code != 0 { 296 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 297 } 298 299 if _, err := os.Stat(statePath); err != nil { 300 t.Fatalf("err: %s", err) 301 } 302 303 f, err := os.Open(statePath) 304 if err != nil { 305 t.Fatalf("err: %s", err) 306 } 307 defer f.Close() 308 309 state, err := terraform.ReadState(f) 310 if err != nil { 311 t.Fatalf("err: %s", err) 312 } 313 if state == nil { 314 t.Fatal("state should not be nil") 315 } 316 } 317 318 func TestApply_plan(t *testing.T) { 319 // Disable test mode so input would be asked 320 test = false 321 defer func() { test = true }() 322 323 // Set some default reader/writers for the inputs 324 defaultInputReader = new(bytes.Buffer) 325 defaultInputWriter = new(bytes.Buffer) 326 327 planPath := testPlanFile(t, &terraform.Plan{ 328 Module: testModule(t, "apply"), 329 }) 330 statePath := testTempFile(t) 331 332 p := testProvider() 333 ui := new(cli.MockUi) 334 c := &ApplyCommand{ 335 Meta: Meta{ 336 ContextOpts: testCtxConfig(p), 337 Ui: ui, 338 }, 339 } 340 341 args := []string{ 342 "-state", statePath, 343 planPath, 344 } 345 if code := c.Run(args); code != 0 { 346 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 347 } 348 349 if p.InputCalled { 350 t.Fatalf("input should not be called for plans") 351 } 352 353 if _, err := os.Stat(statePath); err != nil { 354 t.Fatalf("err: %s", err) 355 } 356 357 f, err := os.Open(statePath) 358 if err != nil { 359 t.Fatalf("err: %s", err) 360 } 361 defer f.Close() 362 363 state, err := terraform.ReadState(f) 364 if err != nil { 365 t.Fatalf("err: %s", err) 366 } 367 if state == nil { 368 t.Fatal("state should not be nil") 369 } 370 } 371 372 func TestApply_plan_remoteState(t *testing.T) { 373 // Disable test mode so input would be asked 374 test = false 375 defer func() { test = true }() 376 tmp, cwd := testCwd(t) 377 defer testFixCwd(t, tmp, cwd) 378 if err := remote.EnsureDirectory(); err != nil { 379 t.Fatalf("err: %v", err) 380 } 381 382 // Set some default reader/writers for the inputs 383 defaultInputReader = new(bytes.Buffer) 384 defaultInputWriter = new(bytes.Buffer) 385 386 // Create a remote state 387 state := testState() 388 conf, srv := testRemoteState(t, state, 200) 389 defer srv.Close() 390 state.Remote = conf 391 392 planPath := testPlanFile(t, &terraform.Plan{ 393 Module: testModule(t, "apply"), 394 State: state, 395 }) 396 397 p := testProvider() 398 ui := new(cli.MockUi) 399 c := &ApplyCommand{ 400 Meta: Meta{ 401 ContextOpts: testCtxConfig(p), 402 Ui: ui, 403 }, 404 } 405 406 args := []string{ 407 planPath, 408 } 409 if code := c.Run(args); code != 0 { 410 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 411 } 412 413 if p.InputCalled { 414 t.Fatalf("input should not be called for plans") 415 } 416 417 // State file should be not be installed 418 exists, err := remote.ExistsFile(DefaultStateFilename) 419 if err != nil { 420 t.Fatalf("err: %v", err) 421 } 422 if exists { 423 t.Fatalf("State path should not exist") 424 } 425 426 // Check for remote state 427 output, _, err := remote.ReadLocalState() 428 if err != nil { 429 t.Fatalf("err: %v", err) 430 } 431 if output == nil { 432 t.Fatalf("missing remote state") 433 } 434 } 435 436 func TestApply_planWithVarFile(t *testing.T) { 437 varFileDir := testTempDir(t) 438 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 439 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { 440 t.Fatalf("err: %s", err) 441 } 442 443 planPath := testPlanFile(t, &terraform.Plan{ 444 Module: testModule(t, "apply"), 445 }) 446 statePath := testTempFile(t) 447 448 cwd, err := os.Getwd() 449 if err != nil { 450 t.Fatalf("err: %s", err) 451 } 452 if err := os.Chdir(varFileDir); err != nil { 453 t.Fatalf("err: %s", err) 454 } 455 defer os.Chdir(cwd) 456 457 p := testProvider() 458 ui := new(cli.MockUi) 459 c := &ApplyCommand{ 460 Meta: Meta{ 461 ContextOpts: testCtxConfig(p), 462 Ui: ui, 463 }, 464 } 465 466 args := []string{ 467 "-state", statePath, 468 planPath, 469 } 470 if code := c.Run(args); code != 0 { 471 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 472 } 473 474 if _, err := os.Stat(statePath); err != nil { 475 t.Fatalf("err: %s", err) 476 } 477 478 f, err := os.Open(statePath) 479 if err != nil { 480 t.Fatalf("err: %s", err) 481 } 482 defer f.Close() 483 484 state, err := terraform.ReadState(f) 485 if err != nil { 486 t.Fatalf("err: %s", err) 487 } 488 if state == nil { 489 t.Fatal("state should not be nil") 490 } 491 } 492 493 func TestApply_planVars(t *testing.T) { 494 planPath := testPlanFile(t, &terraform.Plan{ 495 Module: testModule(t, "apply"), 496 }) 497 statePath := testTempFile(t) 498 499 p := testProvider() 500 ui := new(cli.MockUi) 501 c := &ApplyCommand{ 502 Meta: Meta{ 503 ContextOpts: testCtxConfig(p), 504 Ui: ui, 505 }, 506 } 507 508 args := []string{ 509 "-state", statePath, 510 "-var", "foo=bar", 511 planPath, 512 } 513 if code := c.Run(args); code == 0 { 514 t.Fatal("should've failed") 515 } 516 } 517 518 func TestApply_refresh(t *testing.T) { 519 originalState := &terraform.State{ 520 Modules: []*terraform.ModuleState{ 521 &terraform.ModuleState{ 522 Path: []string{"root"}, 523 Resources: map[string]*terraform.ResourceState{ 524 "test_instance.foo": &terraform.ResourceState{ 525 Type: "test_instance", 526 Primary: &terraform.InstanceState{ 527 ID: "bar", 528 }, 529 }, 530 }, 531 }, 532 }, 533 } 534 535 statePath := testStateFile(t, originalState) 536 537 p := testProvider() 538 ui := new(cli.MockUi) 539 c := &ApplyCommand{ 540 Meta: Meta{ 541 ContextOpts: testCtxConfig(p), 542 Ui: ui, 543 }, 544 } 545 546 args := []string{ 547 "-state", statePath, 548 testFixturePath("apply"), 549 } 550 if code := c.Run(args); code != 0 { 551 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 552 } 553 554 if !p.RefreshCalled { 555 t.Fatal("should call refresh") 556 } 557 558 if _, err := os.Stat(statePath); err != nil { 559 t.Fatalf("err: %s", err) 560 } 561 562 f, err := os.Open(statePath) 563 if err != nil { 564 t.Fatalf("err: %s", err) 565 } 566 defer f.Close() 567 568 state, err := terraform.ReadState(f) 569 if err != nil { 570 t.Fatalf("err: %s", err) 571 } 572 if state == nil { 573 t.Fatal("state should not be nil") 574 } 575 576 // Should have a backup file 577 f, err = os.Open(statePath + DefaultBackupExtention) 578 if err != nil { 579 t.Fatalf("err: %s", err) 580 } 581 582 backupState, err := terraform.ReadState(f) 583 f.Close() 584 if err != nil { 585 t.Fatalf("err: %s", err) 586 } 587 588 actualStr := strings.TrimSpace(backupState.String()) 589 expectedStr := strings.TrimSpace(originalState.String()) 590 if actualStr != expectedStr { 591 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 592 } 593 } 594 595 func TestApply_shutdown(t *testing.T) { 596 stopped := false 597 stopCh := make(chan struct{}) 598 stopReplyCh := make(chan struct{}) 599 600 statePath := testTempFile(t) 601 602 p := testProvider() 603 shutdownCh := make(chan struct{}) 604 ui := new(cli.MockUi) 605 c := &ApplyCommand{ 606 Meta: Meta{ 607 ContextOpts: testCtxConfig(p), 608 Ui: ui, 609 }, 610 611 ShutdownCh: shutdownCh, 612 } 613 614 p.DiffFn = func( 615 *terraform.InstanceInfo, 616 *terraform.InstanceState, 617 *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 618 return &terraform.InstanceDiff{ 619 Attributes: map[string]*terraform.ResourceAttrDiff{ 620 "ami": &terraform.ResourceAttrDiff{ 621 New: "bar", 622 }, 623 }, 624 }, nil 625 } 626 p.ApplyFn = func( 627 *terraform.InstanceInfo, 628 *terraform.InstanceState, 629 *terraform.InstanceDiff) (*terraform.InstanceState, error) { 630 if !stopped { 631 stopped = true 632 close(stopCh) 633 <-stopReplyCh 634 } 635 636 return &terraform.InstanceState{ 637 ID: "foo", 638 Attributes: map[string]string{ 639 "ami": "2", 640 }, 641 }, nil 642 } 643 644 go func() { 645 <-stopCh 646 shutdownCh <- struct{}{} 647 648 // This is really dirty, but we have no other way to assure that 649 // tf.Stop() has been called. This doesn't assure it either, but 650 // it makes it much more certain. 651 time.Sleep(50 * time.Millisecond) 652 653 close(stopReplyCh) 654 }() 655 656 args := []string{ 657 "-state", statePath, 658 testFixturePath("apply-shutdown"), 659 } 660 if code := c.Run(args); code != 0 { 661 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 662 } 663 664 if _, err := os.Stat(statePath); err != nil { 665 t.Fatalf("err: %s", err) 666 } 667 668 f, err := os.Open(statePath) 669 if err != nil { 670 t.Fatalf("err: %s", err) 671 } 672 defer f.Close() 673 674 state, err := terraform.ReadState(f) 675 if err != nil { 676 t.Fatalf("err: %s", err) 677 } 678 if state == nil { 679 t.Fatal("state should not be nil") 680 } 681 682 if len(state.RootModule().Resources) != 1 { 683 t.Fatalf("bad: %d", len(state.RootModule().Resources)) 684 } 685 } 686 687 func TestApply_state(t *testing.T) { 688 originalState := &terraform.State{ 689 Modules: []*terraform.ModuleState{ 690 &terraform.ModuleState{ 691 Path: []string{"root"}, 692 Resources: map[string]*terraform.ResourceState{ 693 "test_instance.foo": &terraform.ResourceState{ 694 Type: "test_instance", 695 Primary: &terraform.InstanceState{ 696 ID: "bar", 697 }, 698 }, 699 }, 700 }, 701 }, 702 } 703 704 statePath := testStateFile(t, originalState) 705 706 p := testProvider() 707 p.DiffReturn = &terraform.InstanceDiff{ 708 Attributes: map[string]*terraform.ResourceAttrDiff{ 709 "ami": &terraform.ResourceAttrDiff{ 710 New: "bar", 711 }, 712 }, 713 } 714 715 ui := new(cli.MockUi) 716 c := &ApplyCommand{ 717 Meta: Meta{ 718 ContextOpts: testCtxConfig(p), 719 Ui: ui, 720 }, 721 } 722 723 // Run the apply command pointing to our existing state 724 args := []string{ 725 "-state", statePath, 726 testFixturePath("apply"), 727 } 728 if code := c.Run(args); code != 0 { 729 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 730 } 731 732 // Verify that the provider was called with the existing state 733 actual := strings.TrimSpace(p.DiffState.String()) 734 expected := strings.TrimSpace(testApplyStateDiffStr) 735 if actual != expected { 736 t.Fatalf("bad:\n\n%s", actual) 737 } 738 739 actual = strings.TrimSpace(p.ApplyState.String()) 740 expected = strings.TrimSpace(testApplyStateStr) 741 if actual != expected { 742 t.Fatalf("bad:\n\n%s", actual) 743 } 744 745 // Verify a new state exists 746 if _, err := os.Stat(statePath); err != nil { 747 t.Fatalf("err: %s", err) 748 } 749 750 f, err := os.Open(statePath) 751 if err != nil { 752 t.Fatalf("err: %s", err) 753 } 754 defer f.Close() 755 756 state, err := terraform.ReadState(f) 757 if err != nil { 758 t.Fatalf("err: %s", err) 759 } 760 if state == nil { 761 t.Fatal("state should not be nil") 762 } 763 764 // Should have a backup file 765 f, err = os.Open(statePath + DefaultBackupExtention) 766 if err != nil { 767 t.Fatalf("err: %s", err) 768 } 769 770 backupState, err := terraform.ReadState(f) 771 f.Close() 772 if err != nil { 773 t.Fatalf("err: %s", err) 774 } 775 776 // nil out the ConnInfo since that should not be restored 777 originalState.RootModule().Resources["test_instance.foo"].Primary.Ephemeral.ConnInfo = nil 778 779 actualStr := strings.TrimSpace(backupState.String()) 780 expectedStr := strings.TrimSpace(originalState.String()) 781 if actualStr != expectedStr { 782 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 783 } 784 } 785 786 func TestApply_stateNoExist(t *testing.T) { 787 p := testProvider() 788 ui := new(cli.MockUi) 789 c := &ApplyCommand{ 790 Meta: Meta{ 791 ContextOpts: testCtxConfig(p), 792 Ui: ui, 793 }, 794 } 795 796 args := []string{ 797 "idontexist.tfstate", 798 testFixturePath("apply"), 799 } 800 if code := c.Run(args); code != 1 { 801 t.Fatalf("bad: \n%s", ui.OutputWriter.String()) 802 } 803 } 804 805 func TestApply_vars(t *testing.T) { 806 statePath := testTempFile(t) 807 808 p := testProvider() 809 ui := new(cli.MockUi) 810 c := &ApplyCommand{ 811 Meta: Meta{ 812 ContextOpts: testCtxConfig(p), 813 Ui: ui, 814 }, 815 } 816 817 actual := "" 818 p.DiffFn = func( 819 info *terraform.InstanceInfo, 820 s *terraform.InstanceState, 821 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 822 if v, ok := c.Config["value"]; ok { 823 actual = v.(string) 824 } 825 826 return &terraform.InstanceDiff{}, nil 827 } 828 829 args := []string{ 830 "-var", "foo=bar", 831 "-state", statePath, 832 testFixturePath("apply-vars"), 833 } 834 if code := c.Run(args); code != 0 { 835 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 836 } 837 838 if actual != "bar" { 839 t.Fatal("didn't work") 840 } 841 } 842 843 func TestApply_varFile(t *testing.T) { 844 varFilePath := testTempFile(t) 845 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { 846 t.Fatalf("err: %s", err) 847 } 848 849 statePath := testTempFile(t) 850 851 p := testProvider() 852 ui := new(cli.MockUi) 853 c := &ApplyCommand{ 854 Meta: Meta{ 855 ContextOpts: testCtxConfig(p), 856 Ui: ui, 857 }, 858 } 859 860 actual := "" 861 p.DiffFn = func( 862 info *terraform.InstanceInfo, 863 s *terraform.InstanceState, 864 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 865 if v, ok := c.Config["value"]; ok { 866 actual = v.(string) 867 } 868 869 return &terraform.InstanceDiff{}, nil 870 } 871 872 args := []string{ 873 "-var-file", varFilePath, 874 "-state", statePath, 875 testFixturePath("apply-vars"), 876 } 877 if code := c.Run(args); code != 0 { 878 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 879 } 880 881 if actual != "bar" { 882 t.Fatal("didn't work") 883 } 884 } 885 886 func TestApply_varFileDefault(t *testing.T) { 887 varFileDir := testTempDir(t) 888 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 889 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { 890 t.Fatalf("err: %s", err) 891 } 892 893 statePath := testTempFile(t) 894 895 cwd, err := os.Getwd() 896 if err != nil { 897 t.Fatalf("err: %s", err) 898 } 899 if err := os.Chdir(varFileDir); err != nil { 900 t.Fatalf("err: %s", err) 901 } 902 defer os.Chdir(cwd) 903 904 p := testProvider() 905 ui := new(cli.MockUi) 906 c := &ApplyCommand{ 907 Meta: Meta{ 908 ContextOpts: testCtxConfig(p), 909 Ui: ui, 910 }, 911 } 912 913 actual := "" 914 p.DiffFn = func( 915 info *terraform.InstanceInfo, 916 s *terraform.InstanceState, 917 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 918 if v, ok := c.Config["value"]; ok { 919 actual = v.(string) 920 } 921 922 return &terraform.InstanceDiff{}, nil 923 } 924 925 args := []string{ 926 "-state", statePath, 927 testFixturePath("apply-vars"), 928 } 929 if code := c.Run(args); code != 0 { 930 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 931 } 932 933 if actual != "bar" { 934 t.Fatal("didn't work") 935 } 936 } 937 938 func TestApply_backup(t *testing.T) { 939 originalState := &terraform.State{ 940 Modules: []*terraform.ModuleState{ 941 &terraform.ModuleState{ 942 Path: []string{"root"}, 943 Resources: map[string]*terraform.ResourceState{ 944 "test_instance.foo": &terraform.ResourceState{ 945 Type: "test_instance", 946 Primary: &terraform.InstanceState{ 947 ID: "bar", 948 }, 949 }, 950 }, 951 }, 952 }, 953 } 954 955 statePath := testStateFile(t, originalState) 956 backupPath := testTempFile(t) 957 958 p := testProvider() 959 p.DiffReturn = &terraform.InstanceDiff{ 960 Attributes: map[string]*terraform.ResourceAttrDiff{ 961 "ami": &terraform.ResourceAttrDiff{ 962 New: "bar", 963 }, 964 }, 965 } 966 967 ui := new(cli.MockUi) 968 c := &ApplyCommand{ 969 Meta: Meta{ 970 ContextOpts: testCtxConfig(p), 971 Ui: ui, 972 }, 973 } 974 975 // Run the apply command pointing to our existing state 976 args := []string{ 977 "-state", statePath, 978 "-backup", backupPath, 979 testFixturePath("apply"), 980 } 981 if code := c.Run(args); code != 0 { 982 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 983 } 984 985 // Verify a new state exists 986 if _, err := os.Stat(statePath); err != nil { 987 t.Fatalf("err: %s", err) 988 } 989 990 f, err := os.Open(statePath) 991 if err != nil { 992 t.Fatalf("err: %s", err) 993 } 994 defer f.Close() 995 996 state, err := terraform.ReadState(f) 997 if err != nil { 998 t.Fatalf("err: %s", err) 999 } 1000 if state == nil { 1001 t.Fatal("state should not be nil") 1002 } 1003 1004 // Should have a backup file 1005 f, err = os.Open(backupPath) 1006 if err != nil { 1007 t.Fatalf("err: %s", err) 1008 } 1009 1010 backupState, err := terraform.ReadState(f) 1011 f.Close() 1012 if err != nil { 1013 t.Fatalf("err: %s", err) 1014 } 1015 1016 actual := backupState.RootModule().Resources["test_instance.foo"] 1017 expected := originalState.RootModule().Resources["test_instance.foo"] 1018 if !reflect.DeepEqual(actual, expected) { 1019 t.Fatalf("bad: %#v %#v", actual, expected) 1020 } 1021 } 1022 1023 func TestApply_disableBackup(t *testing.T) { 1024 originalState := testState() 1025 statePath := testStateFile(t, originalState) 1026 1027 p := testProvider() 1028 p.DiffReturn = &terraform.InstanceDiff{ 1029 Attributes: map[string]*terraform.ResourceAttrDiff{ 1030 "ami": &terraform.ResourceAttrDiff{ 1031 New: "bar", 1032 }, 1033 }, 1034 } 1035 1036 ui := new(cli.MockUi) 1037 c := &ApplyCommand{ 1038 Meta: Meta{ 1039 ContextOpts: testCtxConfig(p), 1040 Ui: ui, 1041 }, 1042 } 1043 1044 // Run the apply command pointing to our existing state 1045 args := []string{ 1046 "-state", statePath, 1047 "-backup", "-", 1048 testFixturePath("apply"), 1049 } 1050 if code := c.Run(args); code != 0 { 1051 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1052 } 1053 1054 // Verify that the provider was called with the existing state 1055 actual := strings.TrimSpace(p.DiffState.String()) 1056 expected := strings.TrimSpace(testApplyDisableBackupStr) 1057 if actual != expected { 1058 t.Fatalf("bad:\n\n%s", actual) 1059 } 1060 1061 actual = strings.TrimSpace(p.ApplyState.String()) 1062 expected = strings.TrimSpace(testApplyDisableBackupStateStr) 1063 if actual != expected { 1064 t.Fatalf("bad:\n\n%s", actual) 1065 } 1066 1067 // Verify a new state exists 1068 if _, err := os.Stat(statePath); err != nil { 1069 t.Fatalf("err: %s", err) 1070 } 1071 1072 f, err := os.Open(statePath) 1073 if err != nil { 1074 t.Fatalf("err: %s", err) 1075 } 1076 defer f.Close() 1077 1078 state, err := terraform.ReadState(f) 1079 if err != nil { 1080 t.Fatalf("err: %s", err) 1081 } 1082 if state == nil { 1083 t.Fatal("state should not be nil") 1084 } 1085 1086 // Ensure there is no backup 1087 _, err = os.Stat(statePath + DefaultBackupExtention) 1088 if err == nil || !os.IsNotExist(err) { 1089 t.Fatalf("backup should not exist") 1090 } 1091 } 1092 1093 func testHttpServer(t *testing.T) net.Listener { 1094 ln, err := net.Listen("tcp", ":0") 1095 if err != nil { 1096 t.Fatalf("err: %s", err) 1097 } 1098 1099 mux := http.NewServeMux() 1100 mux.HandleFunc("/header", testHttpHandlerHeader) 1101 1102 var server http.Server 1103 server.Handler = mux 1104 go server.Serve(ln) 1105 1106 return ln 1107 } 1108 1109 func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) { 1110 var url url.URL 1111 url.Scheme = "file" 1112 url.Path = testFixturePath("init") 1113 1114 w.Header().Add("X-Terraform-Get", url.String()) 1115 w.WriteHeader(200) 1116 } 1117 1118 const applyVarFile = ` 1119 foo = "bar" 1120 ` 1121 1122 const testApplyDisableBackupStr = ` 1123 ID = bar 1124 ` 1125 1126 const testApplyDisableBackupStateStr = ` 1127 ID = bar 1128 ` 1129 1130 const testApplyStateStr = ` 1131 ID = bar 1132 ` 1133 1134 const testApplyStateDiffStr = ` 1135 ID = bar 1136 `