github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/command/apply_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "net" 9 "net/http" 10 "net/url" 11 "os" 12 "path/filepath" 13 "reflect" 14 "strings" 15 "sync" 16 "testing" 17 "time" 18 19 "github.com/hashicorp/terraform/state" 20 "github.com/hashicorp/terraform/terraform" 21 "github.com/mitchellh/cli" 22 ) 23 24 func TestApply(t *testing.T) { 25 statePath := testTempFile(t) 26 27 p := testProvider() 28 ui := new(cli.MockUi) 29 c := &ApplyCommand{ 30 Meta: Meta{ 31 ContextOpts: testCtxConfig(p), 32 Ui: ui, 33 }, 34 } 35 36 args := []string{ 37 "-state", statePath, 38 testFixturePath("apply"), 39 } 40 if code := c.Run(args); code != 0 { 41 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 42 } 43 44 if _, err := os.Stat(statePath); err != nil { 45 t.Fatalf("err: %s", err) 46 } 47 48 f, err := os.Open(statePath) 49 if err != nil { 50 t.Fatalf("err: %s", err) 51 } 52 defer f.Close() 53 54 state, err := terraform.ReadState(f) 55 if err != nil { 56 t.Fatalf("err: %s", err) 57 } 58 if state == nil { 59 t.Fatal("state should not be nil") 60 } 61 } 62 63 // test apply with locked state 64 func TestApply_lockedState(t *testing.T) { 65 statePath := testTempFile(t) 66 67 unlock, err := testLockState("./testdata", statePath) 68 if err != nil { 69 t.Fatal(err) 70 } 71 defer unlock() 72 73 p := testProvider() 74 ui := new(cli.MockUi) 75 c := &ApplyCommand{ 76 Meta: Meta{ 77 ContextOpts: testCtxConfig(p), 78 Ui: ui, 79 }, 80 } 81 82 args := []string{ 83 "-state", statePath, 84 testFixturePath("apply"), 85 } 86 if code := c.Run(args); code == 0 { 87 t.Fatal("expected error") 88 } 89 90 output := ui.ErrorWriter.String() 91 if !strings.Contains(output, "lock") { 92 t.Fatal("command output does not look like a lock error:", output) 93 } 94 } 95 96 // test apply with locked state, waiting for unlock 97 func TestApply_lockedStateWait(t *testing.T) { 98 statePath := testTempFile(t) 99 100 unlock, err := testLockState("./testdata", statePath) 101 if err != nil { 102 t.Fatal(err) 103 } 104 105 // unlock during apply 106 go func() { 107 time.Sleep(500 * time.Millisecond) 108 unlock() 109 }() 110 111 p := testProvider() 112 ui := new(cli.MockUi) 113 c := &ApplyCommand{ 114 Meta: Meta{ 115 ContextOpts: testCtxConfig(p), 116 Ui: ui, 117 }, 118 } 119 120 // wait 4s just in case the lock process doesn't release in under a second, 121 // and we want our context to be alive for a second retry at the 3s mark. 122 args := []string{ 123 "-state", statePath, 124 "-lock-timeout", "4s", 125 testFixturePath("apply"), 126 } 127 if code := c.Run(args); code != 0 { 128 log.Fatalf("lock should have succeed in less than 3s: %s", ui.ErrorWriter) 129 } 130 } 131 132 // high water mark counter 133 type hwm struct { 134 sync.Mutex 135 val int 136 max int 137 } 138 139 func (t *hwm) Inc() { 140 t.Lock() 141 defer t.Unlock() 142 t.val++ 143 if t.val > t.max { 144 t.max = t.val 145 } 146 } 147 148 func (t *hwm) Dec() { 149 t.Lock() 150 defer t.Unlock() 151 t.val-- 152 } 153 154 func (t *hwm) Max() int { 155 t.Lock() 156 defer t.Unlock() 157 return t.max 158 } 159 160 func TestApply_parallelism(t *testing.T) { 161 provider := testProvider() 162 statePath := testTempFile(t) 163 164 par := 4 165 166 // This blocks all the appy functions. We close it when we exit so 167 // they end quickly after this test finishes. 168 block := make(chan struct{}) 169 // signal how many goroutines have started 170 started := make(chan int, 100) 171 172 runCount := &hwm{} 173 174 provider.ApplyFn = func( 175 i *terraform.InstanceInfo, 176 s *terraform.InstanceState, 177 d *terraform.InstanceDiff) (*terraform.InstanceState, error) { 178 // Increment so we're counting parallelism 179 started <- 1 180 runCount.Inc() 181 defer runCount.Dec() 182 // Block here to stage up our max number of parallel instances 183 <-block 184 185 return nil, nil 186 } 187 188 ui := new(cli.MockUi) 189 c := &ApplyCommand{ 190 Meta: Meta{ 191 ContextOpts: testCtxConfig(provider), 192 Ui: ui, 193 }, 194 } 195 196 args := []string{ 197 "-state", statePath, 198 fmt.Sprintf("-parallelism=%d", par), 199 testFixturePath("parallelism"), 200 } 201 202 // Run in a goroutine. We can get any errors from the ui.OutputWriter 203 doneCh := make(chan int, 1) 204 go func() { 205 doneCh <- c.Run(args) 206 }() 207 208 timeout := time.After(5 * time.Second) 209 210 // ensure things are running 211 for i := 0; i < par; i++ { 212 select { 213 case <-timeout: 214 t.Fatal("timeout waiting for all goroutines to start") 215 case <-started: 216 } 217 } 218 219 // a little extra sleep, since we can't ensure all goroutines from the walk have 220 // really started 221 time.Sleep(100 * time.Millisecond) 222 close(block) 223 224 select { 225 case res := <-doneCh: 226 if res != 0 { 227 t.Fatal(ui.OutputWriter.String()) 228 } 229 case <-timeout: 230 t.Fatal("timeout waiting from Run()") 231 } 232 233 // The total in flight should equal the parallelism 234 if runCount.Max() != par { 235 t.Fatalf("Expected parallelism: %d, got: %d", par, runCount.Max()) 236 } 237 } 238 239 func TestApply_configInvalid(t *testing.T) { 240 p := testProvider() 241 ui := new(cli.MockUi) 242 c := &ApplyCommand{ 243 Meta: Meta{ 244 ContextOpts: testCtxConfig(p), 245 Ui: ui, 246 }, 247 } 248 249 args := []string{ 250 "-state", testTempFile(t), 251 testFixturePath("apply-config-invalid"), 252 } 253 if code := c.Run(args); code != 1 { 254 t.Fatalf("bad: \n%s", ui.OutputWriter.String()) 255 } 256 } 257 258 func TestApply_defaultState(t *testing.T) { 259 td, err := ioutil.TempDir("", "tf") 260 if err != nil { 261 t.Fatalf("err: %s", err) 262 } 263 statePath := filepath.Join(td, DefaultStateFilename) 264 265 // Change to the temporary directory 266 cwd, err := os.Getwd() 267 if err != nil { 268 t.Fatalf("err: %s", err) 269 } 270 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 271 t.Fatalf("err: %s", err) 272 } 273 defer os.Chdir(cwd) 274 275 p := testProvider() 276 ui := new(cli.MockUi) 277 c := &ApplyCommand{ 278 Meta: Meta{ 279 ContextOpts: testCtxConfig(p), 280 Ui: ui, 281 }, 282 } 283 284 args := []string{ 285 testFixturePath("apply"), 286 } 287 if code := c.Run(args); code != 0 { 288 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 289 } 290 291 if _, err := os.Stat(statePath); err != nil { 292 t.Fatalf("err: %s", err) 293 } 294 295 f, err := os.Open(statePath) 296 if err != nil { 297 t.Fatalf("err: %s", err) 298 } 299 defer f.Close() 300 301 state, err := terraform.ReadState(f) 302 if err != nil { 303 t.Fatalf("err: %s", err) 304 } 305 if state == nil { 306 t.Fatal("state should not be nil") 307 } 308 } 309 310 func TestApply_error(t *testing.T) { 311 statePath := testTempFile(t) 312 313 p := testProvider() 314 ui := new(cli.MockUi) 315 c := &ApplyCommand{ 316 Meta: Meta{ 317 ContextOpts: testCtxConfig(p), 318 Ui: ui, 319 }, 320 } 321 322 var lock sync.Mutex 323 errored := false 324 p.ApplyFn = func( 325 info *terraform.InstanceInfo, 326 s *terraform.InstanceState, 327 d *terraform.InstanceDiff) (*terraform.InstanceState, error) { 328 lock.Lock() 329 defer lock.Unlock() 330 331 if !errored { 332 errored = true 333 return nil, fmt.Errorf("error") 334 } 335 336 return &terraform.InstanceState{ID: "foo"}, nil 337 } 338 p.DiffFn = func( 339 *terraform.InstanceInfo, 340 *terraform.InstanceState, 341 *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 342 return &terraform.InstanceDiff{ 343 Attributes: map[string]*terraform.ResourceAttrDiff{ 344 "ami": &terraform.ResourceAttrDiff{ 345 New: "bar", 346 }, 347 }, 348 }, nil 349 } 350 351 args := []string{ 352 "-state", statePath, 353 testFixturePath("apply-error"), 354 } 355 if code := c.Run(args); code != 1 { 356 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 357 } 358 359 if _, err := os.Stat(statePath); err != nil { 360 t.Fatalf("err: %s", err) 361 } 362 363 f, err := os.Open(statePath) 364 if err != nil { 365 t.Fatalf("err: %s", err) 366 } 367 defer f.Close() 368 369 state, err := terraform.ReadState(f) 370 if err != nil { 371 t.Fatalf("err: %s", err) 372 } 373 if state == nil { 374 t.Fatal("state should not be nil") 375 } 376 if len(state.RootModule().Resources) == 0 { 377 t.Fatal("no resources in state") 378 } 379 } 380 381 func TestApply_init(t *testing.T) { 382 // Change to the temporary directory 383 cwd, err := os.Getwd() 384 if err != nil { 385 t.Fatalf("err: %s", err) 386 } 387 dir := tempDir(t) 388 if err := os.MkdirAll(dir, 0755); err != nil { 389 t.Fatalf("err: %s", err) 390 } 391 if err := os.Chdir(dir); err != nil { 392 t.Fatalf("err: %s", err) 393 } 394 defer os.Chdir(cwd) 395 396 // Create the test fixtures 397 statePath := testTempFile(t) 398 ln := testHttpServer(t) 399 defer ln.Close() 400 401 // Initialize the command 402 p := testProvider() 403 ui := new(cli.MockUi) 404 c := &ApplyCommand{ 405 Meta: Meta{ 406 ContextOpts: testCtxConfig(p), 407 Ui: ui, 408 }, 409 } 410 411 // Build the URL to the init 412 var u url.URL 413 u.Scheme = "http" 414 u.Host = ln.Addr().String() 415 u.Path = "/header" 416 417 args := []string{ 418 "-state", statePath, 419 u.String(), 420 } 421 if code := c.Run(args); code != 0 { 422 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 423 } 424 425 if _, err := os.Stat("hello.tf"); err != nil { 426 t.Fatalf("err: %s", err) 427 } 428 429 if _, err := os.Stat(statePath); err != nil { 430 t.Fatalf("err: %s", err) 431 } 432 433 f, err := os.Open(statePath) 434 if err != nil { 435 t.Fatalf("err: %s", err) 436 } 437 defer f.Close() 438 439 state, err := terraform.ReadState(f) 440 if err != nil { 441 t.Fatalf("err: %s", err) 442 } 443 if state == nil { 444 t.Fatal("state should not be nil") 445 } 446 } 447 448 func TestApply_input(t *testing.T) { 449 // Disable test mode so input would be asked 450 test = false 451 defer func() { test = true }() 452 453 // Set some default reader/writers for the inputs 454 defaultInputReader = bytes.NewBufferString("foo\n") 455 defaultInputWriter = new(bytes.Buffer) 456 457 statePath := testTempFile(t) 458 459 p := testProvider() 460 ui := new(cli.MockUi) 461 c := &ApplyCommand{ 462 Meta: Meta{ 463 ContextOpts: testCtxConfig(p), 464 Ui: ui, 465 }, 466 } 467 468 args := []string{ 469 "-state", statePath, 470 testFixturePath("apply-input"), 471 } 472 if code := c.Run(args); code != 0 { 473 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 474 } 475 476 if !p.InputCalled { 477 t.Fatal("input should be called") 478 } 479 } 480 481 // When only a partial set of the variables are set, Terraform 482 // should still ask for the unset ones by default (with -input=true) 483 func TestApply_inputPartial(t *testing.T) { 484 // Disable test mode so input would be asked 485 test = false 486 defer func() { test = true }() 487 488 // Set some default reader/writers for the inputs 489 defaultInputReader = bytes.NewBufferString("one\ntwo\n") 490 defaultInputWriter = new(bytes.Buffer) 491 492 statePath := testTempFile(t) 493 494 p := testProvider() 495 ui := new(cli.MockUi) 496 c := &ApplyCommand{ 497 Meta: Meta{ 498 ContextOpts: testCtxConfig(p), 499 Ui: ui, 500 }, 501 } 502 503 args := []string{ 504 "-state", statePath, 505 "-var", "foo=foovalue", 506 testFixturePath("apply-input-partial"), 507 } 508 if code := c.Run(args); code != 0 { 509 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 510 } 511 512 expected := strings.TrimSpace(` 513 <no state> 514 Outputs: 515 516 bar = one 517 foo = foovalue 518 `) 519 testStateOutput(t, statePath, expected) 520 } 521 522 func TestApply_noArgs(t *testing.T) { 523 cwd, err := os.Getwd() 524 if err != nil { 525 t.Fatalf("err: %s", err) 526 } 527 if err := os.Chdir(testFixturePath("plan")); err != nil { 528 t.Fatalf("err: %s", err) 529 } 530 defer os.Chdir(cwd) 531 532 statePath := testTempFile(t) 533 534 p := testProvider() 535 ui := new(cli.MockUi) 536 c := &ApplyCommand{ 537 Meta: Meta{ 538 ContextOpts: testCtxConfig(p), 539 Ui: ui, 540 }, 541 } 542 543 args := []string{ 544 "-state", statePath, 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 _, err := os.Stat(statePath); err != nil { 551 t.Fatalf("err: %s", err) 552 } 553 554 f, err := os.Open(statePath) 555 if err != nil { 556 t.Fatalf("err: %s", err) 557 } 558 defer f.Close() 559 560 state, err := terraform.ReadState(f) 561 if err != nil { 562 t.Fatalf("err: %s", err) 563 } 564 if state == nil { 565 t.Fatal("state should not be nil") 566 } 567 } 568 569 func TestApply_plan(t *testing.T) { 570 // Disable test mode so input would be asked 571 test = false 572 defer func() { test = true }() 573 574 // Set some default reader/writers for the inputs 575 defaultInputReader = new(bytes.Buffer) 576 defaultInputWriter = new(bytes.Buffer) 577 578 planPath := testPlanFile(t, &terraform.Plan{ 579 Module: testModule(t, "apply"), 580 }) 581 statePath := testTempFile(t) 582 583 p := testProvider() 584 ui := new(cli.MockUi) 585 c := &ApplyCommand{ 586 Meta: Meta{ 587 ContextOpts: testCtxConfig(p), 588 Ui: ui, 589 }, 590 } 591 592 args := []string{ 593 "-state-out", statePath, 594 planPath, 595 } 596 if code := c.Run(args); code != 0 { 597 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 598 } 599 600 if p.InputCalled { 601 t.Fatalf("input should not be called for plans") 602 } 603 604 if _, err := os.Stat(statePath); err != nil { 605 t.Fatalf("err: %s", err) 606 } 607 608 f, err := os.Open(statePath) 609 if err != nil { 610 t.Fatalf("err: %s", err) 611 } 612 defer f.Close() 613 614 state, err := terraform.ReadState(f) 615 if err != nil { 616 t.Fatalf("err: %s", err) 617 } 618 if state == nil { 619 t.Fatal("state should not be nil") 620 } 621 } 622 623 func TestApply_plan_backup(t *testing.T) { 624 plan := testPlan(t) 625 planPath := testPlanFile(t, plan) 626 statePath := testTempFile(t) 627 backupPath := testTempFile(t) 628 629 p := testProvider() 630 ui := new(cli.MockUi) 631 c := &ApplyCommand{ 632 Meta: Meta{ 633 ContextOpts: testCtxConfig(p), 634 Ui: ui, 635 }, 636 } 637 638 // create a state file that needs to be backed up 639 err := (&state.LocalState{Path: statePath}).WriteState(plan.State) 640 if err != nil { 641 t.Fatal(err) 642 } 643 args := []string{ 644 "-state-out", statePath, 645 "-backup", backupPath, 646 planPath, 647 } 648 if code := c.Run(args); code != 0 { 649 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 650 } 651 652 { 653 // Should have a backup file 654 f, err := os.Open(backupPath) 655 if err != nil { 656 t.Fatalf("err: %s", err) 657 } 658 659 _, err = terraform.ReadState(f) 660 f.Close() 661 if err != nil { 662 t.Fatalf("err: %s", err) 663 } 664 } 665 } 666 667 func TestApply_plan_noBackup(t *testing.T) { 668 planPath := testPlanFile(t, testPlan(t)) 669 statePath := testTempFile(t) 670 671 p := testProvider() 672 ui := new(cli.MockUi) 673 c := &ApplyCommand{ 674 Meta: Meta{ 675 ContextOpts: testCtxConfig(p), 676 Ui: ui, 677 }, 678 } 679 680 args := []string{ 681 "-state-out", statePath, 682 "-backup", "-", 683 planPath, 684 } 685 if code := c.Run(args); code != 0 { 686 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 687 } 688 689 // Ensure there is no backup 690 _, err := os.Stat(statePath + DefaultBackupExtension) 691 if err == nil || !os.IsNotExist(err) { 692 t.Fatalf("backup should not exist") 693 } 694 695 // Ensure there is no literal "-" 696 _, err = os.Stat("-") 697 if err == nil || !os.IsNotExist(err) { 698 t.Fatalf("backup should not exist") 699 } 700 } 701 702 func TestApply_plan_remoteState(t *testing.T) { 703 // Disable test mode so input would be asked 704 test = false 705 defer func() { test = true }() 706 tmp, cwd := testCwd(t) 707 defer testFixCwd(t, tmp, cwd) 708 remoteStatePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) 709 if err := os.MkdirAll(filepath.Dir(remoteStatePath), 0755); err != nil { 710 t.Fatalf("err: %s", err) 711 } 712 713 // Set some default reader/writers for the inputs 714 defaultInputReader = new(bytes.Buffer) 715 defaultInputWriter = new(bytes.Buffer) 716 717 // Create a remote state 718 state := testState() 719 conf, srv := testRemoteState(t, state, 200) 720 defer srv.Close() 721 state.Remote = conf 722 723 planPath := testPlanFile(t, &terraform.Plan{ 724 Module: testModule(t, "apply"), 725 State: state, 726 }) 727 728 p := testProvider() 729 ui := new(cli.MockUi) 730 c := &ApplyCommand{ 731 Meta: Meta{ 732 ContextOpts: testCtxConfig(p), 733 Ui: ui, 734 }, 735 } 736 737 args := []string{ 738 planPath, 739 } 740 if code := c.Run(args); code != 0 { 741 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 742 } 743 744 if p.InputCalled { 745 t.Fatalf("input should not be called for plans") 746 } 747 748 // State file should be not be installed 749 if _, err := os.Stat(filepath.Join(tmp, DefaultStateFilename)); err == nil { 750 data, _ := ioutil.ReadFile(DefaultStateFilename) 751 t.Fatalf("State path should not exist: %s", string(data)) 752 } 753 754 // Check that there is no remote state config 755 if _, err := os.Stat(remoteStatePath); err == nil { 756 t.Fatalf("has remote state config") 757 } 758 } 759 760 func TestApply_planWithVarFile(t *testing.T) { 761 varFileDir := testTempDir(t) 762 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 763 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { 764 t.Fatalf("err: %s", err) 765 } 766 767 planPath := testPlanFile(t, &terraform.Plan{ 768 Module: testModule(t, "apply"), 769 }) 770 statePath := testTempFile(t) 771 772 cwd, err := os.Getwd() 773 if err != nil { 774 t.Fatalf("err: %s", err) 775 } 776 if err := os.Chdir(varFileDir); err != nil { 777 t.Fatalf("err: %s", err) 778 } 779 defer os.Chdir(cwd) 780 781 p := testProvider() 782 ui := new(cli.MockUi) 783 c := &ApplyCommand{ 784 Meta: Meta{ 785 ContextOpts: testCtxConfig(p), 786 Ui: ui, 787 }, 788 } 789 790 args := []string{ 791 "-state-out", statePath, 792 planPath, 793 } 794 if code := c.Run(args); code != 0 { 795 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 796 } 797 798 if _, err := os.Stat(statePath); err != nil { 799 t.Fatalf("err: %s", err) 800 } 801 802 f, err := os.Open(statePath) 803 if err != nil { 804 t.Fatalf("err: %s", err) 805 } 806 defer f.Close() 807 808 state, err := terraform.ReadState(f) 809 if err != nil { 810 t.Fatalf("err: %s", err) 811 } 812 if state == nil { 813 t.Fatal("state should not be nil") 814 } 815 } 816 817 func TestApply_planVars(t *testing.T) { 818 planPath := testPlanFile(t, &terraform.Plan{ 819 Module: testModule(t, "apply"), 820 }) 821 statePath := testTempFile(t) 822 823 p := testProvider() 824 ui := new(cli.MockUi) 825 c := &ApplyCommand{ 826 Meta: Meta{ 827 ContextOpts: testCtxConfig(p), 828 Ui: ui, 829 }, 830 } 831 832 args := []string{ 833 "-state", statePath, 834 "-var", "foo=bar", 835 planPath, 836 } 837 if code := c.Run(args); code == 0 { 838 t.Fatal("should've failed") 839 } 840 } 841 842 // we should be able to apply a plan file with no other file dependencies 843 func TestApply_planNoModuleFiles(t *testing.T) { 844 // temporary data directory which we can remove between commands 845 td, err := ioutil.TempDir("", "tf") 846 if err != nil { 847 t.Fatal(err) 848 } 849 defer os.RemoveAll(td) 850 851 defer testChdir(t, td)() 852 853 p := testProvider() 854 planFile := testPlanFile(t, &terraform.Plan{ 855 Module: testModule(t, "apply-plan-no-module"), 856 }) 857 858 contextOpts := testCtxConfig(p) 859 860 apply := &ApplyCommand{ 861 Meta: Meta{ 862 ContextOpts: contextOpts, 863 Ui: new(cli.MockUi), 864 }, 865 } 866 args := []string{ 867 planFile, 868 } 869 apply.Run(args) 870 if p.ValidateCalled { 871 t.Fatal("Validate should not be called with a plan") 872 } 873 } 874 875 func TestApply_refresh(t *testing.T) { 876 originalState := &terraform.State{ 877 Modules: []*terraform.ModuleState{ 878 &terraform.ModuleState{ 879 Path: []string{"root"}, 880 Resources: map[string]*terraform.ResourceState{ 881 "test_instance.foo": &terraform.ResourceState{ 882 Type: "test_instance", 883 Primary: &terraform.InstanceState{ 884 ID: "bar", 885 }, 886 }, 887 }, 888 }, 889 }, 890 } 891 892 statePath := testStateFile(t, originalState) 893 894 p := testProvider() 895 ui := new(cli.MockUi) 896 c := &ApplyCommand{ 897 Meta: Meta{ 898 ContextOpts: testCtxConfig(p), 899 Ui: ui, 900 }, 901 } 902 903 args := []string{ 904 "-state", statePath, 905 testFixturePath("apply"), 906 } 907 if code := c.Run(args); code != 0 { 908 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 909 } 910 911 if !p.RefreshCalled { 912 t.Fatal("should call refresh") 913 } 914 915 if _, err := os.Stat(statePath); err != nil { 916 t.Fatalf("err: %s", err) 917 } 918 919 f, err := os.Open(statePath) 920 if err != nil { 921 t.Fatalf("err: %s", err) 922 } 923 defer f.Close() 924 925 state, err := terraform.ReadState(f) 926 if err != nil { 927 t.Fatalf("err: %s", err) 928 } 929 if state == nil { 930 t.Fatal("state should not be nil") 931 } 932 933 // Should have a backup file 934 f, err = os.Open(statePath + DefaultBackupExtension) 935 if err != nil { 936 t.Fatalf("err: %s", err) 937 } 938 939 backupState, err := terraform.ReadState(f) 940 f.Close() 941 if err != nil { 942 t.Fatalf("err: %s", err) 943 } 944 945 actualStr := strings.TrimSpace(backupState.String()) 946 expectedStr := strings.TrimSpace(originalState.String()) 947 if actualStr != expectedStr { 948 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 949 } 950 } 951 952 func TestApply_shutdown(t *testing.T) { 953 stopped := false 954 stopCh := make(chan struct{}) 955 stopReplyCh := make(chan struct{}) 956 957 statePath := testTempFile(t) 958 959 p := testProvider() 960 shutdownCh := make(chan struct{}) 961 ui := new(cli.MockUi) 962 c := &ApplyCommand{ 963 Meta: Meta{ 964 ContextOpts: testCtxConfig(p), 965 Ui: ui, 966 }, 967 968 ShutdownCh: shutdownCh, 969 } 970 971 p.DiffFn = func( 972 *terraform.InstanceInfo, 973 *terraform.InstanceState, 974 *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 975 return &terraform.InstanceDiff{ 976 Attributes: map[string]*terraform.ResourceAttrDiff{ 977 "ami": &terraform.ResourceAttrDiff{ 978 New: "bar", 979 }, 980 }, 981 }, nil 982 } 983 p.ApplyFn = func( 984 *terraform.InstanceInfo, 985 *terraform.InstanceState, 986 *terraform.InstanceDiff) (*terraform.InstanceState, error) { 987 if !stopped { 988 stopped = true 989 close(stopCh) 990 <-stopReplyCh 991 } 992 993 return &terraform.InstanceState{ 994 ID: "foo", 995 Attributes: map[string]string{ 996 "ami": "2", 997 }, 998 }, nil 999 } 1000 1001 go func() { 1002 <-stopCh 1003 shutdownCh <- struct{}{} 1004 1005 // This is really dirty, but we have no other way to assure that 1006 // tf.Stop() has been called. This doesn't assure it either, but 1007 // it makes it much more certain. 1008 time.Sleep(50 * time.Millisecond) 1009 1010 close(stopReplyCh) 1011 }() 1012 1013 args := []string{ 1014 "-state", statePath, 1015 testFixturePath("apply-shutdown"), 1016 } 1017 if code := c.Run(args); code != 0 { 1018 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1019 } 1020 1021 if _, err := os.Stat(statePath); err != nil { 1022 t.Fatalf("err: %s", err) 1023 } 1024 1025 f, err := os.Open(statePath) 1026 if err != nil { 1027 t.Fatalf("err: %s", err) 1028 } 1029 defer f.Close() 1030 1031 state, err := terraform.ReadState(f) 1032 if err != nil { 1033 t.Fatalf("err: %s", err) 1034 } 1035 if state == nil { 1036 t.Fatal("state should not be nil") 1037 } 1038 1039 if len(state.RootModule().Resources) != 1 { 1040 t.Fatalf("bad: %d", len(state.RootModule().Resources)) 1041 } 1042 } 1043 1044 func TestApply_state(t *testing.T) { 1045 originalState := &terraform.State{ 1046 Modules: []*terraform.ModuleState{ 1047 &terraform.ModuleState{ 1048 Path: []string{"root"}, 1049 Resources: map[string]*terraform.ResourceState{ 1050 "test_instance.foo": &terraform.ResourceState{ 1051 Type: "test_instance", 1052 Primary: &terraform.InstanceState{ 1053 ID: "bar", 1054 }, 1055 }, 1056 }, 1057 }, 1058 }, 1059 } 1060 1061 statePath := testStateFile(t, originalState) 1062 1063 p := testProvider() 1064 p.DiffReturn = &terraform.InstanceDiff{ 1065 Attributes: map[string]*terraform.ResourceAttrDiff{ 1066 "ami": &terraform.ResourceAttrDiff{ 1067 New: "bar", 1068 }, 1069 }, 1070 } 1071 1072 ui := new(cli.MockUi) 1073 c := &ApplyCommand{ 1074 Meta: Meta{ 1075 ContextOpts: testCtxConfig(p), 1076 Ui: ui, 1077 }, 1078 } 1079 1080 // Run the apply command pointing to our existing state 1081 args := []string{ 1082 "-state", statePath, 1083 testFixturePath("apply"), 1084 } 1085 if code := c.Run(args); code != 0 { 1086 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1087 } 1088 1089 // Verify that the provider was called with the existing state 1090 actual := strings.TrimSpace(p.DiffState.String()) 1091 expected := strings.TrimSpace(testApplyStateDiffStr) 1092 if actual != expected { 1093 t.Fatalf("bad:\n\n%s", actual) 1094 } 1095 1096 actual = strings.TrimSpace(p.ApplyState.String()) 1097 expected = strings.TrimSpace(testApplyStateStr) 1098 if actual != expected { 1099 t.Fatalf("bad:\n\n%s", actual) 1100 } 1101 1102 // Verify a new state exists 1103 if _, err := os.Stat(statePath); err != nil { 1104 t.Fatalf("err: %s", err) 1105 } 1106 1107 f, err := os.Open(statePath) 1108 if err != nil { 1109 t.Fatalf("err: %s", err) 1110 } 1111 defer f.Close() 1112 1113 state, err := terraform.ReadState(f) 1114 if err != nil { 1115 t.Fatalf("err: %s", err) 1116 } 1117 if state == nil { 1118 t.Fatal("state should not be nil") 1119 } 1120 1121 // Should have a backup file 1122 f, err = os.Open(statePath + DefaultBackupExtension) 1123 if err != nil { 1124 t.Fatalf("err: %s", err) 1125 } 1126 1127 backupState, err := terraform.ReadState(f) 1128 f.Close() 1129 if err != nil { 1130 t.Fatalf("err: %s", err) 1131 } 1132 1133 // nil out the ConnInfo since that should not be restored 1134 originalState.RootModule().Resources["test_instance.foo"].Primary.Ephemeral.ConnInfo = nil 1135 1136 actualStr := strings.TrimSpace(backupState.String()) 1137 expectedStr := strings.TrimSpace(originalState.String()) 1138 if actualStr != expectedStr { 1139 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 1140 } 1141 } 1142 1143 func TestApply_stateNoExist(t *testing.T) { 1144 p := testProvider() 1145 ui := new(cli.MockUi) 1146 c := &ApplyCommand{ 1147 Meta: Meta{ 1148 ContextOpts: testCtxConfig(p), 1149 Ui: ui, 1150 }, 1151 } 1152 1153 args := []string{ 1154 "idontexist.tfstate", 1155 testFixturePath("apply"), 1156 } 1157 if code := c.Run(args); code != 1 { 1158 t.Fatalf("bad: \n%s", ui.OutputWriter.String()) 1159 } 1160 } 1161 1162 func TestApply_sensitiveOutput(t *testing.T) { 1163 p := testProvider() 1164 ui := new(cli.MockUi) 1165 c := &ApplyCommand{ 1166 Meta: Meta{ 1167 ContextOpts: testCtxConfig(p), 1168 Ui: ui, 1169 }, 1170 } 1171 1172 statePath := testTempFile(t) 1173 1174 args := []string{ 1175 "-state", statePath, 1176 testFixturePath("apply-sensitive-output"), 1177 } 1178 1179 if code := c.Run(args); code != 0 { 1180 t.Fatalf("bad: \n%s", ui.OutputWriter.String()) 1181 } 1182 1183 output := ui.OutputWriter.String() 1184 if !strings.Contains(output, "notsensitive = Hello world") { 1185 t.Fatalf("bad: output should contain 'notsensitive' output\n%s", output) 1186 } 1187 if !strings.Contains(output, "sensitive = <sensitive>") { 1188 t.Fatalf("bad: output should contain 'sensitive' output\n%s", output) 1189 } 1190 } 1191 1192 func TestApply_stateFuture(t *testing.T) { 1193 originalState := testState() 1194 originalState.TFVersion = "99.99.99" 1195 statePath := testStateFile(t, originalState) 1196 1197 p := testProvider() 1198 ui := new(cli.MockUi) 1199 c := &ApplyCommand{ 1200 Meta: Meta{ 1201 ContextOpts: testCtxConfig(p), 1202 Ui: ui, 1203 }, 1204 } 1205 1206 args := []string{ 1207 "-state", statePath, 1208 testFixturePath("apply"), 1209 } 1210 if code := c.Run(args); code == 0 { 1211 t.Fatal("should fail") 1212 } 1213 1214 f, err := os.Open(statePath) 1215 if err != nil { 1216 t.Fatalf("err: %s", err) 1217 } 1218 1219 newState, err := terraform.ReadState(f) 1220 f.Close() 1221 if err != nil { 1222 t.Fatalf("err: %s", err) 1223 } 1224 1225 if !newState.Equal(originalState) { 1226 t.Fatalf("bad: %#v", newState) 1227 } 1228 if newState.TFVersion != originalState.TFVersion { 1229 t.Fatalf("bad: %#v", newState) 1230 } 1231 } 1232 1233 func TestApply_statePast(t *testing.T) { 1234 originalState := testState() 1235 originalState.TFVersion = "0.1.0" 1236 statePath := testStateFile(t, originalState) 1237 1238 p := testProvider() 1239 ui := new(cli.MockUi) 1240 c := &ApplyCommand{ 1241 Meta: Meta{ 1242 ContextOpts: testCtxConfig(p), 1243 Ui: ui, 1244 }, 1245 } 1246 1247 args := []string{ 1248 "-state", statePath, 1249 testFixturePath("apply"), 1250 } 1251 if code := c.Run(args); code != 0 { 1252 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1253 } 1254 } 1255 1256 func TestApply_vars(t *testing.T) { 1257 statePath := testTempFile(t) 1258 1259 p := testProvider() 1260 ui := new(cli.MockUi) 1261 c := &ApplyCommand{ 1262 Meta: Meta{ 1263 ContextOpts: testCtxConfig(p), 1264 Ui: ui, 1265 }, 1266 } 1267 1268 actual := "" 1269 p.DiffFn = func( 1270 info *terraform.InstanceInfo, 1271 s *terraform.InstanceState, 1272 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 1273 if v, ok := c.Config["value"]; ok { 1274 actual = v.(string) 1275 } 1276 1277 return &terraform.InstanceDiff{}, nil 1278 } 1279 1280 args := []string{ 1281 "-var", "foo=bar", 1282 "-state", statePath, 1283 testFixturePath("apply-vars"), 1284 } 1285 if code := c.Run(args); code != 0 { 1286 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1287 } 1288 1289 if actual != "bar" { 1290 t.Fatal("didn't work") 1291 } 1292 } 1293 1294 func TestApply_varFile(t *testing.T) { 1295 varFilePath := testTempFile(t) 1296 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { 1297 t.Fatalf("err: %s", err) 1298 } 1299 1300 statePath := testTempFile(t) 1301 1302 p := testProvider() 1303 ui := new(cli.MockUi) 1304 c := &ApplyCommand{ 1305 Meta: Meta{ 1306 ContextOpts: testCtxConfig(p), 1307 Ui: ui, 1308 }, 1309 } 1310 1311 actual := "" 1312 p.DiffFn = func( 1313 info *terraform.InstanceInfo, 1314 s *terraform.InstanceState, 1315 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 1316 if v, ok := c.Config["value"]; ok { 1317 actual = v.(string) 1318 } 1319 1320 return &terraform.InstanceDiff{}, nil 1321 } 1322 1323 args := []string{ 1324 "-var-file", varFilePath, 1325 "-state", statePath, 1326 testFixturePath("apply-vars"), 1327 } 1328 if code := c.Run(args); code != 0 { 1329 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1330 } 1331 1332 if actual != "bar" { 1333 t.Fatal("didn't work") 1334 } 1335 } 1336 1337 func TestApply_varFileDefault(t *testing.T) { 1338 varFileDir := testTempDir(t) 1339 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 1340 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { 1341 t.Fatalf("err: %s", err) 1342 } 1343 1344 statePath := testTempFile(t) 1345 1346 cwd, err := os.Getwd() 1347 if err != nil { 1348 t.Fatalf("err: %s", err) 1349 } 1350 if err := os.Chdir(varFileDir); err != nil { 1351 t.Fatalf("err: %s", err) 1352 } 1353 defer os.Chdir(cwd) 1354 1355 p := testProvider() 1356 ui := new(cli.MockUi) 1357 c := &ApplyCommand{ 1358 Meta: Meta{ 1359 ContextOpts: testCtxConfig(p), 1360 Ui: ui, 1361 }, 1362 } 1363 1364 actual := "" 1365 p.DiffFn = func( 1366 info *terraform.InstanceInfo, 1367 s *terraform.InstanceState, 1368 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 1369 if v, ok := c.Config["value"]; ok { 1370 actual = v.(string) 1371 } 1372 1373 return &terraform.InstanceDiff{}, nil 1374 } 1375 1376 args := []string{ 1377 "-state", statePath, 1378 testFixturePath("apply-vars"), 1379 } 1380 if code := c.Run(args); code != 0 { 1381 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1382 } 1383 1384 if actual != "bar" { 1385 t.Fatal("didn't work") 1386 } 1387 } 1388 1389 func TestApply_varFileDefaultJSON(t *testing.T) { 1390 varFileDir := testTempDir(t) 1391 varFilePath := filepath.Join(varFileDir, "terraform.tfvars.json") 1392 if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil { 1393 t.Fatalf("err: %s", err) 1394 } 1395 1396 statePath := testTempFile(t) 1397 1398 cwd, err := os.Getwd() 1399 if err != nil { 1400 t.Fatalf("err: %s", err) 1401 } 1402 if err := os.Chdir(varFileDir); err != nil { 1403 t.Fatalf("err: %s", err) 1404 } 1405 defer os.Chdir(cwd) 1406 1407 p := testProvider() 1408 ui := new(cli.MockUi) 1409 c := &ApplyCommand{ 1410 Meta: Meta{ 1411 ContextOpts: testCtxConfig(p), 1412 Ui: ui, 1413 }, 1414 } 1415 1416 actual := "" 1417 p.DiffFn = func( 1418 info *terraform.InstanceInfo, 1419 s *terraform.InstanceState, 1420 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 1421 if v, ok := c.Config["value"]; ok { 1422 actual = v.(string) 1423 } 1424 1425 return &terraform.InstanceDiff{}, nil 1426 } 1427 1428 args := []string{ 1429 "-state", statePath, 1430 testFixturePath("apply-vars"), 1431 } 1432 if code := c.Run(args); code != 0 { 1433 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1434 } 1435 1436 if actual != "bar" { 1437 t.Fatal("didn't work") 1438 } 1439 } 1440 1441 func TestApply_backup(t *testing.T) { 1442 originalState := &terraform.State{ 1443 Modules: []*terraform.ModuleState{ 1444 &terraform.ModuleState{ 1445 Path: []string{"root"}, 1446 Resources: map[string]*terraform.ResourceState{ 1447 "test_instance.foo": &terraform.ResourceState{ 1448 Type: "test_instance", 1449 Primary: &terraform.InstanceState{ 1450 ID: "bar", 1451 }, 1452 }, 1453 }, 1454 }, 1455 }, 1456 } 1457 originalState.Init() 1458 1459 statePath := testStateFile(t, originalState) 1460 backupPath := testTempFile(t) 1461 1462 p := testProvider() 1463 p.DiffReturn = &terraform.InstanceDiff{ 1464 Attributes: map[string]*terraform.ResourceAttrDiff{ 1465 "ami": &terraform.ResourceAttrDiff{ 1466 New: "bar", 1467 }, 1468 }, 1469 } 1470 1471 ui := new(cli.MockUi) 1472 c := &ApplyCommand{ 1473 Meta: Meta{ 1474 ContextOpts: testCtxConfig(p), 1475 Ui: ui, 1476 }, 1477 } 1478 1479 // Run the apply command pointing to our existing state 1480 args := []string{ 1481 "-state", statePath, 1482 "-backup", backupPath, 1483 testFixturePath("apply"), 1484 } 1485 if code := c.Run(args); code != 0 { 1486 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1487 } 1488 1489 // Verify a new state exists 1490 if _, err := os.Stat(statePath); err != nil { 1491 t.Fatalf("err: %s", err) 1492 } 1493 1494 f, err := os.Open(statePath) 1495 if err != nil { 1496 t.Fatalf("err: %s", err) 1497 } 1498 defer f.Close() 1499 1500 state, err := terraform.ReadState(f) 1501 if err != nil { 1502 t.Fatalf("err: %s", err) 1503 } 1504 if state == nil { 1505 t.Fatal("state should not be nil") 1506 } 1507 1508 // Should have a backup file 1509 f, err = os.Open(backupPath) 1510 if err != nil { 1511 t.Fatalf("err: %s", err) 1512 } 1513 1514 backupState, err := terraform.ReadState(f) 1515 f.Close() 1516 if err != nil { 1517 t.Fatalf("err: %s", err) 1518 } 1519 1520 actual := backupState.RootModule().Resources["test_instance.foo"] 1521 expected := originalState.RootModule().Resources["test_instance.foo"] 1522 if !reflect.DeepEqual(actual, expected) { 1523 t.Fatalf("bad: %#v %#v", actual, expected) 1524 } 1525 } 1526 1527 func TestApply_disableBackup(t *testing.T) { 1528 originalState := testState() 1529 statePath := testStateFile(t, originalState) 1530 1531 p := testProvider() 1532 p.DiffReturn = &terraform.InstanceDiff{ 1533 Attributes: map[string]*terraform.ResourceAttrDiff{ 1534 "ami": &terraform.ResourceAttrDiff{ 1535 New: "bar", 1536 }, 1537 }, 1538 } 1539 1540 ui := new(cli.MockUi) 1541 c := &ApplyCommand{ 1542 Meta: Meta{ 1543 ContextOpts: testCtxConfig(p), 1544 Ui: ui, 1545 }, 1546 } 1547 1548 // Run the apply command pointing to our existing state 1549 args := []string{ 1550 "-state", statePath, 1551 "-backup", "-", 1552 testFixturePath("apply"), 1553 } 1554 if code := c.Run(args); code != 0 { 1555 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1556 } 1557 1558 // Verify that the provider was called with the existing state 1559 actual := strings.TrimSpace(p.DiffState.String()) 1560 expected := strings.TrimSpace(testApplyDisableBackupStr) 1561 if actual != expected { 1562 t.Fatalf("bad:\n\n%s", actual) 1563 } 1564 1565 actual = strings.TrimSpace(p.ApplyState.String()) 1566 expected = strings.TrimSpace(testApplyDisableBackupStateStr) 1567 if actual != expected { 1568 t.Fatalf("bad:\n\n%s", actual) 1569 } 1570 1571 // Verify a new state exists 1572 if _, err := os.Stat(statePath); err != nil { 1573 t.Fatalf("err: %s", err) 1574 } 1575 1576 f, err := os.Open(statePath) 1577 if err != nil { 1578 t.Fatalf("err: %s", err) 1579 } 1580 defer f.Close() 1581 1582 state, err := terraform.ReadState(f) 1583 if err != nil { 1584 t.Fatalf("err: %s", err) 1585 } 1586 if state == nil { 1587 t.Fatal("state should not be nil") 1588 } 1589 1590 // Ensure there is no backup 1591 _, err = os.Stat(statePath + DefaultBackupExtension) 1592 if err == nil || !os.IsNotExist(err) { 1593 t.Fatalf("backup should not exist") 1594 } 1595 1596 // Ensure there is no literal "-" 1597 _, err = os.Stat("-") 1598 if err == nil || !os.IsNotExist(err) { 1599 t.Fatalf("backup should not exist") 1600 } 1601 } 1602 1603 // Test that the Terraform env is passed through 1604 func TestApply_terraformEnv(t *testing.T) { 1605 statePath := testTempFile(t) 1606 1607 p := testProvider() 1608 ui := new(cli.MockUi) 1609 c := &ApplyCommand{ 1610 Meta: Meta{ 1611 ContextOpts: testCtxConfig(p), 1612 Ui: ui, 1613 }, 1614 } 1615 1616 args := []string{ 1617 "-state", statePath, 1618 testFixturePath("apply-terraform-env"), 1619 } 1620 if code := c.Run(args); code != 0 { 1621 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1622 } 1623 1624 expected := strings.TrimSpace(` 1625 <no state> 1626 Outputs: 1627 1628 output = default 1629 `) 1630 testStateOutput(t, statePath, expected) 1631 } 1632 1633 // Test that the Terraform env is passed through 1634 func TestApply_terraformEnvNonDefault(t *testing.T) { 1635 // Create a temporary working directory that is empty 1636 td := tempDir(t) 1637 os.MkdirAll(td, 0755) 1638 defer os.RemoveAll(td) 1639 defer testChdir(t, td)() 1640 1641 // Create new env 1642 { 1643 ui := new(cli.MockUi) 1644 newCmd := &EnvNewCommand{} 1645 newCmd.Meta = Meta{Ui: ui} 1646 if code := newCmd.Run([]string{"test"}); code != 0 { 1647 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) 1648 } 1649 } 1650 1651 // Switch to it 1652 { 1653 args := []string{"test"} 1654 ui := new(cli.MockUi) 1655 selCmd := &EnvSelectCommand{} 1656 selCmd.Meta = Meta{Ui: ui} 1657 if code := selCmd.Run(args); code != 0 { 1658 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) 1659 } 1660 } 1661 1662 p := testProvider() 1663 ui := new(cli.MockUi) 1664 c := &ApplyCommand{ 1665 Meta: Meta{ 1666 ContextOpts: testCtxConfig(p), 1667 Ui: ui, 1668 }, 1669 } 1670 1671 args := []string{ 1672 testFixturePath("apply-terraform-env"), 1673 } 1674 if code := c.Run(args); code != 0 { 1675 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1676 } 1677 1678 statePath := filepath.Join("terraform.tfstate.d", "test", "terraform.tfstate") 1679 expected := strings.TrimSpace(` 1680 <no state> 1681 Outputs: 1682 1683 output = test 1684 `) 1685 testStateOutput(t, statePath, expected) 1686 } 1687 1688 func testHttpServer(t *testing.T) net.Listener { 1689 ln, err := net.Listen("tcp", "127.0.0.1:0") 1690 if err != nil { 1691 t.Fatalf("err: %s", err) 1692 } 1693 1694 mux := http.NewServeMux() 1695 mux.HandleFunc("/header", testHttpHandlerHeader) 1696 1697 var server http.Server 1698 server.Handler = mux 1699 go server.Serve(ln) 1700 1701 return ln 1702 } 1703 1704 func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) { 1705 var url url.URL 1706 url.Scheme = "file" 1707 url.Path = filepath.ToSlash(testFixturePath("init")) 1708 1709 w.Header().Add("X-Terraform-Get", url.String()) 1710 w.WriteHeader(200) 1711 } 1712 1713 const applyVarFile = ` 1714 foo = "bar" 1715 ` 1716 1717 const applyVarFileJSON = ` 1718 { "foo": "bar" } 1719 ` 1720 1721 const testApplyDisableBackupStr = ` 1722 ID = bar 1723 Tainted = false 1724 ` 1725 1726 const testApplyDisableBackupStateStr = ` 1727 ID = bar 1728 Tainted = false 1729 ` 1730 1731 const testApplyStateStr = ` 1732 ID = bar 1733 Tainted = false 1734 ` 1735 1736 const testApplyStateDiffStr = ` 1737 ID = bar 1738 Tainted = false 1739 `