github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/context_plan_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "os" 8 "reflect" 9 "sort" 10 "strings" 11 "sync" 12 "testing" 13 14 "github.com/davecgh/go-spew/spew" 15 "github.com/google/go-cmp/cmp" 16 "github.com/zclconf/go-cty/cty" 17 18 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 19 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 20 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" 21 "github.com/hashicorp/terraform-plugin-sdk/internal/plans" 22 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 23 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 24 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 25 ) 26 27 func TestContext2Plan_basic(t *testing.T) { 28 m := testModule(t, "plan-good") 29 p := testProvider("aws") 30 p.DiffFn = testDiffFn 31 ctx := testContext2(t, &ContextOpts{ 32 Config: m, 33 ProviderResolver: providers.ResolverFixed( 34 map[string]providers.Factory{ 35 "aws": testProviderFuncFixed(p), 36 }, 37 ), 38 ProviderSHA256s: map[string][]byte{ 39 "aws": []byte("placeholder"), 40 }, 41 }) 42 43 plan, diags := ctx.Plan() 44 if diags.HasErrors() { 45 t.Fatalf("unexpected errors: %s", diags.Err()) 46 } 47 48 if l := len(plan.Changes.Resources); l < 2 { 49 t.Fatalf("wrong number of resources %d; want fewer than two\n%s", l, spew.Sdump(plan.Changes.Resources)) 50 } 51 52 if !reflect.DeepEqual(plan.ProviderSHA256s, ctx.providerSHA256s) { 53 t.Errorf("wrong ProviderSHA256s %#v; want %#v", plan.ProviderSHA256s, ctx.providerSHA256s) 54 } 55 56 if !ctx.State().Empty() { 57 t.Fatalf("expected empty state, got %#v\n", ctx.State()) 58 } 59 60 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 61 ty := schema.ImpliedType() 62 for _, r := range plan.Changes.Resources { 63 ric, err := r.Decode(ty) 64 if err != nil { 65 t.Fatal(err) 66 } 67 68 switch i := ric.Addr.String(); i { 69 case "aws_instance.bar": 70 foo := ric.After.GetAttr("foo").AsString() 71 if foo != "2" { 72 t.Fatalf("incorrect plan for 'bar': %#v", ric.After) 73 } 74 case "aws_instance.foo": 75 num, _ := ric.After.GetAttr("num").AsBigFloat().Int64() 76 if num != 2 { 77 t.Fatalf("incorrect plan for 'foo': %#v", ric.After) 78 } 79 default: 80 t.Fatal("unknown instance:", i) 81 } 82 } 83 } 84 85 func TestContext2Plan_createBefore_deposed(t *testing.T) { 86 m := testModule(t, "plan-cbd") 87 p := testProvider("aws") 88 p.DiffFn = testDiffFn 89 90 s := MustShimLegacyState(&State{ 91 Modules: []*ModuleState{ 92 { 93 Path: []string{"root"}, 94 Resources: map[string]*ResourceState{ 95 "aws_instance.foo": { 96 Type: "aws_instance", 97 Primary: &InstanceState{ 98 ID: "baz", 99 Attributes: map[string]string{ 100 "id": "baz", 101 }, 102 }, 103 Deposed: []*InstanceState{ 104 { 105 ID: "foo", 106 Attributes: map[string]string{ 107 "id": "foo", 108 }, 109 }, 110 }, 111 }, 112 }, 113 }, 114 }, 115 }) 116 117 ctx := testContext2(t, &ContextOpts{ 118 Config: m, 119 ProviderResolver: providers.ResolverFixed( 120 map[string]providers.Factory{ 121 "aws": testProviderFuncFixed(p), 122 }, 123 ), 124 State: s, 125 }) 126 127 plan, diags := ctx.Plan() 128 if diags.HasErrors() { 129 t.Fatalf("unexpected errors: %s", diags.Err()) 130 } 131 132 // the state should still show one deposed 133 expectedState := strings.TrimSpace(` 134 aws_instance.foo: (1 deposed) 135 ID = baz 136 provider = provider.aws 137 Deposed ID 1 = foo`) 138 139 if ctx.State().String() != expectedState { 140 t.Fatalf("\nexpected: %q\ngot: %q\n", expectedState, ctx.State().String()) 141 } 142 143 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 144 ty := schema.ImpliedType() 145 146 type InstanceGen struct { 147 Addr string 148 DeposedKey states.DeposedKey 149 } 150 want := map[InstanceGen]bool{ 151 { 152 Addr: "aws_instance.foo", 153 }: true, 154 { 155 Addr: "aws_instance.foo", 156 DeposedKey: states.DeposedKey("00000001"), 157 }: true, 158 } 159 got := make(map[InstanceGen]bool) 160 changes := make(map[InstanceGen]*plans.ResourceInstanceChangeSrc) 161 162 for _, change := range plan.Changes.Resources { 163 k := InstanceGen{ 164 Addr: change.Addr.String(), 165 DeposedKey: change.DeposedKey, 166 } 167 got[k] = true 168 changes[k] = change 169 } 170 if !reflect.DeepEqual(got, want) { 171 t.Fatalf("wrong resource instance object changes in plan\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(want)) 172 } 173 174 { 175 ric, err := changes[InstanceGen{Addr: "aws_instance.foo"}].Decode(ty) 176 if err != nil { 177 t.Fatal(err) 178 } 179 180 if got, want := ric.Action, plans.NoOp; got != want { 181 t.Errorf("current object change action is %s; want %s", got, want) 182 } 183 184 // the existing instance should only have an unchanged id 185 expected, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{"id": cty.StringVal("baz")})) 186 if err != nil { 187 t.Fatal(err) 188 } 189 190 checkVals(t, expected, ric.After) 191 } 192 193 { 194 ric, err := changes[InstanceGen{Addr: "aws_instance.foo", DeposedKey: states.DeposedKey("00000001")}].Decode(ty) 195 if err != nil { 196 t.Fatal(err) 197 } 198 199 if got, want := ric.Action, plans.Delete; got != want { 200 t.Errorf("deposed object change action is %s; want %s", got, want) 201 } 202 } 203 } 204 205 func TestContext2Plan_createBefore_maintainRoot(t *testing.T) { 206 m := testModule(t, "plan-cbd-maintain-root") 207 p := testProvider("aws") 208 p.DiffFn = testDiffFn 209 ctx := testContext2(t, &ContextOpts{ 210 Config: m, 211 ProviderResolver: providers.ResolverFixed( 212 map[string]providers.Factory{ 213 "aws": testProviderFuncFixed(p), 214 }, 215 ), 216 Variables: InputValues{ 217 "in": &InputValue{ 218 Value: cty.StringVal("a,b,c"), 219 SourceType: ValueFromCaller, 220 }, 221 }, 222 }) 223 224 plan, diags := ctx.Plan() 225 if diags.HasErrors() { 226 t.Fatalf("unexpected errors: %s", diags.Err()) 227 } 228 229 if !ctx.State().Empty() { 230 t.Fatal("expected empty state, got:", ctx.State()) 231 } 232 233 if len(plan.Changes.Resources) != 4 { 234 t.Error("expected 4 resource in plan, got", len(plan.Changes.Resources)) 235 } 236 237 for _, res := range plan.Changes.Resources { 238 // these should all be creates 239 if res.Action != plans.Create { 240 t.Fatalf("unexpected action %s for %s", res.Action, res.Addr.String()) 241 } 242 } 243 } 244 245 func TestContext2Plan_emptyDiff(t *testing.T) { 246 m := testModule(t, "plan-empty") 247 p := testProvider("aws") 248 p.DiffFn = func( 249 info *InstanceInfo, 250 s *InstanceState, 251 c *ResourceConfig) (*InstanceDiff, error) { 252 return nil, nil 253 } 254 255 ctx := testContext2(t, &ContextOpts{ 256 Config: m, 257 ProviderResolver: providers.ResolverFixed( 258 map[string]providers.Factory{ 259 "aws": testProviderFuncFixed(p), 260 }, 261 ), 262 }) 263 264 plan, diags := ctx.Plan() 265 if diags.HasErrors() { 266 t.Fatalf("unexpected errors: %s", diags.Err()) 267 } 268 269 if !ctx.State().Empty() { 270 t.Fatal("expected empty state, got:", ctx.State()) 271 } 272 273 if len(plan.Changes.Resources) != 2 { 274 t.Error("expected 2 resource in plan, got", len(plan.Changes.Resources)) 275 } 276 277 actions := map[string]plans.Action{} 278 279 for _, res := range plan.Changes.Resources { 280 actions[res.Addr.String()] = res.Action 281 } 282 283 expected := map[string]plans.Action{ 284 "aws_instance.foo": plans.Create, 285 "aws_instance.bar": plans.Create, 286 } 287 if !cmp.Equal(expected, actions) { 288 t.Fatal(cmp.Diff(expected, actions)) 289 } 290 } 291 292 func TestContext2Plan_escapedVar(t *testing.T) { 293 m := testModule(t, "plan-escaped-var") 294 p := testProvider("aws") 295 p.DiffFn = testDiffFn 296 ctx := testContext2(t, &ContextOpts{ 297 Config: m, 298 ProviderResolver: providers.ResolverFixed( 299 map[string]providers.Factory{ 300 "aws": testProviderFuncFixed(p), 301 }, 302 ), 303 }) 304 305 plan, diags := ctx.Plan() 306 if diags.HasErrors() { 307 t.Fatalf("unexpected errors: %s", diags.Err()) 308 } 309 310 if len(plan.Changes.Resources) != 1 { 311 t.Error("expected 1 resource in plan, got", len(plan.Changes.Resources)) 312 } 313 314 res := plan.Changes.Resources[0] 315 if res.Action != plans.Create { 316 t.Fatalf("expected resource creation, got %s", res.Action) 317 } 318 319 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 320 ty := schema.ImpliedType() 321 322 ric, err := res.Decode(ty) 323 if err != nil { 324 t.Fatal(err) 325 } 326 327 expected := objectVal(t, schema, map[string]cty.Value{ 328 "id": cty.UnknownVal(cty.String), 329 "foo": cty.StringVal("bar-${baz}"), 330 "type": cty.StringVal("aws_instance")}, 331 ) 332 333 checkVals(t, expected, ric.After) 334 } 335 336 func TestContext2Plan_minimal(t *testing.T) { 337 m := testModule(t, "plan-empty") 338 p := testProvider("aws") 339 p.DiffFn = testDiffFn 340 ctx := testContext2(t, &ContextOpts{ 341 Config: m, 342 ProviderResolver: providers.ResolverFixed( 343 map[string]providers.Factory{ 344 "aws": testProviderFuncFixed(p), 345 }, 346 ), 347 }) 348 349 plan, diags := ctx.Plan() 350 if diags.HasErrors() { 351 t.Fatalf("unexpected errors: %s", diags.Err()) 352 } 353 354 if !ctx.State().Empty() { 355 t.Fatal("expected empty state, got:", ctx.State()) 356 } 357 358 if len(plan.Changes.Resources) != 2 { 359 t.Error("expected 2 resource in plan, got", len(plan.Changes.Resources)) 360 } 361 362 actions := map[string]plans.Action{} 363 364 for _, res := range plan.Changes.Resources { 365 actions[res.Addr.String()] = res.Action 366 } 367 368 expected := map[string]plans.Action{ 369 "aws_instance.foo": plans.Create, 370 "aws_instance.bar": plans.Create, 371 } 372 if !cmp.Equal(expected, actions) { 373 t.Fatal(cmp.Diff(expected, actions)) 374 } 375 } 376 377 func TestContext2Plan_modules(t *testing.T) { 378 m := testModule(t, "plan-modules") 379 p := testProvider("aws") 380 p.DiffFn = testDiffFn 381 ctx := testContext2(t, &ContextOpts{ 382 Config: m, 383 ProviderResolver: providers.ResolverFixed( 384 map[string]providers.Factory{ 385 "aws": testProviderFuncFixed(p), 386 }, 387 ), 388 }) 389 390 plan, diags := ctx.Plan() 391 if diags.HasErrors() { 392 t.Fatalf("unexpected errors: %s", diags.Err()) 393 } 394 395 if len(plan.Changes.Resources) != 3 { 396 t.Error("expected 3 resource in plan, got", len(plan.Changes.Resources)) 397 } 398 399 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 400 ty := schema.ImpliedType() 401 402 expectFoo := objectVal(t, schema, map[string]cty.Value{ 403 "id": cty.UnknownVal(cty.String), 404 "foo": cty.StringVal("2"), 405 "type": cty.StringVal("aws_instance")}, 406 ) 407 408 expectNum := objectVal(t, schema, map[string]cty.Value{ 409 "id": cty.UnknownVal(cty.String), 410 "num": cty.NumberIntVal(2), 411 "type": cty.StringVal("aws_instance")}, 412 ) 413 414 for _, res := range plan.Changes.Resources { 415 if res.Action != plans.Create { 416 t.Fatalf("expected resource creation, got %s", res.Action) 417 } 418 ric, err := res.Decode(ty) 419 if err != nil { 420 t.Fatal(err) 421 } 422 423 var expected cty.Value 424 switch i := ric.Addr.String(); i { 425 case "aws_instance.bar": 426 expected = expectFoo 427 case "aws_instance.foo": 428 expected = expectNum 429 case "module.child.aws_instance.foo": 430 expected = expectNum 431 default: 432 t.Fatal("unknown instance:", i) 433 } 434 435 checkVals(t, expected, ric.After) 436 } 437 } 438 439 // GH-1475 440 func TestContext2Plan_moduleCycle(t *testing.T) { 441 m := testModule(t, "plan-module-cycle") 442 p := testProvider("aws") 443 p.GetSchemaReturn = &ProviderSchema{ 444 ResourceTypes: map[string]*configschema.Block{ 445 "aws_instance": { 446 Attributes: map[string]*configschema.Attribute{ 447 "id": {Type: cty.String, Computed: true}, 448 "some_input": {Type: cty.String, Optional: true}, 449 "type": {Type: cty.String, Computed: true}, 450 }, 451 }, 452 }, 453 } 454 p.DiffFn = testDiffFn 455 456 ctx := testContext2(t, &ContextOpts{ 457 Config: m, 458 ProviderResolver: providers.ResolverFixed( 459 map[string]providers.Factory{ 460 "aws": testProviderFuncFixed(p), 461 }, 462 ), 463 }) 464 465 plan, diags := ctx.Plan() 466 if diags.HasErrors() { 467 t.Fatalf("unexpected errors: %s", diags.Err()) 468 } 469 470 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 471 ty := schema.ImpliedType() 472 473 if len(plan.Changes.Resources) != 2 { 474 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 475 } 476 477 for _, res := range plan.Changes.Resources { 478 if res.Action != plans.Create { 479 t.Fatalf("expected resource creation, got %s", res.Action) 480 } 481 ric, err := res.Decode(ty) 482 if err != nil { 483 t.Fatal(err) 484 } 485 486 var expected cty.Value 487 switch i := ric.Addr.String(); i { 488 case "aws_instance.b": 489 expected = objectVal(t, schema, map[string]cty.Value{ 490 "id": cty.UnknownVal(cty.String), 491 }) 492 case "aws_instance.c": 493 expected = objectVal(t, schema, map[string]cty.Value{ 494 "id": cty.UnknownVal(cty.String), 495 "some_input": cty.UnknownVal(cty.String), 496 "type": cty.StringVal("aws_instance"), 497 }) 498 default: 499 t.Fatal("unknown instance:", i) 500 } 501 502 checkVals(t, expected, ric.After) 503 } 504 } 505 506 func TestContext2Plan_moduleDeadlock(t *testing.T) { 507 testCheckDeadlock(t, func() { 508 m := testModule(t, "plan-module-deadlock") 509 p := testProvider("aws") 510 p.DiffFn = testDiffFn 511 512 ctx := testContext2(t, &ContextOpts{ 513 Config: m, 514 ProviderResolver: providers.ResolverFixed( 515 map[string]providers.Factory{ 516 "aws": testProviderFuncFixed(p), 517 }, 518 ), 519 }) 520 521 plan, err := ctx.Plan() 522 if err != nil { 523 t.Fatalf("err: %s", err) 524 } 525 526 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 527 ty := schema.ImpliedType() 528 529 for _, res := range plan.Changes.Resources { 530 if res.Action != plans.Create { 531 t.Fatalf("expected resource creation, got %s", res.Action) 532 } 533 ric, err := res.Decode(ty) 534 if err != nil { 535 t.Fatal(err) 536 } 537 538 expected := objectVal(t, schema, map[string]cty.Value{ 539 "id": cty.UnknownVal(cty.String), 540 }) 541 switch i := ric.Addr.String(); i { 542 case "module.child.aws_instance.foo[0]": 543 case "module.child.aws_instance.foo[1]": 544 case "module.child.aws_instance.foo[2]": 545 default: 546 t.Fatal("unknown instance:", i) 547 } 548 549 checkVals(t, expected, ric.After) 550 } 551 }) 552 } 553 554 func TestContext2Plan_moduleInput(t *testing.T) { 555 m := testModule(t, "plan-module-input") 556 p := testProvider("aws") 557 p.DiffFn = testDiffFn 558 ctx := testContext2(t, &ContextOpts{ 559 Config: m, 560 ProviderResolver: providers.ResolverFixed( 561 map[string]providers.Factory{ 562 "aws": testProviderFuncFixed(p), 563 }, 564 ), 565 }) 566 567 plan, diags := ctx.Plan() 568 if diags.HasErrors() { 569 t.Fatalf("unexpected errors: %s", diags.Err()) 570 } 571 572 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 573 ty := schema.ImpliedType() 574 575 if len(plan.Changes.Resources) != 2 { 576 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 577 } 578 579 for _, res := range plan.Changes.Resources { 580 if res.Action != plans.Create { 581 t.Fatalf("expected resource creation, got %s", res.Action) 582 } 583 ric, err := res.Decode(ty) 584 if err != nil { 585 t.Fatal(err) 586 } 587 588 var expected cty.Value 589 590 switch i := ric.Addr.String(); i { 591 case "aws_instance.bar": 592 expected = objectVal(t, schema, map[string]cty.Value{ 593 "id": cty.UnknownVal(cty.String), 594 "foo": cty.StringVal("2"), 595 "type": cty.StringVal("aws_instance"), 596 }) 597 case "module.child.aws_instance.foo": 598 expected = objectVal(t, schema, map[string]cty.Value{ 599 "id": cty.UnknownVal(cty.String), 600 "foo": cty.StringVal("42"), 601 "type": cty.StringVal("aws_instance"), 602 }) 603 default: 604 t.Fatal("unknown instance:", i) 605 } 606 607 checkVals(t, expected, ric.After) 608 } 609 } 610 611 func TestContext2Plan_moduleInputComputed(t *testing.T) { 612 m := testModule(t, "plan-module-input-computed") 613 p := testProvider("aws") 614 p.DiffFn = testDiffFn 615 ctx := testContext2(t, &ContextOpts{ 616 Config: m, 617 ProviderResolver: providers.ResolverFixed( 618 map[string]providers.Factory{ 619 "aws": testProviderFuncFixed(p), 620 }, 621 ), 622 }) 623 624 plan, diags := ctx.Plan() 625 if diags.HasErrors() { 626 t.Fatalf("unexpected errors: %s", diags.Err()) 627 } 628 629 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 630 ty := schema.ImpliedType() 631 632 if len(plan.Changes.Resources) != 2 { 633 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 634 } 635 636 for _, res := range plan.Changes.Resources { 637 if res.Action != plans.Create { 638 t.Fatalf("expected resource creation, got %s", res.Action) 639 } 640 ric, err := res.Decode(ty) 641 if err != nil { 642 t.Fatal(err) 643 } 644 645 switch i := ric.Addr.String(); i { 646 case "aws_instance.bar": 647 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 648 "id": cty.UnknownVal(cty.String), 649 "foo": cty.UnknownVal(cty.String), 650 "type": cty.StringVal("aws_instance"), 651 "compute": cty.StringVal("foo"), 652 }), ric.After) 653 case "module.child.aws_instance.foo": 654 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 655 "id": cty.UnknownVal(cty.String), 656 "foo": cty.UnknownVal(cty.String), 657 "type": cty.StringVal("aws_instance"), 658 }), ric.After) 659 default: 660 t.Fatal("unknown instance:", i) 661 } 662 } 663 } 664 665 func TestContext2Plan_moduleInputFromVar(t *testing.T) { 666 m := testModule(t, "plan-module-input-var") 667 p := testProvider("aws") 668 p.DiffFn = testDiffFn 669 ctx := testContext2(t, &ContextOpts{ 670 Config: m, 671 ProviderResolver: providers.ResolverFixed( 672 map[string]providers.Factory{ 673 "aws": testProviderFuncFixed(p), 674 }, 675 ), 676 Variables: InputValues{ 677 "foo": &InputValue{ 678 Value: cty.StringVal("52"), 679 SourceType: ValueFromCaller, 680 }, 681 }, 682 }) 683 684 plan, diags := ctx.Plan() 685 if diags.HasErrors() { 686 t.Fatalf("unexpected errors: %s", diags.Err()) 687 } 688 689 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 690 ty := schema.ImpliedType() 691 692 if len(plan.Changes.Resources) != 2 { 693 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 694 } 695 696 for _, res := range plan.Changes.Resources { 697 if res.Action != plans.Create { 698 t.Fatalf("expected resource creation, got %s", res.Action) 699 } 700 ric, err := res.Decode(ty) 701 if err != nil { 702 t.Fatal(err) 703 } 704 705 switch i := ric.Addr.String(); i { 706 case "aws_instance.bar": 707 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 708 "id": cty.UnknownVal(cty.String), 709 "foo": cty.StringVal("2"), 710 "type": cty.StringVal("aws_instance"), 711 }), ric.After) 712 case "module.child.aws_instance.foo": 713 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 714 "id": cty.UnknownVal(cty.String), 715 "foo": cty.StringVal("52"), 716 "type": cty.StringVal("aws_instance"), 717 }), ric.After) 718 default: 719 t.Fatal("unknown instance:", i) 720 } 721 } 722 } 723 724 func TestContext2Plan_moduleMultiVar(t *testing.T) { 725 m := testModule(t, "plan-module-multi-var") 726 p := testProvider("aws") 727 p.GetSchemaReturn = &ProviderSchema{ 728 ResourceTypes: map[string]*configschema.Block{ 729 "aws_instance": { 730 Attributes: map[string]*configschema.Attribute{ 731 "id": {Type: cty.String, Computed: true}, 732 "foo": {Type: cty.String, Optional: true}, 733 "baz": {Type: cty.String, Optional: true}, 734 }, 735 }, 736 }, 737 } 738 p.DiffFn = testDiffFn 739 740 ctx := testContext2(t, &ContextOpts{ 741 Config: m, 742 ProviderResolver: providers.ResolverFixed( 743 map[string]providers.Factory{ 744 "aws": testProviderFuncFixed(p), 745 }, 746 ), 747 }) 748 749 plan, diags := ctx.Plan() 750 if diags.HasErrors() { 751 t.Fatalf("unexpected errors: %s", diags.Err()) 752 } 753 754 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 755 ty := schema.ImpliedType() 756 757 if len(plan.Changes.Resources) != 5 { 758 t.Fatal("expected 5 changes, got", len(plan.Changes.Resources)) 759 } 760 761 for _, res := range plan.Changes.Resources { 762 if res.Action != plans.Create { 763 t.Fatalf("expected resource creation, got %s", res.Action) 764 } 765 766 ric, err := res.Decode(ty) 767 if err != nil { 768 t.Fatal(err) 769 } 770 771 switch i := ric.Addr.String(); i { 772 case "aws_instance.parent[0]": 773 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 774 "id": cty.UnknownVal(cty.String), 775 }), ric.After) 776 case "aws_instance.parent[1]": 777 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 778 "id": cty.UnknownVal(cty.String), 779 }), ric.After) 780 case "module.child.aws_instance.bar[0]": 781 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 782 "id": cty.UnknownVal(cty.String), 783 "baz": cty.StringVal("baz"), 784 }), ric.After) 785 case "module.child.aws_instance.bar[1]": 786 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 787 "id": cty.UnknownVal(cty.String), 788 "baz": cty.StringVal("baz"), 789 }), ric.After) 790 case "module.child.aws_instance.foo": 791 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 792 "id": cty.UnknownVal(cty.String), 793 "foo": cty.StringVal("baz,baz"), 794 }), ric.After) 795 default: 796 t.Fatal("unknown instance:", i) 797 } 798 } 799 } 800 801 func TestContext2Plan_moduleOrphans(t *testing.T) { 802 m := testModule(t, "plan-modules-remove") 803 p := testProvider("aws") 804 p.DiffFn = testDiffFn 805 s := MustShimLegacyState(&State{ 806 Modules: []*ModuleState{ 807 { 808 Path: []string{"root", "child"}, 809 Resources: map[string]*ResourceState{ 810 "aws_instance.foo": { 811 Type: "aws_instance", 812 Primary: &InstanceState{ 813 ID: "baz", 814 }, 815 Provider: "provider.aws", 816 }, 817 }, 818 }, 819 }, 820 }) 821 ctx := testContext2(t, &ContextOpts{ 822 Config: m, 823 ProviderResolver: providers.ResolverFixed( 824 map[string]providers.Factory{ 825 "aws": testProviderFuncFixed(p), 826 }, 827 ), 828 State: s, 829 }) 830 831 plan, diags := ctx.Plan() 832 if diags.HasErrors() { 833 t.Fatalf("unexpected errors: %s", diags.Err()) 834 } 835 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 836 ty := schema.ImpliedType() 837 838 if len(plan.Changes.Resources) != 2 { 839 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 840 } 841 842 for _, res := range plan.Changes.Resources { 843 844 ric, err := res.Decode(ty) 845 if err != nil { 846 t.Fatal(err) 847 } 848 849 switch i := ric.Addr.String(); i { 850 case "aws_instance.foo": 851 if res.Action != plans.Create { 852 t.Fatalf("expected resource creation, got %s", res.Action) 853 } 854 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 855 "id": cty.UnknownVal(cty.String), 856 "num": cty.NumberIntVal(2), 857 "type": cty.StringVal("aws_instance"), 858 }), ric.After) 859 case "module.child.aws_instance.foo": 860 if res.Action != plans.Delete { 861 t.Fatalf("expected resource delete, got %s", res.Action) 862 } 863 default: 864 t.Fatal("unknown instance:", i) 865 } 866 } 867 868 expectedState := `<no state> 869 module.child: 870 aws_instance.foo: 871 ID = baz 872 provider = provider.aws` 873 874 if ctx.State().String() != expectedState { 875 t.Fatalf("\nexpected state: %q\n\ngot: %q", expectedState, ctx.State().String()) 876 } 877 } 878 879 // https://github.com/hashicorp/terraform-plugin-sdk/issues/3114 880 func TestContext2Plan_moduleOrphansWithProvisioner(t *testing.T) { 881 m := testModule(t, "plan-modules-remove-provisioners") 882 p := testProvider("aws") 883 pr := testProvisioner() 884 p.DiffFn = testDiffFn 885 s := MustShimLegacyState(&State{ 886 Modules: []*ModuleState{ 887 { 888 Path: []string{"root"}, 889 Resources: map[string]*ResourceState{ 890 "aws_instance.top": { 891 Type: "aws_instance", 892 Primary: &InstanceState{ 893 ID: "top", 894 }, 895 }, 896 }, 897 }, 898 { 899 Path: []string{"root", "parent", "childone"}, 900 Resources: map[string]*ResourceState{ 901 "aws_instance.foo": { 902 Type: "aws_instance", 903 Primary: &InstanceState{ 904 ID: "baz", 905 }, 906 Provider: "provider.aws", 907 }, 908 }, 909 }, 910 { 911 Path: []string{"root", "parent", "childtwo"}, 912 Resources: map[string]*ResourceState{ 913 "aws_instance.foo": { 914 Type: "aws_instance", 915 Primary: &InstanceState{ 916 ID: "baz", 917 }, 918 Provider: "provider.aws", 919 }, 920 }, 921 }, 922 }, 923 }) 924 ctx := testContext2(t, &ContextOpts{ 925 Config: m, 926 ProviderResolver: providers.ResolverFixed( 927 map[string]providers.Factory{ 928 "aws": testProviderFuncFixed(p), 929 }, 930 ), 931 Provisioners: map[string]ProvisionerFactory{ 932 "shell": testProvisionerFuncFixed(pr), 933 }, 934 State: s, 935 }) 936 937 plan, diags := ctx.Plan() 938 if diags.HasErrors() { 939 t.Fatalf("unexpected errors: %s", diags.Err()) 940 } 941 942 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 943 ty := schema.ImpliedType() 944 945 if len(plan.Changes.Resources) != 3 { 946 t.Error("expected 3 planned resources, got", len(plan.Changes.Resources)) 947 } 948 949 for _, res := range plan.Changes.Resources { 950 951 ric, err := res.Decode(ty) 952 if err != nil { 953 t.Fatal(err) 954 } 955 956 switch i := ric.Addr.String(); i { 957 case "module.parent.module.childone.aws_instance.foo": 958 if res.Action != plans.Delete { 959 t.Fatalf("expected resource Delete, got %s", res.Action) 960 } 961 case "module.parent.module.childtwo.aws_instance.foo": 962 if res.Action != plans.Delete { 963 t.Fatalf("expected resource Delete, got %s", res.Action) 964 } 965 case "aws_instance.top": 966 if res.Action != plans.NoOp { 967 t.Fatal("expected no changes, got", res.Action) 968 } 969 default: 970 t.Fatalf("unknown instance: %s\nafter: %#v", i, hcl2shim.ConfigValueFromHCL2(ric.After)) 971 } 972 } 973 974 expectedState := `aws_instance.top: 975 ID = top 976 provider = provider.aws 977 978 module.parent.childone: 979 aws_instance.foo: 980 ID = baz 981 provider = provider.aws 982 module.parent.childtwo: 983 aws_instance.foo: 984 ID = baz 985 provider = provider.aws` 986 987 if expectedState != ctx.State().String() { 988 t.Fatalf("\nexpect state: %q\ngot state: %q\n", expectedState, ctx.State().String()) 989 } 990 } 991 992 func TestContext2Plan_moduleProviderInherit(t *testing.T) { 993 var l sync.Mutex 994 var calls []string 995 996 m := testModule(t, "plan-module-provider-inherit") 997 ctx := testContext2(t, &ContextOpts{ 998 Config: m, 999 ProviderResolver: providers.ResolverFixed( 1000 map[string]providers.Factory{ 1001 "aws": func() (providers.Interface, error) { 1002 l.Lock() 1003 defer l.Unlock() 1004 1005 p := testProvider("aws") 1006 p.GetSchemaReturn = &ProviderSchema{ 1007 Provider: &configschema.Block{ 1008 Attributes: map[string]*configschema.Attribute{ 1009 "from": {Type: cty.String, Optional: true}, 1010 }, 1011 }, 1012 ResourceTypes: map[string]*configschema.Block{ 1013 "aws_instance": { 1014 Attributes: map[string]*configschema.Attribute{ 1015 "from": {Type: cty.String, Optional: true}, 1016 }, 1017 }, 1018 }, 1019 } 1020 p.ConfigureFn = func(c *ResourceConfig) error { 1021 if v, ok := c.Get("from"); !ok || v.(string) != "root" { 1022 return fmt.Errorf("bad") 1023 } 1024 1025 return nil 1026 } 1027 p.DiffFn = func( 1028 info *InstanceInfo, 1029 state *InstanceState, 1030 c *ResourceConfig) (*InstanceDiff, error) { 1031 v, _ := c.Get("from") 1032 1033 l.Lock() 1034 defer l.Unlock() 1035 calls = append(calls, v.(string)) 1036 return testDiffFn(info, state, c) 1037 } 1038 return p, nil 1039 }, 1040 }, 1041 ), 1042 }) 1043 1044 _, err := ctx.Plan() 1045 if err != nil { 1046 t.Fatalf("err: %s", err) 1047 } 1048 1049 actual := calls 1050 sort.Strings(actual) 1051 expected := []string{"child", "root"} 1052 if !reflect.DeepEqual(actual, expected) { 1053 t.Fatalf("bad: %#v", actual) 1054 } 1055 } 1056 1057 // This tests (for GH-11282) that deeply nested modules properly inherit 1058 // configuration. 1059 func TestContext2Plan_moduleProviderInheritDeep(t *testing.T) { 1060 var l sync.Mutex 1061 1062 m := testModule(t, "plan-module-provider-inherit-deep") 1063 ctx := testContext2(t, &ContextOpts{ 1064 Config: m, 1065 ProviderResolver: providers.ResolverFixed( 1066 map[string]providers.Factory{ 1067 "aws": func() (providers.Interface, error) { 1068 l.Lock() 1069 defer l.Unlock() 1070 1071 var from string 1072 p := testProvider("aws") 1073 1074 p.GetSchemaReturn = &ProviderSchema{ 1075 Provider: &configschema.Block{ 1076 Attributes: map[string]*configschema.Attribute{ 1077 "from": {Type: cty.String, Optional: true}, 1078 }, 1079 }, 1080 ResourceTypes: map[string]*configschema.Block{ 1081 "aws_instance": { 1082 Attributes: map[string]*configschema.Attribute{}, 1083 }, 1084 }, 1085 } 1086 1087 p.ConfigureFn = func(c *ResourceConfig) error { 1088 v, ok := c.Get("from") 1089 if !ok || v.(string) != "root" { 1090 return fmt.Errorf("bad") 1091 } 1092 1093 from = v.(string) 1094 return nil 1095 } 1096 1097 p.DiffFn = func( 1098 info *InstanceInfo, 1099 state *InstanceState, 1100 c *ResourceConfig) (*InstanceDiff, error) { 1101 if from != "root" { 1102 return nil, fmt.Errorf("bad resource") 1103 } 1104 1105 return testDiffFn(info, state, c) 1106 } 1107 return p, nil 1108 }, 1109 }, 1110 ), 1111 }) 1112 1113 _, err := ctx.Plan() 1114 if err != nil { 1115 t.Fatalf("err: %s", err) 1116 } 1117 } 1118 1119 func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { 1120 var l sync.Mutex 1121 var calls []string 1122 1123 m := testModule(t, "plan-module-provider-defaults-var") 1124 ctx := testContext2(t, &ContextOpts{ 1125 Config: m, 1126 ProviderResolver: providers.ResolverFixed( 1127 map[string]providers.Factory{ 1128 "aws": func() (providers.Interface, error) { 1129 l.Lock() 1130 defer l.Unlock() 1131 1132 p := testProvider("aws") 1133 p.GetSchemaReturn = &ProviderSchema{ 1134 Provider: &configschema.Block{ 1135 Attributes: map[string]*configschema.Attribute{ 1136 "to": {Type: cty.String, Optional: true}, 1137 "from": {Type: cty.String, Optional: true}, 1138 }, 1139 }, 1140 ResourceTypes: map[string]*configschema.Block{ 1141 "aws_instance": { 1142 Attributes: map[string]*configschema.Attribute{ 1143 "from": {Type: cty.String, Optional: true}, 1144 }, 1145 }, 1146 }, 1147 } 1148 p.ConfigureFn = func(c *ResourceConfig) error { 1149 var buf bytes.Buffer 1150 if v, ok := c.Get("from"); ok { 1151 buf.WriteString(v.(string) + "\n") 1152 } 1153 if v, ok := c.Get("to"); ok { 1154 buf.WriteString(v.(string) + "\n") 1155 } 1156 1157 l.Lock() 1158 defer l.Unlock() 1159 calls = append(calls, buf.String()) 1160 return nil 1161 } 1162 p.DiffFn = testDiffFn 1163 return p, nil 1164 }, 1165 }, 1166 ), 1167 Variables: InputValues{ 1168 "foo": &InputValue{ 1169 Value: cty.StringVal("root"), 1170 SourceType: ValueFromCaller, 1171 }, 1172 }, 1173 }) 1174 1175 _, err := ctx.Plan() 1176 if err != nil { 1177 t.Fatalf("err: %s", err) 1178 } 1179 1180 expected := []string{ 1181 "child\nchild\n", 1182 "root\n", 1183 } 1184 sort.Strings(calls) 1185 if !reflect.DeepEqual(calls, expected) { 1186 t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, calls) 1187 } 1188 } 1189 1190 func TestContext2Plan_moduleProviderVar(t *testing.T) { 1191 m := testModule(t, "plan-module-provider-var") 1192 p := testProvider("aws") 1193 p.GetSchemaReturn = &ProviderSchema{ 1194 Provider: &configschema.Block{ 1195 Attributes: map[string]*configschema.Attribute{ 1196 "value": {Type: cty.String, Optional: true}, 1197 }, 1198 }, 1199 ResourceTypes: map[string]*configschema.Block{ 1200 "aws_instance": { 1201 Attributes: map[string]*configschema.Attribute{ 1202 "value": {Type: cty.String, Optional: true}, 1203 }, 1204 }, 1205 }, 1206 } 1207 p.DiffFn = testDiffFn 1208 1209 ctx := testContext2(t, &ContextOpts{ 1210 Config: m, 1211 ProviderResolver: providers.ResolverFixed( 1212 map[string]providers.Factory{ 1213 "aws": testProviderFuncFixed(p), 1214 }, 1215 ), 1216 }) 1217 1218 plan, diags := ctx.Plan() 1219 if diags.HasErrors() { 1220 t.Fatalf("unexpected errors: %s", diags.Err()) 1221 } 1222 1223 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 1224 ty := schema.ImpliedType() 1225 1226 if len(plan.Changes.Resources) != 1 { 1227 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 1228 } 1229 1230 for _, res := range plan.Changes.Resources { 1231 if res.Action != plans.Create { 1232 t.Fatalf("expected resource creation, got %s", res.Action) 1233 } 1234 ric, err := res.Decode(ty) 1235 if err != nil { 1236 t.Fatal(err) 1237 } 1238 1239 switch i := ric.Addr.String(); i { 1240 case "module.child.aws_instance.test": 1241 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 1242 "value": cty.StringVal("hello"), 1243 }), ric.After) 1244 default: 1245 t.Fatal("unknown instance:", i) 1246 } 1247 } 1248 } 1249 1250 func TestContext2Plan_moduleVar(t *testing.T) { 1251 m := testModule(t, "plan-module-var") 1252 p := testProvider("aws") 1253 p.DiffFn = testDiffFn 1254 ctx := testContext2(t, &ContextOpts{ 1255 Config: m, 1256 ProviderResolver: providers.ResolverFixed( 1257 map[string]providers.Factory{ 1258 "aws": testProviderFuncFixed(p), 1259 }, 1260 ), 1261 }) 1262 1263 plan, diags := ctx.Plan() 1264 if diags.HasErrors() { 1265 t.Fatalf("unexpected errors: %s", diags.Err()) 1266 } 1267 1268 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 1269 ty := schema.ImpliedType() 1270 1271 if len(plan.Changes.Resources) != 2 { 1272 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 1273 } 1274 1275 for _, res := range plan.Changes.Resources { 1276 if res.Action != plans.Create { 1277 t.Fatalf("expected resource creation, got %s", res.Action) 1278 } 1279 ric, err := res.Decode(ty) 1280 if err != nil { 1281 t.Fatal(err) 1282 } 1283 1284 var expected cty.Value 1285 1286 switch i := ric.Addr.String(); i { 1287 case "aws_instance.bar": 1288 expected = objectVal(t, schema, map[string]cty.Value{ 1289 "id": cty.UnknownVal(cty.String), 1290 "foo": cty.StringVal("2"), 1291 "type": cty.StringVal("aws_instance"), 1292 }) 1293 case "module.child.aws_instance.foo": 1294 expected = objectVal(t, schema, map[string]cty.Value{ 1295 "id": cty.UnknownVal(cty.String), 1296 "num": cty.NumberIntVal(2), 1297 "type": cty.StringVal("aws_instance"), 1298 }) 1299 default: 1300 t.Fatal("unknown instance:", i) 1301 } 1302 1303 checkVals(t, expected, ric.After) 1304 } 1305 } 1306 1307 func TestContext2Plan_moduleVarWrongTypeBasic(t *testing.T) { 1308 m := testModule(t, "plan-module-wrong-var-type") 1309 p := testProvider("aws") 1310 p.DiffFn = testDiffFn 1311 ctx := testContext2(t, &ContextOpts{ 1312 Config: m, 1313 ProviderResolver: providers.ResolverFixed( 1314 map[string]providers.Factory{ 1315 "aws": testProviderFuncFixed(p), 1316 }, 1317 ), 1318 }) 1319 1320 _, diags := ctx.Plan() 1321 if !diags.HasErrors() { 1322 t.Fatalf("succeeded; want errors") 1323 } 1324 } 1325 1326 func TestContext2Plan_moduleVarWrongTypeNested(t *testing.T) { 1327 m := testModule(t, "plan-module-wrong-var-type-nested") 1328 p := testProvider("null") 1329 p.DiffFn = testDiffFn 1330 ctx := testContext2(t, &ContextOpts{ 1331 Config: m, 1332 ProviderResolver: providers.ResolverFixed( 1333 map[string]providers.Factory{ 1334 "null": testProviderFuncFixed(p), 1335 }, 1336 ), 1337 }) 1338 1339 _, diags := ctx.Plan() 1340 if !diags.HasErrors() { 1341 t.Fatalf("succeeded; want errors") 1342 } 1343 } 1344 1345 func TestContext2Plan_moduleVarWithDefaultValue(t *testing.T) { 1346 m := testModule(t, "plan-module-var-with-default-value") 1347 p := testProvider("null") 1348 p.DiffFn = testDiffFn 1349 ctx := testContext2(t, &ContextOpts{ 1350 Config: m, 1351 ProviderResolver: providers.ResolverFixed( 1352 map[string]providers.Factory{ 1353 "null": testProviderFuncFixed(p), 1354 }, 1355 ), 1356 }) 1357 1358 _, diags := ctx.Plan() 1359 if diags.HasErrors() { 1360 t.Fatalf("unexpected errors: %s", diags.Err()) 1361 } 1362 } 1363 1364 func TestContext2Plan_moduleVarComputed(t *testing.T) { 1365 m := testModule(t, "plan-module-var-computed") 1366 p := testProvider("aws") 1367 p.DiffFn = testDiffFn 1368 ctx := testContext2(t, &ContextOpts{ 1369 Config: m, 1370 ProviderResolver: providers.ResolverFixed( 1371 map[string]providers.Factory{ 1372 "aws": testProviderFuncFixed(p), 1373 }, 1374 ), 1375 }) 1376 1377 plan, diags := ctx.Plan() 1378 if diags.HasErrors() { 1379 t.Fatalf("unexpected errors: %s", diags.Err()) 1380 } 1381 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 1382 ty := schema.ImpliedType() 1383 1384 if len(plan.Changes.Resources) != 2 { 1385 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 1386 } 1387 1388 for _, res := range plan.Changes.Resources { 1389 if res.Action != plans.Create { 1390 t.Fatalf("expected resource creation, got %s", res.Action) 1391 } 1392 ric, err := res.Decode(ty) 1393 if err != nil { 1394 t.Fatal(err) 1395 } 1396 1397 switch i := ric.Addr.String(); i { 1398 case "aws_instance.bar": 1399 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 1400 "id": cty.UnknownVal(cty.String), 1401 "foo": cty.UnknownVal(cty.String), 1402 "type": cty.StringVal("aws_instance"), 1403 }), ric.After) 1404 case "module.child.aws_instance.foo": 1405 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 1406 "id": cty.UnknownVal(cty.String), 1407 "foo": cty.UnknownVal(cty.String), 1408 "type": cty.StringVal("aws_instance"), 1409 "compute": cty.StringVal("foo"), 1410 }), ric.After) 1411 default: 1412 t.Fatal("unknown instance:", i) 1413 } 1414 } 1415 } 1416 1417 func TestContext2Plan_preventDestroy_bad(t *testing.T) { 1418 m := testModule(t, "plan-prevent-destroy-bad") 1419 p := testProvider("aws") 1420 p.DiffFn = testDiffFn 1421 ctx := testContext2(t, &ContextOpts{ 1422 Config: m, 1423 ProviderResolver: providers.ResolverFixed( 1424 map[string]providers.Factory{ 1425 "aws": testProviderFuncFixed(p), 1426 }, 1427 ), 1428 State: MustShimLegacyState(&State{ 1429 Modules: []*ModuleState{ 1430 { 1431 Path: rootModulePath, 1432 Resources: map[string]*ResourceState{ 1433 "aws_instance.foo": { 1434 Type: "aws_instance", 1435 Primary: &InstanceState{ 1436 ID: "i-abc123", 1437 }, 1438 }, 1439 }, 1440 }, 1441 }, 1442 }), 1443 }) 1444 1445 plan, err := ctx.Plan() 1446 1447 expectedErr := "aws_instance.foo has lifecycle.prevent_destroy" 1448 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 1449 if plan != nil { 1450 t.Logf(legacyDiffComparisonString(plan.Changes)) 1451 } 1452 t.Fatalf("expected err would contain %q\nerr: %s", expectedErr, err) 1453 } 1454 } 1455 1456 func TestContext2Plan_preventDestroy_good(t *testing.T) { 1457 m := testModule(t, "plan-prevent-destroy-good") 1458 p := testProvider("aws") 1459 p.DiffFn = testDiffFn 1460 ctx := testContext2(t, &ContextOpts{ 1461 Config: m, 1462 ProviderResolver: providers.ResolverFixed( 1463 map[string]providers.Factory{ 1464 "aws": testProviderFuncFixed(p), 1465 }, 1466 ), 1467 State: MustShimLegacyState(&State{ 1468 Modules: []*ModuleState{ 1469 { 1470 Path: rootModulePath, 1471 Resources: map[string]*ResourceState{ 1472 "aws_instance.foo": { 1473 Type: "aws_instance", 1474 Primary: &InstanceState{ 1475 ID: "i-abc123", 1476 }, 1477 }, 1478 }, 1479 }, 1480 }, 1481 }), 1482 }) 1483 1484 plan, diags := ctx.Plan() 1485 if diags.HasErrors() { 1486 t.Fatalf("unexpected errors: %s", diags.Err()) 1487 } 1488 1489 if !plan.Changes.Empty() { 1490 t.Fatalf("expected no changes, got %#v\n", plan.Changes) 1491 } 1492 } 1493 1494 func TestContext2Plan_preventDestroy_countBad(t *testing.T) { 1495 m := testModule(t, "plan-prevent-destroy-count-bad") 1496 p := testProvider("aws") 1497 p.DiffFn = testDiffFn 1498 ctx := testContext2(t, &ContextOpts{ 1499 Config: m, 1500 ProviderResolver: providers.ResolverFixed( 1501 map[string]providers.Factory{ 1502 "aws": testProviderFuncFixed(p), 1503 }, 1504 ), 1505 State: MustShimLegacyState(&State{ 1506 Modules: []*ModuleState{ 1507 { 1508 Path: rootModulePath, 1509 Resources: map[string]*ResourceState{ 1510 "aws_instance.foo.0": { 1511 Type: "aws_instance", 1512 Primary: &InstanceState{ 1513 ID: "i-abc123", 1514 }, 1515 }, 1516 "aws_instance.foo.1": { 1517 Type: "aws_instance", 1518 Primary: &InstanceState{ 1519 ID: "i-abc345", 1520 }, 1521 }, 1522 }, 1523 }, 1524 }, 1525 }), 1526 }) 1527 1528 plan, err := ctx.Plan() 1529 1530 expectedErr := "aws_instance.foo[1] has lifecycle.prevent_destroy" 1531 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 1532 if plan != nil { 1533 t.Logf(legacyDiffComparisonString(plan.Changes)) 1534 } 1535 t.Fatalf("expected err would contain %q\nerr: %s", expectedErr, err) 1536 } 1537 } 1538 1539 func TestContext2Plan_preventDestroy_countGood(t *testing.T) { 1540 m := testModule(t, "plan-prevent-destroy-count-good") 1541 p := testProvider("aws") 1542 p.GetSchemaReturn = &ProviderSchema{ 1543 ResourceTypes: map[string]*configschema.Block{ 1544 "aws_instance": { 1545 Attributes: map[string]*configschema.Attribute{ 1546 "current": {Type: cty.String, Optional: true}, 1547 }, 1548 }, 1549 }, 1550 } 1551 p.DiffFn = testDiffFn 1552 1553 ctx := testContext2(t, &ContextOpts{ 1554 Config: m, 1555 ProviderResolver: providers.ResolverFixed( 1556 map[string]providers.Factory{ 1557 "aws": testProviderFuncFixed(p), 1558 }, 1559 ), 1560 State: MustShimLegacyState(&State{ 1561 Modules: []*ModuleState{ 1562 { 1563 Path: rootModulePath, 1564 Resources: map[string]*ResourceState{ 1565 "aws_instance.foo.0": { 1566 Type: "aws_instance", 1567 Primary: &InstanceState{ 1568 ID: "i-abc123", 1569 }, 1570 }, 1571 "aws_instance.foo.1": { 1572 Type: "aws_instance", 1573 Primary: &InstanceState{ 1574 ID: "i-abc345", 1575 }, 1576 }, 1577 }, 1578 }, 1579 }, 1580 }), 1581 }) 1582 1583 plan, diags := ctx.Plan() 1584 if diags.HasErrors() { 1585 t.Fatalf("unexpected errors: %s", diags.Err()) 1586 } 1587 1588 if plan.Changes.Empty() { 1589 t.Fatalf("Expected non-empty plan, got %s", legacyDiffComparisonString(plan.Changes)) 1590 } 1591 } 1592 1593 func TestContext2Plan_preventDestroy_countGoodNoChange(t *testing.T) { 1594 m := testModule(t, "plan-prevent-destroy-count-good") 1595 p := testProvider("aws") 1596 p.GetSchemaReturn = &ProviderSchema{ 1597 ResourceTypes: map[string]*configschema.Block{ 1598 "aws_instance": { 1599 Attributes: map[string]*configschema.Attribute{ 1600 "current": {Type: cty.String, Optional: true}, 1601 }, 1602 }, 1603 }, 1604 } 1605 p.DiffFn = testDiffFn 1606 1607 ctx := testContext2(t, &ContextOpts{ 1608 Config: m, 1609 ProviderResolver: providers.ResolverFixed( 1610 map[string]providers.Factory{ 1611 "aws": testProviderFuncFixed(p), 1612 }, 1613 ), 1614 State: MustShimLegacyState(&State{ 1615 Modules: []*ModuleState{ 1616 { 1617 Path: rootModulePath, 1618 Resources: map[string]*ResourceState{ 1619 "aws_instance.foo.0": { 1620 Type: "aws_instance", 1621 Primary: &InstanceState{ 1622 ID: "i-abc123", 1623 Attributes: map[string]string{ 1624 "current": "0", 1625 "type": "aws_instance", 1626 }, 1627 }, 1628 }, 1629 }, 1630 }, 1631 }, 1632 }), 1633 }) 1634 1635 plan, diags := ctx.Plan() 1636 if diags.HasErrors() { 1637 t.Fatalf("unexpected errors: %s", diags.Err()) 1638 } 1639 1640 if !plan.Changes.Empty() { 1641 t.Fatalf("Expected empty plan, got %s", legacyDiffComparisonString(plan.Changes)) 1642 } 1643 } 1644 1645 func TestContext2Plan_preventDestroy_destroyPlan(t *testing.T) { 1646 m := testModule(t, "plan-prevent-destroy-good") 1647 p := testProvider("aws") 1648 p.DiffFn = testDiffFn 1649 ctx := testContext2(t, &ContextOpts{ 1650 Config: m, 1651 ProviderResolver: providers.ResolverFixed( 1652 map[string]providers.Factory{ 1653 "aws": testProviderFuncFixed(p), 1654 }, 1655 ), 1656 State: MustShimLegacyState(&State{ 1657 Modules: []*ModuleState{ 1658 { 1659 Path: rootModulePath, 1660 Resources: map[string]*ResourceState{ 1661 "aws_instance.foo": { 1662 Type: "aws_instance", 1663 Primary: &InstanceState{ 1664 ID: "i-abc123", 1665 }, 1666 }, 1667 }, 1668 }, 1669 }, 1670 }), 1671 Destroy: true, 1672 }) 1673 1674 plan, diags := ctx.Plan() 1675 1676 expectedErr := "aws_instance.foo has lifecycle.prevent_destroy" 1677 if !strings.Contains(fmt.Sprintf("%s", diags.Err()), expectedErr) { 1678 if plan != nil { 1679 t.Logf(legacyDiffComparisonString(plan.Changes)) 1680 } 1681 t.Fatalf("expected err would contain %q\nerr: %s", expectedErr, diags.Err()) 1682 } 1683 } 1684 1685 func TestContext2Plan_provisionerCycle(t *testing.T) { 1686 m := testModule(t, "plan-provisioner-cycle") 1687 p := testProvider("aws") 1688 p.DiffFn = testDiffFn 1689 pr := testProvisioner() 1690 ctx := testContext2(t, &ContextOpts{ 1691 Config: m, 1692 ProviderResolver: providers.ResolverFixed( 1693 map[string]providers.Factory{ 1694 "aws": testProviderFuncFixed(p), 1695 }, 1696 ), 1697 Provisioners: map[string]ProvisionerFactory{ 1698 "local-exec": testProvisionerFuncFixed(pr), 1699 }, 1700 }) 1701 1702 _, diags := ctx.Plan() 1703 if !diags.HasErrors() { 1704 t.Fatalf("succeeded; want errors") 1705 } 1706 } 1707 1708 func TestContext2Plan_computed(t *testing.T) { 1709 m := testModule(t, "plan-computed") 1710 p := testProvider("aws") 1711 p.DiffFn = testDiffFn 1712 ctx := testContext2(t, &ContextOpts{ 1713 Config: m, 1714 ProviderResolver: providers.ResolverFixed( 1715 map[string]providers.Factory{ 1716 "aws": testProviderFuncFixed(p), 1717 }, 1718 ), 1719 }) 1720 1721 plan, diags := ctx.Plan() 1722 if diags.HasErrors() { 1723 t.Fatalf("unexpected errors: %s", diags.Err()) 1724 } 1725 1726 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 1727 ty := schema.ImpliedType() 1728 1729 if len(plan.Changes.Resources) != 2 { 1730 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 1731 } 1732 1733 for _, res := range plan.Changes.Resources { 1734 if res.Action != plans.Create { 1735 t.Fatalf("expected resource creation, got %s", res.Action) 1736 } 1737 ric, err := res.Decode(ty) 1738 if err != nil { 1739 t.Fatal(err) 1740 } 1741 1742 switch i := ric.Addr.String(); i { 1743 case "aws_instance.bar": 1744 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 1745 "id": cty.UnknownVal(cty.String), 1746 "foo": cty.UnknownVal(cty.String), 1747 "type": cty.StringVal("aws_instance"), 1748 }), ric.After) 1749 case "aws_instance.foo": 1750 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 1751 "id": cty.UnknownVal(cty.String), 1752 "foo": cty.UnknownVal(cty.String), 1753 "num": cty.NumberIntVal(2), 1754 "type": cty.StringVal("aws_instance"), 1755 "compute": cty.StringVal("foo"), 1756 }), ric.After) 1757 default: 1758 t.Fatal("unknown instance:", i) 1759 } 1760 } 1761 } 1762 1763 func TestContext2Plan_blockNestingGroup(t *testing.T) { 1764 m := testModule(t, "plan-block-nesting-group") 1765 p := testProvider("test") 1766 p.GetSchemaReturn = &ProviderSchema{ 1767 ResourceTypes: map[string]*configschema.Block{ 1768 "test": { 1769 BlockTypes: map[string]*configschema.NestedBlock{ 1770 "blah": { 1771 Nesting: configschema.NestingGroup, 1772 Block: configschema.Block{ 1773 Attributes: map[string]*configschema.Attribute{ 1774 "baz": {Type: cty.String, Required: true}, 1775 }, 1776 }, 1777 }, 1778 }, 1779 }, 1780 }, 1781 } 1782 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 1783 return providers.PlanResourceChangeResponse{ 1784 PlannedState: req.ProposedNewState, 1785 } 1786 } 1787 ctx := testContext2(t, &ContextOpts{ 1788 Config: m, 1789 ProviderResolver: providers.ResolverFixed( 1790 map[string]providers.Factory{ 1791 "test": testProviderFuncFixed(p), 1792 }, 1793 ), 1794 }) 1795 1796 plan, diags := ctx.Plan() 1797 if diags.HasErrors() { 1798 t.Fatalf("unexpected errors: %s", diags.Err()) 1799 } 1800 1801 if got, want := 1, len(plan.Changes.Resources); got != want { 1802 t.Fatalf("wrong number of planned resource changes %d; want %d\n%s", got, want, spew.Sdump(plan.Changes.Resources)) 1803 } 1804 1805 if !p.PlanResourceChangeCalled { 1806 t.Fatalf("PlanResourceChange was not called at all") 1807 } 1808 1809 got := p.PlanResourceChangeRequest 1810 want := providers.PlanResourceChangeRequest{ 1811 TypeName: "test", 1812 1813 // Because block type "blah" is defined as NestingGroup, we get a non-null 1814 // value for it with null nested attributes, rather than the "blah" object 1815 // itself being null, when there's no "blah" block in the config at all. 1816 // 1817 // This represents the situation where the remote service _always_ creates 1818 // a single "blah", regardless of whether the block is present, but when 1819 // the block _is_ present the user can override some aspects of it. The 1820 // absense of the block means "use the defaults", in that case. 1821 Config: cty.ObjectVal(map[string]cty.Value{ 1822 "blah": cty.ObjectVal(map[string]cty.Value{ 1823 "baz": cty.NullVal(cty.String), 1824 }), 1825 }), 1826 ProposedNewState: cty.ObjectVal(map[string]cty.Value{ 1827 "blah": cty.ObjectVal(map[string]cty.Value{ 1828 "baz": cty.NullVal(cty.String), 1829 }), 1830 }), 1831 } 1832 if !cmp.Equal(got, want, valueTrans) { 1833 t.Errorf("wrong PlanResourceChange request\n%s", cmp.Diff(got, want, valueTrans)) 1834 } 1835 } 1836 1837 func TestContext2Plan_computedDataResource(t *testing.T) { 1838 m := testModule(t, "plan-computed-data-resource") 1839 p := testProvider("aws") 1840 p.GetSchemaReturn = &ProviderSchema{ 1841 ResourceTypes: map[string]*configschema.Block{ 1842 "aws_instance": { 1843 Attributes: map[string]*configschema.Attribute{ 1844 "num": {Type: cty.String, Optional: true}, 1845 "compute": {Type: cty.String, Optional: true}, 1846 "foo": {Type: cty.String, Computed: true}, 1847 }, 1848 }, 1849 }, 1850 DataSources: map[string]*configschema.Block{ 1851 "aws_vpc": { 1852 Attributes: map[string]*configschema.Attribute{ 1853 "foo": {Type: cty.String, Optional: true}, 1854 }, 1855 }, 1856 }, 1857 } 1858 p.DiffFn = testDiffFn 1859 1860 ctx := testContext2(t, &ContextOpts{ 1861 Config: m, 1862 ProviderResolver: providers.ResolverFixed( 1863 map[string]providers.Factory{ 1864 "aws": testProviderFuncFixed(p), 1865 }, 1866 ), 1867 }) 1868 1869 plan, diags := ctx.Plan() 1870 if diags.HasErrors() { 1871 t.Fatalf("unexpected errors: %s", diags.Err()) 1872 } 1873 schema := p.GetSchemaReturn.DataSources["aws_vpc"] 1874 ty := schema.ImpliedType() 1875 1876 if rc := plan.Changes.ResourceInstance(addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "aws_instance", Name: "foo"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)); rc == nil { 1877 t.Fatalf("missing diff for aws_instance.foo") 1878 } 1879 rcs := plan.Changes.ResourceInstance(addrs.Resource{ 1880 Mode: addrs.DataResourceMode, 1881 Type: "aws_vpc", 1882 Name: "bar", 1883 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 1884 if rcs == nil { 1885 t.Fatalf("missing diff for data.aws_vpc.bar") 1886 } 1887 1888 rc, err := rcs.Decode(ty) 1889 if err != nil { 1890 t.Fatal(err) 1891 } 1892 1893 checkVals(t, 1894 cty.ObjectVal(map[string]cty.Value{ 1895 "foo": cty.UnknownVal(cty.String), 1896 }), 1897 rc.After, 1898 ) 1899 } 1900 1901 func TestContext2Plan_computedInFunction(t *testing.T) { 1902 m := testModule(t, "plan-computed-in-function") 1903 p := testProvider("aws") 1904 p.GetSchemaReturn = &ProviderSchema{ 1905 ResourceTypes: map[string]*configschema.Block{ 1906 "aws_instance": { 1907 Attributes: map[string]*configschema.Attribute{ 1908 "attr": {Type: cty.Number, Optional: true}, 1909 }, 1910 }, 1911 }, 1912 DataSources: map[string]*configschema.Block{ 1913 "aws_data_source": { 1914 Attributes: map[string]*configschema.Attribute{ 1915 "computed": {Type: cty.List(cty.String), Computed: true}, 1916 }, 1917 }, 1918 }, 1919 } 1920 p.DiffFn = testDiffFn 1921 p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ 1922 State: cty.ObjectVal(map[string]cty.Value{ 1923 "computed": cty.ListVal([]cty.Value{ 1924 cty.StringVal("foo"), 1925 }), 1926 }), 1927 } 1928 1929 ctx := testContext2(t, &ContextOpts{ 1930 Config: m, 1931 ProviderResolver: providers.ResolverFixed( 1932 map[string]providers.Factory{ 1933 "aws": testProviderFuncFixed(p), 1934 }, 1935 ), 1936 }) 1937 1938 diags := ctx.Validate() 1939 assertNoErrors(t, diags) 1940 1941 state, diags := ctx.Refresh() // data resource is read in this step 1942 assertNoErrors(t, diags) 1943 1944 if !p.ReadDataSourceCalled { 1945 t.Fatalf("ReadDataSource was not called on provider during refresh; should've been called") 1946 } 1947 p.ReadDataSourceCalled = false // reset for next call 1948 1949 t.Logf("state after refresh:\n%s", state) 1950 1951 _, diags = ctx.Plan() // should do nothing with data resource in this step, since it was already read 1952 assertNoErrors(t, diags) 1953 1954 if p.ReadDataSourceCalled { 1955 t.Fatalf("ReadDataSource was called on provider during plan; should not have been called") 1956 } 1957 1958 } 1959 1960 func TestContext2Plan_computedDataCountResource(t *testing.T) { 1961 m := testModule(t, "plan-computed-data-count") 1962 p := testProvider("aws") 1963 p.GetSchemaReturn = &ProviderSchema{ 1964 ResourceTypes: map[string]*configschema.Block{ 1965 "aws_instance": { 1966 Attributes: map[string]*configschema.Attribute{ 1967 "num": {Type: cty.String, Optional: true}, 1968 "compute": {Type: cty.String, Optional: true}, 1969 "foo": {Type: cty.String, Computed: true}, 1970 }, 1971 }, 1972 }, 1973 DataSources: map[string]*configschema.Block{ 1974 "aws_vpc": { 1975 Attributes: map[string]*configschema.Attribute{ 1976 "foo": {Type: cty.String, Optional: true}, 1977 }, 1978 }, 1979 }, 1980 } 1981 p.DiffFn = testDiffFn 1982 1983 ctx := testContext2(t, &ContextOpts{ 1984 Config: m, 1985 ProviderResolver: providers.ResolverFixed( 1986 map[string]providers.Factory{ 1987 "aws": testProviderFuncFixed(p), 1988 }, 1989 ), 1990 }) 1991 1992 plan, diags := ctx.Plan() 1993 if diags.HasErrors() { 1994 t.Fatalf("unexpected errors: %s", diags.Err()) 1995 } 1996 1997 // make sure we created 3 "bar"s 1998 for i := 0; i < 3; i++ { 1999 addr := addrs.Resource{ 2000 Mode: addrs.DataResourceMode, 2001 Type: "aws_vpc", 2002 Name: "bar", 2003 }.Instance(addrs.IntKey(i)).Absolute(addrs.RootModuleInstance) 2004 2005 if rcs := plan.Changes.ResourceInstance(addr); rcs == nil { 2006 t.Fatalf("missing changes for %s", addr) 2007 } 2008 } 2009 } 2010 2011 func TestContext2Plan_localValueCount(t *testing.T) { 2012 m := testModule(t, "plan-local-value-count") 2013 p := testProvider("test") 2014 p.DiffFn = testDiffFn 2015 ctx := testContext2(t, &ContextOpts{ 2016 Config: m, 2017 ProviderResolver: providers.ResolverFixed( 2018 map[string]providers.Factory{ 2019 "test": testProviderFuncFixed(p), 2020 }, 2021 ), 2022 }) 2023 2024 plan, diags := ctx.Plan() 2025 if diags.HasErrors() { 2026 t.Fatalf("unexpected errors: %s", diags.Err()) 2027 } 2028 2029 // make sure we created 3 "foo"s 2030 for i := 0; i < 3; i++ { 2031 addr := addrs.Resource{ 2032 Mode: addrs.ManagedResourceMode, 2033 Type: "test_resource", 2034 Name: "foo", 2035 }.Instance(addrs.IntKey(i)).Absolute(addrs.RootModuleInstance) 2036 2037 if rcs := plan.Changes.ResourceInstance(addr); rcs == nil { 2038 t.Fatalf("missing changes for %s", addr) 2039 } 2040 } 2041 } 2042 2043 func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { 2044 m := testModule(t, "plan-data-resource-becomes-computed") 2045 p := testProvider("aws") 2046 2047 p.GetSchemaReturn = &ProviderSchema{ 2048 ResourceTypes: map[string]*configschema.Block{ 2049 "aws_instance": { 2050 Attributes: map[string]*configschema.Attribute{ 2051 "foo": {Type: cty.String, Optional: true}, 2052 "computed": {Type: cty.String, Computed: true}, 2053 }, 2054 }, 2055 }, 2056 DataSources: map[string]*configschema.Block{ 2057 "aws_data_source": { 2058 Attributes: map[string]*configschema.Attribute{ 2059 "foo": {Type: cty.String, Optional: true}, 2060 }, 2061 }, 2062 }, 2063 } 2064 2065 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 2066 fooVal := req.ProposedNewState.GetAttr("foo") 2067 return providers.PlanResourceChangeResponse{ 2068 PlannedState: cty.ObjectVal(map[string]cty.Value{ 2069 "foo": fooVal, 2070 "computed": cty.UnknownVal(cty.String), 2071 }), 2072 PlannedPrivate: req.PriorPrivate, 2073 } 2074 } 2075 2076 schema := p.GetSchemaReturn.DataSources["aws_data_source"] 2077 ty := schema.ImpliedType() 2078 2079 p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ 2080 // This should not be called, because the configuration for the 2081 // data resource contains an unknown value for "foo". 2082 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("ReadDataSource called, but should not have been")), 2083 } 2084 2085 ctx := testContext2(t, &ContextOpts{ 2086 Config: m, 2087 ProviderResolver: providers.ResolverFixed( 2088 map[string]providers.Factory{ 2089 "aws": testProviderFuncFixed(p), 2090 }, 2091 ), 2092 State: MustShimLegacyState(&State{ 2093 Modules: []*ModuleState{ 2094 { 2095 Path: rootModulePath, 2096 Resources: map[string]*ResourceState{ 2097 "data.aws_data_source.foo": { 2098 Type: "aws_data_source", 2099 Primary: &InstanceState{ 2100 ID: "i-abc123", 2101 Attributes: map[string]string{ 2102 "id": "i-abc123", 2103 "foo": "baz", 2104 }, 2105 }, 2106 }, 2107 }, 2108 }, 2109 }, 2110 }), 2111 }) 2112 2113 _, diags := ctx.Refresh() 2114 if diags.HasErrors() { 2115 t.Fatalf("unexpected errors during refresh: %s", diags.Err()) 2116 } 2117 2118 plan, diags := ctx.Plan() 2119 if diags.HasErrors() { 2120 t.Fatalf("unexpected errors during plan: %s", diags.Err()) 2121 } 2122 2123 rcs := plan.Changes.ResourceInstance(addrs.Resource{ 2124 Mode: addrs.DataResourceMode, 2125 Type: "aws_data_source", 2126 Name: "foo", 2127 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 2128 if rcs == nil { 2129 t.Logf("full changeset: %s", spew.Sdump(plan.Changes)) 2130 t.Fatalf("missing diff for data.aws_data_resource.foo") 2131 } 2132 2133 rc, err := rcs.Decode(ty) 2134 if err != nil { 2135 t.Fatal(err) 2136 } 2137 2138 // foo should now be unknown 2139 foo := rc.After.GetAttr("foo") 2140 if foo.IsKnown() { 2141 t.Fatalf("foo should be unknown, got %#v", foo) 2142 } 2143 } 2144 2145 func TestContext2Plan_computedList(t *testing.T) { 2146 m := testModule(t, "plan-computed-list") 2147 p := testProvider("aws") 2148 p.GetSchemaReturn = &ProviderSchema{ 2149 ResourceTypes: map[string]*configschema.Block{ 2150 "aws_instance": { 2151 Attributes: map[string]*configschema.Attribute{ 2152 "compute": {Type: cty.String, Optional: true}, 2153 "foo": {Type: cty.String, Optional: true}, 2154 "num": {Type: cty.String, Optional: true}, 2155 "list": {Type: cty.List(cty.String), Computed: true}, 2156 }, 2157 }, 2158 }, 2159 } 2160 p.DiffFn = func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 2161 diff := &InstanceDiff{ 2162 Attributes: map[string]*ResourceAttrDiff{}, 2163 } 2164 2165 computedKeys := map[string]bool{} 2166 for _, k := range c.ComputedKeys { 2167 computedKeys[k] = true 2168 } 2169 2170 compute, _ := c.Raw["compute"].(string) 2171 if compute != "" { 2172 diff.Attributes[compute] = &ResourceAttrDiff{ 2173 Old: "", 2174 New: "", 2175 NewComputed: true, 2176 } 2177 diff.Attributes["compute"] = &ResourceAttrDiff{ 2178 Old: "", 2179 New: compute, 2180 } 2181 } 2182 2183 fooOld := s.Attributes["foo"] 2184 fooNew, _ := c.Raw["foo"].(string) 2185 if fooOld != fooNew { 2186 diff.Attributes["foo"] = &ResourceAttrDiff{ 2187 Old: fooOld, 2188 New: fooNew, 2189 NewComputed: computedKeys["foo"], 2190 } 2191 } 2192 2193 numOld := s.Attributes["num"] 2194 numNew, _ := c.Raw["num"].(string) 2195 if numOld != numNew { 2196 diff.Attributes["num"] = &ResourceAttrDiff{ 2197 Old: numOld, 2198 New: numNew, 2199 NewComputed: computedKeys["num"], 2200 } 2201 } 2202 2203 listOld := s.Attributes["list.#"] 2204 if listOld == "" { 2205 diff.Attributes["list.#"] = &ResourceAttrDiff{ 2206 Old: "", 2207 New: "", 2208 NewComputed: true, 2209 } 2210 } 2211 2212 return diff, nil 2213 } 2214 2215 ctx := testContext2(t, &ContextOpts{ 2216 Config: m, 2217 ProviderResolver: providers.ResolverFixed( 2218 map[string]providers.Factory{ 2219 "aws": testProviderFuncFixed(p), 2220 }, 2221 ), 2222 }) 2223 2224 plan, diags := ctx.Plan() 2225 if diags.HasErrors() { 2226 t.Fatalf("unexpected errors: %s", diags.Err()) 2227 } 2228 2229 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2230 ty := schema.ImpliedType() 2231 2232 if len(plan.Changes.Resources) != 2 { 2233 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 2234 } 2235 2236 for _, res := range plan.Changes.Resources { 2237 if res.Action != plans.Create { 2238 t.Fatalf("expected resource creation, got %s", res.Action) 2239 } 2240 ric, err := res.Decode(ty) 2241 if err != nil { 2242 t.Fatal(err) 2243 } 2244 2245 switch i := ric.Addr.String(); i { 2246 case "aws_instance.bar": 2247 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2248 "list": cty.UnknownVal(cty.List(cty.String)), 2249 "foo": cty.UnknownVal(cty.String), 2250 }), ric.After) 2251 case "aws_instance.foo": 2252 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2253 "list": cty.UnknownVal(cty.List(cty.String)), 2254 "num": cty.NumberIntVal(2), 2255 "compute": cty.StringVal("list.#"), 2256 }), ric.After) 2257 default: 2258 t.Fatal("unknown instance:", i) 2259 } 2260 } 2261 } 2262 2263 // GH-8695. This tests that you can index into a computed list on a 2264 // splatted resource. 2265 func TestContext2Plan_computedMultiIndex(t *testing.T) { 2266 m := testModule(t, "plan-computed-multi-index") 2267 p := testProvider("aws") 2268 p.DiffFn = testDiffFn 2269 2270 p.GetSchemaReturn = &ProviderSchema{ 2271 ResourceTypes: map[string]*configschema.Block{ 2272 "aws_instance": { 2273 Attributes: map[string]*configschema.Attribute{ 2274 "compute": {Type: cty.String, Optional: true}, 2275 "foo": {Type: cty.List(cty.String), Optional: true}, 2276 "ip": {Type: cty.List(cty.String), Computed: true}, 2277 }, 2278 }, 2279 }, 2280 } 2281 2282 p.DiffFn = func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 2283 diff := &InstanceDiff{ 2284 Attributes: map[string]*ResourceAttrDiff{}, 2285 } 2286 2287 compute, _ := c.Raw["compute"].(string) 2288 if compute != "" { 2289 diff.Attributes[compute] = &ResourceAttrDiff{ 2290 Old: "", 2291 New: "", 2292 NewComputed: true, 2293 } 2294 diff.Attributes["compute"] = &ResourceAttrDiff{ 2295 Old: "", 2296 New: compute, 2297 } 2298 } 2299 2300 fooOld := s.Attributes["foo"] 2301 fooNew, _ := c.Raw["foo"].(string) 2302 fooComputed := false 2303 for _, k := range c.ComputedKeys { 2304 if k == "foo" { 2305 fooComputed = true 2306 } 2307 } 2308 if fooNew != "" { 2309 diff.Attributes["foo"] = &ResourceAttrDiff{ 2310 Old: fooOld, 2311 New: fooNew, 2312 NewComputed: fooComputed, 2313 } 2314 } 2315 2316 ipOld := s.Attributes["ip"] 2317 ipComputed := ipOld == "" 2318 diff.Attributes["ip"] = &ResourceAttrDiff{ 2319 Old: ipOld, 2320 New: "", 2321 NewComputed: ipComputed, 2322 } 2323 2324 return diff, nil 2325 } 2326 2327 ctx := testContext2(t, &ContextOpts{ 2328 Config: m, 2329 ProviderResolver: providers.ResolverFixed( 2330 map[string]providers.Factory{ 2331 "aws": testProviderFuncFixed(p), 2332 }, 2333 ), 2334 }) 2335 2336 plan, diags := ctx.Plan() 2337 if diags.HasErrors() { 2338 t.Fatalf("unexpected errors: %s", diags.Err()) 2339 } 2340 2341 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2342 ty := schema.ImpliedType() 2343 2344 if len(plan.Changes.Resources) != 3 { 2345 t.Fatal("expected 3 changes, got", len(plan.Changes.Resources)) 2346 } 2347 2348 for _, res := range plan.Changes.Resources { 2349 if res.Action != plans.Create { 2350 t.Fatalf("expected resource creation, got %s", res.Action) 2351 } 2352 ric, err := res.Decode(ty) 2353 if err != nil { 2354 t.Fatal(err) 2355 } 2356 2357 switch i := ric.Addr.String(); i { 2358 case "aws_instance.foo[0]": 2359 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2360 "ip": cty.UnknownVal(cty.List(cty.String)), 2361 "foo": cty.NullVal(cty.List(cty.String)), 2362 "compute": cty.StringVal("ip.#"), 2363 }), ric.After) 2364 case "aws_instance.foo[1]": 2365 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2366 "ip": cty.UnknownVal(cty.List(cty.String)), 2367 "foo": cty.NullVal(cty.List(cty.String)), 2368 "compute": cty.StringVal("ip.#"), 2369 }), ric.After) 2370 case "aws_instance.bar[0]": 2371 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2372 "ip": cty.UnknownVal(cty.List(cty.String)), 2373 "foo": cty.UnknownVal(cty.List(cty.String)), 2374 }), ric.After) 2375 default: 2376 t.Fatal("unknown instance:", i) 2377 } 2378 } 2379 } 2380 2381 func TestContext2Plan_count(t *testing.T) { 2382 m := testModule(t, "plan-count") 2383 p := testProvider("aws") 2384 p.DiffFn = testDiffFn 2385 ctx := testContext2(t, &ContextOpts{ 2386 Config: m, 2387 ProviderResolver: providers.ResolverFixed( 2388 map[string]providers.Factory{ 2389 "aws": testProviderFuncFixed(p), 2390 }, 2391 ), 2392 }) 2393 2394 plan, diags := ctx.Plan() 2395 if diags.HasErrors() { 2396 t.Fatalf("unexpected errors: %s", diags.Err()) 2397 } 2398 2399 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2400 ty := schema.ImpliedType() 2401 2402 if len(plan.Changes.Resources) != 6 { 2403 t.Fatal("expected 6 changes, got", len(plan.Changes.Resources)) 2404 } 2405 2406 for _, res := range plan.Changes.Resources { 2407 if res.Action != plans.Create { 2408 t.Fatalf("expected resource creation, got %s", res.Action) 2409 } 2410 ric, err := res.Decode(ty) 2411 if err != nil { 2412 t.Fatal(err) 2413 } 2414 2415 switch i := ric.Addr.String(); i { 2416 case "aws_instance.bar": 2417 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2418 "id": cty.UnknownVal(cty.String), 2419 "foo": cty.StringVal("foo,foo,foo,foo,foo"), 2420 "type": cty.StringVal("aws_instance"), 2421 }), ric.After) 2422 case "aws_instance.foo[0]": 2423 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2424 "id": cty.UnknownVal(cty.String), 2425 "foo": cty.StringVal("foo"), 2426 "type": cty.StringVal("aws_instance"), 2427 }), ric.After) 2428 case "aws_instance.foo[1]": 2429 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2430 "id": cty.UnknownVal(cty.String), 2431 "foo": cty.StringVal("foo"), 2432 "type": cty.StringVal("aws_instance"), 2433 }), ric.After) 2434 case "aws_instance.foo[2]": 2435 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2436 "id": cty.UnknownVal(cty.String), 2437 "foo": cty.StringVal("foo"), 2438 "type": cty.StringVal("aws_instance"), 2439 }), ric.After) 2440 case "aws_instance.foo[3]": 2441 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2442 "id": cty.UnknownVal(cty.String), 2443 "foo": cty.StringVal("foo"), 2444 "type": cty.StringVal("aws_instance"), 2445 }), ric.After) 2446 case "aws_instance.foo[4]": 2447 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2448 "id": cty.UnknownVal(cty.String), 2449 "foo": cty.StringVal("foo"), 2450 "type": cty.StringVal("aws_instance"), 2451 }), ric.After) 2452 default: 2453 t.Fatal("unknown instance:", i) 2454 } 2455 } 2456 } 2457 2458 func TestContext2Plan_countComputed(t *testing.T) { 2459 m := testModule(t, "plan-count-computed") 2460 p := testProvider("aws") 2461 p.DiffFn = testDiffFn 2462 ctx := testContext2(t, &ContextOpts{ 2463 Config: m, 2464 ProviderResolver: providers.ResolverFixed( 2465 map[string]providers.Factory{ 2466 "aws": testProviderFuncFixed(p), 2467 }, 2468 ), 2469 }) 2470 2471 _, err := ctx.Plan() 2472 if err == nil { 2473 t.Fatal("should error") 2474 } 2475 } 2476 2477 func TestContext2Plan_countComputedModule(t *testing.T) { 2478 m := testModule(t, "plan-count-computed-module") 2479 p := testProvider("aws") 2480 p.DiffFn = testDiffFn 2481 ctx := testContext2(t, &ContextOpts{ 2482 Config: m, 2483 ProviderResolver: providers.ResolverFixed( 2484 map[string]providers.Factory{ 2485 "aws": testProviderFuncFixed(p), 2486 }, 2487 ), 2488 }) 2489 2490 _, err := ctx.Plan() 2491 2492 expectedErr := `The "count" value depends on resource attributes` 2493 if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { 2494 t.Fatalf("expected err would contain %q\nerr: %s\n", 2495 expectedErr, err) 2496 } 2497 } 2498 2499 func TestContext2Plan_countModuleStatic(t *testing.T) { 2500 m := testModule(t, "plan-count-module-static") 2501 p := testProvider("aws") 2502 p.DiffFn = testDiffFn 2503 ctx := testContext2(t, &ContextOpts{ 2504 Config: m, 2505 ProviderResolver: providers.ResolverFixed( 2506 map[string]providers.Factory{ 2507 "aws": testProviderFuncFixed(p), 2508 }, 2509 ), 2510 }) 2511 2512 plan, diags := ctx.Plan() 2513 if diags.HasErrors() { 2514 t.Fatalf("unexpected errors: %s", diags.Err()) 2515 } 2516 2517 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2518 ty := schema.ImpliedType() 2519 2520 if len(plan.Changes.Resources) != 3 { 2521 t.Fatal("expected 3 changes, got", len(plan.Changes.Resources)) 2522 } 2523 2524 for _, res := range plan.Changes.Resources { 2525 if res.Action != plans.Create { 2526 t.Fatalf("expected resource creation, got %s", res.Action) 2527 } 2528 ric, err := res.Decode(ty) 2529 if err != nil { 2530 t.Fatal(err) 2531 } 2532 2533 switch i := ric.Addr.String(); i { 2534 case "module.child.aws_instance.foo[0]": 2535 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2536 "id": cty.UnknownVal(cty.String), 2537 }), ric.After) 2538 case "module.child.aws_instance.foo[1]": 2539 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2540 "id": cty.UnknownVal(cty.String), 2541 }), ric.After) 2542 case "module.child.aws_instance.foo[2]": 2543 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2544 "id": cty.UnknownVal(cty.String), 2545 }), ric.After) 2546 default: 2547 t.Fatal("unknown instance:", i) 2548 } 2549 } 2550 } 2551 2552 func TestContext2Plan_countModuleStaticGrandchild(t *testing.T) { 2553 m := testModule(t, "plan-count-module-static-grandchild") 2554 p := testProvider("aws") 2555 p.DiffFn = testDiffFn 2556 ctx := testContext2(t, &ContextOpts{ 2557 Config: m, 2558 ProviderResolver: providers.ResolverFixed( 2559 map[string]providers.Factory{ 2560 "aws": testProviderFuncFixed(p), 2561 }, 2562 ), 2563 }) 2564 2565 plan, diags := ctx.Plan() 2566 if diags.HasErrors() { 2567 t.Fatalf("unexpected errors: %s", diags.Err()) 2568 } 2569 2570 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2571 ty := schema.ImpliedType() 2572 2573 if len(plan.Changes.Resources) != 3 { 2574 t.Fatal("expected 3 changes, got", len(plan.Changes.Resources)) 2575 } 2576 2577 for _, res := range plan.Changes.Resources { 2578 if res.Action != plans.Create { 2579 t.Fatalf("expected resource creation, got %s", res.Action) 2580 } 2581 ric, err := res.Decode(ty) 2582 if err != nil { 2583 t.Fatal(err) 2584 } 2585 2586 switch i := ric.Addr.String(); i { 2587 case "module.child.module.child.aws_instance.foo[0]": 2588 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2589 "id": cty.UnknownVal(cty.String), 2590 }), ric.After) 2591 case "module.child.module.child.aws_instance.foo[1]": 2592 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2593 "id": cty.UnknownVal(cty.String), 2594 }), ric.After) 2595 case "module.child.module.child.aws_instance.foo[2]": 2596 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2597 "id": cty.UnknownVal(cty.String), 2598 }), ric.After) 2599 default: 2600 t.Fatal("unknown instance:", i) 2601 } 2602 } 2603 } 2604 2605 func TestContext2Plan_countIndex(t *testing.T) { 2606 m := testModule(t, "plan-count-index") 2607 p := testProvider("aws") 2608 p.DiffFn = testDiffFn 2609 ctx := testContext2(t, &ContextOpts{ 2610 Config: m, 2611 ProviderResolver: providers.ResolverFixed( 2612 map[string]providers.Factory{ 2613 "aws": testProviderFuncFixed(p), 2614 }, 2615 ), 2616 }) 2617 2618 plan, diags := ctx.Plan() 2619 if diags.HasErrors() { 2620 t.Fatalf("unexpected errors: %s", diags.Err()) 2621 } 2622 2623 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2624 ty := schema.ImpliedType() 2625 2626 if len(plan.Changes.Resources) != 2 { 2627 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 2628 } 2629 2630 for _, res := range plan.Changes.Resources { 2631 if res.Action != plans.Create { 2632 t.Fatalf("expected resource creation, got %s", res.Action) 2633 } 2634 ric, err := res.Decode(ty) 2635 if err != nil { 2636 t.Fatal(err) 2637 } 2638 2639 switch i := ric.Addr.String(); i { 2640 case "aws_instance.foo[0]": 2641 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2642 "id": cty.UnknownVal(cty.String), 2643 "foo": cty.StringVal("0"), 2644 "type": cty.StringVal("aws_instance"), 2645 }), ric.After) 2646 case "aws_instance.foo[1]": 2647 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2648 "id": cty.UnknownVal(cty.String), 2649 "foo": cty.StringVal("1"), 2650 "type": cty.StringVal("aws_instance"), 2651 }), ric.After) 2652 default: 2653 t.Fatal("unknown instance:", i) 2654 } 2655 } 2656 } 2657 2658 func TestContext2Plan_countVar(t *testing.T) { 2659 m := testModule(t, "plan-count-var") 2660 p := testProvider("aws") 2661 p.DiffFn = testDiffFn 2662 ctx := testContext2(t, &ContextOpts{ 2663 Config: m, 2664 ProviderResolver: providers.ResolverFixed( 2665 map[string]providers.Factory{ 2666 "aws": testProviderFuncFixed(p), 2667 }, 2668 ), 2669 Variables: InputValues{ 2670 "instance_count": &InputValue{ 2671 Value: cty.StringVal("3"), 2672 SourceType: ValueFromCaller, 2673 }, 2674 }, 2675 }) 2676 2677 plan, diags := ctx.Plan() 2678 if diags.HasErrors() { 2679 t.Fatalf("unexpected errors: %s", diags.Err()) 2680 } 2681 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2682 ty := schema.ImpliedType() 2683 2684 if len(plan.Changes.Resources) != 4 { 2685 t.Fatal("expected 4 changes, got", len(plan.Changes.Resources)) 2686 } 2687 2688 for _, res := range plan.Changes.Resources { 2689 if res.Action != plans.Create { 2690 t.Fatalf("expected resource creation, got %s", res.Action) 2691 } 2692 ric, err := res.Decode(ty) 2693 if err != nil { 2694 t.Fatal(err) 2695 } 2696 2697 switch i := ric.Addr.String(); i { 2698 case "aws_instance.bar": 2699 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2700 "id": cty.UnknownVal(cty.String), 2701 "foo": cty.StringVal("foo,foo,foo"), 2702 "type": cty.StringVal("aws_instance"), 2703 }), ric.After) 2704 case "aws_instance.foo[0]": 2705 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2706 "id": cty.UnknownVal(cty.String), 2707 "foo": cty.StringVal("foo"), 2708 "type": cty.StringVal("aws_instance"), 2709 }), ric.After) 2710 case "aws_instance.foo[1]": 2711 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2712 "id": cty.UnknownVal(cty.String), 2713 "foo": cty.StringVal("foo"), 2714 "type": cty.StringVal("aws_instance"), 2715 }), ric.After) 2716 case "aws_instance.foo[2]": 2717 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2718 "id": cty.UnknownVal(cty.String), 2719 "foo": cty.StringVal("foo"), 2720 "type": cty.StringVal("aws_instance"), 2721 }), ric.After) 2722 default: 2723 t.Fatal("unknown instance:", i) 2724 } 2725 } 2726 } 2727 2728 func TestContext2Plan_countZero(t *testing.T) { 2729 m := testModule(t, "plan-count-zero") 2730 p := testProvider("aws") 2731 p.GetSchemaReturn = &ProviderSchema{ 2732 ResourceTypes: map[string]*configschema.Block{ 2733 "aws_instance": { 2734 Attributes: map[string]*configschema.Attribute{ 2735 "foo": {Type: cty.DynamicPseudoType, Optional: true}, 2736 }, 2737 }, 2738 }, 2739 } 2740 2741 // This schema contains a DynamicPseudoType, and therefore can't go through any shim functions 2742 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 2743 resp.PlannedState = req.ProposedNewState 2744 resp.PlannedPrivate = req.PriorPrivate 2745 return resp 2746 } 2747 2748 ctx := testContext2(t, &ContextOpts{ 2749 Config: m, 2750 ProviderResolver: providers.ResolverFixed( 2751 map[string]providers.Factory{ 2752 "aws": testProviderFuncFixed(p), 2753 }, 2754 ), 2755 }) 2756 2757 plan, diags := ctx.Plan() 2758 if diags.HasErrors() { 2759 t.Fatalf("unexpected errors: %s", diags.Err()) 2760 } 2761 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2762 ty := schema.ImpliedType() 2763 2764 if len(plan.Changes.Resources) != 1 { 2765 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 2766 } 2767 2768 res := plan.Changes.Resources[0] 2769 2770 if res.Action != plans.Create { 2771 t.Fatalf("expected resource creation, got %s", res.Action) 2772 } 2773 ric, err := res.Decode(ty) 2774 if err != nil { 2775 t.Fatal(err) 2776 } 2777 2778 expected := cty.TupleVal(nil) 2779 2780 foo := ric.After.GetAttr("foo") 2781 2782 if !cmp.Equal(expected, foo, valueComparer) { 2783 t.Fatal(cmp.Diff(expected, foo, valueComparer)) 2784 } 2785 } 2786 2787 func TestContext2Plan_countOneIndex(t *testing.T) { 2788 m := testModule(t, "plan-count-one-index") 2789 p := testProvider("aws") 2790 p.DiffFn = testDiffFn 2791 ctx := testContext2(t, &ContextOpts{ 2792 Config: m, 2793 ProviderResolver: providers.ResolverFixed( 2794 map[string]providers.Factory{ 2795 "aws": testProviderFuncFixed(p), 2796 }, 2797 ), 2798 }) 2799 2800 plan, diags := ctx.Plan() 2801 if diags.HasErrors() { 2802 t.Fatalf("unexpected errors: %s", diags.Err()) 2803 } 2804 2805 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2806 ty := schema.ImpliedType() 2807 2808 if len(plan.Changes.Resources) != 2 { 2809 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 2810 } 2811 2812 for _, res := range plan.Changes.Resources { 2813 if res.Action != plans.Create { 2814 t.Fatalf("expected resource creation, got %s", res.Action) 2815 } 2816 ric, err := res.Decode(ty) 2817 if err != nil { 2818 t.Fatal(err) 2819 } 2820 2821 switch i := ric.Addr.String(); i { 2822 case "aws_instance.bar": 2823 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2824 "id": cty.UnknownVal(cty.String), 2825 "foo": cty.StringVal("foo"), 2826 "type": cty.StringVal("aws_instance"), 2827 }), ric.After) 2828 case "aws_instance.foo[0]": 2829 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2830 "id": cty.UnknownVal(cty.String), 2831 "foo": cty.StringVal("foo"), 2832 "type": cty.StringVal("aws_instance"), 2833 }), ric.After) 2834 default: 2835 t.Fatal("unknown instance:", i) 2836 } 2837 } 2838 } 2839 2840 func TestContext2Plan_countDecreaseToOne(t *testing.T) { 2841 m := testModule(t, "plan-count-dec") 2842 p := testProvider("aws") 2843 p.DiffFn = testDiffFn 2844 s := MustShimLegacyState(&State{ 2845 Modules: []*ModuleState{ 2846 { 2847 Path: rootModulePath, 2848 Resources: map[string]*ResourceState{ 2849 "aws_instance.foo.0": { 2850 Type: "aws_instance", 2851 Primary: &InstanceState{ 2852 ID: "bar", 2853 Attributes: map[string]string{ 2854 "foo": "foo", 2855 "type": "aws_instance", 2856 }, 2857 }, 2858 }, 2859 "aws_instance.foo.1": { 2860 Type: "aws_instance", 2861 Primary: &InstanceState{ 2862 ID: "bar", 2863 }, 2864 }, 2865 "aws_instance.foo.2": { 2866 Type: "aws_instance", 2867 Primary: &InstanceState{ 2868 ID: "bar", 2869 }, 2870 }, 2871 }, 2872 }, 2873 }, 2874 }) 2875 ctx := testContext2(t, &ContextOpts{ 2876 Config: m, 2877 ProviderResolver: providers.ResolverFixed( 2878 map[string]providers.Factory{ 2879 "aws": testProviderFuncFixed(p), 2880 }, 2881 ), 2882 State: s, 2883 }) 2884 2885 plan, diags := ctx.Plan() 2886 if diags.HasErrors() { 2887 t.Fatalf("unexpected errors: %s", diags.Err()) 2888 } 2889 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2890 ty := schema.ImpliedType() 2891 2892 if len(plan.Changes.Resources) != 4 { 2893 t.Fatal("expected 4 changes, got", len(plan.Changes.Resources)) 2894 } 2895 2896 for _, res := range plan.Changes.Resources { 2897 2898 ric, err := res.Decode(ty) 2899 if err != nil { 2900 t.Fatal(err) 2901 } 2902 2903 switch i := ric.Addr.String(); i { 2904 case "aws_instance.bar": 2905 if res.Action != plans.Create { 2906 t.Fatalf("expected resource create, got %s", res.Action) 2907 } 2908 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 2909 "id": cty.UnknownVal(cty.String), 2910 "foo": cty.StringVal("bar"), 2911 "type": cty.StringVal("aws_instance"), 2912 }), ric.After) 2913 case "aws_instance.foo": 2914 if res.Action != plans.NoOp { 2915 t.Fatalf("resource %s should be unchanged", i) 2916 } 2917 case "aws_instance.foo[1]": 2918 if res.Action != plans.Delete { 2919 t.Fatalf("expected resource delete, got %s", res.Action) 2920 } 2921 case "aws_instance.foo[2]": 2922 if res.Action != plans.Delete { 2923 t.Fatalf("expected resource delete, got %s", res.Action) 2924 } 2925 default: 2926 t.Fatal("unknown instance:", i) 2927 } 2928 } 2929 2930 expectedState := `aws_instance.foo.0: 2931 ID = bar 2932 provider = provider.aws 2933 foo = foo 2934 type = aws_instance 2935 aws_instance.foo.1: 2936 ID = bar 2937 provider = provider.aws 2938 aws_instance.foo.2: 2939 ID = bar 2940 provider = provider.aws` 2941 2942 if ctx.State().String() != expectedState { 2943 t.Fatalf("epected state:\n%q\n\ngot state:\n%q\n", expectedState, ctx.State().String()) 2944 } 2945 } 2946 2947 func TestContext2Plan_countIncreaseFromNotSet(t *testing.T) { 2948 m := testModule(t, "plan-count-inc") 2949 p := testProvider("aws") 2950 p.DiffFn = testDiffFn 2951 s := MustShimLegacyState(&State{ 2952 Modules: []*ModuleState{ 2953 { 2954 Path: rootModulePath, 2955 Resources: map[string]*ResourceState{ 2956 "aws_instance.foo": { 2957 Type: "aws_instance", 2958 Primary: &InstanceState{ 2959 ID: "bar", 2960 Attributes: map[string]string{ 2961 "foo": "foo", 2962 "type": "aws_instance", 2963 }, 2964 }, 2965 }, 2966 }, 2967 }, 2968 }, 2969 }) 2970 ctx := testContext2(t, &ContextOpts{ 2971 Config: m, 2972 ProviderResolver: providers.ResolverFixed( 2973 map[string]providers.Factory{ 2974 "aws": testProviderFuncFixed(p), 2975 }, 2976 ), 2977 State: s, 2978 }) 2979 2980 plan, diags := ctx.Plan() 2981 if diags.HasErrors() { 2982 t.Fatalf("unexpected errors: %s", diags.Err()) 2983 } 2984 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 2985 ty := schema.ImpliedType() 2986 2987 if len(plan.Changes.Resources) != 4 { 2988 t.Fatal("expected 4 changes, got", len(plan.Changes.Resources)) 2989 } 2990 2991 for _, res := range plan.Changes.Resources { 2992 2993 ric, err := res.Decode(ty) 2994 if err != nil { 2995 t.Fatal(err) 2996 } 2997 2998 switch i := ric.Addr.String(); i { 2999 case "aws_instance.bar": 3000 if res.Action != plans.Create { 3001 t.Fatalf("expected resource create, got %s", res.Action) 3002 } 3003 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3004 "id": cty.UnknownVal(cty.String), 3005 "foo": cty.StringVal("bar"), 3006 "type": cty.StringVal("aws_instance"), 3007 }), ric.After) 3008 case "aws_instance.foo[0]": 3009 if res.Action != plans.NoOp { 3010 t.Fatalf("resource %s should be unchanged", i) 3011 } 3012 case "aws_instance.foo[1]": 3013 if res.Action != plans.Create { 3014 t.Fatalf("expected resource create, got %s", res.Action) 3015 } 3016 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3017 "id": cty.UnknownVal(cty.String), 3018 "foo": cty.StringVal("foo"), 3019 "type": cty.StringVal("aws_instance"), 3020 }), ric.After) 3021 case "aws_instance.foo[2]": 3022 if res.Action != plans.Create { 3023 t.Fatalf("expected resource create, got %s", res.Action) 3024 } 3025 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3026 "id": cty.UnknownVal(cty.String), 3027 "foo": cty.StringVal("foo"), 3028 "type": cty.StringVal("aws_instance"), 3029 }), ric.After) 3030 default: 3031 t.Fatal("unknown instance:", i) 3032 } 3033 } 3034 } 3035 3036 func TestContext2Plan_countIncreaseFromOne(t *testing.T) { 3037 m := testModule(t, "plan-count-inc") 3038 p := testProvider("aws") 3039 p.DiffFn = testDiffFn 3040 s := MustShimLegacyState(&State{ 3041 Modules: []*ModuleState{ 3042 { 3043 Path: rootModulePath, 3044 Resources: map[string]*ResourceState{ 3045 "aws_instance.foo.0": { 3046 Type: "aws_instance", 3047 Primary: &InstanceState{ 3048 ID: "bar", 3049 Attributes: map[string]string{ 3050 "foo": "foo", 3051 "type": "aws_instance", 3052 }, 3053 }, 3054 }, 3055 }, 3056 }, 3057 }, 3058 }) 3059 ctx := testContext2(t, &ContextOpts{ 3060 Config: m, 3061 ProviderResolver: providers.ResolverFixed( 3062 map[string]providers.Factory{ 3063 "aws": testProviderFuncFixed(p), 3064 }, 3065 ), 3066 State: s, 3067 }) 3068 3069 plan, diags := ctx.Plan() 3070 if diags.HasErrors() { 3071 t.Fatalf("unexpected errors: %s", diags.Err()) 3072 } 3073 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3074 ty := schema.ImpliedType() 3075 3076 if len(plan.Changes.Resources) != 4 { 3077 t.Fatal("expected 4 changes, got", len(plan.Changes.Resources)) 3078 } 3079 3080 for _, res := range plan.Changes.Resources { 3081 3082 ric, err := res.Decode(ty) 3083 if err != nil { 3084 t.Fatal(err) 3085 } 3086 3087 switch i := ric.Addr.String(); i { 3088 case "aws_instance.bar": 3089 if res.Action != plans.Create { 3090 t.Fatalf("expected resource create, got %s", res.Action) 3091 } 3092 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3093 "id": cty.UnknownVal(cty.String), 3094 "foo": cty.StringVal("bar"), 3095 "type": cty.StringVal("aws_instance"), 3096 }), ric.After) 3097 case "aws_instance.foo[0]": 3098 if res.Action != plans.NoOp { 3099 t.Fatalf("resource %s should be unchanged", i) 3100 } 3101 case "aws_instance.foo[1]": 3102 if res.Action != plans.Create { 3103 t.Fatalf("expected resource create, got %s", res.Action) 3104 } 3105 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3106 "id": cty.UnknownVal(cty.String), 3107 "foo": cty.StringVal("foo"), 3108 "type": cty.StringVal("aws_instance"), 3109 }), ric.After) 3110 case "aws_instance.foo[2]": 3111 if res.Action != plans.Create { 3112 t.Fatalf("expected resource create, got %s", res.Action) 3113 } 3114 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3115 "id": cty.UnknownVal(cty.String), 3116 "foo": cty.StringVal("foo"), 3117 "type": cty.StringVal("aws_instance"), 3118 }), ric.After) 3119 default: 3120 t.Fatal("unknown instance:", i) 3121 } 3122 } 3123 } 3124 3125 // https://github.com/PeoplePerHour/terraform/pull/11 3126 // 3127 // This tests a case where both a "resource" and "resource.0" are in 3128 // the state file, which apparently is a reasonable backwards compatibility 3129 // concern found in the above 3rd party repo. 3130 func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) { 3131 m := testModule(t, "plan-count-inc") 3132 p := testProvider("aws") 3133 p.DiffFn = testDiffFn 3134 s := MustShimLegacyState(&State{ 3135 Modules: []*ModuleState{ 3136 { 3137 Path: rootModulePath, 3138 Resources: map[string]*ResourceState{ 3139 "aws_instance.foo": { 3140 Type: "aws_instance", 3141 Primary: &InstanceState{ 3142 ID: "bar", 3143 Attributes: map[string]string{ 3144 "foo": "foo", 3145 "type": "aws_instance", 3146 }, 3147 }, 3148 }, 3149 "aws_instance.foo.0": { 3150 Type: "aws_instance", 3151 Primary: &InstanceState{ 3152 ID: "bar", 3153 Attributes: map[string]string{ 3154 "foo": "foo", 3155 "type": "aws_instance", 3156 }, 3157 }, 3158 }, 3159 }, 3160 }, 3161 }, 3162 }) 3163 ctx := testContext2(t, &ContextOpts{ 3164 Config: m, 3165 ProviderResolver: providers.ResolverFixed( 3166 map[string]providers.Factory{ 3167 "aws": testProviderFuncFixed(p), 3168 }, 3169 ), 3170 State: s, 3171 }) 3172 3173 plan, diags := ctx.Plan() 3174 if diags.HasErrors() { 3175 t.Fatalf("unexpected errors: %s", diags.Err()) 3176 } 3177 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3178 ty := schema.ImpliedType() 3179 3180 if len(plan.Changes.Resources) != 5 { 3181 t.Fatal("expected 5 changes, got", len(plan.Changes.Resources)) 3182 } 3183 3184 for _, res := range plan.Changes.Resources { 3185 3186 ric, err := res.Decode(ty) 3187 if err != nil { 3188 t.Fatal(err) 3189 } 3190 3191 switch i := ric.Addr.String(); i { 3192 case "aws_instance.bar": 3193 if res.Action != plans.Create { 3194 t.Fatalf("expected resource create, got %s", res.Action) 3195 } 3196 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3197 "id": cty.UnknownVal(cty.String), 3198 "foo": cty.StringVal("bar"), 3199 "type": cty.StringVal("aws_instance"), 3200 }), ric.After) 3201 case "aws_instance.foo": 3202 if res.Action != plans.Delete { 3203 t.Fatalf("resource %s should be removed", i) 3204 } 3205 case "aws_instance.foo[0]": 3206 if res.Action != plans.NoOp { 3207 t.Fatalf("resource %s should be unchanged", i) 3208 } 3209 case "aws_instance.foo[1]": 3210 if res.Action != plans.Create { 3211 t.Fatalf("expected resource create, got %s", res.Action) 3212 } 3213 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3214 "id": cty.UnknownVal(cty.String), 3215 "foo": cty.StringVal("foo"), 3216 "type": cty.StringVal("aws_instance"), 3217 }), ric.After) 3218 case "aws_instance.foo[2]": 3219 if res.Action != plans.Create { 3220 t.Fatalf("expected resource create, got %s", res.Action) 3221 } 3222 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3223 "id": cty.UnknownVal(cty.String), 3224 "foo": cty.StringVal("foo"), 3225 "type": cty.StringVal("aws_instance"), 3226 }), ric.After) 3227 default: 3228 t.Fatal("unknown instance:", i) 3229 } 3230 } 3231 } 3232 3233 // A common pattern in TF configs is to have a set of resources with the same 3234 // count and to use count.index to create correspondences between them: 3235 // 3236 // foo_id = "${foo.bar.*.id[count.index]}" 3237 // 3238 // This test is for the situation where some instances already exist and the 3239 // count is increased. In that case, we should see only the create diffs 3240 // for the new instances and not any update diffs for the existing ones. 3241 func TestContext2Plan_countIncreaseWithSplatReference(t *testing.T) { 3242 m := testModule(t, "plan-count-splat-reference") 3243 p := testProvider("aws") 3244 p.GetSchemaReturn = &ProviderSchema{ 3245 ResourceTypes: map[string]*configschema.Block{ 3246 "aws_instance": { 3247 Attributes: map[string]*configschema.Attribute{ 3248 "name": {Type: cty.String, Optional: true}, 3249 "foo_name": {Type: cty.String, Optional: true}, 3250 }, 3251 }, 3252 }, 3253 } 3254 p.DiffFn = testDiffFn 3255 3256 s := MustShimLegacyState(&State{ 3257 Modules: []*ModuleState{ 3258 { 3259 Path: rootModulePath, 3260 Resources: map[string]*ResourceState{ 3261 "aws_instance.foo.0": { 3262 Type: "aws_instance", 3263 Primary: &InstanceState{ 3264 ID: "bar", 3265 Attributes: map[string]string{ 3266 "name": "foo 0", 3267 }, 3268 }, 3269 }, 3270 "aws_instance.foo.1": { 3271 Type: "aws_instance", 3272 Primary: &InstanceState{ 3273 ID: "bar", 3274 Attributes: map[string]string{ 3275 "name": "foo 1", 3276 }, 3277 }, 3278 }, 3279 "aws_instance.bar.0": { 3280 Type: "aws_instance", 3281 Primary: &InstanceState{ 3282 ID: "bar", 3283 Attributes: map[string]string{ 3284 "foo_name": "foo 0", 3285 }, 3286 }, 3287 }, 3288 "aws_instance.bar.1": { 3289 Type: "aws_instance", 3290 Primary: &InstanceState{ 3291 ID: "bar", 3292 Attributes: map[string]string{ 3293 "foo_name": "foo 1", 3294 }, 3295 }, 3296 }, 3297 }, 3298 }, 3299 }, 3300 }) 3301 ctx := testContext2(t, &ContextOpts{ 3302 Config: m, 3303 ProviderResolver: providers.ResolverFixed( 3304 map[string]providers.Factory{ 3305 "aws": testProviderFuncFixed(p), 3306 }, 3307 ), 3308 State: s, 3309 }) 3310 3311 plan, diags := ctx.Plan() 3312 if diags.HasErrors() { 3313 t.Fatalf("unexpected errors: %s", diags.Err()) 3314 } 3315 3316 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3317 ty := schema.ImpliedType() 3318 3319 if len(plan.Changes.Resources) != 6 { 3320 t.Fatal("expected 6 changes, got", len(plan.Changes.Resources)) 3321 } 3322 3323 for _, res := range plan.Changes.Resources { 3324 ric, err := res.Decode(ty) 3325 if err != nil { 3326 t.Fatal(err) 3327 } 3328 3329 switch i := ric.Addr.String(); i { 3330 case "aws_instance.bar[0]", "aws_instance.bar[1]", "aws_instance.foo[0]", "aws_instance.foo[1]": 3331 if res.Action != plans.NoOp { 3332 t.Fatalf("resource %s should be unchanged", i) 3333 } 3334 case "aws_instance.bar[2]": 3335 if res.Action != plans.Create { 3336 t.Fatalf("expected resource create, got %s", res.Action) 3337 } 3338 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3339 "foo_name": cty.StringVal("foo 2"), 3340 }), ric.After) 3341 case "aws_instance.foo[2]": 3342 if res.Action != plans.Create { 3343 t.Fatalf("expected resource create, got %s", res.Action) 3344 } 3345 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3346 "name": cty.StringVal("foo 2"), 3347 }), ric.After) 3348 default: 3349 t.Fatal("unknown instance:", i) 3350 } 3351 } 3352 } 3353 3354 func TestContext2Plan_forEach(t *testing.T) { 3355 m := testModule(t, "plan-for-each") 3356 p := testProvider("aws") 3357 p.DiffFn = testDiffFn 3358 ctx := testContext2(t, &ContextOpts{ 3359 Config: m, 3360 ProviderResolver: providers.ResolverFixed( 3361 map[string]providers.Factory{ 3362 "aws": testProviderFuncFixed(p), 3363 }, 3364 ), 3365 }) 3366 3367 plan, diags := ctx.Plan() 3368 if diags.HasErrors() { 3369 t.Fatalf("unexpected errors: %s", diags.Err()) 3370 } 3371 3372 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3373 ty := schema.ImpliedType() 3374 3375 if len(plan.Changes.Resources) != 8 { 3376 t.Fatal("expected 8 changes, got", len(plan.Changes.Resources)) 3377 } 3378 3379 for _, res := range plan.Changes.Resources { 3380 if res.Action != plans.Create { 3381 t.Fatalf("expected resource creation, got %s", res.Action) 3382 } 3383 _, err := res.Decode(ty) 3384 if err != nil { 3385 t.Fatal(err) 3386 } 3387 } 3388 } 3389 3390 func TestContext2Plan_forEachUnknownValue(t *testing.T) { 3391 // This module has a variable defined, but it is not provided 3392 // in the context below and we expect the plan to error, but not panic 3393 m := testModule(t, "plan-for-each-unknown-value") 3394 p := testProvider("aws") 3395 p.DiffFn = testDiffFn 3396 ctx := testContext2(t, &ContextOpts{ 3397 Config: m, 3398 ProviderResolver: providers.ResolverFixed( 3399 map[string]providers.Factory{ 3400 "aws": testProviderFuncFixed(p), 3401 }, 3402 ), 3403 }) 3404 3405 _, diags := ctx.Plan() 3406 if !diags.HasErrors() { 3407 // Should get this error: 3408 // Invalid for_each argument: The "for_each" value depends on resource attributes that cannot be determined until apply... 3409 t.Fatal("succeeded; want errors") 3410 } 3411 3412 gotErrStr := diags.Err().Error() 3413 wantErrStr := "Invalid for_each argument" 3414 if !strings.Contains(gotErrStr, wantErrStr) { 3415 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) 3416 } 3417 } 3418 3419 func TestContext2Plan_destroy(t *testing.T) { 3420 m := testModule(t, "plan-destroy") 3421 p := testProvider("aws") 3422 p.DiffFn = testDiffFn 3423 s := MustShimLegacyState(&State{ 3424 Modules: []*ModuleState{ 3425 { 3426 Path: rootModulePath, 3427 Resources: map[string]*ResourceState{ 3428 "aws_instance.one": { 3429 Type: "aws_instance", 3430 Primary: &InstanceState{ 3431 ID: "bar", 3432 }, 3433 }, 3434 "aws_instance.two": { 3435 Type: "aws_instance", 3436 Primary: &InstanceState{ 3437 ID: "baz", 3438 }, 3439 }, 3440 }, 3441 }, 3442 }, 3443 }) 3444 ctx := testContext2(t, &ContextOpts{ 3445 Config: m, 3446 ProviderResolver: providers.ResolverFixed( 3447 map[string]providers.Factory{ 3448 "aws": testProviderFuncFixed(p), 3449 }, 3450 ), 3451 State: s, 3452 Destroy: true, 3453 }) 3454 3455 plan, diags := ctx.Plan() 3456 if diags.HasErrors() { 3457 t.Fatalf("unexpected errors: %s", diags.Err()) 3458 } 3459 3460 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3461 ty := schema.ImpliedType() 3462 3463 if len(plan.Changes.Resources) != 2 { 3464 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 3465 } 3466 3467 for _, res := range plan.Changes.Resources { 3468 ric, err := res.Decode(ty) 3469 if err != nil { 3470 t.Fatal(err) 3471 } 3472 3473 switch i := ric.Addr.String(); i { 3474 case "aws_instance.one", "aws_instance.two": 3475 if res.Action != plans.Delete { 3476 t.Fatalf("resource %s should be removed", i) 3477 } 3478 3479 default: 3480 t.Fatal("unknown instance:", i) 3481 } 3482 } 3483 } 3484 3485 func TestContext2Plan_moduleDestroy(t *testing.T) { 3486 m := testModule(t, "plan-module-destroy") 3487 p := testProvider("aws") 3488 p.DiffFn = testDiffFn 3489 s := MustShimLegacyState(&State{ 3490 Modules: []*ModuleState{ 3491 { 3492 Path: rootModulePath, 3493 Resources: map[string]*ResourceState{ 3494 "aws_instance.foo": { 3495 Type: "aws_instance", 3496 Primary: &InstanceState{ 3497 ID: "bar", 3498 }, 3499 }, 3500 }, 3501 }, 3502 { 3503 Path: []string{"root", "child"}, 3504 Resources: map[string]*ResourceState{ 3505 "aws_instance.foo": { 3506 Type: "aws_instance", 3507 Primary: &InstanceState{ 3508 ID: "bar", 3509 }, 3510 }, 3511 }, 3512 }, 3513 }, 3514 }) 3515 ctx := testContext2(t, &ContextOpts{ 3516 Config: m, 3517 ProviderResolver: providers.ResolverFixed( 3518 map[string]providers.Factory{ 3519 "aws": testProviderFuncFixed(p), 3520 }, 3521 ), 3522 State: s, 3523 Destroy: true, 3524 }) 3525 3526 plan, diags := ctx.Plan() 3527 if diags.HasErrors() { 3528 t.Fatalf("unexpected errors: %s", diags.Err()) 3529 } 3530 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3531 ty := schema.ImpliedType() 3532 3533 if len(plan.Changes.Resources) != 2 { 3534 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 3535 } 3536 3537 for _, res := range plan.Changes.Resources { 3538 ric, err := res.Decode(ty) 3539 if err != nil { 3540 t.Fatal(err) 3541 } 3542 3543 switch i := ric.Addr.String(); i { 3544 case "aws_instance.foo", "module.child.aws_instance.foo": 3545 if res.Action != plans.Delete { 3546 t.Fatalf("resource %s should be removed", i) 3547 } 3548 3549 default: 3550 t.Fatal("unknown instance:", i) 3551 } 3552 } 3553 } 3554 3555 // GH-1835 3556 func TestContext2Plan_moduleDestroyCycle(t *testing.T) { 3557 m := testModule(t, "plan-module-destroy-gh-1835") 3558 p := testProvider("aws") 3559 p.DiffFn = testDiffFn 3560 s := MustShimLegacyState(&State{ 3561 Modules: []*ModuleState{ 3562 { 3563 Path: []string{"root", "a_module"}, 3564 Resources: map[string]*ResourceState{ 3565 "aws_instance.a": { 3566 Type: "aws_instance", 3567 Primary: &InstanceState{ 3568 ID: "a", 3569 }, 3570 }, 3571 }, 3572 }, 3573 { 3574 Path: []string{"root", "b_module"}, 3575 Resources: map[string]*ResourceState{ 3576 "aws_instance.b": { 3577 Type: "aws_instance", 3578 Primary: &InstanceState{ 3579 ID: "b", 3580 }, 3581 }, 3582 }, 3583 }, 3584 }, 3585 }) 3586 ctx := testContext2(t, &ContextOpts{ 3587 Config: m, 3588 ProviderResolver: providers.ResolverFixed( 3589 map[string]providers.Factory{ 3590 "aws": testProviderFuncFixed(p), 3591 }, 3592 ), 3593 State: s, 3594 Destroy: true, 3595 }) 3596 3597 plan, diags := ctx.Plan() 3598 if diags.HasErrors() { 3599 t.Fatalf("unexpected errors: %s", diags.Err()) 3600 } 3601 3602 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3603 ty := schema.ImpliedType() 3604 3605 if len(plan.Changes.Resources) != 2 { 3606 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 3607 } 3608 3609 for _, res := range plan.Changes.Resources { 3610 ric, err := res.Decode(ty) 3611 if err != nil { 3612 t.Fatal(err) 3613 } 3614 3615 switch i := ric.Addr.String(); i { 3616 case "module.a_module.aws_instance.a", "module.b_module.aws_instance.b": 3617 if res.Action != plans.Delete { 3618 t.Fatalf("resource %s should be removed", i) 3619 } 3620 3621 default: 3622 t.Fatal("unknown instance:", i) 3623 } 3624 } 3625 } 3626 3627 func TestContext2Plan_moduleDestroyMultivar(t *testing.T) { 3628 m := testModule(t, "plan-module-destroy-multivar") 3629 p := testProvider("aws") 3630 p.DiffFn = testDiffFn 3631 s := MustShimLegacyState(&State{ 3632 Modules: []*ModuleState{ 3633 { 3634 Path: rootModulePath, 3635 Resources: map[string]*ResourceState{}, 3636 }, 3637 { 3638 Path: []string{"root", "child"}, 3639 Resources: map[string]*ResourceState{ 3640 "aws_instance.foo.0": { 3641 Type: "aws_instance", 3642 Primary: &InstanceState{ 3643 ID: "bar0", 3644 }, 3645 }, 3646 "aws_instance.foo.1": { 3647 Type: "aws_instance", 3648 Primary: &InstanceState{ 3649 ID: "bar1", 3650 }, 3651 }, 3652 }, 3653 }, 3654 }, 3655 }) 3656 ctx := testContext2(t, &ContextOpts{ 3657 Config: m, 3658 ProviderResolver: providers.ResolverFixed( 3659 map[string]providers.Factory{ 3660 "aws": testProviderFuncFixed(p), 3661 }, 3662 ), 3663 State: s, 3664 Destroy: true, 3665 }) 3666 3667 plan, diags := ctx.Plan() 3668 if diags.HasErrors() { 3669 t.Fatalf("unexpected errors: %s", diags.Err()) 3670 } 3671 3672 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3673 ty := schema.ImpliedType() 3674 3675 if len(plan.Changes.Resources) != 2 { 3676 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 3677 } 3678 3679 for _, res := range plan.Changes.Resources { 3680 ric, err := res.Decode(ty) 3681 if err != nil { 3682 t.Fatal(err) 3683 } 3684 3685 switch i := ric.Addr.String(); i { 3686 case "module.child.aws_instance.foo[0]", "module.child.aws_instance.foo[1]": 3687 if res.Action != plans.Delete { 3688 t.Fatalf("resource %s should be removed", i) 3689 } 3690 3691 default: 3692 t.Fatal("unknown instance:", i) 3693 } 3694 } 3695 } 3696 3697 func TestContext2Plan_pathVar(t *testing.T) { 3698 cwd, err := os.Getwd() 3699 if err != nil { 3700 t.Fatalf("err: %s", err) 3701 } 3702 3703 m := testModule(t, "plan-path-var") 3704 p := testProvider("aws") 3705 p.GetSchemaReturn = &ProviderSchema{ 3706 ResourceTypes: map[string]*configschema.Block{ 3707 "aws_instance": { 3708 Attributes: map[string]*configschema.Attribute{ 3709 "cwd": {Type: cty.String, Optional: true}, 3710 "module": {Type: cty.String, Optional: true}, 3711 "root": {Type: cty.String, Optional: true}, 3712 }, 3713 }, 3714 }, 3715 } 3716 p.DiffFn = testDiffFn 3717 3718 ctx := testContext2(t, &ContextOpts{ 3719 Config: m, 3720 ProviderResolver: providers.ResolverFixed( 3721 map[string]providers.Factory{ 3722 "aws": testProviderFuncFixed(p), 3723 }, 3724 ), 3725 }) 3726 3727 plan, diags := ctx.Plan() 3728 if diags.HasErrors() { 3729 t.Fatalf("err: %s", diags.Err()) 3730 } 3731 3732 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3733 ty := schema.ImpliedType() 3734 3735 if len(plan.Changes.Resources) != 1 { 3736 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 3737 } 3738 3739 for _, res := range plan.Changes.Resources { 3740 ric, err := res.Decode(ty) 3741 if err != nil { 3742 t.Fatal(err) 3743 } 3744 3745 switch i := ric.Addr.String(); i { 3746 case "aws_instance.foo": 3747 if res.Action != plans.Create { 3748 t.Fatalf("resource %s should be created", i) 3749 } 3750 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3751 "cwd": cty.StringVal(cwd + "/barpath"), 3752 "module": cty.StringVal(m.Module.SourceDir + "/foopath"), 3753 "root": cty.StringVal(m.Module.SourceDir + "/barpath"), 3754 }), ric.After) 3755 default: 3756 t.Fatal("unknown instance:", i) 3757 } 3758 } 3759 } 3760 3761 func TestContext2Plan_diffVar(t *testing.T) { 3762 m := testModule(t, "plan-diffvar") 3763 p := testProvider("aws") 3764 s := MustShimLegacyState(&State{ 3765 Modules: []*ModuleState{ 3766 { 3767 Path: rootModulePath, 3768 Resources: map[string]*ResourceState{ 3769 "aws_instance.foo": { 3770 Type: "aws_instance", 3771 Primary: &InstanceState{ 3772 ID: "bar", 3773 Attributes: map[string]string{ 3774 "num": "2", 3775 }, 3776 }, 3777 }, 3778 }, 3779 }, 3780 }, 3781 }) 3782 ctx := testContext2(t, &ContextOpts{ 3783 Config: m, 3784 ProviderResolver: providers.ResolverFixed( 3785 map[string]providers.Factory{ 3786 "aws": testProviderFuncFixed(p), 3787 }, 3788 ), 3789 State: s, 3790 }) 3791 3792 p.DiffFn = func( 3793 info *InstanceInfo, 3794 s *InstanceState, 3795 c *ResourceConfig) (*InstanceDiff, error) { 3796 if s.ID != "bar" { 3797 return testDiffFn(info, s, c) 3798 } 3799 3800 return &InstanceDiff{ 3801 Attributes: map[string]*ResourceAttrDiff{ 3802 "num": { 3803 Old: "2", 3804 New: "3", 3805 }, 3806 }, 3807 }, nil 3808 } 3809 3810 plan, diags := ctx.Plan() 3811 if diags.HasErrors() { 3812 t.Fatalf("unexpected errors: %s", diags.Err()) 3813 } 3814 3815 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3816 ty := schema.ImpliedType() 3817 3818 if len(plan.Changes.Resources) != 2 { 3819 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 3820 } 3821 3822 for _, res := range plan.Changes.Resources { 3823 ric, err := res.Decode(ty) 3824 if err != nil { 3825 t.Fatal(err) 3826 } 3827 3828 switch i := ric.Addr.String(); i { 3829 case "aws_instance.bar": 3830 if res.Action != plans.Create { 3831 t.Fatalf("resource %s should be created", i) 3832 } 3833 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3834 "id": cty.UnknownVal(cty.String), 3835 "num": cty.NumberIntVal(3), 3836 "type": cty.StringVal("aws_instance"), 3837 }), ric.After) 3838 case "aws_instance.foo": 3839 if res.Action != plans.Update { 3840 t.Fatalf("resource %s should be updated", i) 3841 } 3842 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3843 "id": cty.StringVal("bar"), 3844 "num": cty.NumberIntVal(2), 3845 }), ric.Before) 3846 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3847 "id": cty.StringVal("bar"), 3848 "num": cty.NumberIntVal(3), 3849 }), ric.After) 3850 default: 3851 t.Fatal("unknown instance:", i) 3852 } 3853 } 3854 } 3855 3856 func TestContext2Plan_hook(t *testing.T) { 3857 m := testModule(t, "plan-good") 3858 h := new(MockHook) 3859 p := testProvider("aws") 3860 p.DiffFn = testDiffFn 3861 ctx := testContext2(t, &ContextOpts{ 3862 Config: m, 3863 Hooks: []Hook{h}, 3864 ProviderResolver: providers.ResolverFixed( 3865 map[string]providers.Factory{ 3866 "aws": testProviderFuncFixed(p), 3867 }, 3868 ), 3869 }) 3870 3871 _, diags := ctx.Plan() 3872 if diags.HasErrors() { 3873 t.Fatalf("unexpected errors: %s", diags.Err()) 3874 } 3875 3876 if !h.PreDiffCalled { 3877 t.Fatal("should be called") 3878 } 3879 if !h.PostDiffCalled { 3880 t.Fatal("should be called") 3881 } 3882 } 3883 3884 func TestContext2Plan_closeProvider(t *testing.T) { 3885 // this fixture only has an aliased provider located in the module, to make 3886 // sure that the provier name contains a path more complex than 3887 // "provider.aws". 3888 m := testModule(t, "plan-close-module-provider") 3889 p := testProvider("aws") 3890 p.DiffFn = testDiffFn 3891 ctx := testContext2(t, &ContextOpts{ 3892 Config: m, 3893 ProviderResolver: providers.ResolverFixed( 3894 map[string]providers.Factory{ 3895 "aws": testProviderFuncFixed(p), 3896 }, 3897 ), 3898 }) 3899 3900 _, diags := ctx.Plan() 3901 if diags.HasErrors() { 3902 t.Fatalf("unexpected errors: %s", diags.Err()) 3903 } 3904 3905 if !p.CloseCalled { 3906 t.Fatal("provider not closed") 3907 } 3908 } 3909 3910 func TestContext2Plan_orphan(t *testing.T) { 3911 m := testModule(t, "plan-orphan") 3912 p := testProvider("aws") 3913 p.DiffFn = testDiffFn 3914 s := MustShimLegacyState(&State{ 3915 Modules: []*ModuleState{ 3916 { 3917 Path: rootModulePath, 3918 Resources: map[string]*ResourceState{ 3919 "aws_instance.baz": { 3920 Type: "aws_instance", 3921 Primary: &InstanceState{ 3922 ID: "bar", 3923 }, 3924 }, 3925 }, 3926 }, 3927 }, 3928 }) 3929 ctx := testContext2(t, &ContextOpts{ 3930 Config: m, 3931 ProviderResolver: providers.ResolverFixed( 3932 map[string]providers.Factory{ 3933 "aws": testProviderFuncFixed(p), 3934 }, 3935 ), 3936 State: s, 3937 }) 3938 3939 plan, diags := ctx.Plan() 3940 if diags.HasErrors() { 3941 t.Fatalf("unexpected errors: %s", diags.Err()) 3942 } 3943 3944 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 3945 ty := schema.ImpliedType() 3946 3947 if len(plan.Changes.Resources) != 2 { 3948 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 3949 } 3950 3951 for _, res := range plan.Changes.Resources { 3952 ric, err := res.Decode(ty) 3953 if err != nil { 3954 t.Fatal(err) 3955 } 3956 3957 switch i := ric.Addr.String(); i { 3958 case "aws_instance.baz": 3959 if res.Action != plans.Delete { 3960 t.Fatalf("resource %s should be removed", i) 3961 } 3962 case "aws_instance.foo": 3963 if res.Action != plans.Create { 3964 t.Fatalf("resource %s should be created", i) 3965 } 3966 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 3967 "id": cty.UnknownVal(cty.String), 3968 "num": cty.NumberIntVal(2), 3969 "type": cty.StringVal("aws_instance"), 3970 }), ric.After) 3971 default: 3972 t.Fatal("unknown instance:", i) 3973 } 3974 } 3975 } 3976 3977 // This tests that configurations with UUIDs don't produce errors. 3978 // For shadows, this would produce errors since a UUID changes every time. 3979 func TestContext2Plan_shadowUuid(t *testing.T) { 3980 m := testModule(t, "plan-shadow-uuid") 3981 p := testProvider("aws") 3982 p.DiffFn = testDiffFn 3983 ctx := testContext2(t, &ContextOpts{ 3984 Config: m, 3985 ProviderResolver: providers.ResolverFixed( 3986 map[string]providers.Factory{ 3987 "aws": testProviderFuncFixed(p), 3988 }, 3989 ), 3990 }) 3991 3992 _, diags := ctx.Plan() 3993 if diags.HasErrors() { 3994 t.Fatalf("unexpected errors: %s", diags.Err()) 3995 } 3996 } 3997 3998 func TestContext2Plan_state(t *testing.T) { 3999 m := testModule(t, "plan-good") 4000 p := testProvider("aws") 4001 p.DiffFn = testDiffFn 4002 s := MustShimLegacyState(&State{ 4003 Modules: []*ModuleState{ 4004 { 4005 Path: rootModulePath, 4006 Resources: map[string]*ResourceState{ 4007 "aws_instance.foo": { 4008 Type: "aws_instance", 4009 Primary: &InstanceState{ 4010 ID: "bar", 4011 }, 4012 }, 4013 }, 4014 }, 4015 }, 4016 }) 4017 ctx := testContext2(t, &ContextOpts{ 4018 Config: m, 4019 ProviderResolver: providers.ResolverFixed( 4020 map[string]providers.Factory{ 4021 "aws": testProviderFuncFixed(p), 4022 }, 4023 ), 4024 State: s, 4025 }) 4026 4027 plan, diags := ctx.Plan() 4028 if diags.HasErrors() { 4029 t.Fatalf("unexpected errors: %s", diags.Err()) 4030 } 4031 4032 if len(plan.Changes.Resources) < 2 { 4033 t.Fatalf("bad: %#v", plan.Changes.Resources) 4034 } 4035 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4036 ty := schema.ImpliedType() 4037 4038 if len(plan.Changes.Resources) != 2 { 4039 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 4040 } 4041 4042 for _, res := range plan.Changes.Resources { 4043 ric, err := res.Decode(ty) 4044 if err != nil { 4045 t.Fatal(err) 4046 } 4047 4048 switch i := ric.Addr.String(); i { 4049 case "aws_instance.bar": 4050 if res.Action != plans.Create { 4051 t.Fatalf("resource %s should be created", i) 4052 } 4053 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4054 "id": cty.UnknownVal(cty.String), 4055 "foo": cty.StringVal("2"), 4056 "type": cty.StringVal("aws_instance"), 4057 }), ric.After) 4058 case "aws_instance.foo": 4059 if res.Action != plans.Update { 4060 t.Fatalf("resource %s should be updated", i) 4061 } 4062 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4063 "id": cty.StringVal("bar"), 4064 "num": cty.NullVal(cty.Number), 4065 "type": cty.NullVal(cty.String), 4066 }), ric.Before) 4067 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4068 "id": cty.StringVal("bar"), 4069 "num": cty.NumberIntVal(2), 4070 "type": cty.StringVal("aws_instance"), 4071 }), ric.After) 4072 default: 4073 t.Fatal("unknown instance:", i) 4074 } 4075 } 4076 } 4077 4078 func TestContext2Plan_taint(t *testing.T) { 4079 m := testModule(t, "plan-taint") 4080 p := testProvider("aws") 4081 p.DiffFn = testDiffFn 4082 s := MustShimLegacyState(&State{ 4083 Modules: []*ModuleState{ 4084 { 4085 Path: rootModulePath, 4086 Resources: map[string]*ResourceState{ 4087 "aws_instance.foo": { 4088 Type: "aws_instance", 4089 Primary: &InstanceState{ 4090 ID: "bar", 4091 Attributes: map[string]string{"num": "2"}, 4092 }, 4093 }, 4094 "aws_instance.bar": { 4095 Type: "aws_instance", 4096 Primary: &InstanceState{ 4097 ID: "baz", 4098 Tainted: true, 4099 }, 4100 }, 4101 }, 4102 }, 4103 }, 4104 }) 4105 ctx := testContext2(t, &ContextOpts{ 4106 Config: m, 4107 ProviderResolver: providers.ResolverFixed( 4108 map[string]providers.Factory{ 4109 "aws": testProviderFuncFixed(p), 4110 }, 4111 ), 4112 State: s, 4113 }) 4114 4115 plan, diags := ctx.Plan() 4116 if diags.HasErrors() { 4117 t.Fatalf("unexpected errors: %s", diags.Err()) 4118 } 4119 4120 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4121 ty := schema.ImpliedType() 4122 4123 if len(plan.Changes.Resources) != 2 { 4124 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 4125 } 4126 4127 for _, res := range plan.Changes.Resources { 4128 ric, err := res.Decode(ty) 4129 if err != nil { 4130 t.Fatal(err) 4131 } 4132 4133 switch i := ric.Addr.String(); i { 4134 case "aws_instance.bar": 4135 if res.Action != plans.DeleteThenCreate { 4136 t.Fatalf("resource %s should be replaced", i) 4137 } 4138 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4139 "id": cty.UnknownVal(cty.String), 4140 "foo": cty.StringVal("2"), 4141 "type": cty.StringVal("aws_instance"), 4142 }), ric.After) 4143 case "aws_instance.foo": 4144 if res.Action != plans.NoOp { 4145 t.Fatalf("resource %s should not be changed", i) 4146 } 4147 default: 4148 t.Fatal("unknown instance:", i) 4149 } 4150 } 4151 } 4152 4153 func TestContext2Plan_taintIgnoreChanges(t *testing.T) { 4154 m := testModule(t, "plan-taint-ignore-changes") 4155 p := testProvider("aws") 4156 p.GetSchemaReturn = &ProviderSchema{ 4157 ResourceTypes: map[string]*configschema.Block{ 4158 "aws_instance": { 4159 Attributes: map[string]*configschema.Attribute{ 4160 "id": {Type: cty.String, Computed: true}, 4161 "vars": {Type: cty.String, Optional: true}, 4162 "type": {Type: cty.String, Computed: true}, 4163 }, 4164 }, 4165 }, 4166 } 4167 p.ApplyFn = testApplyFn 4168 p.DiffFn = testDiffFn 4169 4170 s := MustShimLegacyState(&State{ 4171 Modules: []*ModuleState{ 4172 { 4173 Path: rootModulePath, 4174 Resources: map[string]*ResourceState{ 4175 "aws_instance.foo": { 4176 Type: "aws_instance", 4177 Primary: &InstanceState{ 4178 ID: "foo", 4179 Attributes: map[string]string{ 4180 "vars": "foo", 4181 "type": "aws_instance", 4182 }, 4183 Tainted: true, 4184 }, 4185 }, 4186 }, 4187 }, 4188 }, 4189 }) 4190 ctx := testContext2(t, &ContextOpts{ 4191 Config: m, 4192 ProviderResolver: providers.ResolverFixed( 4193 map[string]providers.Factory{ 4194 "aws": testProviderFuncFixed(p), 4195 }, 4196 ), 4197 State: s, 4198 }) 4199 4200 plan, diags := ctx.Plan() 4201 if diags.HasErrors() { 4202 t.Fatalf("unexpected errors: %s", diags.Err()) 4203 } 4204 4205 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4206 ty := schema.ImpliedType() 4207 4208 if len(plan.Changes.Resources) != 1 { 4209 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 4210 } 4211 4212 for _, res := range plan.Changes.Resources { 4213 ric, err := res.Decode(ty) 4214 if err != nil { 4215 t.Fatal(err) 4216 } 4217 4218 switch i := ric.Addr.String(); i { 4219 case "aws_instance.foo": 4220 if res.Action != plans.DeleteThenCreate { 4221 t.Fatalf("resource %s should be replaced", i) 4222 } 4223 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4224 "id": cty.StringVal("foo"), 4225 "vars": cty.StringVal("foo"), 4226 "type": cty.StringVal("aws_instance"), 4227 }), ric.Before) 4228 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4229 "id": cty.UnknownVal(cty.String), 4230 "vars": cty.StringVal("foo"), 4231 "type": cty.StringVal("aws_instance"), 4232 }), ric.After) 4233 default: 4234 t.Fatal("unknown instance:", i) 4235 } 4236 } 4237 } 4238 4239 // Fails about 50% of the time before the fix for GH-4982, covers the fix. 4240 func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) { 4241 m := testModule(t, "plan-taint-interpolated-count") 4242 p := testProvider("aws") 4243 p.DiffFn = testDiffFn 4244 s := MustShimLegacyState(&State{ 4245 Modules: []*ModuleState{ 4246 { 4247 Path: rootModulePath, 4248 Resources: map[string]*ResourceState{ 4249 "aws_instance.foo.0": { 4250 Type: "aws_instance", 4251 Primary: &InstanceState{ 4252 ID: "bar", 4253 Tainted: true, 4254 }, 4255 }, 4256 "aws_instance.foo.1": { 4257 Type: "aws_instance", 4258 Primary: &InstanceState{ID: "bar"}, 4259 }, 4260 "aws_instance.foo.2": { 4261 Type: "aws_instance", 4262 Primary: &InstanceState{ID: "bar"}, 4263 }, 4264 }, 4265 }, 4266 }, 4267 }) 4268 4269 for i := 0; i < 100; i++ { 4270 ctx := testContext2(t, &ContextOpts{ 4271 Config: m, 4272 ProviderResolver: providers.ResolverFixed( 4273 map[string]providers.Factory{ 4274 "aws": testProviderFuncFixed(p), 4275 }, 4276 ), 4277 State: s, 4278 }) 4279 4280 plan, diags := ctx.Plan() 4281 if diags.HasErrors() { 4282 t.Fatalf("unexpected errors: %s", diags.Err()) 4283 } 4284 4285 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4286 ty := schema.ImpliedType() 4287 4288 if len(plan.Changes.Resources) != 3 { 4289 t.Fatal("expected 3 changes, got", len(plan.Changes.Resources)) 4290 } 4291 4292 for _, res := range plan.Changes.Resources { 4293 ric, err := res.Decode(ty) 4294 if err != nil { 4295 t.Fatal(err) 4296 } 4297 4298 switch i := ric.Addr.String(); i { 4299 case "aws_instance.foo[0]": 4300 if res.Action != plans.DeleteThenCreate { 4301 t.Fatalf("resource %s should be replaced", i) 4302 } 4303 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4304 "id": cty.StringVal("bar"), 4305 }), ric.Before) 4306 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4307 "id": cty.UnknownVal(cty.String), 4308 }), ric.After) 4309 case "aws_instance.foo[1]", "aws_instance.foo[2]": 4310 if res.Action != plans.NoOp { 4311 t.Fatalf("resource %s should not be changed", i) 4312 } 4313 default: 4314 t.Fatal("unknown instance:", i) 4315 } 4316 } 4317 } 4318 } 4319 4320 func TestContext2Plan_targeted(t *testing.T) { 4321 m := testModule(t, "plan-targeted") 4322 p := testProvider("aws") 4323 p.DiffFn = testDiffFn 4324 ctx := testContext2(t, &ContextOpts{ 4325 Config: m, 4326 ProviderResolver: providers.ResolverFixed( 4327 map[string]providers.Factory{ 4328 "aws": testProviderFuncFixed(p), 4329 }, 4330 ), 4331 Targets: []addrs.Targetable{ 4332 addrs.RootModuleInstance.Resource( 4333 addrs.ManagedResourceMode, "aws_instance", "foo", 4334 ), 4335 }, 4336 }) 4337 4338 plan, diags := ctx.Plan() 4339 if diags.HasErrors() { 4340 t.Fatalf("unexpected errors: %s", diags.Err()) 4341 } 4342 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4343 ty := schema.ImpliedType() 4344 4345 if len(plan.Changes.Resources) != 1 { 4346 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 4347 } 4348 4349 for _, res := range plan.Changes.Resources { 4350 ric, err := res.Decode(ty) 4351 if err != nil { 4352 t.Fatal(err) 4353 } 4354 4355 switch i := ric.Addr.String(); i { 4356 case "aws_instance.foo": 4357 if res.Action != plans.Create { 4358 t.Fatalf("resource %s should be created", i) 4359 } 4360 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4361 "id": cty.UnknownVal(cty.String), 4362 "num": cty.NumberIntVal(2), 4363 "type": cty.StringVal("aws_instance"), 4364 }), ric.After) 4365 default: 4366 t.Fatal("unknown instance:", i) 4367 } 4368 } 4369 } 4370 4371 // Test that targeting a module properly plans any inputs that depend 4372 // on another module. 4373 func TestContext2Plan_targetedCrossModule(t *testing.T) { 4374 m := testModule(t, "plan-targeted-cross-module") 4375 p := testProvider("aws") 4376 p.DiffFn = testDiffFn 4377 ctx := testContext2(t, &ContextOpts{ 4378 Config: m, 4379 ProviderResolver: providers.ResolverFixed( 4380 map[string]providers.Factory{ 4381 "aws": testProviderFuncFixed(p), 4382 }, 4383 ), 4384 Targets: []addrs.Targetable{ 4385 addrs.RootModuleInstance.Child("B", addrs.NoKey), 4386 }, 4387 }) 4388 4389 plan, diags := ctx.Plan() 4390 if diags.HasErrors() { 4391 t.Fatalf("unexpected errors: %s", diags.Err()) 4392 } 4393 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4394 ty := schema.ImpliedType() 4395 4396 if len(plan.Changes.Resources) != 2 { 4397 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 4398 } 4399 4400 for _, res := range plan.Changes.Resources { 4401 ric, err := res.Decode(ty) 4402 if err != nil { 4403 t.Fatal(err) 4404 } 4405 if res.Action != plans.Create { 4406 t.Fatalf("resource %s should be created", ric.Addr) 4407 } 4408 switch i := ric.Addr.String(); i { 4409 case "module.A.aws_instance.foo": 4410 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4411 "id": cty.UnknownVal(cty.String), 4412 "foo": cty.StringVal("bar"), 4413 "type": cty.StringVal("aws_instance"), 4414 }), ric.After) 4415 case "module.B.aws_instance.bar": 4416 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4417 "id": cty.UnknownVal(cty.String), 4418 "foo": cty.UnknownVal(cty.String), 4419 "type": cty.StringVal("aws_instance"), 4420 }), ric.After) 4421 default: 4422 t.Fatal("unknown instance:", i) 4423 } 4424 } 4425 } 4426 4427 func TestContext2Plan_targetedModuleWithProvider(t *testing.T) { 4428 m := testModule(t, "plan-targeted-module-with-provider") 4429 p := testProvider("null") 4430 p.GetSchemaReturn = &ProviderSchema{ 4431 Provider: &configschema.Block{ 4432 Attributes: map[string]*configschema.Attribute{ 4433 "key": {Type: cty.String, Optional: true}, 4434 }, 4435 }, 4436 ResourceTypes: map[string]*configschema.Block{ 4437 "null_resource": { 4438 Attributes: map[string]*configschema.Attribute{}, 4439 }, 4440 }, 4441 } 4442 p.DiffFn = testDiffFn 4443 4444 ctx := testContext2(t, &ContextOpts{ 4445 Config: m, 4446 ProviderResolver: providers.ResolverFixed( 4447 map[string]providers.Factory{ 4448 "null": testProviderFuncFixed(p), 4449 }, 4450 ), 4451 Targets: []addrs.Targetable{ 4452 addrs.RootModuleInstance.Child("child2", addrs.NoKey), 4453 }, 4454 }) 4455 4456 plan, diags := ctx.Plan() 4457 if diags.HasErrors() { 4458 t.Fatalf("unexpected errors: %s", diags.Err()) 4459 } 4460 4461 schema := p.GetSchemaReturn.ResourceTypes["null_resource"] 4462 ty := schema.ImpliedType() 4463 4464 if len(plan.Changes.Resources) != 1 { 4465 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 4466 } 4467 4468 res := plan.Changes.Resources[0] 4469 ric, err := res.Decode(ty) 4470 if err != nil { 4471 t.Fatal(err) 4472 } 4473 4474 if ric.Addr.String() != "module.child2.null_resource.foo" { 4475 t.Fatalf("unexpcetd resource: %s", ric.Addr) 4476 } 4477 } 4478 4479 func TestContext2Plan_targetedOrphan(t *testing.T) { 4480 m := testModule(t, "plan-targeted-orphan") 4481 p := testProvider("aws") 4482 p.DiffFn = testDiffFn 4483 ctx := testContext2(t, &ContextOpts{ 4484 Config: m, 4485 ProviderResolver: providers.ResolverFixed( 4486 map[string]providers.Factory{ 4487 "aws": testProviderFuncFixed(p), 4488 }, 4489 ), 4490 State: MustShimLegacyState(&State{ 4491 Modules: []*ModuleState{ 4492 { 4493 Path: rootModulePath, 4494 Resources: map[string]*ResourceState{ 4495 "aws_instance.orphan": { 4496 Type: "aws_instance", 4497 Primary: &InstanceState{ 4498 ID: "i-789xyz", 4499 }, 4500 }, 4501 "aws_instance.nottargeted": { 4502 Type: "aws_instance", 4503 Primary: &InstanceState{ 4504 ID: "i-abc123", 4505 }, 4506 }, 4507 }, 4508 }, 4509 }, 4510 }), 4511 Destroy: true, 4512 Targets: []addrs.Targetable{ 4513 addrs.RootModuleInstance.Resource( 4514 addrs.ManagedResourceMode, "aws_instance", "orphan", 4515 ), 4516 }, 4517 }) 4518 4519 plan, diags := ctx.Plan() 4520 if diags.HasErrors() { 4521 t.Fatalf("unexpected errors: %s", diags.Err()) 4522 } 4523 4524 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4525 ty := schema.ImpliedType() 4526 4527 if len(plan.Changes.Resources) != 1 { 4528 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 4529 } 4530 4531 for _, res := range plan.Changes.Resources { 4532 ric, err := res.Decode(ty) 4533 if err != nil { 4534 t.Fatal(err) 4535 } 4536 4537 switch i := ric.Addr.String(); i { 4538 case "aws_instance.orphan": 4539 if res.Action != plans.Delete { 4540 t.Fatalf("resource %s should be destroyed", ric.Addr) 4541 } 4542 default: 4543 t.Fatal("unknown instance:", i) 4544 } 4545 } 4546 } 4547 4548 // https://github.com/hashicorp/terraform-plugin-sdk/issues/2538 4549 func TestContext2Plan_targetedModuleOrphan(t *testing.T) { 4550 m := testModule(t, "plan-targeted-module-orphan") 4551 p := testProvider("aws") 4552 p.DiffFn = testDiffFn 4553 ctx := testContext2(t, &ContextOpts{ 4554 Config: m, 4555 ProviderResolver: providers.ResolverFixed( 4556 map[string]providers.Factory{ 4557 "aws": testProviderFuncFixed(p), 4558 }, 4559 ), 4560 State: MustShimLegacyState(&State{ 4561 Modules: []*ModuleState{ 4562 { 4563 Path: []string{"root", "child"}, 4564 Resources: map[string]*ResourceState{ 4565 "aws_instance.orphan": { 4566 Type: "aws_instance", 4567 Primary: &InstanceState{ 4568 ID: "i-789xyz", 4569 }, 4570 Provider: "provider.aws", 4571 }, 4572 "aws_instance.nottargeted": { 4573 Type: "aws_instance", 4574 Primary: &InstanceState{ 4575 ID: "i-abc123", 4576 }, 4577 Provider: "provider.aws", 4578 }, 4579 }, 4580 }, 4581 }, 4582 }), 4583 Destroy: true, 4584 Targets: []addrs.Targetable{ 4585 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 4586 addrs.ManagedResourceMode, "aws_instance", "orphan", 4587 ), 4588 }, 4589 }) 4590 4591 plan, diags := ctx.Plan() 4592 if diags.HasErrors() { 4593 t.Fatalf("unexpected errors: %s", diags.Err()) 4594 } 4595 4596 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4597 ty := schema.ImpliedType() 4598 4599 if len(plan.Changes.Resources) != 1 { 4600 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 4601 } 4602 4603 res := plan.Changes.Resources[0] 4604 ric, err := res.Decode(ty) 4605 if err != nil { 4606 t.Fatal(err) 4607 } 4608 4609 if ric.Addr.String() != "module.child.aws_instance.orphan" { 4610 t.Fatalf("unexpected resource :%s", ric.Addr) 4611 } 4612 if res.Action != plans.Delete { 4613 t.Fatalf("resource %s should be deleted", ric.Addr) 4614 } 4615 } 4616 4617 func TestContext2Plan_targetedModuleUntargetedVariable(t *testing.T) { 4618 m := testModule(t, "plan-targeted-module-untargeted-variable") 4619 p := testProvider("aws") 4620 p.DiffFn = testDiffFn 4621 ctx := testContext2(t, &ContextOpts{ 4622 Config: m, 4623 ProviderResolver: providers.ResolverFixed( 4624 map[string]providers.Factory{ 4625 "aws": testProviderFuncFixed(p), 4626 }, 4627 ), 4628 Targets: []addrs.Targetable{ 4629 addrs.RootModuleInstance.Resource( 4630 addrs.ManagedResourceMode, "aws_instance", "blue", 4631 ), 4632 addrs.RootModuleInstance.Child("blue_mod", addrs.NoKey), 4633 }, 4634 }) 4635 4636 plan, diags := ctx.Plan() 4637 if diags.HasErrors() { 4638 t.Fatalf("unexpected errors: %s", diags.Err()) 4639 } 4640 4641 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4642 ty := schema.ImpliedType() 4643 4644 if len(plan.Changes.Resources) != 2 { 4645 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 4646 } 4647 4648 for _, res := range plan.Changes.Resources { 4649 ric, err := res.Decode(ty) 4650 if err != nil { 4651 t.Fatal(err) 4652 } 4653 if res.Action != plans.Create { 4654 t.Fatalf("resource %s should be created", ric.Addr) 4655 } 4656 switch i := ric.Addr.String(); i { 4657 case "aws_instance.blue": 4658 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4659 "id": cty.UnknownVal(cty.String), 4660 }), ric.After) 4661 case "module.blue_mod.aws_instance.mod": 4662 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4663 "id": cty.UnknownVal(cty.String), 4664 "value": cty.UnknownVal(cty.String), 4665 "type": cty.StringVal("aws_instance"), 4666 }), ric.After) 4667 default: 4668 t.Fatal("unknown instance:", i) 4669 } 4670 } 4671 } 4672 4673 // ensure that outputs missing references due to targetting are removed from 4674 // the graph. 4675 func TestContext2Plan_outputContainsTargetedResource(t *testing.T) { 4676 m := testModule(t, "plan-untargeted-resource-output") 4677 p := testProvider("aws") 4678 p.DiffFn = testDiffFn 4679 ctx := testContext2(t, &ContextOpts{ 4680 Config: m, 4681 ProviderResolver: providers.ResolverFixed( 4682 map[string]providers.Factory{ 4683 "aws": testProviderFuncFixed(p), 4684 }, 4685 ), 4686 Targets: []addrs.Targetable{ 4687 addrs.RootModuleInstance.Child("mod", addrs.NoKey).Resource( 4688 addrs.ManagedResourceMode, "aws_instance", "a", 4689 ), 4690 }, 4691 }) 4692 4693 _, diags := ctx.Plan() 4694 if diags.HasErrors() { 4695 t.Fatalf("err: %s", diags) 4696 } 4697 if len(diags) != 1 { 4698 t.Fatalf("got %d diagnostics; want 1", diags) 4699 } 4700 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 4701 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 4702 } 4703 if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want { 4704 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 4705 } 4706 } 4707 4708 // https://github.com/hashicorp/terraform-plugin-sdk/issues/4515 4709 func TestContext2Plan_targetedOverTen(t *testing.T) { 4710 m := testModule(t, "plan-targeted-over-ten") 4711 p := testProvider("aws") 4712 p.DiffFn = testDiffFn 4713 4714 resources := make(map[string]*ResourceState) 4715 var expectedState []string 4716 for i := 0; i < 13; i++ { 4717 key := fmt.Sprintf("aws_instance.foo.%d", i) 4718 id := fmt.Sprintf("i-abc%d", i) 4719 resources[key] = &ResourceState{ 4720 Type: "aws_instance", 4721 Primary: &InstanceState{ID: id}, 4722 } 4723 expectedState = append(expectedState, 4724 fmt.Sprintf("%s:\n ID = %s\n", key, id)) 4725 } 4726 ctx := testContext2(t, &ContextOpts{ 4727 Config: m, 4728 ProviderResolver: providers.ResolverFixed( 4729 map[string]providers.Factory{ 4730 "aws": testProviderFuncFixed(p), 4731 }, 4732 ), 4733 State: MustShimLegacyState(&State{ 4734 Modules: []*ModuleState{ 4735 { 4736 Path: rootModulePath, 4737 Resources: resources, 4738 }, 4739 }, 4740 }), 4741 Targets: []addrs.Targetable{ 4742 addrs.RootModuleInstance.ResourceInstance( 4743 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1), 4744 ), 4745 }, 4746 }) 4747 4748 plan, diags := ctx.Plan() 4749 if diags.HasErrors() { 4750 t.Fatalf("unexpected errors: %s", diags.Err()) 4751 } 4752 4753 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4754 ty := schema.ImpliedType() 4755 4756 for _, res := range plan.Changes.Resources { 4757 ric, err := res.Decode(ty) 4758 if err != nil { 4759 t.Fatal(err) 4760 } 4761 if res.Action != plans.NoOp { 4762 t.Fatalf("unexpected action %s for %s", res.Action, ric.Addr) 4763 } 4764 } 4765 } 4766 4767 func TestContext2Plan_provider(t *testing.T) { 4768 m := testModule(t, "plan-provider") 4769 p := testProvider("aws") 4770 p.DiffFn = testDiffFn 4771 4772 var value interface{} 4773 p.ConfigureFn = func(c *ResourceConfig) error { 4774 value, _ = c.Get("foo") 4775 return nil 4776 } 4777 4778 ctx := testContext2(t, &ContextOpts{ 4779 Config: m, 4780 ProviderResolver: providers.ResolverFixed( 4781 map[string]providers.Factory{ 4782 "aws": testProviderFuncFixed(p), 4783 }, 4784 ), 4785 Variables: InputValues{ 4786 "foo": &InputValue{ 4787 Value: cty.StringVal("bar"), 4788 SourceType: ValueFromCaller, 4789 }, 4790 }, 4791 }) 4792 4793 if _, err := ctx.Plan(); err != nil { 4794 t.Fatalf("err: %s", err) 4795 } 4796 4797 if value != "bar" { 4798 t.Fatalf("bad: %#v", value) 4799 } 4800 } 4801 4802 func TestContext2Plan_varListErr(t *testing.T) { 4803 m := testModule(t, "plan-var-list-err") 4804 p := testProvider("aws") 4805 ctx := testContext2(t, &ContextOpts{ 4806 Config: m, 4807 ProviderResolver: providers.ResolverFixed( 4808 map[string]providers.Factory{ 4809 "aws": testProviderFuncFixed(p), 4810 }, 4811 ), 4812 }) 4813 4814 _, err := ctx.Plan() 4815 4816 if err == nil { 4817 t.Fatal("should error") 4818 } 4819 } 4820 4821 func TestContext2Plan_ignoreChanges(t *testing.T) { 4822 m := testModule(t, "plan-ignore-changes") 4823 p := testProvider("aws") 4824 4825 p.DiffFn = testDiffFn 4826 4827 s := MustShimLegacyState(&State{ 4828 Modules: []*ModuleState{ 4829 { 4830 Path: rootModulePath, 4831 Resources: map[string]*ResourceState{ 4832 "aws_instance.foo": { 4833 Type: "aws_instance", 4834 Primary: &InstanceState{ 4835 ID: "bar", 4836 Attributes: map[string]string{"ami": "ami-abcd1234"}, 4837 }, 4838 }, 4839 }, 4840 }, 4841 }, 4842 }) 4843 ctx := testContext2(t, &ContextOpts{ 4844 Config: m, 4845 ProviderResolver: providers.ResolverFixed( 4846 map[string]providers.Factory{ 4847 "aws": testProviderFuncFixed(p), 4848 }, 4849 ), 4850 Variables: InputValues{ 4851 "foo": &InputValue{ 4852 Value: cty.StringVal("ami-1234abcd"), 4853 SourceType: ValueFromCaller, 4854 }, 4855 }, 4856 State: s, 4857 }) 4858 4859 plan, diags := ctx.Plan() 4860 if diags.HasErrors() { 4861 t.Fatalf("unexpected errors: %s", diags.Err()) 4862 } 4863 4864 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 4865 ty := schema.ImpliedType() 4866 4867 if len(plan.Changes.Resources) != 1 { 4868 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 4869 } 4870 4871 res := plan.Changes.Resources[0] 4872 ric, err := res.Decode(ty) 4873 if err != nil { 4874 t.Fatal(err) 4875 } 4876 if res.Action != plans.Update { 4877 t.Fatalf("resource %s should be updated, got %s", ric.Addr, res.Action) 4878 } 4879 4880 if ric.Addr.String() != "aws_instance.foo" { 4881 t.Fatalf("unexpected resource: %s", ric.Addr) 4882 } 4883 4884 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 4885 "id": cty.StringVal("bar"), 4886 "ami": cty.StringVal("ami-abcd1234"), 4887 "type": cty.StringVal("aws_instance"), 4888 }), ric.After) 4889 } 4890 4891 func TestContext2Plan_ignoreChangesWildcard(t *testing.T) { 4892 m := testModule(t, "plan-ignore-changes-wildcard") 4893 p := testProvider("aws") 4894 p.DiffFn = testDiffFn 4895 4896 s := MustShimLegacyState(&State{ 4897 Modules: []*ModuleState{ 4898 { 4899 Path: rootModulePath, 4900 Resources: map[string]*ResourceState{ 4901 "aws_instance.foo": { 4902 Type: "aws_instance", 4903 Primary: &InstanceState{ 4904 ID: "bar", 4905 Attributes: map[string]string{ 4906 "ami": "ami-abcd1234", 4907 "instance": "t2.micro", 4908 }, 4909 }, 4910 }, 4911 }, 4912 }, 4913 }, 4914 }) 4915 ctx := testContext2(t, &ContextOpts{ 4916 Config: m, 4917 ProviderResolver: providers.ResolverFixed( 4918 map[string]providers.Factory{ 4919 "aws": testProviderFuncFixed(p), 4920 }, 4921 ), 4922 Variables: InputValues{ 4923 "foo": &InputValue{ 4924 Value: cty.StringVal("ami-1234abcd"), 4925 SourceType: ValueFromCaller, 4926 }, 4927 "bar": &InputValue{ 4928 Value: cty.StringVal("t2.small"), 4929 SourceType: ValueFromCaller, 4930 }, 4931 }, 4932 State: s, 4933 }) 4934 4935 plan, diags := ctx.Plan() 4936 if diags.HasErrors() { 4937 t.Fatalf("unexpected errors: %s", diags.Err()) 4938 } 4939 4940 for _, res := range plan.Changes.Resources { 4941 if res.Action != plans.NoOp { 4942 t.Fatalf("unexpected resource diffs in root module: %s", spew.Sdump(plan.Changes.Resources)) 4943 } 4944 } 4945 } 4946 4947 func TestContext2Plan_ignoreChangesInMap(t *testing.T) { 4948 p := testProvider("test") 4949 4950 p.GetSchemaReturn = &ProviderSchema{ 4951 ResourceTypes: map[string]*configschema.Block{ 4952 "test_ignore_changes_map": { 4953 Attributes: map[string]*configschema.Attribute{ 4954 "tags": {Type: cty.Map(cty.String), Optional: true}, 4955 }, 4956 }, 4957 }, 4958 } 4959 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 4960 return providers.PlanResourceChangeResponse{ 4961 PlannedState: req.ProposedNewState, 4962 } 4963 } 4964 4965 p.DiffFn = testDiffFn 4966 4967 s := states.BuildState(func(ss *states.SyncState) { 4968 ss.SetResourceInstanceCurrent( 4969 addrs.Resource{ 4970 Mode: addrs.ManagedResourceMode, 4971 Type: "test_ignore_changes_map", 4972 Name: "foo", 4973 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 4974 &states.ResourceInstanceObjectSrc{ 4975 Status: states.ObjectReady, 4976 AttrsJSON: []byte(`{"tags":{"ignored":"from state","other":"from state"}}`), 4977 }, 4978 addrs.ProviderConfig{ 4979 Type: "test", 4980 }.Absolute(addrs.RootModuleInstance), 4981 ) 4982 }) 4983 m := testModule(t, "plan-ignore-changes-in-map") 4984 4985 ctx := testContext2(t, &ContextOpts{ 4986 Config: m, 4987 ProviderResolver: providers.ResolverFixed( 4988 map[string]providers.Factory{ 4989 "test": testProviderFuncFixed(p), 4990 }, 4991 ), 4992 State: s, 4993 }) 4994 4995 plan, diags := ctx.Plan() 4996 if diags.HasErrors() { 4997 t.Fatalf("unexpected errors: %s", diags.Err()) 4998 } 4999 5000 schema := p.GetSchemaReturn.ResourceTypes["test_ignore_changes_map"] 5001 ty := schema.ImpliedType() 5002 5003 if got, want := len(plan.Changes.Resources), 1; got != want { 5004 t.Fatalf("wrong number of changes %d; want %d", got, want) 5005 } 5006 5007 res := plan.Changes.Resources[0] 5008 ric, err := res.Decode(ty) 5009 if err != nil { 5010 t.Fatal(err) 5011 } 5012 if res.Action != plans.Update { 5013 t.Fatalf("resource %s should be updated, got %s", ric.Addr, res.Action) 5014 } 5015 5016 if got, want := ric.Addr.String(), "test_ignore_changes_map.foo"; got != want { 5017 t.Fatalf("unexpected resource address %s; want %s", got, want) 5018 } 5019 5020 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5021 "tags": cty.MapVal(map[string]cty.Value{ 5022 "ignored": cty.StringVal("from state"), 5023 "other": cty.StringVal("from config"), 5024 }), 5025 }), ric.After) 5026 } 5027 5028 func TestContext2Plan_moduleMapLiteral(t *testing.T) { 5029 m := testModule(t, "plan-module-map-literal") 5030 p := testProvider("aws") 5031 p.GetSchemaReturn = &ProviderSchema{ 5032 ResourceTypes: map[string]*configschema.Block{ 5033 "aws_instance": { 5034 Attributes: map[string]*configschema.Attribute{ 5035 "meta": {Type: cty.Map(cty.String), Optional: true}, 5036 "tags": {Type: cty.Map(cty.String), Optional: true}, 5037 }, 5038 }, 5039 }, 5040 } 5041 p.ApplyFn = testApplyFn 5042 p.DiffFn = func(i *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 5043 // Here we verify that both the populated and empty map literals made it 5044 // through to the resource attributes 5045 val, _ := c.Get("tags") 5046 m, ok := val.(map[string]interface{}) 5047 if !ok { 5048 t.Fatalf("Tags attr not map: %#v", val) 5049 } 5050 if m["foo"] != "bar" { 5051 t.Fatalf("Bad value in tags attr: %#v", m) 5052 } 5053 { 5054 val, _ := c.Get("meta") 5055 m, ok := val.(map[string]interface{}) 5056 if !ok { 5057 t.Fatalf("Meta attr not map: %#v", val) 5058 } 5059 if len(m) != 0 { 5060 t.Fatalf("Meta attr not empty: %#v", val) 5061 } 5062 } 5063 return nil, nil 5064 } 5065 ctx := testContext2(t, &ContextOpts{ 5066 Config: m, 5067 ProviderResolver: providers.ResolverFixed( 5068 map[string]providers.Factory{ 5069 "aws": testProviderFuncFixed(p), 5070 }, 5071 ), 5072 }) 5073 5074 _, diags := ctx.Plan() 5075 if diags.HasErrors() { 5076 t.Fatalf("unexpected errors: %s", diags.Err()) 5077 } 5078 } 5079 5080 func TestContext2Plan_computedValueInMap(t *testing.T) { 5081 m := testModule(t, "plan-computed-value-in-map") 5082 p := testProvider("aws") 5083 p.GetSchemaReturn = &ProviderSchema{ 5084 ResourceTypes: map[string]*configschema.Block{ 5085 "aws_instance": { 5086 Attributes: map[string]*configschema.Attribute{ 5087 "looked_up": {Type: cty.String, Optional: true}, 5088 }, 5089 }, 5090 "aws_computed_source": { 5091 Attributes: map[string]*configschema.Attribute{ 5092 "computed_read_only": {Type: cty.String, Computed: true}, 5093 }, 5094 }, 5095 }, 5096 } 5097 p.DiffFn = func(info *InstanceInfo, state *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 5098 switch info.Type { 5099 case "aws_computed_source": 5100 return &InstanceDiff{ 5101 Attributes: map[string]*ResourceAttrDiff{ 5102 "computed_read_only": { 5103 NewComputed: true, 5104 }, 5105 }, 5106 }, nil 5107 } 5108 5109 return testDiffFn(info, state, c) 5110 } 5111 5112 ctx := testContext2(t, &ContextOpts{ 5113 Config: m, 5114 ProviderResolver: providers.ResolverFixed( 5115 map[string]providers.Factory{ 5116 "aws": testProviderFuncFixed(p), 5117 }, 5118 ), 5119 }) 5120 5121 plan, diags := ctx.Plan() 5122 if diags.HasErrors() { 5123 t.Fatalf("unexpected errors: %s", diags.Err()) 5124 } 5125 5126 if len(plan.Changes.Resources) != 2 { 5127 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 5128 } 5129 5130 for _, res := range plan.Changes.Resources { 5131 schema := p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] 5132 5133 ric, err := res.Decode(schema.ImpliedType()) 5134 if err != nil { 5135 t.Fatal(err) 5136 } 5137 5138 if res.Action != plans.Create { 5139 t.Fatalf("resource %s should be created", ric.Addr) 5140 } 5141 5142 switch i := ric.Addr.String(); i { 5143 case "aws_computed_source.intermediates": 5144 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5145 "computed_read_only": cty.UnknownVal(cty.String), 5146 }), ric.After) 5147 case "module.test_mod.aws_instance.inner2": 5148 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5149 "looked_up": cty.UnknownVal(cty.String), 5150 }), ric.After) 5151 default: 5152 t.Fatal("unknown instance:", i) 5153 } 5154 } 5155 } 5156 5157 func TestContext2Plan_moduleVariableFromSplat(t *testing.T) { 5158 m := testModule(t, "plan-module-variable-from-splat") 5159 p := testProvider("aws") 5160 p.DiffFn = testDiffFn 5161 p.GetSchemaReturn = &ProviderSchema{ 5162 ResourceTypes: map[string]*configschema.Block{ 5163 "aws_instance": { 5164 Attributes: map[string]*configschema.Attribute{ 5165 "thing": {Type: cty.String, Optional: true}, 5166 }, 5167 }, 5168 }, 5169 } 5170 5171 ctx := testContext2(t, &ContextOpts{ 5172 Config: m, 5173 ProviderResolver: providers.ResolverFixed( 5174 map[string]providers.Factory{ 5175 "aws": testProviderFuncFixed(p), 5176 }, 5177 ), 5178 }) 5179 5180 plan, diags := ctx.Plan() 5181 if diags.HasErrors() { 5182 t.Fatalf("unexpected errors: %s", diags.Err()) 5183 } 5184 5185 if len(plan.Changes.Resources) != 4 { 5186 t.Fatal("expected 4 changes, got", len(plan.Changes.Resources)) 5187 } 5188 5189 for _, res := range plan.Changes.Resources { 5190 schema := p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] 5191 5192 ric, err := res.Decode(schema.ImpliedType()) 5193 if err != nil { 5194 t.Fatal(err) 5195 } 5196 5197 if res.Action != plans.Create { 5198 t.Fatalf("resource %s should be created", ric.Addr) 5199 } 5200 5201 switch i := ric.Addr.String(); i { 5202 case "module.mod1.aws_instance.test[0]", 5203 "module.mod1.aws_instance.test[1]", 5204 "module.mod2.aws_instance.test[0]", 5205 "module.mod2.aws_instance.test[1]": 5206 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5207 "thing": cty.StringVal("doesnt"), 5208 }), ric.After) 5209 default: 5210 t.Fatal("unknown instance:", i) 5211 } 5212 } 5213 } 5214 5215 func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { 5216 m := testModule(t, "plan-cbd-depends-datasource") 5217 p := testProvider("aws") 5218 p.GetSchemaReturn = &ProviderSchema{ 5219 ResourceTypes: map[string]*configschema.Block{ 5220 "aws_instance": { 5221 Attributes: map[string]*configschema.Attribute{ 5222 "num": {Type: cty.String, Optional: true}, 5223 "computed": {Type: cty.String, Optional: true, Computed: true}, 5224 }, 5225 }, 5226 }, 5227 DataSources: map[string]*configschema.Block{ 5228 "aws_vpc": { 5229 Attributes: map[string]*configschema.Attribute{ 5230 "id": {Type: cty.String, Computed: true}, 5231 "foo": {Type: cty.Number, Optional: true}, 5232 }, 5233 }, 5234 }, 5235 } 5236 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 5237 computedVal := req.ProposedNewState.GetAttr("computed") 5238 if computedVal.IsNull() { 5239 computedVal = cty.UnknownVal(cty.String) 5240 } 5241 return providers.PlanResourceChangeResponse{ 5242 PlannedState: cty.ObjectVal(map[string]cty.Value{ 5243 "num": req.ProposedNewState.GetAttr("num"), 5244 "computed": computedVal, 5245 }), 5246 } 5247 } 5248 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 5249 return providers.ReadDataSourceResponse{ 5250 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("ReadDataSource called, but should not have been")), 5251 } 5252 } 5253 5254 ctx := testContext2(t, &ContextOpts{ 5255 Config: m, 5256 ProviderResolver: providers.ResolverFixed( 5257 map[string]providers.Factory{ 5258 "aws": testProviderFuncFixed(p), 5259 }, 5260 ), 5261 }) 5262 5263 // We're skipping ctx.Refresh here, which simulates what happens when 5264 // running "terraform plan -refresh=false". As a result, we don't get our 5265 // usual opportunity to read the data source during the refresh step and 5266 // thus the plan call below is forced to produce a deferred read action. 5267 5268 plan, diags := ctx.Plan() 5269 if p.ReadDataSourceCalled { 5270 t.Errorf("ReadDataSource was called on the provider, but should not have been because we didn't refresh") 5271 } 5272 if diags.HasErrors() { 5273 t.Fatalf("unexpected errors: %s", diags.Err()) 5274 } 5275 5276 seenAddrs := make(map[string]struct{}) 5277 for _, res := range plan.Changes.Resources { 5278 var schema *configschema.Block 5279 switch res.Addr.Resource.Resource.Mode { 5280 case addrs.DataResourceMode: 5281 schema = p.GetSchemaReturn.DataSources[res.Addr.Resource.Resource.Type] 5282 case addrs.ManagedResourceMode: 5283 schema = p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] 5284 } 5285 5286 ric, err := res.Decode(schema.ImpliedType()) 5287 if err != nil { 5288 t.Fatal(err) 5289 } 5290 5291 seenAddrs[ric.Addr.String()] = struct{}{} 5292 5293 t.Run(ric.Addr.String(), func(t *testing.T) { 5294 switch i := ric.Addr.String(); i { 5295 case "aws_instance.foo[0]": 5296 if res.Action != plans.Create { 5297 t.Fatalf("resource %s should be created, got %s", ric.Addr, ric.Action) 5298 } 5299 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5300 "num": cty.StringVal("2"), 5301 "computed": cty.UnknownVal(cty.String), 5302 }), ric.After) 5303 case "aws_instance.foo[1]": 5304 if res.Action != plans.Create { 5305 t.Fatalf("resource %s should be created, got %s", ric.Addr, ric.Action) 5306 } 5307 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5308 "num": cty.StringVal("2"), 5309 "computed": cty.UnknownVal(cty.String), 5310 }), ric.After) 5311 case "data.aws_vpc.bar[0]": 5312 if res.Action != plans.Read { 5313 t.Fatalf("resource %s should be read, got %s", ric.Addr, ric.Action) 5314 } 5315 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5316 // In a normal flow we would've read an exact value in 5317 // ReadDataSource, but because this test doesn't run 5318 // cty.Refresh we have no opportunity to do that lookup 5319 // and a deferred read is forced. 5320 "id": cty.UnknownVal(cty.String), 5321 "foo": cty.StringVal("0"), 5322 }), ric.After) 5323 case "data.aws_vpc.bar[1]": 5324 if res.Action != plans.Read { 5325 t.Fatalf("resource %s should be read, got %s", ric.Addr, ric.Action) 5326 } 5327 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5328 // In a normal flow we would've read an exact value in 5329 // ReadDataSource, but because this test doesn't run 5330 // cty.Refresh we have no opportunity to do that lookup 5331 // and a deferred read is forced. 5332 "id": cty.UnknownVal(cty.String), 5333 "foo": cty.StringVal("1"), 5334 }), ric.After) 5335 default: 5336 t.Fatal("unknown instance:", i) 5337 } 5338 }) 5339 } 5340 5341 wantAddrs := map[string]struct{}{ 5342 "aws_instance.foo[0]": {}, 5343 "aws_instance.foo[1]": {}, 5344 "data.aws_vpc.bar[0]": {}, 5345 "data.aws_vpc.bar[1]": {}, 5346 } 5347 if !cmp.Equal(seenAddrs, wantAddrs) { 5348 t.Errorf("incorrect addresses in changeset:\n%s", cmp.Diff(wantAddrs, seenAddrs)) 5349 } 5350 } 5351 5352 // interpolated lists need to be stored in the original order. 5353 func TestContext2Plan_listOrder(t *testing.T) { 5354 m := testModule(t, "plan-list-order") 5355 p := testProvider("aws") 5356 p.ApplyFn = testApplyFn 5357 p.DiffFn = testDiffFn 5358 p.GetSchemaReturn = &ProviderSchema{ 5359 ResourceTypes: map[string]*configschema.Block{ 5360 "aws_instance": { 5361 Attributes: map[string]*configschema.Attribute{ 5362 "foo": {Type: cty.List(cty.String), Optional: true}, 5363 }, 5364 }, 5365 }, 5366 } 5367 ctx := testContext2(t, &ContextOpts{ 5368 Config: m, 5369 ProviderResolver: providers.ResolverFixed( 5370 map[string]providers.Factory{ 5371 "aws": testProviderFuncFixed(p), 5372 }, 5373 ), 5374 }) 5375 5376 plan, diags := ctx.Plan() 5377 if diags.HasErrors() { 5378 t.Fatalf("unexpected errors: %s", diags.Err()) 5379 } 5380 5381 changes := plan.Changes 5382 rDiffA := changes.ResourceInstance(addrs.Resource{ 5383 Mode: addrs.ManagedResourceMode, 5384 Type: "aws_instance", 5385 Name: "a", 5386 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 5387 rDiffB := changes.ResourceInstance(addrs.Resource{ 5388 Mode: addrs.ManagedResourceMode, 5389 Type: "aws_instance", 5390 Name: "b", 5391 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 5392 5393 if !cmp.Equal(rDiffA.After, rDiffB.After, valueComparer) { 5394 t.Fatal(cmp.Diff(rDiffA.After, rDiffB.After, valueComparer)) 5395 } 5396 } 5397 5398 // Make sure ignore-changes doesn't interfere with set/list/map diffs. 5399 // If a resource was being replaced by a RequiresNew attribute that gets 5400 // ignored, we need to filter the diff properly to properly update rather than 5401 // replace. 5402 func TestContext2Plan_ignoreChangesWithFlatmaps(t *testing.T) { 5403 m := testModule(t, "plan-ignore-changes-with-flatmaps") 5404 p := testProvider("aws") 5405 p.DiffFn = testDiffFn 5406 p.GetSchemaReturn = &ProviderSchema{ 5407 ResourceTypes: map[string]*configschema.Block{ 5408 "aws_instance": { 5409 Attributes: map[string]*configschema.Attribute{ 5410 "user_data": {Type: cty.String, Optional: true}, 5411 "require_new": {Type: cty.String, Optional: true}, 5412 5413 // This test predates the 0.12 work to integrate cty and 5414 // HCL, and so it was ported as-is where its expected 5415 // test output was clearly expecting a list of maps here 5416 // even though it is named "set". 5417 "set": {Type: cty.List(cty.Map(cty.String)), Optional: true}, 5418 "lst": {Type: cty.List(cty.String), Optional: true}, 5419 }, 5420 }, 5421 }, 5422 } 5423 s := MustShimLegacyState(&State{ 5424 Modules: []*ModuleState{ 5425 { 5426 Path: rootModulePath, 5427 Resources: map[string]*ResourceState{ 5428 "aws_instance.foo": { 5429 Type: "aws_instance", 5430 Primary: &InstanceState{ 5431 ID: "bar", 5432 Attributes: map[string]string{ 5433 "user_data": "x", 5434 "require_new": "", 5435 "set.#": "1", 5436 "set.0.%": "1", 5437 "set.0.a": "1", 5438 "lst.#": "1", 5439 "lst.0": "j", 5440 }, 5441 }, 5442 }, 5443 }, 5444 }, 5445 }, 5446 }) 5447 5448 ctx := testContext2(t, &ContextOpts{ 5449 Config: m, 5450 ProviderResolver: providers.ResolverFixed( 5451 map[string]providers.Factory{ 5452 "aws": testProviderFuncFixed(p), 5453 }, 5454 ), 5455 State: s, 5456 }) 5457 5458 plan, diags := ctx.Plan() 5459 if diags.HasErrors() { 5460 t.Fatalf("unexpected errors: %s", diags.Err()) 5461 } 5462 5463 if len(plan.Changes.Resources) != 1 { 5464 t.Fatal("expected 1 changes, got", len(plan.Changes.Resources)) 5465 } 5466 5467 res := plan.Changes.Resources[0] 5468 schema := p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] 5469 5470 ric, err := res.Decode(schema.ImpliedType()) 5471 if err != nil { 5472 t.Fatal(err) 5473 } 5474 5475 if res.Action != plans.Update { 5476 t.Fatalf("resource %s should be updated, got %s", ric.Addr, ric.Action) 5477 } 5478 5479 if ric.Addr.String() != "aws_instance.foo" { 5480 t.Fatalf("unknown resource: %s", ric.Addr) 5481 } 5482 5483 checkVals(t, objectVal(t, schema, map[string]cty.Value{ 5484 "lst": cty.ListVal([]cty.Value{ 5485 cty.StringVal("j"), 5486 cty.StringVal("k"), 5487 }), 5488 "require_new": cty.StringVal(""), 5489 "user_data": cty.StringVal("x"), 5490 "set": cty.ListVal([]cty.Value{cty.MapVal(map[string]cty.Value{ 5491 "a": cty.StringVal("1"), 5492 "b": cty.StringVal("2"), 5493 })}), 5494 }), ric.After) 5495 } 5496 5497 // TestContext2Plan_resourceNestedCount ensures resource sets that depend on 5498 // the count of another resource set (ie: count of a data source that depends 5499 // on another data source's instance count - data.x.foo.*.id) get properly 5500 // normalized to the indexes they should be. This case comes up when there is 5501 // an existing state (after an initial apply). 5502 func TestContext2Plan_resourceNestedCount(t *testing.T) { 5503 m := testModule(t, "nested-resource-count-plan") 5504 p := testProvider("aws") 5505 p.DiffFn = testDiffFn 5506 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 5507 return providers.ReadResourceResponse{ 5508 NewState: req.PriorState, 5509 } 5510 } 5511 s := MustShimLegacyState(&State{ 5512 Modules: []*ModuleState{ 5513 { 5514 Path: rootModulePath, 5515 Resources: map[string]*ResourceState{ 5516 "aws_instance.foo.0": { 5517 Type: "aws_instance", 5518 Provider: "provider.aws", 5519 Primary: &InstanceState{ 5520 ID: "foo0", 5521 Attributes: map[string]string{ 5522 "id": "foo0", 5523 }, 5524 }, 5525 }, 5526 "aws_instance.foo.1": { 5527 Type: "aws_instance", 5528 Provider: "provider.aws", 5529 Primary: &InstanceState{ 5530 ID: "foo1", 5531 Attributes: map[string]string{ 5532 "id": "foo1", 5533 }, 5534 }, 5535 }, 5536 "aws_instance.bar.0": { 5537 Type: "aws_instance", 5538 Provider: "provider.aws", 5539 Dependencies: []string{"aws_instance.foo"}, 5540 Primary: &InstanceState{ 5541 ID: "bar0", 5542 Attributes: map[string]string{ 5543 "id": "bar0", 5544 }, 5545 }, 5546 }, 5547 "aws_instance.bar.1": { 5548 Type: "aws_instance", 5549 Provider: "provider.aws", 5550 Dependencies: []string{"aws_instance.foo"}, 5551 Primary: &InstanceState{ 5552 ID: "bar1", 5553 Attributes: map[string]string{ 5554 "id": "bar1", 5555 }, 5556 }, 5557 }, 5558 "aws_instance.baz.0": { 5559 Type: "aws_instance", 5560 Provider: "provider.aws", 5561 Dependencies: []string{"aws_instance.bar"}, 5562 Primary: &InstanceState{ 5563 ID: "baz0", 5564 Attributes: map[string]string{ 5565 "id": "baz0", 5566 }, 5567 }, 5568 }, 5569 "aws_instance.baz.1": { 5570 Type: "aws_instance", 5571 Provider: "provider.aws", 5572 Dependencies: []string{"aws_instance.bar"}, 5573 Primary: &InstanceState{ 5574 ID: "baz1", 5575 Attributes: map[string]string{ 5576 "id": "baz1", 5577 }, 5578 }, 5579 }, 5580 }, 5581 }, 5582 }, 5583 }) 5584 ctx := testContext2(t, &ContextOpts{ 5585 Config: m, 5586 ProviderResolver: providers.ResolverFixed( 5587 map[string]providers.Factory{ 5588 "aws": testProviderFuncFixed(p), 5589 }, 5590 ), 5591 State: s, 5592 }) 5593 5594 diags := ctx.Validate() 5595 if diags.HasErrors() { 5596 t.Fatalf("validate errors: %s", diags.Err()) 5597 } 5598 5599 _, diags = ctx.Refresh() 5600 if diags.HasErrors() { 5601 t.Fatalf("refresh errors: %s", diags.Err()) 5602 } 5603 5604 plan, diags := ctx.Plan() 5605 if diags.HasErrors() { 5606 t.Fatalf("plan errors: %s", diags.Err()) 5607 } 5608 5609 for _, res := range plan.Changes.Resources { 5610 if res.Action != plans.NoOp { 5611 t.Fatalf("resource %s should now change, plan returned %s", res.Addr, res.Action) 5612 } 5613 } 5614 } 5615 5616 // Higher level test at TestResource_dataSourceListApplyPanic 5617 func TestContext2Plan_computedAttrRefTypeMismatch(t *testing.T) { 5618 m := testModule(t, "plan-computed-attr-ref-type-mismatch") 5619 p := testProvider("aws") 5620 p.DiffFn = testDiffFn 5621 p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { 5622 var diags tfdiags.Diagnostics 5623 if req.TypeName == "aws_instance" { 5624 amiVal := req.Config.GetAttr("ami") 5625 if amiVal.Type() != cty.String { 5626 diags = diags.Append(fmt.Errorf("Expected ami to be cty.String, got %#v", amiVal)) 5627 } 5628 } 5629 return providers.ValidateResourceTypeConfigResponse{ 5630 Diagnostics: diags, 5631 } 5632 } 5633 p.DiffFn = func( 5634 info *InstanceInfo, 5635 state *InstanceState, 5636 c *ResourceConfig) (*InstanceDiff, error) { 5637 switch info.Type { 5638 case "aws_ami_list": 5639 // Emulate a diff that says "we'll create this list and ids will be populated" 5640 return &InstanceDiff{ 5641 Attributes: map[string]*ResourceAttrDiff{ 5642 "ids.#": {NewComputed: true}, 5643 }, 5644 }, nil 5645 case "aws_instance": 5646 // If we get to the diff for instance, we should be able to assume types 5647 ami, _ := c.Get("ami") 5648 _ = ami.(string) 5649 } 5650 return nil, nil 5651 } 5652 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 5653 if info.Type != "aws_ami_list" { 5654 t.Fatalf("Reached apply for unexpected resource type! %s", info.Type) 5655 } 5656 // Pretend like we make a thing and the computed list "ids" is populated 5657 return &InstanceState{ 5658 ID: "someid", 5659 Attributes: map[string]string{ 5660 "ids.#": "2", 5661 "ids.0": "ami-abc123", 5662 "ids.1": "ami-bcd345", 5663 }, 5664 }, nil 5665 } 5666 ctx := testContext2(t, &ContextOpts{ 5667 Config: m, 5668 ProviderResolver: providers.ResolverFixed( 5669 map[string]providers.Factory{ 5670 "aws": testProviderFuncFixed(p), 5671 }, 5672 ), 5673 }) 5674 5675 _, diags := ctx.Plan() 5676 if !diags.HasErrors() { 5677 t.Fatalf("Succeeded; want type mismatch error for 'ami' argument") 5678 } 5679 5680 expected := `Inappropriate value for attribute "ami"` 5681 if errStr := diags.Err().Error(); !strings.Contains(errStr, expected) { 5682 t.Fatalf("expected:\n\n%s\n\nto contain:\n\n%s", errStr, expected) 5683 } 5684 } 5685 5686 func TestContext2Plan_selfRef(t *testing.T) { 5687 p := testProvider("aws") 5688 p.GetSchemaReturn = &ProviderSchema{ 5689 ResourceTypes: map[string]*configschema.Block{ 5690 "aws_instance": { 5691 Attributes: map[string]*configschema.Attribute{ 5692 "foo": {Type: cty.String, Optional: true}, 5693 }, 5694 }, 5695 }, 5696 } 5697 5698 m := testModule(t, "plan-self-ref") 5699 c := testContext2(t, &ContextOpts{ 5700 Config: m, 5701 ProviderResolver: providers.ResolverFixed( 5702 map[string]providers.Factory{ 5703 "aws": testProviderFuncFixed(p), 5704 }, 5705 ), 5706 }) 5707 5708 diags := c.Validate() 5709 if diags.HasErrors() { 5710 t.Fatalf("unexpected validation failure: %s", diags.Err()) 5711 } 5712 5713 _, diags = c.Plan() 5714 if !diags.HasErrors() { 5715 t.Fatalf("plan succeeded; want error") 5716 } 5717 5718 gotErrStr := diags.Err().Error() 5719 wantErrStr := "Self-referential block" 5720 if !strings.Contains(gotErrStr, wantErrStr) { 5721 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) 5722 } 5723 } 5724 5725 func TestContext2Plan_selfRefMulti(t *testing.T) { 5726 p := testProvider("aws") 5727 p.GetSchemaReturn = &ProviderSchema{ 5728 ResourceTypes: map[string]*configschema.Block{ 5729 "aws_instance": { 5730 Attributes: map[string]*configschema.Attribute{ 5731 "foo": {Type: cty.String, Optional: true}, 5732 }, 5733 }, 5734 }, 5735 } 5736 5737 m := testModule(t, "plan-self-ref-multi") 5738 c := testContext2(t, &ContextOpts{ 5739 Config: m, 5740 ProviderResolver: providers.ResolverFixed( 5741 map[string]providers.Factory{ 5742 "aws": testProviderFuncFixed(p), 5743 }, 5744 ), 5745 }) 5746 5747 diags := c.Validate() 5748 if diags.HasErrors() { 5749 t.Fatalf("unexpected validation failure: %s", diags.Err()) 5750 } 5751 5752 _, diags = c.Plan() 5753 if !diags.HasErrors() { 5754 t.Fatalf("plan succeeded; want error") 5755 } 5756 5757 gotErrStr := diags.Err().Error() 5758 wantErrStr := "Self-referential block" 5759 if !strings.Contains(gotErrStr, wantErrStr) { 5760 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) 5761 } 5762 } 5763 5764 func TestContext2Plan_selfRefMultiAll(t *testing.T) { 5765 p := testProvider("aws") 5766 p.GetSchemaReturn = &ProviderSchema{ 5767 ResourceTypes: map[string]*configschema.Block{ 5768 "aws_instance": { 5769 Attributes: map[string]*configschema.Attribute{ 5770 "foo": {Type: cty.List(cty.String), Optional: true}, 5771 }, 5772 }, 5773 }, 5774 } 5775 5776 m := testModule(t, "plan-self-ref-multi-all") 5777 c := testContext2(t, &ContextOpts{ 5778 Config: m, 5779 ProviderResolver: providers.ResolverFixed( 5780 map[string]providers.Factory{ 5781 "aws": testProviderFuncFixed(p), 5782 }, 5783 ), 5784 }) 5785 5786 diags := c.Validate() 5787 if diags.HasErrors() { 5788 t.Fatalf("unexpected validation failure: %s", diags.Err()) 5789 } 5790 5791 _, diags = c.Plan() 5792 if !diags.HasErrors() { 5793 t.Fatalf("plan succeeded; want error") 5794 } 5795 5796 gotErrStr := diags.Err().Error() 5797 5798 // The graph is checked for cycles before we can walk it, so we don't 5799 // encounter the self-reference check. 5800 //wantErrStr := "Self-referential block" 5801 wantErrStr := "Cycle" 5802 if !strings.Contains(gotErrStr, wantErrStr) { 5803 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) 5804 } 5805 } 5806 5807 func TestContext2Plan_invalidOutput(t *testing.T) { 5808 m := testModuleInline(t, map[string]string{ 5809 "main.tf": ` 5810 data "aws_data_source" "name" {} 5811 5812 output "out" { 5813 value = "${data.aws_data_source.name.missing}" 5814 }`, 5815 }) 5816 5817 p := testProvider("aws") 5818 ctx := testContext2(t, &ContextOpts{ 5819 Config: m, 5820 ProviderResolver: providers.ResolverFixed( 5821 map[string]providers.Factory{ 5822 "aws": testProviderFuncFixed(p), 5823 }, 5824 ), 5825 }) 5826 5827 _, diags := ctx.Plan() 5828 if !diags.HasErrors() { 5829 // Should get this error: 5830 // Unsupported attribute: This object does not have an attribute named "missing" 5831 t.Fatal("succeeded; want errors") 5832 } 5833 5834 gotErrStr := diags.Err().Error() 5835 wantErrStr := "Unsupported attribute" 5836 if !strings.Contains(gotErrStr, wantErrStr) { 5837 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) 5838 } 5839 } 5840 5841 func TestContext2Plan_invalidModuleOutput(t *testing.T) { 5842 m := testModuleInline(t, map[string]string{ 5843 "child/main.tf": ` 5844 data "aws_data_source" "name" {} 5845 5846 output "out" { 5847 value = "${data.aws_data_source.name.missing}" 5848 }`, 5849 "main.tf": ` 5850 module "child" { 5851 source = "./child" 5852 } 5853 5854 resource "aws_instance" "foo" { 5855 foo = "${module.child.out}" 5856 }`, 5857 }) 5858 5859 p := testProvider("aws") 5860 ctx := testContext2(t, &ContextOpts{ 5861 Config: m, 5862 ProviderResolver: providers.ResolverFixed( 5863 map[string]providers.Factory{ 5864 "aws": testProviderFuncFixed(p), 5865 }, 5866 ), 5867 }) 5868 5869 _, diags := ctx.Plan() 5870 if !diags.HasErrors() { 5871 // Should get this error: 5872 // Unsupported attribute: This object does not have an attribute named "missing" 5873 t.Fatal("succeeded; want errors") 5874 } 5875 5876 gotErrStr := diags.Err().Error() 5877 wantErrStr := "Unsupported attribute" 5878 if !strings.Contains(gotErrStr, wantErrStr) { 5879 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) 5880 } 5881 } 5882 5883 func TestContext2Plan_variableValidation(t *testing.T) { 5884 m := testModuleInline(t, map[string]string{ 5885 "main.tf": ` 5886 variable "x" { 5887 default = "bar" 5888 } 5889 5890 resource "aws_instance" "foo" { 5891 foo = var.x 5892 }`, 5893 }) 5894 5895 p := testProvider("aws") 5896 p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) { 5897 foo := req.Config.GetAttr("foo").AsString() 5898 if foo == "bar" { 5899 resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo cannot be bar")) 5900 } 5901 return 5902 } 5903 5904 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 5905 resp.PlannedState = req.ProposedNewState 5906 return 5907 } 5908 5909 ctx := testContext2(t, &ContextOpts{ 5910 Config: m, 5911 ProviderResolver: providers.ResolverFixed( 5912 map[string]providers.Factory{ 5913 "aws": testProviderFuncFixed(p), 5914 }, 5915 ), 5916 }) 5917 5918 _, diags := ctx.Plan() 5919 if !diags.HasErrors() { 5920 // Should get this error: 5921 // Unsupported attribute: This object does not have an attribute named "missing" 5922 t.Fatal("succeeded; want errors") 5923 } 5924 } 5925 5926 func checkVals(t *testing.T, expected, got cty.Value) { 5927 t.Helper() 5928 if !cmp.Equal(expected, got, valueComparer, typeComparer, equateEmpty) { 5929 t.Fatal(cmp.Diff(expected, got, valueTrans, equateEmpty)) 5930 } 5931 } 5932 5933 func objectVal(t *testing.T, schema *configschema.Block, m map[string]cty.Value) cty.Value { 5934 t.Helper() 5935 v, err := schema.CoerceValue( 5936 cty.ObjectVal(m), 5937 ) 5938 if err != nil { 5939 t.Fatal(err) 5940 } 5941 return v 5942 } 5943 5944 func TestContext2Plan_requiredModuleOutput(t *testing.T) { 5945 m := testModule(t, "plan-required-output") 5946 p := testProvider("test") 5947 p.GetSchemaReturn = &ProviderSchema{ 5948 ResourceTypes: map[string]*configschema.Block{ 5949 "test_resource": { 5950 Attributes: map[string]*configschema.Attribute{ 5951 "id": {Type: cty.String, Computed: true}, 5952 "required": {Type: cty.String, Required: true}, 5953 }, 5954 }, 5955 }, 5956 } 5957 p.DiffFn = testDiffFn 5958 5959 ctx := testContext2(t, &ContextOpts{ 5960 Config: m, 5961 ProviderResolver: providers.ResolverFixed( 5962 map[string]providers.Factory{ 5963 "test": testProviderFuncFixed(p), 5964 }, 5965 ), 5966 }) 5967 5968 plan, diags := ctx.Plan() 5969 if diags.HasErrors() { 5970 t.Fatalf("unexpected errors: %s", diags.Err()) 5971 } 5972 5973 schema := p.GetSchemaReturn.ResourceTypes["test_resource"] 5974 ty := schema.ImpliedType() 5975 5976 if len(plan.Changes.Resources) != 2 { 5977 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 5978 } 5979 5980 for _, res := range plan.Changes.Resources { 5981 t.Run(fmt.Sprintf("%s %s", res.Action, res.Addr), func(t *testing.T) { 5982 if res.Action != plans.Create { 5983 t.Fatalf("expected resource creation, got %s", res.Action) 5984 } 5985 ric, err := res.Decode(ty) 5986 if err != nil { 5987 t.Fatal(err) 5988 } 5989 5990 var expected cty.Value 5991 switch i := ric.Addr.String(); i { 5992 case "test_resource.root": 5993 expected = objectVal(t, schema, map[string]cty.Value{ 5994 "id": cty.UnknownVal(cty.String), 5995 "required": cty.UnknownVal(cty.String), 5996 }) 5997 case "module.mod.test_resource.for_output": 5998 expected = objectVal(t, schema, map[string]cty.Value{ 5999 "id": cty.UnknownVal(cty.String), 6000 "required": cty.StringVal("val"), 6001 }) 6002 default: 6003 t.Fatal("unknown instance:", i) 6004 } 6005 6006 checkVals(t, expected, ric.After) 6007 }) 6008 } 6009 } 6010 6011 func TestContext2Plan_requiredModuleObject(t *testing.T) { 6012 m := testModule(t, "plan-required-whole-mod") 6013 p := testProvider("test") 6014 p.GetSchemaReturn = &ProviderSchema{ 6015 ResourceTypes: map[string]*configschema.Block{ 6016 "test_resource": { 6017 Attributes: map[string]*configschema.Attribute{ 6018 "id": {Type: cty.String, Computed: true}, 6019 "required": {Type: cty.String, Required: true}, 6020 }, 6021 }, 6022 }, 6023 } 6024 p.DiffFn = testDiffFn 6025 6026 ctx := testContext2(t, &ContextOpts{ 6027 Config: m, 6028 ProviderResolver: providers.ResolverFixed( 6029 map[string]providers.Factory{ 6030 "test": testProviderFuncFixed(p), 6031 }, 6032 ), 6033 }) 6034 6035 plan, diags := ctx.Plan() 6036 if diags.HasErrors() { 6037 t.Fatalf("unexpected errors: %s", diags.Err()) 6038 } 6039 6040 schema := p.GetSchemaReturn.ResourceTypes["test_resource"] 6041 ty := schema.ImpliedType() 6042 6043 if len(plan.Changes.Resources) != 2 { 6044 t.Fatal("expected 2 changes, got", len(plan.Changes.Resources)) 6045 } 6046 6047 for _, res := range plan.Changes.Resources { 6048 t.Run(fmt.Sprintf("%s %s", res.Action, res.Addr), func(t *testing.T) { 6049 if res.Action != plans.Create { 6050 t.Fatalf("expected resource creation, got %s", res.Action) 6051 } 6052 ric, err := res.Decode(ty) 6053 if err != nil { 6054 t.Fatal(err) 6055 } 6056 6057 var expected cty.Value 6058 switch i := ric.Addr.String(); i { 6059 case "test_resource.root": 6060 expected = objectVal(t, schema, map[string]cty.Value{ 6061 "id": cty.UnknownVal(cty.String), 6062 "required": cty.UnknownVal(cty.String), 6063 }) 6064 case "module.mod.test_resource.for_output": 6065 expected = objectVal(t, schema, map[string]cty.Value{ 6066 "id": cty.UnknownVal(cty.String), 6067 "required": cty.StringVal("val"), 6068 }) 6069 default: 6070 t.Fatal("unknown instance:", i) 6071 } 6072 6073 checkVals(t, expected, ric.After) 6074 }) 6075 } 6076 }