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