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