github.com/ves/terraform@v0.8.0-beta2/terraform/context_plan_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "sort" 9 "strings" 10 "sync" 11 "testing" 12 ) 13 14 func TestContext2Plan_basic(t *testing.T) { 15 m := testModule(t, "plan-good") 16 p := testProvider("aws") 17 p.DiffFn = testDiffFn 18 ctx := testContext2(t, &ContextOpts{ 19 Module: m, 20 Providers: map[string]ResourceProviderFactory{ 21 "aws": testProviderFuncFixed(p), 22 }, 23 }) 24 25 plan, err := ctx.Plan() 26 if err != nil { 27 t.Fatalf("err: %s", err) 28 } 29 30 if len(plan.Diff.RootModule().Resources) < 2 { 31 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 32 } 33 34 actual := strings.TrimSpace(plan.String()) 35 expected := strings.TrimSpace(testTerraformPlanStr) 36 if actual != expected { 37 t.Fatalf("bad:\n%s", actual) 38 } 39 } 40 41 func TestContext2Plan_createBefore_maintainRoot(t *testing.T) { 42 m := testModule(t, "plan-cbd-maintain-root") 43 p := testProvider("aws") 44 p.DiffFn = testDiffFn 45 ctx := testContext2(t, &ContextOpts{ 46 Module: m, 47 Providers: map[string]ResourceProviderFactory{ 48 "aws": testProviderFuncFixed(p), 49 }, 50 Variables: map[string]interface{}{ 51 "in": "a,b,c", 52 }, 53 }) 54 55 plan, err := ctx.Plan() 56 if err != nil { 57 t.Fatalf("err: %s", err) 58 } 59 60 actual := strings.TrimSpace(plan.String()) 61 expected := strings.TrimSpace(` 62 DIFF: 63 64 CREATE: aws_instance.bar.0 65 CREATE: aws_instance.bar.1 66 CREATE: aws_instance.foo.0 67 CREATE: aws_instance.foo.1 68 69 STATE: 70 71 <no state> 72 `) 73 if actual != expected { 74 t.Fatalf("expected:\n%s, got:\n%s", expected, actual) 75 } 76 } 77 78 func TestContext2Plan_emptyDiff(t *testing.T) { 79 m := testModule(t, "plan-empty") 80 p := testProvider("aws") 81 p.DiffFn = func( 82 info *InstanceInfo, 83 s *InstanceState, 84 c *ResourceConfig) (*InstanceDiff, error) { 85 return nil, nil 86 } 87 88 ctx := testContext2(t, &ContextOpts{ 89 Module: m, 90 Providers: map[string]ResourceProviderFactory{ 91 "aws": testProviderFuncFixed(p), 92 }, 93 }) 94 95 plan, err := ctx.Plan() 96 if err != nil { 97 t.Fatalf("err: %s", err) 98 } 99 100 actual := strings.TrimSpace(plan.String()) 101 expected := strings.TrimSpace(testTerraformPlanEmptyStr) 102 if actual != expected { 103 t.Fatalf("bad:\n%s", actual) 104 } 105 } 106 107 func TestContext2Plan_escapedVar(t *testing.T) { 108 m := testModule(t, "plan-escaped-var") 109 p := testProvider("aws") 110 p.DiffFn = testDiffFn 111 ctx := testContext2(t, &ContextOpts{ 112 Module: m, 113 Providers: map[string]ResourceProviderFactory{ 114 "aws": testProviderFuncFixed(p), 115 }, 116 }) 117 118 plan, err := ctx.Plan() 119 if err != nil { 120 t.Fatalf("err: %s", err) 121 } 122 123 actual := strings.TrimSpace(plan.String()) 124 expected := strings.TrimSpace(testTerraformPlanEscapedVarStr) 125 if actual != expected { 126 t.Fatalf("bad:\n%s", actual) 127 } 128 } 129 130 func TestContext2Plan_minimal(t *testing.T) { 131 m := testModule(t, "plan-empty") 132 p := testProvider("aws") 133 p.DiffFn = testDiffFn 134 ctx := testContext2(t, &ContextOpts{ 135 Module: m, 136 Providers: map[string]ResourceProviderFactory{ 137 "aws": testProviderFuncFixed(p), 138 }, 139 }) 140 141 plan, err := ctx.Plan() 142 if err != nil { 143 t.Fatalf("err: %s", err) 144 } 145 146 actual := strings.TrimSpace(plan.String()) 147 expected := strings.TrimSpace(testTerraformPlanEmptyStr) 148 if actual != expected { 149 t.Fatalf("bad:\n%s", actual) 150 } 151 } 152 153 func TestContext2Plan_modules(t *testing.T) { 154 m := testModule(t, "plan-modules") 155 p := testProvider("aws") 156 p.DiffFn = testDiffFn 157 ctx := testContext2(t, &ContextOpts{ 158 Module: m, 159 Providers: map[string]ResourceProviderFactory{ 160 "aws": testProviderFuncFixed(p), 161 }, 162 }) 163 164 plan, err := ctx.Plan() 165 if err != nil { 166 t.Fatalf("err: %s", err) 167 } 168 169 actual := strings.TrimSpace(plan.String()) 170 expected := strings.TrimSpace(testTerraformPlanModulesStr) 171 if actual != expected { 172 t.Fatalf("bad:\n%s", actual) 173 } 174 } 175 176 // GH-1475 177 func TestContext2Plan_moduleCycle(t *testing.T) { 178 m := testModule(t, "plan-module-cycle") 179 p := testProvider("aws") 180 p.DiffFn = testDiffFn 181 ctx := testContext2(t, &ContextOpts{ 182 Module: m, 183 Providers: map[string]ResourceProviderFactory{ 184 "aws": testProviderFuncFixed(p), 185 }, 186 }) 187 188 plan, err := ctx.Plan() 189 if err != nil { 190 t.Fatalf("err: %s", err) 191 } 192 193 actual := strings.TrimSpace(plan.String()) 194 expected := strings.TrimSpace(testTerraformPlanModuleCycleStr) 195 if actual != expected { 196 t.Fatalf("bad:\n%s", actual) 197 } 198 } 199 200 func TestContext2Plan_moduleDeadlock(t *testing.T) { 201 testCheckDeadlock(t, func() { 202 m := testModule(t, "plan-module-deadlock") 203 p := testProvider("aws") 204 p.DiffFn = testDiffFn 205 206 ctx := testContext2(t, &ContextOpts{ 207 Module: m, 208 Providers: map[string]ResourceProviderFactory{ 209 "aws": testProviderFuncFixed(p), 210 }, 211 }) 212 213 plan, err := ctx.Plan() 214 if err != nil { 215 t.Fatalf("err: %s", err) 216 } 217 218 actual := strings.TrimSpace(plan.String()) 219 expected := strings.TrimSpace(` 220 DIFF: 221 222 module.child: 223 CREATE: aws_instance.foo.0 224 CREATE: aws_instance.foo.1 225 CREATE: aws_instance.foo.2 226 227 STATE: 228 229 <no state> 230 `) 231 if actual != expected { 232 t.Fatalf("expected:\n%sgot:\n%s", expected, actual) 233 } 234 }) 235 } 236 237 func TestContext2Plan_moduleInput(t *testing.T) { 238 m := testModule(t, "plan-module-input") 239 p := testProvider("aws") 240 p.DiffFn = testDiffFn 241 ctx := testContext2(t, &ContextOpts{ 242 Module: m, 243 Providers: map[string]ResourceProviderFactory{ 244 "aws": testProviderFuncFixed(p), 245 }, 246 }) 247 248 plan, err := ctx.Plan() 249 if err != nil { 250 t.Fatalf("err: %s", err) 251 } 252 253 actual := strings.TrimSpace(plan.String()) 254 expected := strings.TrimSpace(testTerraformPlanModuleInputStr) 255 if actual != expected { 256 t.Fatalf("bad:\n%s", actual) 257 } 258 } 259 260 func TestContext2Plan_moduleInputComputed(t *testing.T) { 261 m := testModule(t, "plan-module-input-computed") 262 p := testProvider("aws") 263 p.DiffFn = testDiffFn 264 ctx := testContext2(t, &ContextOpts{ 265 Module: m, 266 Providers: map[string]ResourceProviderFactory{ 267 "aws": testProviderFuncFixed(p), 268 }, 269 }) 270 271 plan, err := ctx.Plan() 272 if err != nil { 273 t.Fatalf("err: %s", err) 274 } 275 276 actual := strings.TrimSpace(plan.String()) 277 expected := strings.TrimSpace(testTerraformPlanModuleInputComputedStr) 278 if actual != expected { 279 t.Fatalf("bad:\n%s", actual) 280 } 281 } 282 283 func TestContext2Plan_moduleInputFromVar(t *testing.T) { 284 m := testModule(t, "plan-module-input-var") 285 p := testProvider("aws") 286 p.DiffFn = testDiffFn 287 ctx := testContext2(t, &ContextOpts{ 288 Module: m, 289 Providers: map[string]ResourceProviderFactory{ 290 "aws": testProviderFuncFixed(p), 291 }, 292 Variables: map[string]interface{}{ 293 "foo": "52", 294 }, 295 }) 296 297 plan, err := ctx.Plan() 298 if err != nil { 299 t.Fatalf("err: %s", err) 300 } 301 302 actual := strings.TrimSpace(plan.String()) 303 expected := strings.TrimSpace(testTerraformPlanModuleInputVarStr) 304 if actual != expected { 305 t.Fatalf("bad:\n%s", actual) 306 } 307 } 308 309 func TestContext2Plan_moduleMultiVar(t *testing.T) { 310 m := testModule(t, "plan-module-multi-var") 311 p := testProvider("aws") 312 p.DiffFn = testDiffFn 313 ctx := testContext2(t, &ContextOpts{ 314 Module: m, 315 Providers: map[string]ResourceProviderFactory{ 316 "aws": testProviderFuncFixed(p), 317 }, 318 }) 319 320 plan, err := ctx.Plan() 321 if err != nil { 322 t.Fatalf("err: %s", err) 323 } 324 325 actual := strings.TrimSpace(plan.String()) 326 expected := strings.TrimSpace(testTerraformPlanModuleMultiVarStr) 327 if actual != expected { 328 t.Fatalf("bad:\n%s", actual) 329 } 330 } 331 332 func TestContext2Plan_moduleOrphans(t *testing.T) { 333 m := testModule(t, "plan-modules-remove") 334 p := testProvider("aws") 335 p.DiffFn = testDiffFn 336 s := &State{ 337 Modules: []*ModuleState{ 338 &ModuleState{ 339 Path: []string{"root", "child"}, 340 Resources: map[string]*ResourceState{ 341 "aws_instance.foo": &ResourceState{ 342 Type: "aws_instance", 343 Primary: &InstanceState{ 344 ID: "baz", 345 }, 346 }, 347 }, 348 }, 349 }, 350 } 351 ctx := testContext2(t, &ContextOpts{ 352 Module: m, 353 Providers: map[string]ResourceProviderFactory{ 354 "aws": testProviderFuncFixed(p), 355 }, 356 State: s, 357 }) 358 359 plan, err := ctx.Plan() 360 if err != nil { 361 t.Fatalf("err: %s", err) 362 } 363 364 actual := strings.TrimSpace(plan.String()) 365 expected := strings.TrimSpace(testTerraformPlanModuleOrphansStr) 366 if actual != expected { 367 t.Fatalf("bad:\n%s", actual) 368 } 369 } 370 371 // https://github.com/hashicorp/terraform/issues/3114 372 func TestContext2Plan_moduleOrphansWithProvisioner(t *testing.T) { 373 m := testModule(t, "plan-modules-remove-provisioners") 374 p := testProvider("aws") 375 pr := testProvisioner() 376 p.DiffFn = testDiffFn 377 s := &State{ 378 Modules: []*ModuleState{ 379 &ModuleState{ 380 Path: []string{"root"}, 381 Resources: map[string]*ResourceState{ 382 "aws_instance.top": &ResourceState{ 383 Type: "aws_instance", 384 Primary: &InstanceState{ 385 ID: "top", 386 }, 387 }, 388 }, 389 }, 390 &ModuleState{ 391 Path: []string{"root", "parent", "childone"}, 392 Resources: map[string]*ResourceState{ 393 "aws_instance.foo": &ResourceState{ 394 Type: "aws_instance", 395 Primary: &InstanceState{ 396 ID: "baz", 397 }, 398 }, 399 }, 400 }, 401 &ModuleState{ 402 Path: []string{"root", "parent", "childtwo"}, 403 Resources: map[string]*ResourceState{ 404 "aws_instance.foo": &ResourceState{ 405 Type: "aws_instance", 406 Primary: &InstanceState{ 407 ID: "baz", 408 }, 409 }, 410 }, 411 }, 412 }, 413 } 414 ctx := testContext2(t, &ContextOpts{ 415 Module: m, 416 Providers: map[string]ResourceProviderFactory{ 417 "aws": testProviderFuncFixed(p), 418 }, 419 Provisioners: map[string]ResourceProvisionerFactory{ 420 "shell": testProvisionerFuncFixed(pr), 421 }, 422 State: s, 423 }) 424 425 plan, err := ctx.Plan() 426 if err != nil { 427 t.Fatalf("err: %s", err) 428 } 429 430 actual := strings.TrimSpace(plan.String()) 431 expected := strings.TrimSpace(` 432 DIFF: 433 434 module.parent.childone: 435 DESTROY: aws_instance.foo 436 module.parent.childtwo: 437 DESTROY: aws_instance.foo 438 439 STATE: 440 441 aws_instance.top: 442 ID = top 443 444 module.parent.childone: 445 aws_instance.foo: 446 ID = baz 447 module.parent.childtwo: 448 aws_instance.foo: 449 ID = baz 450 `) 451 if actual != expected { 452 t.Fatalf("bad:\n%s", actual) 453 } 454 } 455 456 func TestContext2Plan_moduleProviderInherit(t *testing.T) { 457 var l sync.Mutex 458 var calls []string 459 460 m := testModule(t, "plan-module-provider-inherit") 461 ctx := testContext2(t, &ContextOpts{ 462 Module: m, 463 Providers: map[string]ResourceProviderFactory{ 464 "aws": func() (ResourceProvider, error) { 465 l.Lock() 466 defer l.Unlock() 467 468 p := testProvider("aws") 469 p.ConfigureFn = func(c *ResourceConfig) error { 470 if v, ok := c.Get("from"); !ok || v.(string) != "root" { 471 return fmt.Errorf("bad") 472 } 473 474 return nil 475 } 476 p.DiffFn = func( 477 info *InstanceInfo, 478 state *InstanceState, 479 c *ResourceConfig) (*InstanceDiff, error) { 480 v, _ := c.Get("from") 481 calls = append(calls, v.(string)) 482 return testDiffFn(info, state, c) 483 } 484 return p, nil 485 }, 486 }, 487 }) 488 489 _, err := ctx.Plan() 490 if err != nil { 491 t.Fatalf("err: %s", err) 492 } 493 494 actual := calls 495 sort.Strings(actual) 496 expected := []string{"child", "root"} 497 if !reflect.DeepEqual(actual, expected) { 498 t.Fatalf("bad: %#v", actual) 499 } 500 } 501 502 func TestContext2Plan_moduleProviderDefaults(t *testing.T) { 503 var l sync.Mutex 504 var calls []string 505 toCount := 0 506 507 m := testModule(t, "plan-module-provider-defaults") 508 ctx := testContext2(t, &ContextOpts{ 509 Module: m, 510 Providers: map[string]ResourceProviderFactory{ 511 "aws": func() (ResourceProvider, error) { 512 l.Lock() 513 defer l.Unlock() 514 515 p := testProvider("aws") 516 p.ConfigureFn = func(c *ResourceConfig) error { 517 if v, ok := c.Get("from"); !ok || v.(string) != "root" { 518 return fmt.Errorf("bad") 519 } 520 if v, ok := c.Get("to"); ok && v.(string) == "child" { 521 toCount++ 522 } 523 524 return nil 525 } 526 p.DiffFn = func( 527 info *InstanceInfo, 528 state *InstanceState, 529 c *ResourceConfig) (*InstanceDiff, error) { 530 v, _ := c.Get("from") 531 calls = append(calls, v.(string)) 532 return testDiffFn(info, state, c) 533 } 534 return p, nil 535 }, 536 }, 537 }) 538 539 _, err := ctx.Plan() 540 if err != nil { 541 t.Fatalf("err: %s", err) 542 } 543 544 if toCount != 1 { 545 t.Fatalf( 546 "provider in child didn't set proper config\n\n"+ 547 "toCount: %d", toCount) 548 } 549 550 actual := calls 551 sort.Strings(actual) 552 expected := []string{"child", "root"} 553 if !reflect.DeepEqual(actual, expected) { 554 t.Fatalf("bad: %#v", actual) 555 } 556 } 557 558 func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { 559 var l sync.Mutex 560 var calls []string 561 562 m := testModule(t, "plan-module-provider-defaults-var") 563 ctx := testContext2(t, &ContextOpts{ 564 Module: m, 565 Providers: map[string]ResourceProviderFactory{ 566 "aws": func() (ResourceProvider, error) { 567 l.Lock() 568 defer l.Unlock() 569 570 p := testProvider("aws") 571 p.ConfigureFn = func(c *ResourceConfig) error { 572 var buf bytes.Buffer 573 if v, ok := c.Get("from"); ok { 574 buf.WriteString(v.(string) + "\n") 575 } 576 if v, ok := c.Get("to"); ok { 577 buf.WriteString(v.(string) + "\n") 578 } 579 580 calls = append(calls, buf.String()) 581 return nil 582 } 583 p.DiffFn = testDiffFn 584 return p, nil 585 }, 586 }, 587 Variables: map[string]interface{}{ 588 "foo": "root", 589 }, 590 }) 591 592 _, err := ctx.Plan() 593 if err != nil { 594 t.Fatalf("err: %s", err) 595 } 596 597 expected := []string{ 598 "root\n", 599 "root\nchild\n", 600 } 601 if !reflect.DeepEqual(calls, expected) { 602 t.Fatalf("BAD: %#v", calls) 603 } 604 } 605 606 func TestContext2Plan_moduleVar(t *testing.T) { 607 m := testModule(t, "plan-module-var") 608 p := testProvider("aws") 609 p.DiffFn = testDiffFn 610 ctx := testContext2(t, &ContextOpts{ 611 Module: m, 612 Providers: map[string]ResourceProviderFactory{ 613 "aws": testProviderFuncFixed(p), 614 }, 615 }) 616 617 plan, err := ctx.Plan() 618 if err != nil { 619 t.Fatalf("err: %s", err) 620 } 621 622 actual := strings.TrimSpace(plan.String()) 623 expected := strings.TrimSpace(testTerraformPlanModuleVarStr) 624 if actual != expected { 625 t.Fatalf("bad:\n%s", actual) 626 } 627 } 628 629 func TestContext2Plan_moduleVarWrongTypeBasic(t *testing.T) { 630 m := testModule(t, "plan-module-wrong-var-type") 631 p := testProvider("aws") 632 p.DiffFn = testDiffFn 633 ctx := testContext2(t, &ContextOpts{ 634 Module: m, 635 Providers: map[string]ResourceProviderFactory{ 636 "aws": testProviderFuncFixed(p), 637 }, 638 }) 639 640 _, err := ctx.Plan() 641 if err == nil { 642 t.Fatalf("should error") 643 } 644 } 645 646 func TestContext2Plan_moduleVarWrongTypeNested(t *testing.T) { 647 m := testModule(t, "plan-module-wrong-var-type-nested") 648 p := testProvider("aws") 649 p.DiffFn = testDiffFn 650 ctx := testContext2(t, &ContextOpts{ 651 Module: m, 652 Providers: map[string]ResourceProviderFactory{ 653 "aws": testProviderFuncFixed(p), 654 }, 655 }) 656 657 _, err := ctx.Plan() 658 if err == nil { 659 t.Fatalf("should error") 660 } 661 } 662 663 func TestContext2Plan_moduleVarWithDefaultValue(t *testing.T) { 664 m := testModule(t, "plan-module-var-with-default-value") 665 p := testProvider("null") 666 p.DiffFn = testDiffFn 667 ctx := testContext2(t, &ContextOpts{ 668 Module: m, 669 Providers: map[string]ResourceProviderFactory{ 670 "null": testProviderFuncFixed(p), 671 }, 672 }) 673 674 _, err := ctx.Plan() 675 if err != nil { 676 t.Fatalf("bad: %s", err) 677 } 678 } 679 680 func TestContext2Plan_moduleVarComputed(t *testing.T) { 681 m := testModule(t, "plan-module-var-computed") 682 p := testProvider("aws") 683 p.DiffFn = testDiffFn 684 ctx := testContext2(t, &ContextOpts{ 685 Module: m, 686 Providers: map[string]ResourceProviderFactory{ 687 "aws": testProviderFuncFixed(p), 688 }, 689 }) 690 691 plan, err := ctx.Plan() 692 if err != nil { 693 t.Fatalf("err: %s", err) 694 } 695 696 actual := strings.TrimSpace(plan.String()) 697 expected := strings.TrimSpace(testTerraformPlanModuleVarComputedStr) 698 if actual != expected { 699 t.Fatalf("bad:\n%s", actual) 700 } 701 } 702 703 func TestContext2Plan_nil(t *testing.T) { 704 m := testModule(t, "plan-nil") 705 p := testProvider("aws") 706 p.DiffFn = testDiffFn 707 ctx := testContext2(t, &ContextOpts{ 708 Module: m, 709 Providers: map[string]ResourceProviderFactory{ 710 "aws": testProviderFuncFixed(p), 711 }, 712 State: &State{ 713 Modules: []*ModuleState{ 714 &ModuleState{ 715 Path: rootModulePath, 716 Resources: map[string]*ResourceState{ 717 "aws_instance.foo": &ResourceState{ 718 Type: "aws_instance", 719 Primary: &InstanceState{ 720 ID: "bar", 721 }, 722 }, 723 }, 724 }, 725 }, 726 }, 727 }) 728 729 plan, err := ctx.Plan() 730 if err != nil { 731 t.Fatalf("err: %s", err) 732 } 733 if len(plan.Diff.RootModule().Resources) != 0 { 734 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 735 } 736 } 737 738 func TestContext2Plan_preventDestroy_bad(t *testing.T) { 739 m := testModule(t, "plan-prevent-destroy-bad") 740 p := testProvider("aws") 741 p.DiffFn = testDiffFn 742 ctx := testContext2(t, &ContextOpts{ 743 Module: m, 744 Providers: map[string]ResourceProviderFactory{ 745 "aws": testProviderFuncFixed(p), 746 }, 747 State: &State{ 748 Modules: []*ModuleState{ 749 &ModuleState{ 750 Path: rootModulePath, 751 Resources: map[string]*ResourceState{ 752 "aws_instance.foo": &ResourceState{ 753 Type: "aws_instance", 754 Primary: &InstanceState{ 755 ID: "i-abc123", 756 }, 757 }, 758 }, 759 }, 760 }, 761 }, 762 }) 763 764 plan, err := ctx.Plan() 765 766 expectedErr := "aws_instance.foo: the plan would destroy" 767 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 768 t.Fatalf("expected err would contain %q\nerr: %s\nplan: %s", 769 expectedErr, err, plan) 770 } 771 } 772 773 func TestContext2Plan_preventDestroy_good(t *testing.T) { 774 m := testModule(t, "plan-prevent-destroy-good") 775 p := testProvider("aws") 776 p.DiffFn = testDiffFn 777 ctx := testContext2(t, &ContextOpts{ 778 Module: m, 779 Providers: map[string]ResourceProviderFactory{ 780 "aws": testProviderFuncFixed(p), 781 }, 782 State: &State{ 783 Modules: []*ModuleState{ 784 &ModuleState{ 785 Path: rootModulePath, 786 Resources: map[string]*ResourceState{ 787 "aws_instance.foo": &ResourceState{ 788 Type: "aws_instance", 789 Primary: &InstanceState{ 790 ID: "i-abc123", 791 }, 792 }, 793 }, 794 }, 795 }, 796 }, 797 }) 798 799 plan, err := ctx.Plan() 800 if err != nil { 801 t.Fatalf("err: %s", err) 802 } 803 if !plan.Diff.Empty() { 804 t.Fatalf("Expected empty plan, got %s", plan.String()) 805 } 806 } 807 808 func TestContext2Plan_preventDestroy_countBad(t *testing.T) { 809 m := testModule(t, "plan-prevent-destroy-count-bad") 810 p := testProvider("aws") 811 p.DiffFn = testDiffFn 812 ctx := testContext2(t, &ContextOpts{ 813 Module: m, 814 Providers: map[string]ResourceProviderFactory{ 815 "aws": testProviderFuncFixed(p), 816 }, 817 State: &State{ 818 Modules: []*ModuleState{ 819 &ModuleState{ 820 Path: rootModulePath, 821 Resources: map[string]*ResourceState{ 822 "aws_instance.foo.0": &ResourceState{ 823 Type: "aws_instance", 824 Primary: &InstanceState{ 825 ID: "i-abc123", 826 }, 827 }, 828 "aws_instance.foo.1": &ResourceState{ 829 Type: "aws_instance", 830 Primary: &InstanceState{ 831 ID: "i-abc345", 832 }, 833 }, 834 }, 835 }, 836 }, 837 }, 838 }) 839 840 plan, err := ctx.Plan() 841 842 expectedErr := "aws_instance.foo.1: the plan would destroy" 843 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 844 t.Fatalf("expected err would contain %q\nerr: %s\nplan: %s", 845 expectedErr, err, plan) 846 } 847 } 848 849 func TestContext2Plan_preventDestroy_countGood(t *testing.T) { 850 m := testModule(t, "plan-prevent-destroy-count-good") 851 p := testProvider("aws") 852 p.DiffFn = testDiffFn 853 ctx := testContext2(t, &ContextOpts{ 854 Module: m, 855 Providers: map[string]ResourceProviderFactory{ 856 "aws": testProviderFuncFixed(p), 857 }, 858 State: &State{ 859 Modules: []*ModuleState{ 860 &ModuleState{ 861 Path: rootModulePath, 862 Resources: map[string]*ResourceState{ 863 "aws_instance.foo.0": &ResourceState{ 864 Type: "aws_instance", 865 Primary: &InstanceState{ 866 ID: "i-abc123", 867 }, 868 }, 869 "aws_instance.foo.1": &ResourceState{ 870 Type: "aws_instance", 871 Primary: &InstanceState{ 872 ID: "i-abc345", 873 }, 874 }, 875 }, 876 }, 877 }, 878 }, 879 }) 880 881 plan, err := ctx.Plan() 882 if err != nil { 883 t.Fatalf("err: %s", err) 884 } 885 if plan.Diff.Empty() { 886 t.Fatalf("Expected non-empty plan, got %s", plan.String()) 887 } 888 } 889 890 func TestContext2Plan_preventDestroy_countGoodNoChange(t *testing.T) { 891 m := testModule(t, "plan-prevent-destroy-count-good") 892 p := testProvider("aws") 893 p.DiffFn = testDiffFn 894 ctx := testContext2(t, &ContextOpts{ 895 Module: m, 896 Providers: map[string]ResourceProviderFactory{ 897 "aws": testProviderFuncFixed(p), 898 }, 899 State: &State{ 900 Modules: []*ModuleState{ 901 &ModuleState{ 902 Path: rootModulePath, 903 Resources: map[string]*ResourceState{ 904 "aws_instance.foo.0": &ResourceState{ 905 Type: "aws_instance", 906 Primary: &InstanceState{ 907 ID: "i-abc123", 908 Attributes: map[string]string{ 909 "current": "0", 910 "type": "aws_instance", 911 }, 912 }, 913 }, 914 }, 915 }, 916 }, 917 }, 918 }) 919 920 plan, err := ctx.Plan() 921 if err != nil { 922 t.Fatalf("err: %s", err) 923 } 924 if !plan.Diff.Empty() { 925 t.Fatalf("Expected empty plan, got %s", plan.String()) 926 } 927 } 928 929 func TestContext2Plan_preventDestroy_destroyPlan(t *testing.T) { 930 m := testModule(t, "plan-prevent-destroy-good") 931 p := testProvider("aws") 932 p.DiffFn = testDiffFn 933 ctx := testContext2(t, &ContextOpts{ 934 Module: m, 935 Providers: map[string]ResourceProviderFactory{ 936 "aws": testProviderFuncFixed(p), 937 }, 938 State: &State{ 939 Modules: []*ModuleState{ 940 &ModuleState{ 941 Path: rootModulePath, 942 Resources: map[string]*ResourceState{ 943 "aws_instance.foo": &ResourceState{ 944 Type: "aws_instance", 945 Primary: &InstanceState{ 946 ID: "i-abc123", 947 }, 948 }, 949 }, 950 }, 951 }, 952 }, 953 Destroy: true, 954 }) 955 956 plan, err := ctx.Plan() 957 958 expectedErr := "aws_instance.foo: the plan would destroy" 959 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 960 t.Fatalf("expected err would contain %q\nerr: %s\nplan: %s", 961 expectedErr, err, plan) 962 } 963 } 964 965 func TestContext2Plan_provisionerCycle(t *testing.T) { 966 m := testModule(t, "plan-provisioner-cycle") 967 p := testProvider("aws") 968 p.DiffFn = testDiffFn 969 pr := testProvisioner() 970 ctx := testContext2(t, &ContextOpts{ 971 Module: m, 972 Providers: map[string]ResourceProviderFactory{ 973 "aws": testProviderFuncFixed(p), 974 }, 975 Provisioners: map[string]ResourceProvisionerFactory{ 976 "local-exec": testProvisionerFuncFixed(pr), 977 }, 978 }) 979 980 _, err := ctx.Plan() 981 if err == nil { 982 t.Fatalf("should error") 983 } 984 } 985 986 func TestContext2Plan_computed(t *testing.T) { 987 m := testModule(t, "plan-computed") 988 p := testProvider("aws") 989 p.DiffFn = testDiffFn 990 ctx := testContext2(t, &ContextOpts{ 991 Module: m, 992 Providers: map[string]ResourceProviderFactory{ 993 "aws": testProviderFuncFixed(p), 994 }, 995 }) 996 997 plan, err := ctx.Plan() 998 if err != nil { 999 t.Fatalf("err: %s", err) 1000 } 1001 1002 actual := strings.TrimSpace(plan.String()) 1003 expected := strings.TrimSpace(testTerraformPlanComputedStr) 1004 if actual != expected { 1005 t.Fatalf("bad:\n%s", actual) 1006 } 1007 } 1008 1009 func TestContext2Plan_computedDataResource(t *testing.T) { 1010 m := testModule(t, "plan-computed-data-resource") 1011 p := testProvider("aws") 1012 p.DiffFn = testDiffFn 1013 ctx := testContext2(t, &ContextOpts{ 1014 Module: m, 1015 Providers: map[string]ResourceProviderFactory{ 1016 "aws": testProviderFuncFixed(p), 1017 }, 1018 }) 1019 1020 plan, err := ctx.Plan() 1021 if err != nil { 1022 t.Fatalf("err: %s", err) 1023 } 1024 1025 if got := len(plan.Diff.Modules); got != 1 { 1026 t.Fatalf("got %d modules; want 1", got) 1027 } 1028 1029 moduleDiff := plan.Diff.Modules[0] 1030 1031 if _, ok := moduleDiff.Resources["aws_instance.foo"]; !ok { 1032 t.Fatalf("missing diff for aws_instance.foo") 1033 } 1034 iDiff, ok := moduleDiff.Resources["data.aws_vpc.bar"] 1035 if !ok { 1036 t.Fatalf("missing diff for data.aws_vpc.bar") 1037 } 1038 1039 expectedDiff := &InstanceDiff{ 1040 Attributes: map[string]*ResourceAttrDiff{ 1041 "id": { 1042 NewComputed: true, 1043 RequiresNew: true, 1044 Type: DiffAttrOutput, 1045 }, 1046 }, 1047 } 1048 if same, _ := expectedDiff.Same(iDiff); !same { 1049 t.Fatalf( 1050 "incorrect diff for data.aws_vpc.bar\ngot: %#v\nwant: %#v", 1051 iDiff, expectedDiff, 1052 ) 1053 } 1054 } 1055 1056 func TestContext2Plan_computedDataCountResource(t *testing.T) { 1057 m := testModule(t, "plan-computed-data-count") 1058 p := testProvider("aws") 1059 p.DiffFn = testDiffFn 1060 ctx := testContext2(t, &ContextOpts{ 1061 Module: m, 1062 Providers: map[string]ResourceProviderFactory{ 1063 "aws": testProviderFuncFixed(p), 1064 }, 1065 }) 1066 1067 plan, err := ctx.Plan() 1068 if err != nil { 1069 t.Fatalf("err: %s", err) 1070 } 1071 1072 if got := len(plan.Diff.Modules); got != 1 { 1073 t.Fatalf("got %d modules; want 1", got) 1074 } 1075 1076 moduleDiff := plan.Diff.Modules[0] 1077 1078 // make sure we created 3 "bar"s 1079 for i := 0; i < 3; i++ { 1080 resource := fmt.Sprintf("data.aws_vpc.bar.%d", i) 1081 if _, ok := moduleDiff.Resources[resource]; !ok { 1082 t.Fatalf("missing diff for %s", resource) 1083 } 1084 } 1085 } 1086 1087 // Higher level test at TestResource_dataSourceListPlanPanic 1088 func TestContext2Plan_dataSourceTypeMismatch(t *testing.T) { 1089 m := testModule(t, "plan-data-source-type-mismatch") 1090 p := testProvider("aws") 1091 p.ValidateResourceFn = func(t string, c *ResourceConfig) (ws []string, es []error) { 1092 // Emulate the type checking behavior of helper/schema based validation 1093 if t == "aws_instance" { 1094 ami, _ := c.Get("ami") 1095 switch a := ami.(type) { 1096 case string: 1097 // ok 1098 default: 1099 es = append(es, fmt.Errorf("Expected ami to be string, got %T", a)) 1100 } 1101 } 1102 return 1103 } 1104 p.DiffFn = func( 1105 info *InstanceInfo, 1106 state *InstanceState, 1107 c *ResourceConfig) (*InstanceDiff, error) { 1108 if info.Type == "aws_instance" { 1109 // If we get to the diff, we should be able to assume types 1110 ami, _ := c.Get("ami") 1111 _ = ami.(string) 1112 } 1113 return nil, nil 1114 } 1115 ctx := testContext2(t, &ContextOpts{ 1116 Module: m, 1117 // Pretend like we ran a Refresh and the AZs data source was populated. 1118 State: &State{ 1119 Modules: []*ModuleState{ 1120 &ModuleState{ 1121 Path: rootModulePath, 1122 Resources: map[string]*ResourceState{ 1123 "data.aws_availability_zones.azs": &ResourceState{ 1124 Type: "aws_availability_zones", 1125 Primary: &InstanceState{ 1126 ID: "i-abc123", 1127 Attributes: map[string]string{ 1128 "names.#": "2", 1129 "names.0": "us-east-1a", 1130 "names.1": "us-east-1b", 1131 }, 1132 }, 1133 }, 1134 }, 1135 }, 1136 }, 1137 }, 1138 Providers: map[string]ResourceProviderFactory{ 1139 "aws": testProviderFuncFixed(p), 1140 }, 1141 }) 1142 1143 _, err := ctx.Plan() 1144 1145 if err == nil { 1146 t.Fatalf("Expected err, got none!") 1147 } 1148 expected := "Expected ami to be string" 1149 if !strings.Contains(err.Error(), expected) { 1150 t.Fatalf("expected:\n\n%s\n\nto contain:\n\n%s", err, expected) 1151 } 1152 } 1153 1154 func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { 1155 m := testModule(t, "plan-data-resource-becomes-computed") 1156 p := testProvider("aws") 1157 1158 p.DiffFn = func(info *InstanceInfo, state *InstanceState, config *ResourceConfig) (*InstanceDiff, error) { 1159 if info.Type != "aws_instance" { 1160 t.Fatalf("don't know how to diff %s", info.Id) 1161 return nil, nil 1162 } 1163 1164 return &InstanceDiff{ 1165 Attributes: map[string]*ResourceAttrDiff{ 1166 "computed": &ResourceAttrDiff{ 1167 Old: "", 1168 New: "", 1169 NewComputed: true, 1170 }, 1171 }, 1172 }, nil 1173 } 1174 p.ReadDataDiffReturn = &InstanceDiff{ 1175 Attributes: map[string]*ResourceAttrDiff{ 1176 "foo": &ResourceAttrDiff{ 1177 Old: "", 1178 New: "", 1179 NewComputed: true, 1180 }, 1181 }, 1182 } 1183 1184 ctx := testContext2(t, &ContextOpts{ 1185 Module: m, 1186 Providers: map[string]ResourceProviderFactory{ 1187 "aws": testProviderFuncFixed(p), 1188 }, 1189 State: &State{ 1190 Modules: []*ModuleState{ 1191 &ModuleState{ 1192 Path: rootModulePath, 1193 Resources: map[string]*ResourceState{ 1194 "data.aws_data_resource.foo": &ResourceState{ 1195 Type: "aws_data_resource", 1196 Primary: &InstanceState{ 1197 ID: "i-abc123", 1198 Attributes: map[string]string{ 1199 "id": "i-abc123", 1200 "value": "baz", 1201 }, 1202 }, 1203 }, 1204 }, 1205 }, 1206 }, 1207 }, 1208 }) 1209 1210 plan, err := ctx.Plan() 1211 if err != nil { 1212 t.Fatalf("err: %s", err) 1213 } 1214 1215 if got := len(plan.Diff.Modules); got != 1 { 1216 t.Fatalf("got %d modules; want 1", got) 1217 } 1218 1219 if !p.ReadDataDiffCalled { 1220 t.Fatal("ReadDataDiff wasn't called, but should've been") 1221 } 1222 if got, want := p.ReadDataDiffInfo.Id, "data.aws_data_resource.foo"; got != want { 1223 t.Fatalf("ReadDataDiff info id is %s; want %s", got, want) 1224 } 1225 1226 moduleDiff := plan.Diff.Modules[0] 1227 1228 iDiff, ok := moduleDiff.Resources["data.aws_data_resource.foo"] 1229 if !ok { 1230 t.Fatalf("missing diff for data.aws_data_resource.foo") 1231 } 1232 1233 // This is added by the diff but we want to verify that we got 1234 // the same diff as above minus the dynamic stuff. 1235 delete(iDiff.Attributes, "id") 1236 1237 if same, _ := p.ReadDataDiffReturn.Same(iDiff); !same { 1238 t.Fatalf( 1239 "incorrect diff for data.data_resource.foo\ngot: %#v\nwant: %#v", 1240 iDiff, p.ReadDataDiffReturn, 1241 ) 1242 } 1243 } 1244 1245 func TestContext2Plan_computedList(t *testing.T) { 1246 m := testModule(t, "plan-computed-list") 1247 p := testProvider("aws") 1248 p.DiffFn = testDiffFn 1249 ctx := testContext2(t, &ContextOpts{ 1250 Module: m, 1251 Providers: map[string]ResourceProviderFactory{ 1252 "aws": testProviderFuncFixed(p), 1253 }, 1254 }) 1255 1256 plan, err := ctx.Plan() 1257 if err != nil { 1258 t.Fatalf("err: %s", err) 1259 } 1260 1261 actual := strings.TrimSpace(plan.String()) 1262 expected := strings.TrimSpace(testTerraformPlanComputedListStr) 1263 if actual != expected { 1264 t.Fatalf("bad:\n%s", actual) 1265 } 1266 } 1267 1268 func TestContext2Plan_count(t *testing.T) { 1269 m := testModule(t, "plan-count") 1270 p := testProvider("aws") 1271 p.DiffFn = testDiffFn 1272 ctx := testContext2(t, &ContextOpts{ 1273 Module: m, 1274 Providers: map[string]ResourceProviderFactory{ 1275 "aws": testProviderFuncFixed(p), 1276 }, 1277 }) 1278 1279 plan, err := ctx.Plan() 1280 if err != nil { 1281 t.Fatalf("err: %s", err) 1282 } 1283 1284 if len(plan.Diff.RootModule().Resources) < 6 { 1285 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 1286 } 1287 1288 actual := strings.TrimSpace(plan.String()) 1289 expected := strings.TrimSpace(testTerraformPlanCountStr) 1290 if actual != expected { 1291 t.Fatalf("bad:\n%s", actual) 1292 } 1293 } 1294 1295 func TestContext2Plan_countComputed(t *testing.T) { 1296 m := testModule(t, "plan-count-computed") 1297 p := testProvider("aws") 1298 p.DiffFn = testDiffFn 1299 ctx := testContext2(t, &ContextOpts{ 1300 Module: m, 1301 Providers: map[string]ResourceProviderFactory{ 1302 "aws": testProviderFuncFixed(p), 1303 }, 1304 }) 1305 1306 _, err := ctx.Plan() 1307 if err == nil { 1308 t.Fatal("should error") 1309 } 1310 } 1311 1312 func TestContext2Plan_countComputedModule(t *testing.T) { 1313 m := testModule(t, "plan-count-computed-module") 1314 p := testProvider("aws") 1315 p.DiffFn = testDiffFn 1316 ctx := testContext2(t, &ContextOpts{ 1317 Module: m, 1318 Providers: map[string]ResourceProviderFactory{ 1319 "aws": testProviderFuncFixed(p), 1320 }, 1321 }) 1322 1323 _, err := ctx.Plan() 1324 1325 expectedErr := "aws_instance.bar: value of 'count'" 1326 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 1327 t.Fatalf("expected err would contain %q\nerr: %s\n", 1328 expectedErr, err) 1329 } 1330 } 1331 1332 func TestContext2Plan_countIndex(t *testing.T) { 1333 m := testModule(t, "plan-count-index") 1334 p := testProvider("aws") 1335 p.DiffFn = testDiffFn 1336 ctx := testContext2(t, &ContextOpts{ 1337 Module: m, 1338 Providers: map[string]ResourceProviderFactory{ 1339 "aws": testProviderFuncFixed(p), 1340 }, 1341 }) 1342 1343 plan, err := ctx.Plan() 1344 if err != nil { 1345 t.Fatalf("err: %s", err) 1346 } 1347 1348 actual := strings.TrimSpace(plan.String()) 1349 expected := strings.TrimSpace(testTerraformPlanCountIndexStr) 1350 if actual != expected { 1351 t.Fatalf("bad:\n%s", actual) 1352 } 1353 } 1354 1355 func TestContext2Plan_countIndexZero(t *testing.T) { 1356 m := testModule(t, "plan-count-index-zero") 1357 p := testProvider("aws") 1358 p.DiffFn = testDiffFn 1359 ctx := testContext2(t, &ContextOpts{ 1360 Module: m, 1361 Providers: map[string]ResourceProviderFactory{ 1362 "aws": testProviderFuncFixed(p), 1363 }, 1364 }) 1365 1366 plan, err := ctx.Plan() 1367 if err != nil { 1368 t.Fatalf("err: %s", err) 1369 } 1370 1371 actual := strings.TrimSpace(plan.String()) 1372 expected := strings.TrimSpace(testTerraformPlanCountIndexZeroStr) 1373 if actual != expected { 1374 t.Fatalf("bad:\n%s", actual) 1375 } 1376 } 1377 1378 func TestContext2Plan_countVar(t *testing.T) { 1379 m := testModule(t, "plan-count-var") 1380 p := testProvider("aws") 1381 p.DiffFn = testDiffFn 1382 ctx := testContext2(t, &ContextOpts{ 1383 Module: m, 1384 Providers: map[string]ResourceProviderFactory{ 1385 "aws": testProviderFuncFixed(p), 1386 }, 1387 Variables: map[string]interface{}{ 1388 "count": "3", 1389 }, 1390 }) 1391 1392 plan, err := ctx.Plan() 1393 if err != nil { 1394 t.Fatalf("err: %s", err) 1395 } 1396 1397 actual := strings.TrimSpace(plan.String()) 1398 expected := strings.TrimSpace(testTerraformPlanCountVarStr) 1399 if actual != expected { 1400 t.Fatalf("bad:\n%s", actual) 1401 } 1402 } 1403 1404 func TestContext2Plan_countZero(t *testing.T) { 1405 m := testModule(t, "plan-count-zero") 1406 p := testProvider("aws") 1407 p.DiffFn = testDiffFn 1408 ctx := testContext2(t, &ContextOpts{ 1409 Module: m, 1410 Providers: map[string]ResourceProviderFactory{ 1411 "aws": testProviderFuncFixed(p), 1412 }, 1413 }) 1414 1415 plan, err := ctx.Plan() 1416 if err != nil { 1417 t.Fatalf("err: %s", err) 1418 } 1419 1420 actual := strings.TrimSpace(plan.String()) 1421 expected := strings.TrimSpace(testTerraformPlanCountZeroStr) 1422 if actual != expected { 1423 t.Logf("expected:\n%s", expected) 1424 t.Fatalf("bad:\n%s", actual) 1425 } 1426 } 1427 1428 func TestContext2Plan_countOneIndex(t *testing.T) { 1429 m := testModule(t, "plan-count-one-index") 1430 p := testProvider("aws") 1431 p.DiffFn = testDiffFn 1432 ctx := testContext2(t, &ContextOpts{ 1433 Module: m, 1434 Providers: map[string]ResourceProviderFactory{ 1435 "aws": testProviderFuncFixed(p), 1436 }, 1437 }) 1438 1439 plan, err := ctx.Plan() 1440 if err != nil { 1441 t.Fatalf("err: %s", err) 1442 } 1443 1444 actual := strings.TrimSpace(plan.String()) 1445 expected := strings.TrimSpace(testTerraformPlanCountOneIndexStr) 1446 if actual != expected { 1447 t.Fatalf("bad:\n%s", actual) 1448 } 1449 } 1450 1451 func TestContext2Plan_countDecreaseToOne(t *testing.T) { 1452 m := testModule(t, "plan-count-dec") 1453 p := testProvider("aws") 1454 p.DiffFn = testDiffFn 1455 s := &State{ 1456 Modules: []*ModuleState{ 1457 &ModuleState{ 1458 Path: rootModulePath, 1459 Resources: map[string]*ResourceState{ 1460 "aws_instance.foo.0": &ResourceState{ 1461 Type: "aws_instance", 1462 Primary: &InstanceState{ 1463 ID: "bar", 1464 Attributes: map[string]string{ 1465 "foo": "foo", 1466 "type": "aws_instance", 1467 }, 1468 }, 1469 }, 1470 "aws_instance.foo.1": &ResourceState{ 1471 Type: "aws_instance", 1472 Primary: &InstanceState{ 1473 ID: "bar", 1474 }, 1475 }, 1476 "aws_instance.foo.2": &ResourceState{ 1477 Type: "aws_instance", 1478 Primary: &InstanceState{ 1479 ID: "bar", 1480 }, 1481 }, 1482 }, 1483 }, 1484 }, 1485 } 1486 ctx := testContext2(t, &ContextOpts{ 1487 Module: m, 1488 Providers: map[string]ResourceProviderFactory{ 1489 "aws": testProviderFuncFixed(p), 1490 }, 1491 State: s, 1492 }) 1493 1494 plan, err := ctx.Plan() 1495 if err != nil { 1496 t.Fatalf("err: %s", err) 1497 } 1498 1499 actual := strings.TrimSpace(plan.String()) 1500 expected := strings.TrimSpace(testTerraformPlanCountDecreaseStr) 1501 if actual != expected { 1502 t.Fatalf("bad:\n%s", actual) 1503 } 1504 } 1505 1506 func TestContext2Plan_countIncreaseFromNotSet(t *testing.T) { 1507 m := testModule(t, "plan-count-inc") 1508 p := testProvider("aws") 1509 p.DiffFn = testDiffFn 1510 s := &State{ 1511 Modules: []*ModuleState{ 1512 &ModuleState{ 1513 Path: rootModulePath, 1514 Resources: map[string]*ResourceState{ 1515 "aws_instance.foo": &ResourceState{ 1516 Type: "aws_instance", 1517 Primary: &InstanceState{ 1518 ID: "bar", 1519 Attributes: map[string]string{ 1520 "foo": "foo", 1521 "type": "aws_instance", 1522 }, 1523 }, 1524 }, 1525 }, 1526 }, 1527 }, 1528 } 1529 ctx := testContext2(t, &ContextOpts{ 1530 Module: m, 1531 Providers: map[string]ResourceProviderFactory{ 1532 "aws": testProviderFuncFixed(p), 1533 }, 1534 State: s, 1535 }) 1536 1537 plan, err := ctx.Plan() 1538 if err != nil { 1539 t.Fatalf("err: %s", err) 1540 } 1541 1542 actual := strings.TrimSpace(plan.String()) 1543 expected := strings.TrimSpace(testTerraformPlanCountIncreaseStr) 1544 if actual != expected { 1545 t.Fatalf("bad:\n%s", actual) 1546 } 1547 } 1548 1549 func TestContext2Plan_countIncreaseFromOne(t *testing.T) { 1550 m := testModule(t, "plan-count-inc") 1551 p := testProvider("aws") 1552 p.DiffFn = testDiffFn 1553 s := &State{ 1554 Modules: []*ModuleState{ 1555 &ModuleState{ 1556 Path: rootModulePath, 1557 Resources: map[string]*ResourceState{ 1558 "aws_instance.foo.0": &ResourceState{ 1559 Type: "aws_instance", 1560 Primary: &InstanceState{ 1561 ID: "bar", 1562 Attributes: map[string]string{ 1563 "foo": "foo", 1564 "type": "aws_instance", 1565 }, 1566 }, 1567 }, 1568 }, 1569 }, 1570 }, 1571 } 1572 ctx := testContext2(t, &ContextOpts{ 1573 Module: m, 1574 Providers: map[string]ResourceProviderFactory{ 1575 "aws": testProviderFuncFixed(p), 1576 }, 1577 State: s, 1578 }) 1579 1580 plan, err := ctx.Plan() 1581 if err != nil { 1582 t.Fatalf("err: %s", err) 1583 } 1584 1585 actual := strings.TrimSpace(plan.String()) 1586 expected := strings.TrimSpace(testTerraformPlanCountIncreaseFromOneStr) 1587 if actual != expected { 1588 t.Fatalf("bad:\n%s", actual) 1589 } 1590 } 1591 1592 // https://github.com/PeoplePerHour/terraform/pull/11 1593 // 1594 // This tests a case where both a "resource" and "resource.0" are in 1595 // the state file, which apparently is a reasonable backwards compatibility 1596 // concern found in the above 3rd party repo. 1597 func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) { 1598 m := testModule(t, "plan-count-inc") 1599 p := testProvider("aws") 1600 p.DiffFn = testDiffFn 1601 s := &State{ 1602 Modules: []*ModuleState{ 1603 &ModuleState{ 1604 Path: rootModulePath, 1605 Resources: map[string]*ResourceState{ 1606 "aws_instance.foo": &ResourceState{ 1607 Type: "aws_instance", 1608 Primary: &InstanceState{ 1609 ID: "bar", 1610 Attributes: map[string]string{ 1611 "foo": "foo", 1612 "type": "aws_instance", 1613 }, 1614 }, 1615 }, 1616 "aws_instance.foo.0": &ResourceState{ 1617 Type: "aws_instance", 1618 Primary: &InstanceState{ 1619 ID: "bar", 1620 Attributes: map[string]string{ 1621 "foo": "foo", 1622 "type": "aws_instance", 1623 }, 1624 }, 1625 }, 1626 }, 1627 }, 1628 }, 1629 } 1630 ctx := testContext2(t, &ContextOpts{ 1631 Module: m, 1632 Providers: map[string]ResourceProviderFactory{ 1633 "aws": testProviderFuncFixed(p), 1634 }, 1635 State: s, 1636 }) 1637 1638 plan, err := ctx.Plan() 1639 if err != nil { 1640 t.Fatalf("err: %s", err) 1641 } 1642 1643 actual := strings.TrimSpace(plan.String()) 1644 expected := strings.TrimSpace(testTerraformPlanCountIncreaseFromOneCorruptedStr) 1645 if actual != expected { 1646 t.Fatalf("bad:\n%s", actual) 1647 } 1648 } 1649 1650 func TestContext2Plan_destroy(t *testing.T) { 1651 m := testModule(t, "plan-destroy") 1652 p := testProvider("aws") 1653 p.DiffFn = testDiffFn 1654 s := &State{ 1655 Modules: []*ModuleState{ 1656 &ModuleState{ 1657 Path: rootModulePath, 1658 Resources: map[string]*ResourceState{ 1659 "aws_instance.one": &ResourceState{ 1660 Type: "aws_instance", 1661 Primary: &InstanceState{ 1662 ID: "bar", 1663 }, 1664 }, 1665 "aws_instance.two": &ResourceState{ 1666 Type: "aws_instance", 1667 Primary: &InstanceState{ 1668 ID: "baz", 1669 }, 1670 }, 1671 }, 1672 }, 1673 }, 1674 } 1675 ctx := testContext2(t, &ContextOpts{ 1676 Module: m, 1677 Providers: map[string]ResourceProviderFactory{ 1678 "aws": testProviderFuncFixed(p), 1679 }, 1680 State: s, 1681 Destroy: true, 1682 }) 1683 1684 plan, err := ctx.Plan() 1685 if err != nil { 1686 t.Fatalf("err: %s", err) 1687 } 1688 1689 if len(plan.Diff.RootModule().Resources) != 2 { 1690 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 1691 } 1692 1693 actual := strings.TrimSpace(plan.String()) 1694 expected := strings.TrimSpace(testTerraformPlanDestroyStr) 1695 if actual != expected { 1696 t.Fatalf("bad:\n%s", actual) 1697 } 1698 } 1699 1700 func TestContext2Plan_moduleDestroy(t *testing.T) { 1701 m := testModule(t, "plan-module-destroy") 1702 p := testProvider("aws") 1703 p.DiffFn = testDiffFn 1704 s := &State{ 1705 Modules: []*ModuleState{ 1706 &ModuleState{ 1707 Path: rootModulePath, 1708 Resources: map[string]*ResourceState{ 1709 "aws_instance.foo": &ResourceState{ 1710 Type: "aws_instance", 1711 Primary: &InstanceState{ 1712 ID: "bar", 1713 }, 1714 }, 1715 }, 1716 }, 1717 &ModuleState{ 1718 Path: []string{"root", "child"}, 1719 Resources: map[string]*ResourceState{ 1720 "aws_instance.foo": &ResourceState{ 1721 Type: "aws_instance", 1722 Primary: &InstanceState{ 1723 ID: "bar", 1724 }, 1725 }, 1726 }, 1727 }, 1728 }, 1729 } 1730 ctx := testContext2(t, &ContextOpts{ 1731 Module: m, 1732 Providers: map[string]ResourceProviderFactory{ 1733 "aws": testProviderFuncFixed(p), 1734 }, 1735 State: s, 1736 Destroy: true, 1737 }) 1738 1739 plan, err := ctx.Plan() 1740 if err != nil { 1741 t.Fatalf("err: %s", err) 1742 } 1743 1744 actual := strings.TrimSpace(plan.String()) 1745 expected := strings.TrimSpace(testTerraformPlanModuleDestroyStr) 1746 if actual != expected { 1747 t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) 1748 } 1749 } 1750 1751 // GH-1835 1752 func TestContext2Plan_moduleDestroyCycle(t *testing.T) { 1753 m := testModule(t, "plan-module-destroy-gh-1835") 1754 p := testProvider("aws") 1755 p.DiffFn = testDiffFn 1756 s := &State{ 1757 Modules: []*ModuleState{ 1758 &ModuleState{ 1759 Path: []string{"root", "a_module"}, 1760 Resources: map[string]*ResourceState{ 1761 "aws_instance.a": &ResourceState{ 1762 Type: "aws_instance", 1763 Primary: &InstanceState{ 1764 ID: "a", 1765 }, 1766 }, 1767 }, 1768 }, 1769 &ModuleState{ 1770 Path: []string{"root", "b_module"}, 1771 Resources: map[string]*ResourceState{ 1772 "aws_instance.b": &ResourceState{ 1773 Type: "aws_instance", 1774 Primary: &InstanceState{ 1775 ID: "b", 1776 }, 1777 }, 1778 }, 1779 }, 1780 }, 1781 } 1782 ctx := testContext2(t, &ContextOpts{ 1783 Module: m, 1784 Providers: map[string]ResourceProviderFactory{ 1785 "aws": testProviderFuncFixed(p), 1786 }, 1787 State: s, 1788 Destroy: true, 1789 }) 1790 1791 plan, err := ctx.Plan() 1792 if err != nil { 1793 t.Fatalf("err: %s", err) 1794 } 1795 1796 actual := strings.TrimSpace(plan.String()) 1797 expected := strings.TrimSpace(testTerraformPlanModuleDestroyCycleStr) 1798 if actual != expected { 1799 t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) 1800 } 1801 } 1802 1803 func TestContext2Plan_moduleDestroyMultivar(t *testing.T) { 1804 m := testModule(t, "plan-module-destroy-multivar") 1805 p := testProvider("aws") 1806 p.DiffFn = testDiffFn 1807 s := &State{ 1808 Modules: []*ModuleState{ 1809 &ModuleState{ 1810 Path: rootModulePath, 1811 Resources: map[string]*ResourceState{}, 1812 }, 1813 &ModuleState{ 1814 Path: []string{"root", "child"}, 1815 Resources: map[string]*ResourceState{ 1816 "aws_instance.foo.0": &ResourceState{ 1817 Type: "aws_instance", 1818 Primary: &InstanceState{ 1819 ID: "bar0", 1820 }, 1821 }, 1822 "aws_instance.foo.1": &ResourceState{ 1823 Type: "aws_instance", 1824 Primary: &InstanceState{ 1825 ID: "bar1", 1826 }, 1827 }, 1828 }, 1829 }, 1830 }, 1831 } 1832 ctx := testContext2(t, &ContextOpts{ 1833 Module: m, 1834 Providers: map[string]ResourceProviderFactory{ 1835 "aws": testProviderFuncFixed(p), 1836 }, 1837 State: s, 1838 Destroy: true, 1839 }) 1840 1841 plan, err := ctx.Plan() 1842 if err != nil { 1843 t.Fatalf("err: %s", err) 1844 } 1845 1846 actual := strings.TrimSpace(plan.String()) 1847 expected := strings.TrimSpace(testTerraformPlanModuleDestroyMultivarStr) 1848 if actual != expected { 1849 t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) 1850 } 1851 } 1852 1853 func TestContext2Plan_pathVar(t *testing.T) { 1854 cwd, err := os.Getwd() 1855 if err != nil { 1856 t.Fatalf("err: %s", err) 1857 } 1858 1859 m := testModule(t, "plan-path-var") 1860 p := testProvider("aws") 1861 p.DiffFn = testDiffFn 1862 ctx := testContext2(t, &ContextOpts{ 1863 Module: m, 1864 Providers: map[string]ResourceProviderFactory{ 1865 "aws": testProviderFuncFixed(p), 1866 }, 1867 }) 1868 1869 plan, err := ctx.Plan() 1870 if err != nil { 1871 t.Fatalf("err: %s", err) 1872 } 1873 1874 actual := strings.TrimSpace(plan.String()) 1875 expected := strings.TrimSpace(testTerraformPlanPathVarStr) 1876 1877 // Warning: this ordering REALLY matters for this test. The 1878 // order is: cwd, module, root. 1879 expected = fmt.Sprintf( 1880 expected, 1881 cwd, 1882 m.Config().Dir, 1883 m.Config().Dir) 1884 1885 if actual != expected { 1886 t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) 1887 } 1888 } 1889 1890 func TestContext2Plan_diffVar(t *testing.T) { 1891 m := testModule(t, "plan-diffvar") 1892 p := testProvider("aws") 1893 s := &State{ 1894 Modules: []*ModuleState{ 1895 &ModuleState{ 1896 Path: rootModulePath, 1897 Resources: map[string]*ResourceState{ 1898 "aws_instance.foo": &ResourceState{ 1899 Primary: &InstanceState{ 1900 ID: "bar", 1901 Attributes: map[string]string{ 1902 "num": "2", 1903 }, 1904 }, 1905 }, 1906 }, 1907 }, 1908 }, 1909 } 1910 ctx := testContext2(t, &ContextOpts{ 1911 Module: m, 1912 Providers: map[string]ResourceProviderFactory{ 1913 "aws": testProviderFuncFixed(p), 1914 }, 1915 State: s, 1916 }) 1917 1918 p.DiffFn = func( 1919 info *InstanceInfo, 1920 s *InstanceState, 1921 c *ResourceConfig) (*InstanceDiff, error) { 1922 if s.ID != "bar" { 1923 return testDiffFn(info, s, c) 1924 } 1925 1926 return &InstanceDiff{ 1927 Attributes: map[string]*ResourceAttrDiff{ 1928 "num": &ResourceAttrDiff{ 1929 Old: "2", 1930 New: "3", 1931 }, 1932 }, 1933 }, nil 1934 } 1935 1936 plan, err := ctx.Plan() 1937 if err != nil { 1938 t.Fatalf("err: %s", err) 1939 } 1940 1941 actual := strings.TrimSpace(plan.String()) 1942 expected := strings.TrimSpace(testTerraformPlanDiffVarStr) 1943 if actual != expected { 1944 t.Fatalf("actual:\n%s\n\nexpected:\n%s", actual, expected) 1945 } 1946 } 1947 1948 func TestContext2Plan_hook(t *testing.T) { 1949 m := testModule(t, "plan-good") 1950 h := new(MockHook) 1951 p := testProvider("aws") 1952 p.DiffFn = testDiffFn 1953 ctx := testContext2(t, &ContextOpts{ 1954 Module: m, 1955 Hooks: []Hook{h}, 1956 Providers: map[string]ResourceProviderFactory{ 1957 "aws": testProviderFuncFixed(p), 1958 }, 1959 }) 1960 1961 _, err := ctx.Plan() 1962 if err != nil { 1963 t.Fatalf("err: %s", err) 1964 } 1965 1966 if !h.PreDiffCalled { 1967 t.Fatal("should be called") 1968 } 1969 if !h.PostDiffCalled { 1970 t.Fatal("should be called") 1971 } 1972 } 1973 1974 func TestContext2Plan_orphan(t *testing.T) { 1975 m := testModule(t, "plan-orphan") 1976 p := testProvider("aws") 1977 p.DiffFn = testDiffFn 1978 s := &State{ 1979 Modules: []*ModuleState{ 1980 &ModuleState{ 1981 Path: rootModulePath, 1982 Resources: map[string]*ResourceState{ 1983 "aws_instance.baz": &ResourceState{ 1984 Type: "aws_instance", 1985 Primary: &InstanceState{ 1986 ID: "bar", 1987 }, 1988 }, 1989 }, 1990 }, 1991 }, 1992 } 1993 ctx := testContext2(t, &ContextOpts{ 1994 Module: m, 1995 Providers: map[string]ResourceProviderFactory{ 1996 "aws": testProviderFuncFixed(p), 1997 }, 1998 State: s, 1999 }) 2000 2001 plan, err := ctx.Plan() 2002 if err != nil { 2003 t.Fatalf("err: %s", err) 2004 } 2005 2006 actual := strings.TrimSpace(plan.String()) 2007 expected := strings.TrimSpace(testTerraformPlanOrphanStr) 2008 if actual != expected { 2009 t.Fatalf("bad:\n%s", actual) 2010 } 2011 } 2012 2013 // This tests that configurations with UUIDs don't produce errors. 2014 // For shadows, this would produce errors since a UUID changes every time. 2015 func TestContext2Plan_shadowUuid(t *testing.T) { 2016 m := testModule(t, "plan-shadow-uuid") 2017 p := testProvider("aws") 2018 p.DiffFn = testDiffFn 2019 ctx := testContext2(t, &ContextOpts{ 2020 Module: m, 2021 Providers: map[string]ResourceProviderFactory{ 2022 "aws": testProviderFuncFixed(p), 2023 }, 2024 }) 2025 2026 _, err := ctx.Plan() 2027 if err != nil { 2028 t.Fatalf("err: %s", err) 2029 } 2030 } 2031 2032 func TestContext2Plan_state(t *testing.T) { 2033 m := testModule(t, "plan-good") 2034 p := testProvider("aws") 2035 p.DiffFn = testDiffFn 2036 s := &State{ 2037 Modules: []*ModuleState{ 2038 &ModuleState{ 2039 Path: rootModulePath, 2040 Resources: map[string]*ResourceState{ 2041 "aws_instance.foo": &ResourceState{ 2042 Primary: &InstanceState{ 2043 ID: "bar", 2044 }, 2045 }, 2046 }, 2047 }, 2048 }, 2049 } 2050 ctx := testContext2(t, &ContextOpts{ 2051 Module: m, 2052 Providers: map[string]ResourceProviderFactory{ 2053 "aws": testProviderFuncFixed(p), 2054 }, 2055 State: s, 2056 }) 2057 2058 plan, err := ctx.Plan() 2059 if err != nil { 2060 t.Fatalf("err: %s", err) 2061 } 2062 2063 if len(plan.Diff.RootModule().Resources) < 2 { 2064 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 2065 } 2066 2067 actual := strings.TrimSpace(plan.String()) 2068 expected := strings.TrimSpace(testTerraformPlanStateStr) 2069 if actual != expected { 2070 t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) 2071 } 2072 } 2073 2074 func TestContext2Plan_taint(t *testing.T) { 2075 m := testModule(t, "plan-taint") 2076 p := testProvider("aws") 2077 p.DiffFn = testDiffFn 2078 s := &State{ 2079 Modules: []*ModuleState{ 2080 &ModuleState{ 2081 Path: rootModulePath, 2082 Resources: map[string]*ResourceState{ 2083 "aws_instance.foo": &ResourceState{ 2084 Type: "aws_instance", 2085 Primary: &InstanceState{ 2086 ID: "bar", 2087 Attributes: map[string]string{"num": "2"}, 2088 }, 2089 }, 2090 "aws_instance.bar": &ResourceState{ 2091 Type: "aws_instance", 2092 Primary: &InstanceState{ 2093 ID: "baz", 2094 Tainted: true, 2095 }, 2096 }, 2097 }, 2098 }, 2099 }, 2100 } 2101 ctx := testContext2(t, &ContextOpts{ 2102 Module: m, 2103 Providers: map[string]ResourceProviderFactory{ 2104 "aws": testProviderFuncFixed(p), 2105 }, 2106 State: s, 2107 }) 2108 2109 plan, err := ctx.Plan() 2110 if err != nil { 2111 t.Fatalf("err: %s", err) 2112 } 2113 2114 actual := strings.TrimSpace(plan.String()) 2115 expected := strings.TrimSpace(testTerraformPlanTaintStr) 2116 if actual != expected { 2117 t.Fatalf("bad:\n%s", actual) 2118 } 2119 } 2120 2121 func TestContext2Apply_taintIgnoreChanges(t *testing.T) { 2122 m := testModule(t, "plan-taint-ignore-changes") 2123 p := testProvider("aws") 2124 p.ApplyFn = testApplyFn 2125 p.DiffFn = testDiffFn 2126 s := &State{ 2127 Modules: []*ModuleState{ 2128 &ModuleState{ 2129 Path: rootModulePath, 2130 Resources: map[string]*ResourceState{ 2131 "aws_instance.foo": &ResourceState{ 2132 Type: "aws_instance", 2133 Primary: &InstanceState{ 2134 ID: "foo", 2135 Attributes: map[string]string{ 2136 "vars": "foo", 2137 "type": "aws_instance", 2138 }, 2139 Tainted: true, 2140 }, 2141 }, 2142 }, 2143 }, 2144 }, 2145 } 2146 ctx := testContext2(t, &ContextOpts{ 2147 Module: m, 2148 Providers: map[string]ResourceProviderFactory{ 2149 "aws": testProviderFuncFixed(p), 2150 }, 2151 State: s, 2152 }) 2153 2154 plan, err := ctx.Plan() 2155 if err != nil { 2156 t.Fatalf("err: %s", err) 2157 } 2158 2159 actual := strings.TrimSpace(plan.String()) 2160 expected := strings.TrimSpace(testTerraformPlanTaintIgnoreChangesStr) 2161 if actual != expected { 2162 t.Fatalf("bad:\n%s", actual) 2163 } 2164 } 2165 2166 // Fails about 50% of the time before the fix for GH-4982, covers the fix. 2167 func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) { 2168 m := testModule(t, "plan-taint-interpolated-count") 2169 p := testProvider("aws") 2170 p.DiffFn = testDiffFn 2171 s := &State{ 2172 Modules: []*ModuleState{ 2173 &ModuleState{ 2174 Path: rootModulePath, 2175 Resources: map[string]*ResourceState{ 2176 "aws_instance.foo.0": &ResourceState{ 2177 Type: "aws_instance", 2178 Primary: &InstanceState{ 2179 ID: "bar", 2180 Tainted: true, 2181 }, 2182 }, 2183 "aws_instance.foo.1": &ResourceState{ 2184 Type: "aws_instance", 2185 Primary: &InstanceState{ID: "bar"}, 2186 }, 2187 "aws_instance.foo.2": &ResourceState{ 2188 Type: "aws_instance", 2189 Primary: &InstanceState{ID: "bar"}, 2190 }, 2191 }, 2192 }, 2193 }, 2194 } 2195 ctx := testContext2(t, &ContextOpts{ 2196 Module: m, 2197 Providers: map[string]ResourceProviderFactory{ 2198 "aws": testProviderFuncFixed(p), 2199 }, 2200 State: s, 2201 }) 2202 2203 for i := 0; i < 100; i++ { 2204 plan, err := ctx.Plan() 2205 if err != nil { 2206 t.Fatalf("err: %s", err) 2207 } 2208 2209 actual := strings.TrimSpace(plan.String()) 2210 expected := strings.TrimSpace(` 2211 DIFF: 2212 2213 DESTROY/CREATE: aws_instance.foo.0 2214 type: "" => "aws_instance" 2215 2216 STATE: 2217 2218 aws_instance.foo.0: (tainted) 2219 ID = bar 2220 aws_instance.foo.1: 2221 ID = bar 2222 aws_instance.foo.2: 2223 ID = bar 2224 `) 2225 if actual != expected { 2226 t.Fatalf("[%d] bad:\n%s\nexpected:\n%s\n", i, actual, expected) 2227 } 2228 } 2229 } 2230 2231 func TestContext2Plan_targeted(t *testing.T) { 2232 m := testModule(t, "plan-targeted") 2233 p := testProvider("aws") 2234 p.DiffFn = testDiffFn 2235 ctx := testContext2(t, &ContextOpts{ 2236 Module: m, 2237 Providers: map[string]ResourceProviderFactory{ 2238 "aws": testProviderFuncFixed(p), 2239 }, 2240 Targets: []string{"aws_instance.foo"}, 2241 }) 2242 2243 plan, err := ctx.Plan() 2244 if err != nil { 2245 t.Fatalf("err: %s", err) 2246 } 2247 2248 actual := strings.TrimSpace(plan.String()) 2249 expected := strings.TrimSpace(` 2250 DIFF: 2251 2252 CREATE: aws_instance.foo 2253 num: "" => "2" 2254 type: "" => "aws_instance" 2255 2256 STATE: 2257 2258 <no state> 2259 `) 2260 if actual != expected { 2261 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 2262 } 2263 } 2264 2265 func TestContext2Plan_targetedOrphan(t *testing.T) { 2266 m := testModule(t, "plan-targeted-orphan") 2267 p := testProvider("aws") 2268 p.DiffFn = testDiffFn 2269 ctx := testContext2(t, &ContextOpts{ 2270 Module: m, 2271 Providers: map[string]ResourceProviderFactory{ 2272 "aws": testProviderFuncFixed(p), 2273 }, 2274 State: &State{ 2275 Modules: []*ModuleState{ 2276 &ModuleState{ 2277 Path: rootModulePath, 2278 Resources: map[string]*ResourceState{ 2279 "aws_instance.orphan": &ResourceState{ 2280 Type: "aws_instance", 2281 Primary: &InstanceState{ 2282 ID: "i-789xyz", 2283 }, 2284 }, 2285 "aws_instance.nottargeted": &ResourceState{ 2286 Type: "aws_instance", 2287 Primary: &InstanceState{ 2288 ID: "i-abc123", 2289 }, 2290 }, 2291 }, 2292 }, 2293 }, 2294 }, 2295 Destroy: true, 2296 Targets: []string{"aws_instance.orphan"}, 2297 }) 2298 2299 plan, err := ctx.Plan() 2300 if err != nil { 2301 t.Fatalf("err: %s", err) 2302 } 2303 2304 actual := strings.TrimSpace(plan.String()) 2305 expected := strings.TrimSpace(`DIFF: 2306 2307 DESTROY: aws_instance.orphan 2308 2309 STATE: 2310 2311 aws_instance.nottargeted: 2312 ID = i-abc123 2313 aws_instance.orphan: 2314 ID = i-789xyz 2315 `) 2316 if actual != expected { 2317 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 2318 } 2319 } 2320 2321 // https://github.com/hashicorp/terraform/issues/2538 2322 func TestContext2Plan_targetedModuleOrphan(t *testing.T) { 2323 m := testModule(t, "plan-targeted-module-orphan") 2324 p := testProvider("aws") 2325 p.DiffFn = testDiffFn 2326 ctx := testContext2(t, &ContextOpts{ 2327 Module: m, 2328 Providers: map[string]ResourceProviderFactory{ 2329 "aws": testProviderFuncFixed(p), 2330 }, 2331 State: &State{ 2332 Modules: []*ModuleState{ 2333 &ModuleState{ 2334 Path: []string{"root", "child"}, 2335 Resources: map[string]*ResourceState{ 2336 "aws_instance.orphan": &ResourceState{ 2337 Type: "aws_instance", 2338 Primary: &InstanceState{ 2339 ID: "i-789xyz", 2340 }, 2341 }, 2342 "aws_instance.nottargeted": &ResourceState{ 2343 Type: "aws_instance", 2344 Primary: &InstanceState{ 2345 ID: "i-abc123", 2346 }, 2347 }, 2348 }, 2349 }, 2350 }, 2351 }, 2352 Destroy: true, 2353 Targets: []string{"module.child.aws_instance.orphan"}, 2354 }) 2355 2356 plan, err := ctx.Plan() 2357 if err != nil { 2358 t.Fatalf("err: %s", err) 2359 } 2360 2361 actual := strings.TrimSpace(plan.String()) 2362 expected := strings.TrimSpace(`DIFF: 2363 2364 module.child: 2365 DESTROY: aws_instance.orphan 2366 2367 STATE: 2368 2369 module.child: 2370 aws_instance.nottargeted: 2371 ID = i-abc123 2372 aws_instance.orphan: 2373 ID = i-789xyz 2374 `) 2375 if actual != expected { 2376 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 2377 } 2378 } 2379 2380 func TestContext2Plan_targetedModuleUntargetedVariable(t *testing.T) { 2381 m := testModule(t, "plan-targeted-module-untargeted-variable") 2382 p := testProvider("aws") 2383 p.DiffFn = testDiffFn 2384 ctx := testContext2(t, &ContextOpts{ 2385 Module: m, 2386 Providers: map[string]ResourceProviderFactory{ 2387 "aws": testProviderFuncFixed(p), 2388 }, 2389 Targets: []string{"aws_instance.blue", "module.blue_mod"}, 2390 }) 2391 2392 plan, err := ctx.Plan() 2393 if err != nil { 2394 t.Fatalf("err: %s", err) 2395 } 2396 2397 actual := strings.TrimSpace(plan.String()) 2398 expected := strings.TrimSpace(` 2399 DIFF: 2400 2401 CREATE: aws_instance.blue 2402 2403 module.blue_mod: 2404 CREATE: aws_instance.mod 2405 type: "" => "aws_instance" 2406 value: "" => "<computed>" 2407 2408 STATE: 2409 2410 <no state> 2411 `) 2412 if actual != expected { 2413 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 2414 } 2415 } 2416 2417 // https://github.com/hashicorp/terraform/issues/4515 2418 func TestContext2Plan_targetedOverTen(t *testing.T) { 2419 m := testModule(t, "plan-targeted-over-ten") 2420 p := testProvider("aws") 2421 p.DiffFn = testDiffFn 2422 2423 resources := make(map[string]*ResourceState) 2424 var expectedState []string 2425 for i := 0; i < 13; i++ { 2426 key := fmt.Sprintf("aws_instance.foo.%d", i) 2427 id := fmt.Sprintf("i-abc%d", i) 2428 resources[key] = &ResourceState{ 2429 Type: "aws_instance", 2430 Primary: &InstanceState{ID: id}, 2431 } 2432 expectedState = append(expectedState, 2433 fmt.Sprintf("%s:\n ID = %s\n", key, id)) 2434 } 2435 ctx := testContext2(t, &ContextOpts{ 2436 Module: m, 2437 Providers: map[string]ResourceProviderFactory{ 2438 "aws": testProviderFuncFixed(p), 2439 }, 2440 State: &State{ 2441 Modules: []*ModuleState{ 2442 &ModuleState{ 2443 Path: rootModulePath, 2444 Resources: resources, 2445 }, 2446 }, 2447 }, 2448 Targets: []string{"aws_instance.foo[1]"}, 2449 }) 2450 2451 plan, err := ctx.Plan() 2452 if err != nil { 2453 t.Fatalf("err: %s", err) 2454 } 2455 2456 actual := strings.TrimSpace(plan.String()) 2457 sort.Strings(expectedState) 2458 expected := strings.TrimSpace(` 2459 DIFF: 2460 2461 2462 2463 STATE: 2464 2465 aws_instance.foo.0: 2466 ID = i-abc0 2467 aws_instance.foo.1: 2468 ID = i-abc1 2469 aws_instance.foo.10: 2470 ID = i-abc10 2471 aws_instance.foo.11: 2472 ID = i-abc11 2473 aws_instance.foo.12: 2474 ID = i-abc12 2475 aws_instance.foo.2: 2476 ID = i-abc2 2477 aws_instance.foo.3: 2478 ID = i-abc3 2479 aws_instance.foo.4: 2480 ID = i-abc4 2481 aws_instance.foo.5: 2482 ID = i-abc5 2483 aws_instance.foo.6: 2484 ID = i-abc6 2485 aws_instance.foo.7: 2486 ID = i-abc7 2487 aws_instance.foo.8: 2488 ID = i-abc8 2489 aws_instance.foo.9: 2490 ID = i-abc9 2491 `) 2492 if actual != expected { 2493 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 2494 } 2495 } 2496 2497 func TestContext2Plan_provider(t *testing.T) { 2498 m := testModule(t, "plan-provider") 2499 p := testProvider("aws") 2500 p.DiffFn = testDiffFn 2501 2502 var value interface{} 2503 p.ConfigureFn = func(c *ResourceConfig) error { 2504 value, _ = c.Get("foo") 2505 return nil 2506 } 2507 2508 ctx := testContext2(t, &ContextOpts{ 2509 Module: m, 2510 Providers: map[string]ResourceProviderFactory{ 2511 "aws": testProviderFuncFixed(p), 2512 }, 2513 Variables: map[string]interface{}{ 2514 "foo": "bar", 2515 }, 2516 }) 2517 2518 if _, err := ctx.Plan(); err != nil { 2519 t.Fatalf("err: %s", err) 2520 } 2521 2522 if value != "bar" { 2523 t.Fatalf("bad: %#v", value) 2524 } 2525 } 2526 2527 func TestContext2Plan_varListErr(t *testing.T) { 2528 m := testModule(t, "plan-var-list-err") 2529 p := testProvider("aws") 2530 ctx := testContext2(t, &ContextOpts{ 2531 Module: m, 2532 Providers: map[string]ResourceProviderFactory{ 2533 "aws": testProviderFuncFixed(p), 2534 }, 2535 }) 2536 2537 _, err := ctx.Plan() 2538 2539 if err == nil { 2540 t.Fatal("should error") 2541 } 2542 } 2543 2544 func TestContext2Plan_ignoreChanges(t *testing.T) { 2545 m := testModule(t, "plan-ignore-changes") 2546 p := testProvider("aws") 2547 p.DiffFn = testDiffFn 2548 s := &State{ 2549 Modules: []*ModuleState{ 2550 &ModuleState{ 2551 Path: rootModulePath, 2552 Resources: map[string]*ResourceState{ 2553 "aws_instance.foo": &ResourceState{ 2554 Primary: &InstanceState{ 2555 ID: "bar", 2556 Attributes: map[string]string{"ami": "ami-abcd1234"}, 2557 }, 2558 }, 2559 }, 2560 }, 2561 }, 2562 } 2563 ctx := testContext2(t, &ContextOpts{ 2564 Module: m, 2565 Providers: map[string]ResourceProviderFactory{ 2566 "aws": testProviderFuncFixed(p), 2567 }, 2568 Variables: map[string]interface{}{ 2569 "foo": "ami-1234abcd", 2570 }, 2571 State: s, 2572 }) 2573 2574 plan, err := ctx.Plan() 2575 if err != nil { 2576 t.Fatalf("err: %s", err) 2577 } 2578 2579 if len(plan.Diff.RootModule().Resources) < 1 { 2580 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 2581 } 2582 2583 actual := strings.TrimSpace(plan.String()) 2584 expected := strings.TrimSpace(testTerraformPlanIgnoreChangesStr) 2585 if actual != expected { 2586 t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected) 2587 } 2588 } 2589 2590 func TestContext2Plan_ignoreChangesWildcard(t *testing.T) { 2591 m := testModule(t, "plan-ignore-changes-wildcard") 2592 p := testProvider("aws") 2593 p.DiffFn = testDiffFn 2594 s := &State{ 2595 Modules: []*ModuleState{ 2596 &ModuleState{ 2597 Path: rootModulePath, 2598 Resources: map[string]*ResourceState{ 2599 "aws_instance.foo": &ResourceState{ 2600 Primary: &InstanceState{ 2601 ID: "bar", 2602 Attributes: map[string]string{ 2603 "ami": "ami-abcd1234", 2604 "instance_type": "t2.micro", 2605 }, 2606 }, 2607 }, 2608 }, 2609 }, 2610 }, 2611 } 2612 ctx := testContext2(t, &ContextOpts{ 2613 Module: m, 2614 Providers: map[string]ResourceProviderFactory{ 2615 "aws": testProviderFuncFixed(p), 2616 }, 2617 Variables: map[string]interface{}{ 2618 "foo": "ami-1234abcd", 2619 "bar": "t2.small", 2620 }, 2621 State: s, 2622 }) 2623 2624 plan, err := ctx.Plan() 2625 if err != nil { 2626 t.Fatalf("err: %s", err) 2627 } 2628 2629 if len(plan.Diff.RootModule().Resources) > 0 { 2630 t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) 2631 } 2632 2633 actual := strings.TrimSpace(plan.String()) 2634 expected := strings.TrimSpace(testTerraformPlanIgnoreChangesWildcardStr) 2635 if actual != expected { 2636 t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected) 2637 } 2638 } 2639 2640 func TestContext2Plan_moduleMapLiteral(t *testing.T) { 2641 m := testModule(t, "plan-module-map-literal") 2642 p := testProvider("aws") 2643 p.ApplyFn = testApplyFn 2644 p.DiffFn = func(i *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 2645 // Here we verify that both the populated and empty map literals made it 2646 // through to the resource attributes 2647 val, _ := c.Get("tags") 2648 m, ok := val.(map[string]interface{}) 2649 if !ok { 2650 t.Fatalf("Tags attr not map: %#v", val) 2651 } 2652 if m["foo"] != "bar" { 2653 t.Fatalf("Bad value in tags attr: %#v", m) 2654 } 2655 { 2656 val, _ := c.Get("meta") 2657 m, ok := val.(map[string]interface{}) 2658 if !ok { 2659 t.Fatalf("Meta attr not map: %#v", val) 2660 } 2661 if len(m) != 0 { 2662 t.Fatalf("Meta attr not empty: %#v", val) 2663 } 2664 } 2665 return nil, nil 2666 } 2667 ctx := testContext2(t, &ContextOpts{ 2668 Module: m, 2669 Providers: map[string]ResourceProviderFactory{ 2670 "aws": testProviderFuncFixed(p), 2671 }, 2672 }) 2673 2674 if _, err := ctx.Plan(); err != nil { 2675 t.Fatalf("err: %s", err) 2676 } 2677 } 2678 2679 func TestContext2Plan_computedValueInMap(t *testing.T) { 2680 m := testModule(t, "plan-computed-value-in-map") 2681 p := testProvider("aws") 2682 p.DiffFn = func(info *InstanceInfo, state *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 2683 switch info.Type { 2684 case "aws_computed_source": 2685 return &InstanceDiff{ 2686 Attributes: map[string]*ResourceAttrDiff{ 2687 "computed_read_only": &ResourceAttrDiff{ 2688 NewComputed: true, 2689 }, 2690 }, 2691 }, nil 2692 } 2693 2694 return testDiffFn(info, state, c) 2695 } 2696 ctx := testContext2(t, &ContextOpts{ 2697 Module: m, 2698 Providers: map[string]ResourceProviderFactory{ 2699 "aws": testProviderFuncFixed(p), 2700 }, 2701 }) 2702 2703 if _, err := ctx.Plan(); err != nil { 2704 t.Fatalf("err: %s", err) 2705 } 2706 2707 plan, err := ctx.Plan() 2708 if err != nil { 2709 t.Fatalf("err: %s", err) 2710 } 2711 2712 actual := strings.TrimSpace(plan.String()) 2713 expected := strings.TrimSpace(testTerraformPlanComputedValueInMap) 2714 if actual != expected { 2715 t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected) 2716 } 2717 } 2718 2719 func TestContext2Plan_moduleVariableFromSplat(t *testing.T) { 2720 m := testModule(t, "plan-module-variable-from-splat") 2721 p := testProvider("aws") 2722 p.DiffFn = testDiffFn 2723 ctx := testContext2(t, &ContextOpts{ 2724 Module: m, 2725 Providers: map[string]ResourceProviderFactory{ 2726 "aws": testProviderFuncFixed(p), 2727 }, 2728 }) 2729 2730 if _, err := ctx.Plan(); err != nil { 2731 t.Fatalf("err: %s", err) 2732 } 2733 2734 plan, err := ctx.Plan() 2735 if err != nil { 2736 t.Fatalf("err: %s", err) 2737 } 2738 2739 actual := strings.TrimSpace(plan.String()) 2740 expected := strings.TrimSpace(testTerraformPlanModuleVariableFromSplat) 2741 if actual != expected { 2742 t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected) 2743 } 2744 } 2745 2746 func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { 2747 m := testModule(t, "plan-cdb-depends-datasource") 2748 p := testProvider("aws") 2749 p.DiffFn = testDiffFn 2750 ctx := testContext2(t, &ContextOpts{ 2751 Module: m, 2752 Providers: map[string]ResourceProviderFactory{ 2753 "aws": testProviderFuncFixed(p), 2754 }, 2755 }) 2756 2757 plan, err := ctx.Plan() 2758 if err != nil { 2759 t.Fatalf("err: %s", err) 2760 } 2761 2762 if got := len(plan.Diff.Modules); got != 1 { 2763 t.Fatalf("got %d modules; want 1", got) 2764 } 2765 2766 moduleDiff := plan.Diff.Modules[0] 2767 2768 if _, ok := moduleDiff.Resources["aws_instance.foo.0"]; !ok { 2769 t.Fatalf("missing diff for aws_instance.foo.0") 2770 } 2771 if _, ok := moduleDiff.Resources["aws_instance.foo.1"]; !ok { 2772 t.Fatalf("missing diff for aws_instance.foo.1") 2773 } 2774 if _, ok := moduleDiff.Resources["data.aws_vpc.bar.0"]; !ok { 2775 t.Fatalf("missing diff for data.aws_vpc.bar.0") 2776 } 2777 if _, ok := moduleDiff.Resources["data.aws_vpc.bar.1"]; !ok { 2778 t.Fatalf("missing diff for data.aws_vpc.bar.1") 2779 } 2780 }