github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/context_apply_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "log" 9 "reflect" 10 "runtime" 11 "sort" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "testing" 16 "time" 17 18 "github.com/davecgh/go-spew/spew" 19 "github.com/go-test/deep" 20 "github.com/google/go-cmp/cmp" 21 "github.com/muratcelep/terraform/not-internal/addrs" 22 "github.com/muratcelep/terraform/not-internal/configs" 23 "github.com/muratcelep/terraform/not-internal/configs/configschema" 24 "github.com/muratcelep/terraform/not-internal/configs/hcl2shim" 25 "github.com/muratcelep/terraform/not-internal/lang/marks" 26 "github.com/muratcelep/terraform/not-internal/plans" 27 "github.com/muratcelep/terraform/not-internal/providers" 28 "github.com/muratcelep/terraform/not-internal/provisioners" 29 "github.com/muratcelep/terraform/not-internal/states" 30 "github.com/muratcelep/terraform/not-internal/tfdiags" 31 "github.com/zclconf/go-cty/cty" 32 "github.com/zclconf/go-cty/cty/gocty" 33 ) 34 35 func TestContext2Apply_basic(t *testing.T) { 36 m := testModule(t, "apply-good") 37 p := testProvider("aws") 38 p.PlanResourceChangeFn = testDiffFn 39 p.ApplyResourceChangeFn = testApplyFn 40 ctx := testContext2(t, &ContextOpts{ 41 Providers: map[addrs.Provider]providers.Factory{ 42 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 43 }, 44 }) 45 46 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 47 assertNoErrors(t, diags) 48 49 state, diags := ctx.Apply(plan, m) 50 if diags.HasErrors() { 51 t.Fatalf("diags: %s", diags.Err()) 52 } 53 54 mod := state.RootModule() 55 if len(mod.Resources) < 2 { 56 t.Fatalf("bad: %#v", mod.Resources) 57 } 58 59 actual := strings.TrimSpace(state.String()) 60 expected := strings.TrimSpace(testTerraformApplyStr) 61 if actual != expected { 62 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 63 } 64 } 65 66 func TestContext2Apply_unstable(t *testing.T) { 67 // This tests behavior when the configuration contains an unstable value, 68 // such as the result of uuid() or timestamp(), where each call produces 69 // a different result. 70 // 71 // This is an important case to test because we need to ensure that 72 // we don't re-call the function during the apply phase: the value should 73 // be fixed during plan 74 75 m := testModule(t, "apply-unstable") 76 p := testProvider("test") 77 p.PlanResourceChangeFn = testDiffFn 78 ctx := testContext2(t, &ContextOpts{ 79 Providers: map[addrs.Provider]providers.Factory{ 80 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 81 }, 82 }) 83 84 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 85 if diags.HasErrors() { 86 t.Fatalf("unexpected error during Plan: %s", diags.Err()) 87 } 88 89 addr := addrs.Resource{ 90 Mode: addrs.ManagedResourceMode, 91 Type: "test_resource", 92 Name: "foo", 93 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) 94 schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block 95 rds := plan.Changes.ResourceInstance(addr) 96 rd, err := rds.Decode(schema.ImpliedType()) 97 if err != nil { 98 t.Fatal(err) 99 } 100 if rd.After.GetAttr("random").IsKnown() { 101 t.Fatalf("Attribute 'random' has known value %#v; should be unknown in plan", rd.After.GetAttr("random")) 102 } 103 104 state, diags := ctx.Apply(plan, m) 105 if diags.HasErrors() { 106 t.Fatalf("unexpected error during Apply: %s", diags.Err()) 107 } 108 109 mod := state.Module(addr.Module) 110 rss := state.ResourceInstance(addr) 111 112 if len(mod.Resources) != 1 { 113 t.Fatalf("wrong number of resources %d; want 1", len(mod.Resources)) 114 } 115 116 rs, err := rss.Current.Decode(schema.ImpliedType()) 117 if err != nil { 118 t.Fatalf("decode error: %v", err) 119 } 120 got := rs.Value.GetAttr("random") 121 if !got.IsKnown() { 122 t.Fatalf("random is still unknown after apply") 123 } 124 if got, want := len(got.AsString()), 36; got != want { 125 t.Fatalf("random string has wrong length %d; want %d", got, want) 126 } 127 } 128 129 func TestContext2Apply_escape(t *testing.T) { 130 m := testModule(t, "apply-escape") 131 p := testProvider("aws") 132 p.PlanResourceChangeFn = testDiffFn 133 p.ApplyResourceChangeFn = testApplyFn 134 ctx := testContext2(t, &ContextOpts{ 135 Providers: map[addrs.Provider]providers.Factory{ 136 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 137 }, 138 }) 139 140 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 141 assertNoErrors(t, diags) 142 143 state, diags := ctx.Apply(plan, m) 144 if diags.HasErrors() { 145 t.Fatalf("diags: %s", diags.Err()) 146 } 147 148 checkStateString(t, state, ` 149 aws_instance.bar: 150 ID = foo 151 provider = provider["registry.terraform.io/hashicorp/aws"] 152 foo = "bar" 153 type = aws_instance 154 `) 155 } 156 157 func TestContext2Apply_resourceCountOneList(t *testing.T) { 158 m := testModule(t, "apply-resource-count-one-list") 159 p := testProvider("null") 160 p.PlanResourceChangeFn = testDiffFn 161 p.ApplyResourceChangeFn = testApplyFn 162 ctx := testContext2(t, &ContextOpts{ 163 Providers: map[addrs.Provider]providers.Factory{ 164 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 165 }, 166 }) 167 168 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 169 assertNoErrors(t, diags) 170 171 state, diags := ctx.Apply(plan, m) 172 assertNoDiagnostics(t, diags) 173 174 got := strings.TrimSpace(state.String()) 175 want := strings.TrimSpace(`null_resource.foo.0: 176 ID = foo 177 provider = provider["registry.terraform.io/hashicorp/null"] 178 179 Outputs: 180 181 test = [foo]`) 182 if got != want { 183 t.Fatalf("got:\n%s\n\nwant:\n%s\n", got, want) 184 } 185 } 186 func TestContext2Apply_resourceCountZeroList(t *testing.T) { 187 m := testModule(t, "apply-resource-count-zero-list") 188 p := testProvider("null") 189 p.PlanResourceChangeFn = testDiffFn 190 ctx := testContext2(t, &ContextOpts{ 191 Providers: map[addrs.Provider]providers.Factory{ 192 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 193 }, 194 }) 195 196 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 197 assertNoErrors(t, diags) 198 199 state, diags := ctx.Apply(plan, m) 200 if diags.HasErrors() { 201 t.Fatalf("diags: %s", diags.Err()) 202 } 203 204 got := strings.TrimSpace(state.String()) 205 want := strings.TrimSpace(`<no state> 206 Outputs: 207 208 test = []`) 209 if got != want { 210 t.Fatalf("wrong state\n\ngot:\n%s\n\nwant:\n%s\n", got, want) 211 } 212 } 213 214 func TestContext2Apply_resourceDependsOnModule(t *testing.T) { 215 m := testModule(t, "apply-resource-depends-on-module") 216 p := testProvider("aws") 217 p.PlanResourceChangeFn = testDiffFn 218 219 // verify the apply happens in the correct order 220 var mu sync.Mutex 221 var order []string 222 223 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 224 ami := req.PlannedState.GetAttr("ami").AsString() 225 switch ami { 226 case "child": 227 228 // make the child slower than the parent 229 time.Sleep(50 * time.Millisecond) 230 231 mu.Lock() 232 order = append(order, "child") 233 mu.Unlock() 234 case "parent": 235 mu.Lock() 236 order = append(order, "parent") 237 mu.Unlock() 238 } 239 240 return testApplyFn(req) 241 } 242 243 ctx := testContext2(t, &ContextOpts{ 244 Providers: map[addrs.Provider]providers.Factory{ 245 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 246 }, 247 }) 248 249 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 250 assertNoErrors(t, diags) 251 252 state, diags := ctx.Apply(plan, m) 253 if diags.HasErrors() { 254 t.Fatalf("diags: %s", diags.Err()) 255 } 256 257 if !reflect.DeepEqual(order, []string{"child", "parent"}) { 258 t.Fatal("resources applied out of order") 259 } 260 261 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleStr) 262 } 263 264 // Test that without a config, the Dependencies in the state are enough 265 // to maintain proper ordering. 266 func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) { 267 m := testModule(t, "apply-resource-depends-on-module-empty") 268 p := testProvider("aws") 269 p.PlanResourceChangeFn = testDiffFn 270 271 state := states.NewState() 272 root := state.EnsureModule(addrs.RootModuleInstance) 273 root.SetResourceInstanceCurrent( 274 mustResourceInstanceAddr("aws_instance.a").Resource, 275 &states.ResourceInstanceObjectSrc{ 276 Status: states.ObjectReady, 277 AttrsJSON: []byte(`{"id":"parent"}`), 278 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.aws_instance.child")}, 279 }, 280 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 281 ) 282 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 283 child.SetResourceInstanceCurrent( 284 mustResourceInstanceAddr("aws_instance.child").Resource, 285 &states.ResourceInstanceObjectSrc{ 286 Status: states.ObjectReady, 287 AttrsJSON: []byte(`{"id":"child"}`), 288 }, 289 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 290 ) 291 292 { 293 // verify the apply happens in the correct order 294 var mu sync.Mutex 295 var order []string 296 297 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 298 id := req.PriorState.GetAttr("id") 299 if id.IsKnown() && id.AsString() == "parent" { 300 // make the dep slower than the parent 301 time.Sleep(50 * time.Millisecond) 302 303 mu.Lock() 304 order = append(order, "child") 305 mu.Unlock() 306 } else { 307 mu.Lock() 308 order = append(order, "parent") 309 mu.Unlock() 310 } 311 312 return testApplyFn(req) 313 } 314 315 ctx := testContext2(t, &ContextOpts{ 316 Providers: map[addrs.Provider]providers.Factory{ 317 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 318 }, 319 }) 320 321 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 322 assertNoErrors(t, diags) 323 324 state, diags := ctx.Apply(plan, m) 325 assertNoErrors(t, diags) 326 327 if !reflect.DeepEqual(order, []string{"child", "parent"}) { 328 t.Fatal("resources applied out of order") 329 } 330 331 checkStateString(t, state, "<no state>") 332 } 333 } 334 335 func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) { 336 m := testModule(t, "apply-resource-depends-on-module") 337 p := testProvider("aws") 338 p.PlanResourceChangeFn = testDiffFn 339 340 var globalState *states.State 341 { 342 ctx := testContext2(t, &ContextOpts{ 343 Providers: map[addrs.Provider]providers.Factory{ 344 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 345 }, 346 }) 347 348 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 349 assertNoErrors(t, diags) 350 351 state, diags := ctx.Apply(plan, m) 352 if diags.HasErrors() { 353 t.Fatalf("diags: %s", diags.Err()) 354 } 355 356 globalState = state 357 } 358 359 { 360 // Wait for the dependency, sleep, and verify the graph never 361 // called a child. 362 var called int32 363 var checked bool 364 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 365 ami := req.PriorState.GetAttr("ami").AsString() 366 if ami == "parent" { 367 checked = true 368 369 // Sleep to allow parallel execution 370 time.Sleep(50 * time.Millisecond) 371 372 // Verify that called is 0 (dep not called) 373 if atomic.LoadInt32(&called) != 0 { 374 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("module child should not be called")) 375 return resp 376 } 377 } 378 379 atomic.AddInt32(&called, 1) 380 return testApplyFn(req) 381 } 382 383 ctx := testContext2(t, &ContextOpts{ 384 Providers: map[addrs.Provider]providers.Factory{ 385 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 386 }, 387 }) 388 389 plan, diags := ctx.Plan(m, globalState, &PlanOpts{ 390 Mode: plans.DestroyMode, 391 }) 392 assertNoErrors(t, diags) 393 394 state, diags := ctx.Apply(plan, m) 395 if diags.HasErrors() { 396 t.Fatalf("diags: %s", diags.Err()) 397 } 398 399 if !checked { 400 t.Fatal("should check") 401 } 402 403 checkStateString(t, state, `<no state>`) 404 } 405 } 406 407 func TestContext2Apply_resourceDependsOnModuleGrandchild(t *testing.T) { 408 m := testModule(t, "apply-resource-depends-on-module-deep") 409 p := testProvider("aws") 410 p.PlanResourceChangeFn = testDiffFn 411 412 { 413 // Wait for the dependency, sleep, and verify the graph never 414 // called a child. 415 var called int32 416 var checked bool 417 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 418 planned := req.PlannedState.AsValueMap() 419 if ami, ok := planned["ami"]; ok && ami.AsString() == "grandchild" { 420 checked = true 421 422 // Sleep to allow parallel execution 423 time.Sleep(50 * time.Millisecond) 424 425 // Verify that called is 0 (dep not called) 426 if atomic.LoadInt32(&called) != 0 { 427 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("aws_instance.a should not be called")) 428 return resp 429 } 430 } 431 432 atomic.AddInt32(&called, 1) 433 return testApplyFn(req) 434 } 435 436 ctx := testContext2(t, &ContextOpts{ 437 Providers: map[addrs.Provider]providers.Factory{ 438 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 439 }, 440 }) 441 442 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 443 assertNoErrors(t, diags) 444 445 state, diags := ctx.Apply(plan, m) 446 if diags.HasErrors() { 447 t.Fatalf("diags: %s", diags.Err()) 448 } 449 450 if !checked { 451 t.Fatal("should check") 452 } 453 454 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleDeepStr) 455 } 456 } 457 458 func TestContext2Apply_resourceDependsOnModuleInModule(t *testing.T) { 459 m := testModule(t, "apply-resource-depends-on-module-in-module") 460 p := testProvider("aws") 461 p.PlanResourceChangeFn = testDiffFn 462 463 { 464 // Wait for the dependency, sleep, and verify the graph never 465 // called a child. 466 var called int32 467 var checked bool 468 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 469 planned := req.PlannedState.AsValueMap() 470 if ami, ok := planned["ami"]; ok && ami.AsString() == "grandchild" { 471 checked = true 472 473 // Sleep to allow parallel execution 474 time.Sleep(50 * time.Millisecond) 475 476 // Verify that called is 0 (dep not called) 477 if atomic.LoadInt32(&called) != 0 { 478 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("something else was applied before grandchild; grandchild should be first")) 479 return resp 480 } 481 } 482 483 atomic.AddInt32(&called, 1) 484 return testApplyFn(req) 485 } 486 487 ctx := testContext2(t, &ContextOpts{ 488 Providers: map[addrs.Provider]providers.Factory{ 489 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 490 }, 491 }) 492 493 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 494 assertNoErrors(t, diags) 495 496 state, diags := ctx.Apply(plan, m) 497 if diags.HasErrors() { 498 t.Fatalf("diags: %s", diags.Err()) 499 } 500 501 if !checked { 502 t.Fatal("should check") 503 } 504 505 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleInModuleStr) 506 } 507 } 508 509 func TestContext2Apply_mapVarBetweenModules(t *testing.T) { 510 m := testModule(t, "apply-map-var-through-module") 511 p := testProvider("null") 512 p.PlanResourceChangeFn = testDiffFn 513 p.ApplyResourceChangeFn = testApplyFn 514 ctx := testContext2(t, &ContextOpts{ 515 Providers: map[addrs.Provider]providers.Factory{ 516 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 517 }, 518 }) 519 520 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 521 assertNoErrors(t, diags) 522 523 state, diags := ctx.Apply(plan, m) 524 if diags.HasErrors() { 525 t.Fatalf("diags: %s", diags.Err()) 526 } 527 528 actual := strings.TrimSpace(state.String()) 529 expected := strings.TrimSpace(`<no state> 530 Outputs: 531 532 amis_from_module = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 } 533 534 module.test: 535 null_resource.noop: 536 ID = foo 537 provider = provider["registry.terraform.io/hashicorp/null"] 538 539 Outputs: 540 541 amis_out = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }`) 542 if actual != expected { 543 t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual) 544 } 545 } 546 547 func TestContext2Apply_refCount(t *testing.T) { 548 m := testModule(t, "apply-ref-count") 549 p := testProvider("aws") 550 p.PlanResourceChangeFn = testDiffFn 551 p.ApplyResourceChangeFn = testApplyFn 552 ctx := testContext2(t, &ContextOpts{ 553 Providers: map[addrs.Provider]providers.Factory{ 554 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 555 }, 556 }) 557 558 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 559 assertNoErrors(t, diags) 560 561 state, diags := ctx.Apply(plan, m) 562 if diags.HasErrors() { 563 t.Fatalf("diags: %s", diags.Err()) 564 } 565 566 mod := state.RootModule() 567 if len(mod.Resources) < 2 { 568 t.Fatalf("bad: %#v", mod.Resources) 569 } 570 571 actual := strings.TrimSpace(state.String()) 572 expected := strings.TrimSpace(testTerraformApplyRefCountStr) 573 if actual != expected { 574 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 575 } 576 } 577 578 func TestContext2Apply_providerAlias(t *testing.T) { 579 m := testModule(t, "apply-provider-alias") 580 581 // Each provider instance must be completely independent to ensure that we 582 // are verifying the correct state of each. 583 p := func() (providers.Interface, error) { 584 p := testProvider("aws") 585 p.PlanResourceChangeFn = testDiffFn 586 p.ApplyResourceChangeFn = testApplyFn 587 return p, nil 588 } 589 ctx := testContext2(t, &ContextOpts{ 590 Providers: map[addrs.Provider]providers.Factory{ 591 addrs.NewDefaultProvider("aws"): p, 592 }, 593 }) 594 595 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 596 assertNoErrors(t, diags) 597 598 state, diags := ctx.Apply(plan, m) 599 if diags.HasErrors() { 600 t.Fatalf("diags: %s", diags.Err()) 601 } 602 603 mod := state.RootModule() 604 if len(mod.Resources) < 2 { 605 t.Fatalf("bad: %#v", mod.Resources) 606 } 607 608 actual := strings.TrimSpace(state.String()) 609 expected := strings.TrimSpace(testTerraformApplyProviderAliasStr) 610 if actual != expected { 611 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 612 } 613 } 614 615 // Two providers that are configured should both be configured prior to apply 616 func TestContext2Apply_providerAliasConfigure(t *testing.T) { 617 m := testModule(t, "apply-provider-alias-configure") 618 619 // Each provider instance must be completely independent to ensure that we 620 // are verifying the correct state of each. 621 p := func() (providers.Interface, error) { 622 p := testProvider("another") 623 p.ApplyResourceChangeFn = testApplyFn 624 p.PlanResourceChangeFn = testDiffFn 625 return p, nil 626 } 627 628 ctx := testContext2(t, &ContextOpts{ 629 Providers: map[addrs.Provider]providers.Factory{ 630 addrs.NewDefaultProvider("another"): p, 631 }, 632 }) 633 634 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 635 if diags.HasErrors() { 636 t.Fatalf("diags: %s", diags.Err()) 637 } else { 638 t.Logf(legacyDiffComparisonString(plan.Changes)) 639 } 640 641 // Configure to record calls AFTER Plan above 642 var configCount int32 643 p = func() (providers.Interface, error) { 644 p := testProvider("another") 645 p.ApplyResourceChangeFn = testApplyFn 646 p.PlanResourceChangeFn = testDiffFn 647 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 648 atomic.AddInt32(&configCount, 1) 649 650 foo := req.Config.GetAttr("foo").AsString() 651 if foo != "bar" { 652 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("foo: %#v", foo)) 653 } 654 655 return 656 } 657 return p, nil 658 } 659 660 ctx = testContext2(t, &ContextOpts{ 661 Providers: map[addrs.Provider]providers.Factory{ 662 addrs.NewDefaultProvider("another"): p, 663 }, 664 }) 665 666 state, diags := ctx.Apply(plan, m) 667 if diags.HasErrors() { 668 t.Fatalf("diags: %s", diags.Err()) 669 } 670 671 if configCount != 2 { 672 t.Fatalf("provider config expected 2 calls, got: %d", configCount) 673 } 674 675 actual := strings.TrimSpace(state.String()) 676 expected := strings.TrimSpace(testTerraformApplyProviderAliasConfigStr) 677 if actual != expected { 678 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 679 } 680 } 681 682 // GH-2870 683 func TestContext2Apply_providerWarning(t *testing.T) { 684 m := testModule(t, "apply-provider-warning") 685 p := testProvider("aws") 686 p.PlanResourceChangeFn = testDiffFn 687 p.ApplyResourceChangeFn = testApplyFn 688 p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { 689 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("just a warning")) 690 return 691 } 692 ctx := testContext2(t, &ContextOpts{ 693 Providers: map[addrs.Provider]providers.Factory{ 694 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 695 }, 696 }) 697 698 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 699 assertNoErrors(t, diags) 700 701 state, diags := ctx.Apply(plan, m) 702 if diags.HasErrors() { 703 t.Fatalf("diags: %s", diags.Err()) 704 } 705 706 actual := strings.TrimSpace(state.String()) 707 expected := strings.TrimSpace(` 708 aws_instance.foo: 709 ID = foo 710 provider = provider["registry.terraform.io/hashicorp/aws"] 711 type = aws_instance 712 `) 713 if actual != expected { 714 t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected) 715 } 716 717 if !p.ConfigureProviderCalled { 718 t.Fatalf("provider Configure() was never called!") 719 } 720 } 721 722 func TestContext2Apply_emptyModule(t *testing.T) { 723 // A module with only outputs (no resources) 724 m := testModule(t, "apply-empty-module") 725 p := testProvider("aws") 726 p.PlanResourceChangeFn = testDiffFn 727 ctx := testContext2(t, &ContextOpts{ 728 Providers: map[addrs.Provider]providers.Factory{ 729 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 730 }, 731 }) 732 733 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 734 assertNoErrors(t, diags) 735 736 state, diags := ctx.Apply(plan, m) 737 if diags.HasErrors() { 738 t.Fatalf("diags: %s", diags.Err()) 739 } 740 741 actual := strings.TrimSpace(state.String()) 742 actual = strings.Replace(actual, " ", "", -1) 743 expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr) 744 if actual != expected { 745 t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected) 746 } 747 } 748 749 func TestContext2Apply_createBeforeDestroy(t *testing.T) { 750 m := testModule(t, "apply-good-create-before") 751 p := testProvider("aws") 752 p.PlanResourceChangeFn = testDiffFn 753 p.ApplyResourceChangeFn = testApplyFn 754 state := states.NewState() 755 root := state.EnsureModule(addrs.RootModuleInstance) 756 root.SetResourceInstanceCurrent( 757 mustResourceInstanceAddr("aws_instance.bar").Resource, 758 &states.ResourceInstanceObjectSrc{ 759 Status: states.ObjectReady, 760 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 761 }, 762 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 763 ) 764 ctx := testContext2(t, &ContextOpts{ 765 Providers: map[addrs.Provider]providers.Factory{ 766 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 767 }, 768 }) 769 770 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 771 if diags.HasErrors() { 772 t.Fatalf("diags: %s", diags.Err()) 773 } else { 774 t.Logf(legacyDiffComparisonString(plan.Changes)) 775 } 776 777 state, diags = ctx.Apply(plan, m) 778 if diags.HasErrors() { 779 t.Fatalf("diags: %s", diags.Err()) 780 } 781 782 mod := state.RootModule() 783 if got, want := len(mod.Resources), 1; got != want { 784 t.Logf("state:\n%s", state) 785 t.Fatalf("wrong number of resources %d; want %d", got, want) 786 } 787 788 actual := strings.TrimSpace(state.String()) 789 expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr) 790 if actual != expected { 791 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 792 } 793 } 794 795 func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) { 796 m := testModule(t, "apply-good-create-before-update") 797 p := testProvider("aws") 798 p.PlanResourceChangeFn = testDiffFn 799 800 // signal that resource foo has started applying 801 fooChan := make(chan struct{}) 802 803 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 804 id := req.PriorState.GetAttr("id").AsString() 805 switch id { 806 case "bar": 807 select { 808 case <-fooChan: 809 resp.Diagnostics = resp.Diagnostics.Append(errors.New("bar must be updated before foo is destroyed")) 810 return resp 811 case <-time.After(100 * time.Millisecond): 812 // wait a moment to ensure that foo is not going to be destroyed first 813 } 814 case "foo": 815 close(fooChan) 816 } 817 818 return testApplyFn(req) 819 } 820 821 state := states.NewState() 822 root := state.EnsureModule(addrs.RootModuleInstance) 823 fooAddr := mustResourceInstanceAddr("aws_instance.foo") 824 root.SetResourceInstanceCurrent( 825 fooAddr.Resource, 826 &states.ResourceInstanceObjectSrc{ 827 Status: states.ObjectReady, 828 AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`), 829 CreateBeforeDestroy: true, 830 }, 831 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 832 ) 833 root.SetResourceInstanceCurrent( 834 mustResourceInstanceAddr("aws_instance.bar").Resource, 835 &states.ResourceInstanceObjectSrc{ 836 Status: states.ObjectReady, 837 AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), 838 CreateBeforeDestroy: true, 839 Dependencies: []addrs.ConfigResource{fooAddr.ContainingResource().Config()}, 840 }, 841 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 842 ) 843 844 ctx := testContext2(t, &ContextOpts{ 845 Providers: map[addrs.Provider]providers.Factory{ 846 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 847 }, 848 }) 849 850 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 851 if diags.HasErrors() { 852 t.Fatalf("diags: %s", diags.Err()) 853 } else { 854 t.Logf(legacyDiffComparisonString(plan.Changes)) 855 } 856 857 state, diags = ctx.Apply(plan, m) 858 if diags.HasErrors() { 859 t.Fatalf("diags: %s", diags.Err()) 860 } 861 862 mod := state.RootModule() 863 if len(mod.Resources) != 1 { 864 t.Fatalf("bad: %s", state) 865 } 866 867 actual := strings.TrimSpace(state.String()) 868 expected := strings.TrimSpace(testTerraformApplyCreateBeforeUpdateStr) 869 if actual != expected { 870 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 871 } 872 } 873 874 // This tests that when a CBD resource depends on a non-CBD resource, 875 // we can still properly apply changes that require new for both. 876 func TestContext2Apply_createBeforeDestroy_dependsNonCBD(t *testing.T) { 877 m := testModule(t, "apply-cbd-depends-non-cbd") 878 p := testProvider("aws") 879 p.PlanResourceChangeFn = testDiffFn 880 p.ApplyResourceChangeFn = testApplyFn 881 882 state := states.NewState() 883 root := state.EnsureModule(addrs.RootModuleInstance) 884 root.SetResourceInstanceCurrent( 885 mustResourceInstanceAddr("aws_instance.bar").Resource, 886 &states.ResourceInstanceObjectSrc{ 887 Status: states.ObjectReady, 888 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 889 }, 890 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 891 ) 892 root.SetResourceInstanceCurrent( 893 mustResourceInstanceAddr("aws_instance.foo").Resource, 894 &states.ResourceInstanceObjectSrc{ 895 Status: states.ObjectReady, 896 AttrsJSON: []byte(`{"id":"foo", "require_new": "abc"}`), 897 }, 898 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 899 ) 900 901 ctx := testContext2(t, &ContextOpts{ 902 Providers: map[addrs.Provider]providers.Factory{ 903 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 904 }, 905 }) 906 907 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 908 if diags.HasErrors() { 909 t.Fatalf("diags: %s", diags.Err()) 910 } else { 911 t.Logf(legacyDiffComparisonString(plan.Changes)) 912 } 913 914 state, diags = ctx.Apply(plan, m) 915 if diags.HasErrors() { 916 t.Fatalf("diags: %s", diags.Err()) 917 } 918 919 checkStateString(t, state, ` 920 aws_instance.bar: 921 ID = foo 922 provider = provider["registry.terraform.io/hashicorp/aws"] 923 require_new = yes 924 type = aws_instance 925 value = foo 926 927 Dependencies: 928 aws_instance.foo 929 aws_instance.foo: 930 ID = foo 931 provider = provider["registry.terraform.io/hashicorp/aws"] 932 require_new = yes 933 type = aws_instance 934 `) 935 } 936 937 func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) { 938 h := new(MockHook) 939 m := testModule(t, "apply-good-create-before") 940 p := testProvider("aws") 941 p.PlanResourceChangeFn = testDiffFn 942 p.ApplyResourceChangeFn = testApplyFn 943 state := states.NewState() 944 root := state.EnsureModule(addrs.RootModuleInstance) 945 root.SetResourceInstanceCurrent( 946 mustResourceInstanceAddr("aws_instance.bar").Resource, 947 &states.ResourceInstanceObjectSrc{ 948 Status: states.ObjectReady, 949 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 950 }, 951 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 952 ) 953 954 var actual []cty.Value 955 var actualLock sync.Mutex 956 h.PostApplyFn = func(addr addrs.AbsResourceInstance, gen states.Generation, sv cty.Value, e error) (HookAction, error) { 957 actualLock.Lock() 958 959 defer actualLock.Unlock() 960 actual = append(actual, sv) 961 return HookActionContinue, nil 962 } 963 964 ctx := testContext2(t, &ContextOpts{ 965 Hooks: []Hook{h}, 966 Providers: map[addrs.Provider]providers.Factory{ 967 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 968 }, 969 }) 970 971 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 972 if diags.HasErrors() { 973 t.Fatalf("diags: %s", diags.Err()) 974 } else { 975 t.Logf(legacyDiffComparisonString(plan.Changes)) 976 } 977 978 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 979 t.Fatalf("apply errors: %s", diags.Err()) 980 } 981 982 expected := []cty.Value{ 983 cty.ObjectVal(map[string]cty.Value{ 984 "id": cty.StringVal("foo"), 985 "require_new": cty.StringVal("xyz"), 986 "type": cty.StringVal("aws_instance"), 987 }), 988 cty.NullVal(cty.DynamicPseudoType), 989 } 990 991 cmpOpt := cmp.Transformer("ctyshim", hcl2shim.ConfigValueFromHCL2) 992 if !cmp.Equal(actual, expected, cmpOpt) { 993 t.Fatalf("wrong state snapshot sequence\n%s", cmp.Diff(expected, actual, cmpOpt)) 994 } 995 } 996 997 // Test that we can perform an apply with CBD in a count with deposed instances. 998 func TestContext2Apply_createBeforeDestroy_deposedCount(t *testing.T) { 999 m := testModule(t, "apply-cbd-count") 1000 p := testProvider("aws") 1001 p.PlanResourceChangeFn = testDiffFn 1002 p.ApplyResourceChangeFn = testApplyFn 1003 1004 state := states.NewState() 1005 root := state.EnsureModule(addrs.RootModuleInstance) 1006 root.SetResourceInstanceCurrent( 1007 mustResourceInstanceAddr("aws_instance.bar[0]").Resource, 1008 &states.ResourceInstanceObjectSrc{ 1009 Status: states.ObjectTainted, 1010 AttrsJSON: []byte(`{"id":"bar"}`), 1011 }, 1012 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1013 ) 1014 root.SetResourceInstanceDeposed( 1015 mustResourceInstanceAddr("aws_instance.bar[0]").Resource, 1016 states.NewDeposedKey(), 1017 &states.ResourceInstanceObjectSrc{ 1018 Status: states.ObjectTainted, 1019 AttrsJSON: []byte(`{"id":"foo"}`), 1020 }, 1021 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1022 ) 1023 root.SetResourceInstanceCurrent( 1024 mustResourceInstanceAddr("aws_instance.bar[1]").Resource, 1025 &states.ResourceInstanceObjectSrc{ 1026 Status: states.ObjectTainted, 1027 AttrsJSON: []byte(`{"id":"bar"}`), 1028 }, 1029 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1030 ) 1031 root.SetResourceInstanceDeposed( 1032 mustResourceInstanceAddr("aws_instance.bar[1]").Resource, 1033 states.NewDeposedKey(), 1034 &states.ResourceInstanceObjectSrc{ 1035 Status: states.ObjectTainted, 1036 AttrsJSON: []byte(`{"id":"bar"}`), 1037 }, 1038 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1039 ) 1040 1041 ctx := testContext2(t, &ContextOpts{ 1042 Providers: map[addrs.Provider]providers.Factory{ 1043 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1044 }, 1045 }) 1046 1047 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 1048 if diags.HasErrors() { 1049 t.Fatalf("diags: %s", diags.Err()) 1050 } else { 1051 t.Logf(legacyDiffComparisonString(plan.Changes)) 1052 } 1053 1054 state, diags = ctx.Apply(plan, m) 1055 if diags.HasErrors() { 1056 t.Fatalf("diags: %s", diags.Err()) 1057 } 1058 1059 checkStateString(t, state, ` 1060 aws_instance.bar.0: 1061 ID = foo 1062 provider = provider["registry.terraform.io/hashicorp/aws"] 1063 foo = bar 1064 type = aws_instance 1065 aws_instance.bar.1: 1066 ID = foo 1067 provider = provider["registry.terraform.io/hashicorp/aws"] 1068 foo = bar 1069 type = aws_instance 1070 `) 1071 } 1072 1073 // Test that when we have a deposed instance but a good primary, we still 1074 // destroy the deposed instance. 1075 func TestContext2Apply_createBeforeDestroy_deposedOnly(t *testing.T) { 1076 m := testModule(t, "apply-cbd-deposed-only") 1077 p := testProvider("aws") 1078 p.PlanResourceChangeFn = testDiffFn 1079 p.ApplyResourceChangeFn = testApplyFn 1080 1081 state := states.NewState() 1082 root := state.EnsureModule(addrs.RootModuleInstance) 1083 root.SetResourceInstanceCurrent( 1084 mustResourceInstanceAddr("aws_instance.bar").Resource, 1085 &states.ResourceInstanceObjectSrc{ 1086 Status: states.ObjectReady, 1087 AttrsJSON: []byte(`{"id":"bar"}`), 1088 }, 1089 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1090 ) 1091 root.SetResourceInstanceDeposed( 1092 mustResourceInstanceAddr("aws_instance.bar").Resource, 1093 states.NewDeposedKey(), 1094 &states.ResourceInstanceObjectSrc{ 1095 Status: states.ObjectTainted, 1096 AttrsJSON: []byte(`{"id":"foo"}`), 1097 }, 1098 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1099 ) 1100 1101 ctx := testContext2(t, &ContextOpts{ 1102 Providers: map[addrs.Provider]providers.Factory{ 1103 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1104 }, 1105 }) 1106 1107 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 1108 if diags.HasErrors() { 1109 t.Fatalf("diags: %s", diags.Err()) 1110 } else { 1111 t.Logf(legacyDiffComparisonString(plan.Changes)) 1112 } 1113 1114 state, diags = ctx.Apply(plan, m) 1115 if diags.HasErrors() { 1116 t.Fatalf("diags: %s", diags.Err()) 1117 } 1118 1119 checkStateString(t, state, ` 1120 aws_instance.bar: 1121 ID = bar 1122 provider = provider["registry.terraform.io/hashicorp/aws"] 1123 type = aws_instance 1124 `) 1125 } 1126 1127 func TestContext2Apply_destroyComputed(t *testing.T) { 1128 m := testModule(t, "apply-destroy-computed") 1129 p := testProvider("aws") 1130 p.PlanResourceChangeFn = testDiffFn 1131 state := states.NewState() 1132 root := state.EnsureModule(addrs.RootModuleInstance) 1133 root.SetResourceInstanceCurrent( 1134 mustResourceInstanceAddr("aws_instance.foo").Resource, 1135 &states.ResourceInstanceObjectSrc{ 1136 Status: states.ObjectReady, 1137 AttrsJSON: []byte(`{"id":"foo", "output": "value"}`), 1138 }, 1139 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1140 ) 1141 ctx := testContext2(t, &ContextOpts{ 1142 Providers: map[addrs.Provider]providers.Factory{ 1143 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1144 }, 1145 }) 1146 1147 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1148 Mode: plans.DestroyMode, 1149 }) 1150 if diags.HasErrors() { 1151 logDiagnostics(t, diags) 1152 t.Fatal("plan failed") 1153 } else { 1154 t.Logf("plan:\n\n%s", legacyDiffComparisonString(plan.Changes)) 1155 } 1156 1157 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 1158 logDiagnostics(t, diags) 1159 t.Fatal("apply failed") 1160 } 1161 } 1162 1163 // Test that the destroy operation uses depends_on as a source of ordering. 1164 func TestContext2Apply_destroyDependsOn(t *testing.T) { 1165 // It is possible for this to be racy, so we loop a number of times 1166 // just to check. 1167 for i := 0; i < 10; i++ { 1168 testContext2Apply_destroyDependsOn(t) 1169 } 1170 } 1171 1172 func testContext2Apply_destroyDependsOn(t *testing.T) { 1173 m := testModule(t, "apply-destroy-depends-on") 1174 p := testProvider("aws") 1175 p.PlanResourceChangeFn = testDiffFn 1176 1177 state := states.NewState() 1178 root := state.EnsureModule(addrs.RootModuleInstance) 1179 root.SetResourceInstanceCurrent( 1180 mustResourceInstanceAddr("aws_instance.bar").Resource, 1181 &states.ResourceInstanceObjectSrc{ 1182 Status: states.ObjectReady, 1183 AttrsJSON: []byte(`{"id":"bar"}`), 1184 }, 1185 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1186 ) 1187 root.SetResourceInstanceCurrent( 1188 mustResourceInstanceAddr("aws_instance.foo").Resource, 1189 &states.ResourceInstanceObjectSrc{ 1190 Status: states.ObjectReady, 1191 AttrsJSON: []byte(`{"id":"foo"}`), 1192 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.bar")}, 1193 }, 1194 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1195 ) 1196 1197 // Record the order we see Apply 1198 var actual []string 1199 var actualLock sync.Mutex 1200 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1201 actualLock.Lock() 1202 defer actualLock.Unlock() 1203 id := req.PriorState.GetAttr("id").AsString() 1204 actual = append(actual, id) 1205 1206 return testApplyFn(req) 1207 } 1208 1209 ctx := testContext2(t, &ContextOpts{ 1210 Providers: map[addrs.Provider]providers.Factory{ 1211 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1212 }, 1213 Parallelism: 1, // To check ordering 1214 }) 1215 1216 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1217 Mode: plans.DestroyMode, 1218 }) 1219 assertNoErrors(t, diags) 1220 1221 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 1222 t.Fatalf("apply errors: %s", diags.Err()) 1223 } 1224 1225 expected := []string{"foo", "bar"} 1226 if !reflect.DeepEqual(actual, expected) { 1227 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1228 } 1229 } 1230 1231 // Test that destroy ordering is correct with dependencies only 1232 // in the state. 1233 func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) { 1234 newState := states.NewState() 1235 root := newState.EnsureModule(addrs.RootModuleInstance) 1236 root.SetResourceInstanceCurrent( 1237 addrs.Resource{ 1238 Mode: addrs.ManagedResourceMode, 1239 Type: "aws_instance", 1240 Name: "foo", 1241 }.Instance(addrs.NoKey), 1242 &states.ResourceInstanceObjectSrc{ 1243 Status: states.ObjectReady, 1244 AttrsJSON: []byte(`{"id":"foo"}`), 1245 Dependencies: []addrs.ConfigResource{}, 1246 }, 1247 addrs.AbsProviderConfig{ 1248 Provider: addrs.NewDefaultProvider("aws"), 1249 Module: addrs.RootModule, 1250 }, 1251 ) 1252 root.SetResourceInstanceCurrent( 1253 addrs.Resource{ 1254 Mode: addrs.ManagedResourceMode, 1255 Type: "aws_instance", 1256 Name: "bar", 1257 }.Instance(addrs.NoKey), 1258 &states.ResourceInstanceObjectSrc{ 1259 Status: states.ObjectReady, 1260 AttrsJSON: []byte(`{"id":"bar"}`), 1261 Dependencies: []addrs.ConfigResource{ 1262 { 1263 Resource: addrs.Resource{ 1264 Mode: addrs.ManagedResourceMode, 1265 Type: "aws_instance", 1266 Name: "foo", 1267 }, 1268 Module: root.Addr.Module(), 1269 }, 1270 }, 1271 }, 1272 addrs.AbsProviderConfig{ 1273 Provider: addrs.NewDefaultProvider("aws"), 1274 Module: addrs.RootModule, 1275 }, 1276 ) 1277 1278 // It is possible for this to be racy, so we loop a number of times 1279 // just to check. 1280 for i := 0; i < 10; i++ { 1281 t.Run("new", func(t *testing.T) { 1282 testContext2Apply_destroyDependsOnStateOnly(t, newState) 1283 }) 1284 } 1285 } 1286 1287 func testContext2Apply_destroyDependsOnStateOnly(t *testing.T, state *states.State) { 1288 state = state.DeepCopy() 1289 m := testModule(t, "empty") 1290 p := testProvider("aws") 1291 p.PlanResourceChangeFn = testDiffFn 1292 // Record the order we see Apply 1293 var actual []string 1294 var actualLock sync.Mutex 1295 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1296 actualLock.Lock() 1297 defer actualLock.Unlock() 1298 id := req.PriorState.GetAttr("id").AsString() 1299 actual = append(actual, id) 1300 return testApplyFn(req) 1301 } 1302 1303 ctx := testContext2(t, &ContextOpts{ 1304 Providers: map[addrs.Provider]providers.Factory{ 1305 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1306 }, 1307 Parallelism: 1, // To check ordering 1308 }) 1309 1310 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1311 Mode: plans.DestroyMode, 1312 }) 1313 assertNoErrors(t, diags) 1314 1315 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 1316 t.Fatalf("apply errors: %s", diags.Err()) 1317 } 1318 1319 expected := []string{"bar", "foo"} 1320 if !reflect.DeepEqual(actual, expected) { 1321 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1322 } 1323 } 1324 1325 // Test that destroy ordering is correct with dependencies only 1326 // in the state within a module (GH-11749) 1327 func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) { 1328 newState := states.NewState() 1329 child := newState.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 1330 child.SetResourceInstanceCurrent( 1331 addrs.Resource{ 1332 Mode: addrs.ManagedResourceMode, 1333 Type: "aws_instance", 1334 Name: "foo", 1335 }.Instance(addrs.NoKey), 1336 &states.ResourceInstanceObjectSrc{ 1337 Status: states.ObjectReady, 1338 AttrsJSON: []byte(`{"id":"foo"}`), 1339 Dependencies: []addrs.ConfigResource{}, 1340 }, 1341 addrs.AbsProviderConfig{ 1342 Provider: addrs.NewDefaultProvider("aws"), 1343 Module: addrs.RootModule, 1344 }, 1345 ) 1346 child.SetResourceInstanceCurrent( 1347 addrs.Resource{ 1348 Mode: addrs.ManagedResourceMode, 1349 Type: "aws_instance", 1350 Name: "bar", 1351 }.Instance(addrs.NoKey), 1352 &states.ResourceInstanceObjectSrc{ 1353 Status: states.ObjectReady, 1354 AttrsJSON: []byte(`{"id":"bar"}`), 1355 Dependencies: []addrs.ConfigResource{ 1356 { 1357 Resource: addrs.Resource{ 1358 Mode: addrs.ManagedResourceMode, 1359 Type: "aws_instance", 1360 Name: "foo", 1361 }, 1362 Module: child.Addr.Module(), 1363 }, 1364 }, 1365 }, 1366 addrs.AbsProviderConfig{ 1367 Provider: addrs.NewDefaultProvider("aws"), 1368 Module: addrs.RootModule, 1369 }, 1370 ) 1371 1372 // It is possible for this to be racy, so we loop a number of times 1373 // just to check. 1374 for i := 0; i < 10; i++ { 1375 t.Run("new", func(t *testing.T) { 1376 testContext2Apply_destroyDependsOnStateOnlyModule(t, newState) 1377 }) 1378 } 1379 } 1380 1381 func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T, state *states.State) { 1382 state = state.DeepCopy() 1383 m := testModule(t, "empty") 1384 p := testProvider("aws") 1385 p.PlanResourceChangeFn = testDiffFn 1386 1387 // Record the order we see Apply 1388 var actual []string 1389 var actualLock sync.Mutex 1390 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1391 actualLock.Lock() 1392 defer actualLock.Unlock() 1393 id := req.PriorState.GetAttr("id").AsString() 1394 actual = append(actual, id) 1395 return testApplyFn(req) 1396 } 1397 1398 ctx := testContext2(t, &ContextOpts{ 1399 Providers: map[addrs.Provider]providers.Factory{ 1400 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1401 }, 1402 Parallelism: 1, // To check ordering 1403 }) 1404 1405 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1406 Mode: plans.DestroyMode, 1407 }) 1408 assertNoErrors(t, diags) 1409 1410 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 1411 t.Fatalf("apply errors: %s", diags.Err()) 1412 } 1413 1414 expected := []string{"bar", "foo"} 1415 if !reflect.DeepEqual(actual, expected) { 1416 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1417 } 1418 } 1419 1420 func TestContext2Apply_dataBasic(t *testing.T) { 1421 m := testModule(t, "apply-data-basic") 1422 p := testProvider("null") 1423 p.PlanResourceChangeFn = testDiffFn 1424 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 1425 State: cty.ObjectVal(map[string]cty.Value{ 1426 "id": cty.StringVal("yo"), 1427 "foo": cty.NullVal(cty.String), 1428 }), 1429 } 1430 1431 ctx := testContext2(t, &ContextOpts{ 1432 Providers: map[addrs.Provider]providers.Factory{ 1433 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 1434 }, 1435 }) 1436 1437 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 1438 if diags.HasErrors() { 1439 t.Fatalf("diags: %s", diags.Err()) 1440 } else { 1441 t.Logf(legacyDiffComparisonString(plan.Changes)) 1442 } 1443 1444 state, diags := ctx.Apply(plan, m) 1445 assertNoErrors(t, diags) 1446 1447 actual := strings.TrimSpace(state.String()) 1448 expected := strings.TrimSpace(testTerraformApplyDataBasicStr) 1449 if actual != expected { 1450 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1451 } 1452 } 1453 1454 func TestContext2Apply_destroyData(t *testing.T) { 1455 m := testModule(t, "apply-destroy-data-resource") 1456 p := testProvider("null") 1457 p.PlanResourceChangeFn = testDiffFn 1458 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 1459 return providers.ReadDataSourceResponse{ 1460 State: req.Config, 1461 } 1462 } 1463 1464 state := states.NewState() 1465 root := state.EnsureModule(addrs.RootModuleInstance) 1466 root.SetResourceInstanceCurrent( 1467 mustResourceInstanceAddr("data.null_data_source.testing").Resource, 1468 &states.ResourceInstanceObjectSrc{ 1469 Status: states.ObjectReady, 1470 AttrsJSON: []byte(`{"id":"-"}`), 1471 }, 1472 mustProviderConfig(`provider["registry.terraform.io/hashicorp/null"]`), 1473 ) 1474 1475 hook := &testHook{} 1476 ctx := testContext2(t, &ContextOpts{ 1477 Providers: map[addrs.Provider]providers.Factory{ 1478 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 1479 }, 1480 Hooks: []Hook{hook}, 1481 }) 1482 1483 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1484 Mode: plans.DestroyMode, 1485 }) 1486 if diags.HasErrors() { 1487 t.Fatalf("diags: %s", diags.Err()) 1488 } else { 1489 t.Logf(legacyDiffComparisonString(plan.Changes)) 1490 } 1491 1492 newState, diags := ctx.Apply(plan, m) 1493 if diags.HasErrors() { 1494 t.Fatalf("diags: %s", diags.Err()) 1495 } 1496 1497 if got := len(newState.Modules); got != 1 { 1498 t.Fatalf("state has %d modules after destroy; want 1", got) 1499 } 1500 1501 if got := len(newState.RootModule().Resources); got != 0 { 1502 t.Fatalf("state has %d resources after destroy; want 0", got) 1503 } 1504 1505 wantHookCalls := []*testHookCall{ 1506 {"PreDiff", "data.null_data_source.testing"}, 1507 {"PostDiff", "data.null_data_source.testing"}, 1508 {"PreDiff", "data.null_data_source.testing"}, 1509 {"PostDiff", "data.null_data_source.testing"}, 1510 {"PostStateUpdate", ""}, 1511 } 1512 if !reflect.DeepEqual(hook.Calls, wantHookCalls) { 1513 t.Errorf("wrong hook calls\ngot: %swant: %s", spew.Sdump(hook.Calls), spew.Sdump(wantHookCalls)) 1514 } 1515 } 1516 1517 // https://github.com/muratcelep/terraform/pull/5096 1518 func TestContext2Apply_destroySkipsCBD(t *testing.T) { 1519 // Config contains CBD resource depending on non-CBD resource, which triggers 1520 // a cycle if they are both replaced, but should _not_ trigger a cycle when 1521 // just doing a `terraform destroy`. 1522 m := testModule(t, "apply-destroy-cbd") 1523 p := testProvider("aws") 1524 p.PlanResourceChangeFn = testDiffFn 1525 state := states.NewState() 1526 root := state.EnsureModule(addrs.RootModuleInstance) 1527 root.SetResourceInstanceCurrent( 1528 mustResourceInstanceAddr("aws_instance.foo").Resource, 1529 &states.ResourceInstanceObjectSrc{ 1530 Status: states.ObjectReady, 1531 AttrsJSON: []byte(`{"id":"foo"}`), 1532 }, 1533 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1534 ) 1535 root.SetResourceInstanceCurrent( 1536 mustResourceInstanceAddr("aws_instance.bar").Resource, 1537 &states.ResourceInstanceObjectSrc{ 1538 Status: states.ObjectReady, 1539 AttrsJSON: []byte(`{"id":"foo"}`), 1540 }, 1541 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1542 ) 1543 1544 ctx := testContext2(t, &ContextOpts{ 1545 Providers: map[addrs.Provider]providers.Factory{ 1546 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1547 }, 1548 }) 1549 1550 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1551 Mode: plans.DestroyMode, 1552 }) 1553 if diags.HasErrors() { 1554 t.Fatalf("diags: %s", diags.Err()) 1555 } else { 1556 t.Logf(legacyDiffComparisonString(plan.Changes)) 1557 } 1558 1559 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 1560 t.Fatalf("apply errors: %s", diags.Err()) 1561 } 1562 } 1563 1564 func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) { 1565 m := testModule(t, "apply-destroy-mod-var-provider-config") 1566 p := func() (providers.Interface, error) { 1567 p := testProvider("aws") 1568 p.PlanResourceChangeFn = testDiffFn 1569 return p, nil 1570 } 1571 state := states.NewState() 1572 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 1573 child.SetResourceInstanceCurrent( 1574 mustResourceInstanceAddr("aws_instance.foo").Resource, 1575 &states.ResourceInstanceObjectSrc{ 1576 Status: states.ObjectReady, 1577 AttrsJSON: []byte(`{"id":"foo"}`), 1578 }, 1579 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1580 ) 1581 ctx := testContext2(t, &ContextOpts{ 1582 Providers: map[addrs.Provider]providers.Factory{ 1583 addrs.NewDefaultProvider("aws"): p, 1584 }, 1585 }) 1586 1587 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1588 Mode: plans.DestroyMode, 1589 }) 1590 assertNoErrors(t, diags) 1591 1592 _, diags = ctx.Apply(plan, m) 1593 if diags.HasErrors() { 1594 t.Fatalf("diags: %s", diags.Err()) 1595 } 1596 } 1597 1598 func TestContext2Apply_destroyCrossProviders(t *testing.T) { 1599 m := testModule(t, "apply-destroy-cross-providers") 1600 1601 p_aws := testProvider("aws") 1602 p_aws.ApplyResourceChangeFn = testApplyFn 1603 p_aws.PlanResourceChangeFn = testDiffFn 1604 p_aws.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1605 ResourceTypes: map[string]*configschema.Block{ 1606 "aws_instance": { 1607 Attributes: map[string]*configschema.Attribute{ 1608 "id": { 1609 Type: cty.String, 1610 Computed: true, 1611 }, 1612 }, 1613 }, 1614 "aws_vpc": { 1615 Attributes: map[string]*configschema.Attribute{ 1616 "id": { 1617 Type: cty.String, 1618 Computed: true, 1619 }, 1620 "value": { 1621 Type: cty.String, 1622 Optional: true, 1623 }, 1624 }, 1625 }, 1626 }, 1627 }) 1628 1629 providers := map[addrs.Provider]providers.Factory{ 1630 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p_aws), 1631 } 1632 1633 ctx, m, state := getContextForApply_destroyCrossProviders(t, m, providers) 1634 1635 plan, diags := ctx.Plan(m, state, &PlanOpts{ 1636 Mode: plans.DestroyMode, 1637 }) 1638 assertNoErrors(t, diags) 1639 1640 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 1641 logDiagnostics(t, diags) 1642 t.Fatal("apply failed") 1643 } 1644 } 1645 1646 func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, providerFactories map[addrs.Provider]providers.Factory) (*Context, *configs.Config, *states.State) { 1647 state := states.NewState() 1648 root := state.EnsureModule(addrs.RootModuleInstance) 1649 root.SetResourceInstanceCurrent( 1650 mustResourceInstanceAddr("aws_instance.shared").Resource, 1651 &states.ResourceInstanceObjectSrc{ 1652 Status: states.ObjectReady, 1653 AttrsJSON: []byte(`{"id":"test"}`), 1654 }, 1655 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1656 ) 1657 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 1658 child.SetResourceInstanceCurrent( 1659 mustResourceInstanceAddr("aws_vpc.bar").Resource, 1660 &states.ResourceInstanceObjectSrc{ 1661 Status: states.ObjectReady, 1662 AttrsJSON: []byte(`{"id": "vpc-aaabbb12", "value":"test"}`), 1663 }, 1664 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1665 ) 1666 1667 ctx := testContext2(t, &ContextOpts{ 1668 Providers: providerFactories, 1669 }) 1670 1671 return ctx, m, state 1672 } 1673 1674 func TestContext2Apply_minimal(t *testing.T) { 1675 m := testModule(t, "apply-minimal") 1676 p := testProvider("aws") 1677 p.PlanResourceChangeFn = testDiffFn 1678 p.ApplyResourceChangeFn = testApplyFn 1679 ctx := testContext2(t, &ContextOpts{ 1680 Providers: map[addrs.Provider]providers.Factory{ 1681 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1682 }, 1683 }) 1684 1685 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 1686 assertNoErrors(t, diags) 1687 1688 state, diags := ctx.Apply(plan, m) 1689 if diags.HasErrors() { 1690 t.Fatalf("diags: %s", diags.Err()) 1691 } 1692 1693 actual := strings.TrimSpace(state.String()) 1694 expected := strings.TrimSpace(testTerraformApplyMinimalStr) 1695 if actual != expected { 1696 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1697 } 1698 } 1699 1700 func TestContext2Apply_cancel(t *testing.T) { 1701 stopped := false 1702 1703 m := testModule(t, "apply-cancel") 1704 p := testProvider("aws") 1705 ctx := testContext2(t, &ContextOpts{ 1706 Providers: map[addrs.Provider]providers.Factory{ 1707 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1708 }, 1709 }) 1710 1711 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1712 if !stopped { 1713 stopped = true 1714 go ctx.Stop() 1715 1716 for { 1717 if ctx.sh.Stopped() { 1718 break 1719 } 1720 time.Sleep(10 * time.Millisecond) 1721 } 1722 } 1723 return testApplyFn(req) 1724 } 1725 p.PlanResourceChangeFn = testDiffFn 1726 1727 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 1728 assertNoErrors(t, diags) 1729 1730 // Start the Apply in a goroutine 1731 var applyDiags tfdiags.Diagnostics 1732 stateCh := make(chan *states.State) 1733 go func() { 1734 state, diags := ctx.Apply(plan, m) 1735 applyDiags = diags 1736 1737 stateCh <- state 1738 }() 1739 1740 state := <-stateCh 1741 // only expecting an early exit error 1742 if !applyDiags.HasErrors() { 1743 t.Fatal("expected early exit error") 1744 } 1745 1746 for _, d := range applyDiags { 1747 desc := d.Description() 1748 if desc.Summary != "execution halted" { 1749 t.Fatalf("unexpected error: %v", applyDiags.Err()) 1750 } 1751 } 1752 1753 actual := strings.TrimSpace(state.String()) 1754 expected := strings.TrimSpace(testTerraformApplyCancelStr) 1755 if actual != expected { 1756 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1757 } 1758 1759 if !p.StopCalled { 1760 t.Fatal("stop should be called") 1761 } 1762 } 1763 1764 func TestContext2Apply_cancelBlock(t *testing.T) { 1765 m := testModule(t, "apply-cancel-block") 1766 p := testProvider("aws") 1767 ctx := testContext2(t, &ContextOpts{ 1768 Providers: map[addrs.Provider]providers.Factory{ 1769 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1770 }, 1771 }) 1772 1773 applyCh := make(chan struct{}) 1774 p.PlanResourceChangeFn = testDiffFn 1775 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1776 close(applyCh) 1777 1778 for !ctx.sh.Stopped() { 1779 // Wait for stop to be called. We call Gosched here so that 1780 // the other goroutines can always be scheduled to set Stopped. 1781 runtime.Gosched() 1782 } 1783 1784 // Sleep 1785 time.Sleep(100 * time.Millisecond) 1786 return testApplyFn(req) 1787 } 1788 1789 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 1790 assertNoErrors(t, diags) 1791 1792 // Start the Apply in a goroutine 1793 var applyDiags tfdiags.Diagnostics 1794 stateCh := make(chan *states.State) 1795 go func() { 1796 state, diags := ctx.Apply(plan, m) 1797 applyDiags = diags 1798 1799 stateCh <- state 1800 }() 1801 1802 stopDone := make(chan struct{}) 1803 go func() { 1804 defer close(stopDone) 1805 <-applyCh 1806 ctx.Stop() 1807 }() 1808 1809 // Make sure that stop blocks 1810 select { 1811 case <-stopDone: 1812 t.Fatal("stop should block") 1813 case <-time.After(10 * time.Millisecond): 1814 } 1815 1816 // Wait for stop 1817 select { 1818 case <-stopDone: 1819 case <-time.After(500 * time.Millisecond): 1820 t.Fatal("stop should be done") 1821 } 1822 1823 // Wait for apply to complete 1824 state := <-stateCh 1825 // only expecting an early exit error 1826 if !applyDiags.HasErrors() { 1827 t.Fatal("expected early exit error") 1828 } 1829 1830 for _, d := range applyDiags { 1831 desc := d.Description() 1832 if desc.Summary != "execution halted" { 1833 t.Fatalf("unexpected error: %v", applyDiags.Err()) 1834 } 1835 } 1836 1837 checkStateString(t, state, ` 1838 aws_instance.foo: 1839 ID = foo 1840 provider = provider["registry.terraform.io/hashicorp/aws"] 1841 num = 2 1842 type = aws_instance 1843 `) 1844 } 1845 1846 func TestContext2Apply_cancelProvisioner(t *testing.T) { 1847 m := testModule(t, "apply-cancel-provisioner") 1848 p := testProvider("aws") 1849 p.PlanResourceChangeFn = testDiffFn 1850 p.ApplyResourceChangeFn = testApplyFn 1851 1852 pr := testProvisioner() 1853 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 1854 Provisioner: &configschema.Block{ 1855 Attributes: map[string]*configschema.Attribute{ 1856 "foo": { 1857 Type: cty.String, 1858 Optional: true, 1859 }, 1860 }, 1861 }, 1862 } 1863 1864 ctx := testContext2(t, &ContextOpts{ 1865 Providers: map[addrs.Provider]providers.Factory{ 1866 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1867 }, 1868 Provisioners: map[string]provisioners.Factory{ 1869 "shell": testProvisionerFuncFixed(pr), 1870 }, 1871 }) 1872 1873 prStopped := make(chan struct{}) 1874 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 1875 // Start the stop process 1876 go ctx.Stop() 1877 1878 <-prStopped 1879 return 1880 } 1881 pr.StopFn = func() error { 1882 close(prStopped) 1883 return nil 1884 } 1885 1886 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 1887 assertNoErrors(t, diags) 1888 1889 // Start the Apply in a goroutine 1890 var applyDiags tfdiags.Diagnostics 1891 stateCh := make(chan *states.State) 1892 go func() { 1893 state, diags := ctx.Apply(plan, m) 1894 applyDiags = diags 1895 1896 stateCh <- state 1897 }() 1898 1899 // Wait for completion 1900 state := <-stateCh 1901 1902 // we are expecting only an early exit error 1903 if !applyDiags.HasErrors() { 1904 t.Fatal("expected early exit error") 1905 } 1906 1907 for _, d := range applyDiags { 1908 desc := d.Description() 1909 if desc.Summary != "execution halted" { 1910 t.Fatalf("unexpected error: %v", applyDiags.Err()) 1911 } 1912 } 1913 1914 checkStateString(t, state, ` 1915 aws_instance.foo: (tainted) 1916 ID = foo 1917 provider = provider["registry.terraform.io/hashicorp/aws"] 1918 num = 2 1919 type = aws_instance 1920 `) 1921 1922 if !pr.StopCalled { 1923 t.Fatal("stop should be called") 1924 } 1925 } 1926 1927 func TestContext2Apply_compute(t *testing.T) { 1928 m := testModule(t, "apply-compute") 1929 p := testProvider("aws") 1930 p.PlanResourceChangeFn = testDiffFn 1931 p.ApplyResourceChangeFn = testApplyFn 1932 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1933 ResourceTypes: map[string]*configschema.Block{ 1934 "aws_instance": { 1935 Attributes: map[string]*configschema.Attribute{ 1936 "num": { 1937 Type: cty.Number, 1938 Optional: true, 1939 }, 1940 "compute": { 1941 Type: cty.String, 1942 Optional: true, 1943 }, 1944 "compute_value": { 1945 Type: cty.String, 1946 Optional: true, 1947 }, 1948 "foo": { 1949 Type: cty.String, 1950 Optional: true, 1951 }, 1952 "id": { 1953 Type: cty.String, 1954 Computed: true, 1955 }, 1956 "type": { 1957 Type: cty.String, 1958 Computed: true, 1959 }, 1960 "value": { // Populated from compute_value because compute = "value" in the config fixture 1961 Type: cty.String, 1962 Computed: true, 1963 }, 1964 }, 1965 }, 1966 }, 1967 }) 1968 1969 ctx := testContext2(t, &ContextOpts{ 1970 Providers: map[addrs.Provider]providers.Factory{ 1971 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1972 }, 1973 }) 1974 1975 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 1976 SetVariables: InputValues{ 1977 "value": &InputValue{ 1978 Value: cty.NumberIntVal(1), 1979 SourceType: ValueFromCaller, 1980 }, 1981 }, 1982 }) 1983 assertNoErrors(t, diags) 1984 1985 state, diags := ctx.Apply(plan, m) 1986 if diags.HasErrors() { 1987 t.Fatalf("unexpected errors: %s", diags.Err()) 1988 } 1989 1990 actual := strings.TrimSpace(state.String()) 1991 expected := strings.TrimSpace(testTerraformApplyComputeStr) 1992 if actual != expected { 1993 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1994 } 1995 } 1996 1997 func TestContext2Apply_countDecrease(t *testing.T) { 1998 m := testModule(t, "apply-count-dec") 1999 p := testProvider("aws") 2000 p.PlanResourceChangeFn = testDiffFn 2001 p.ApplyResourceChangeFn = testApplyFn 2002 state := states.NewState() 2003 root := state.EnsureModule(addrs.RootModuleInstance) 2004 root.SetResourceInstanceCurrent( 2005 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2006 &states.ResourceInstanceObjectSrc{ 2007 Status: states.ObjectReady, 2008 AttrsJSON: []byte(`{"id":"bar","foo": "foo","type": "aws_instance"}`), 2009 }, 2010 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2011 ) 2012 root.SetResourceInstanceCurrent( 2013 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 2014 &states.ResourceInstanceObjectSrc{ 2015 Status: states.ObjectReady, 2016 AttrsJSON: []byte(`{"id":"bar","foo": "foo","type": "aws_instance"}`), 2017 }, 2018 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2019 ) 2020 root.SetResourceInstanceCurrent( 2021 mustResourceInstanceAddr("aws_instance.foo[2]").Resource, 2022 &states.ResourceInstanceObjectSrc{ 2023 Status: states.ObjectReady, 2024 AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`), 2025 }, 2026 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2027 ) 2028 2029 ctx := testContext2(t, &ContextOpts{ 2030 Providers: map[addrs.Provider]providers.Factory{ 2031 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2032 }, 2033 }) 2034 2035 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2036 assertNoErrors(t, diags) 2037 2038 s, diags := ctx.Apply(plan, m) 2039 assertNoErrors(t, diags) 2040 2041 actual := strings.TrimSpace(s.String()) 2042 expected := strings.TrimSpace(testTerraformApplyCountDecStr) 2043 if actual != expected { 2044 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2045 } 2046 } 2047 2048 func TestContext2Apply_countDecreaseToOneX(t *testing.T) { 2049 m := testModule(t, "apply-count-dec-one") 2050 p := testProvider("aws") 2051 p.PlanResourceChangeFn = testDiffFn 2052 state := states.NewState() 2053 root := state.EnsureModule(addrs.RootModuleInstance) 2054 root.SetResourceInstanceCurrent( 2055 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2056 &states.ResourceInstanceObjectSrc{ 2057 Status: states.ObjectReady, 2058 AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`), 2059 }, 2060 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2061 ) 2062 root.SetResourceInstanceCurrent( 2063 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 2064 &states.ResourceInstanceObjectSrc{ 2065 Status: states.ObjectReady, 2066 AttrsJSON: []byte(`{"id":"bar"}`), 2067 }, 2068 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2069 ) 2070 root.SetResourceInstanceCurrent( 2071 mustResourceInstanceAddr("aws_instance.foo[2]").Resource, 2072 &states.ResourceInstanceObjectSrc{ 2073 Status: states.ObjectReady, 2074 AttrsJSON: []byte(`{"id":"bar"}`), 2075 }, 2076 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2077 ) 2078 2079 ctx := testContext2(t, &ContextOpts{ 2080 Providers: map[addrs.Provider]providers.Factory{ 2081 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2082 }, 2083 }) 2084 2085 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2086 assertNoErrors(t, diags) 2087 2088 s, diags := ctx.Apply(plan, m) 2089 if diags.HasErrors() { 2090 t.Fatalf("diags: %s", diags.Err()) 2091 } 2092 2093 actual := strings.TrimSpace(s.String()) 2094 expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr) 2095 if actual != expected { 2096 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2097 } 2098 } 2099 2100 // https://github.com/PeoplePerHour/terraform/pull/11 2101 // 2102 // This tests a rare but possible situation where we have both a no-key and 2103 // a zero-key instance of the same resource in the configuration when we 2104 // disable count. 2105 // 2106 // The main way to get here is for a provider to fail to destroy the zero-key 2107 // instance but succeed in creating the no-key instance, since those two 2108 // can typically happen concurrently. There are various other ways to get here 2109 // that might be considered user error, such as using "terraform state mv" 2110 // to create a strange combination of different key types on the same resource. 2111 // 2112 // This test indirectly exercises an intentional interaction between 2113 // refactoring.ImpliedMoveStatements and refactoring.ApplyMoves: we'll first 2114 // generate an implied move statement from aws_instance.foo[0] to 2115 // aws_instance.foo, but then refactoring.ApplyMoves should notice that and 2116 // ignore the statement, in the same way as it would if an explicit move 2117 // statement specified the same situation. 2118 func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) { 2119 m := testModule(t, "apply-count-dec-one") 2120 p := testProvider("aws") 2121 p.PlanResourceChangeFn = testDiffFn 2122 state := states.NewState() 2123 root := state.EnsureModule(addrs.RootModuleInstance) 2124 root.SetResourceInstanceCurrent( 2125 mustResourceInstanceAddr("aws_instance.foo").Resource, 2126 &states.ResourceInstanceObjectSrc{ 2127 Status: states.ObjectReady, 2128 AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`), 2129 }, 2130 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2131 ) 2132 root.SetResourceInstanceCurrent( 2133 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2134 &states.ResourceInstanceObjectSrc{ 2135 Status: states.ObjectReady, 2136 AttrsJSON: []byte(`{"id":"baz", "type": "aws_instance"}`), 2137 }, 2138 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2139 ) 2140 2141 ctx := testContext2(t, &ContextOpts{ 2142 Providers: map[addrs.Provider]providers.Factory{ 2143 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2144 }, 2145 }) 2146 2147 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2148 assertNoErrors(t, diags) 2149 { 2150 got := strings.TrimSpace(legacyPlanComparisonString(state, plan.Changes)) 2151 want := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedPlanStr) 2152 if got != want { 2153 t.Fatalf("wrong plan result\ngot:\n%s\nwant:\n%s", got, want) 2154 } 2155 } 2156 { 2157 change := plan.Changes.ResourceInstance(mustResourceInstanceAddr("aws_instance.foo[0]")) 2158 if change == nil { 2159 t.Fatalf("no planned change for instance zero") 2160 } 2161 if got, want := change.Action, plans.Delete; got != want { 2162 t.Errorf("wrong action for instance zero %s; want %s", got, want) 2163 } 2164 if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseWrongRepetition; got != want { 2165 t.Errorf("wrong action reason for instance zero %s; want %s", got, want) 2166 } 2167 } 2168 { 2169 change := plan.Changes.ResourceInstance(mustResourceInstanceAddr("aws_instance.foo")) 2170 if change == nil { 2171 t.Fatalf("no planned change for no-key instance") 2172 } 2173 if got, want := change.Action, plans.NoOp; got != want { 2174 t.Errorf("wrong action for no-key instance %s; want %s", got, want) 2175 } 2176 if got, want := change.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { 2177 t.Errorf("wrong action reason for no-key instance %s; want %s", got, want) 2178 } 2179 } 2180 2181 s, diags := ctx.Apply(plan, m) 2182 if diags.HasErrors() { 2183 t.Fatalf("diags: %s", diags.Err()) 2184 } 2185 2186 actual := strings.TrimSpace(s.String()) 2187 expected := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedStr) 2188 if actual != expected { 2189 t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2190 } 2191 } 2192 2193 func TestContext2Apply_countTainted(t *testing.T) { 2194 m := testModule(t, "apply-count-tainted") 2195 p := testProvider("aws") 2196 p.PlanResourceChangeFn = testDiffFn 2197 p.ApplyResourceChangeFn = testApplyFn 2198 state := states.NewState() 2199 root := state.EnsureModule(addrs.RootModuleInstance) 2200 root.SetResourceInstanceCurrent( 2201 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2202 &states.ResourceInstanceObjectSrc{ 2203 Status: states.ObjectTainted, 2204 AttrsJSON: []byte(`{"id":"bar", "type": "aws_instance", "foo": "foo"}`), 2205 }, 2206 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2207 ) 2208 ctx := testContext2(t, &ContextOpts{ 2209 Providers: map[addrs.Provider]providers.Factory{ 2210 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2211 }, 2212 }) 2213 2214 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2215 assertNoErrors(t, diags) 2216 { 2217 got := strings.TrimSpace(legacyDiffComparisonString(plan.Changes)) 2218 want := strings.TrimSpace(` 2219 DESTROY/CREATE: aws_instance.foo[0] 2220 foo: "foo" => "foo" 2221 id: "bar" => "<computed>" 2222 type: "aws_instance" => "<computed>" 2223 CREATE: aws_instance.foo[1] 2224 foo: "" => "foo" 2225 id: "" => "<computed>" 2226 type: "" => "<computed>" 2227 `) 2228 if got != want { 2229 t.Fatalf("wrong plan\n\ngot:\n%s\n\nwant:\n%s", got, want) 2230 } 2231 } 2232 2233 s, diags := ctx.Apply(plan, m) 2234 assertNoErrors(t, diags) 2235 2236 got := strings.TrimSpace(s.String()) 2237 want := strings.TrimSpace(` 2238 aws_instance.foo.0: 2239 ID = foo 2240 provider = provider["registry.terraform.io/hashicorp/aws"] 2241 foo = foo 2242 type = aws_instance 2243 aws_instance.foo.1: 2244 ID = foo 2245 provider = provider["registry.terraform.io/hashicorp/aws"] 2246 foo = foo 2247 type = aws_instance 2248 `) 2249 if got != want { 2250 t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", got, want) 2251 } 2252 } 2253 2254 func TestContext2Apply_countVariable(t *testing.T) { 2255 m := testModule(t, "apply-count-variable") 2256 p := testProvider("aws") 2257 p.PlanResourceChangeFn = testDiffFn 2258 p.ApplyResourceChangeFn = testApplyFn 2259 ctx := testContext2(t, &ContextOpts{ 2260 Providers: map[addrs.Provider]providers.Factory{ 2261 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2262 }, 2263 }) 2264 2265 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2266 assertNoErrors(t, diags) 2267 2268 state, diags := ctx.Apply(plan, m) 2269 if diags.HasErrors() { 2270 t.Fatalf("diags: %s", diags.Err()) 2271 } 2272 2273 actual := strings.TrimSpace(state.String()) 2274 expected := strings.TrimSpace(testTerraformApplyCountVariableStr) 2275 if actual != expected { 2276 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2277 } 2278 } 2279 2280 func TestContext2Apply_countVariableRef(t *testing.T) { 2281 m := testModule(t, "apply-count-variable-ref") 2282 p := testProvider("aws") 2283 p.PlanResourceChangeFn = testDiffFn 2284 p.ApplyResourceChangeFn = testApplyFn 2285 ctx := testContext2(t, &ContextOpts{ 2286 Providers: map[addrs.Provider]providers.Factory{ 2287 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2288 }, 2289 }) 2290 2291 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2292 assertNoErrors(t, diags) 2293 2294 state, diags := ctx.Apply(plan, m) 2295 if diags.HasErrors() { 2296 t.Fatalf("diags: %s", diags.Err()) 2297 } 2298 2299 actual := strings.TrimSpace(state.String()) 2300 expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr) 2301 if actual != expected { 2302 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2303 } 2304 } 2305 2306 func TestContext2Apply_provisionerInterpCount(t *testing.T) { 2307 // This test ensures that a provisioner can interpolate a resource count 2308 // even though the provisioner expression is evaluated during the plan 2309 // walk. https://github.com/muratcelep/terraform/issues/16840 2310 2311 m, snap := testModuleWithSnapshot(t, "apply-provisioner-interp-count") 2312 2313 p := testProvider("aws") 2314 p.PlanResourceChangeFn = testDiffFn 2315 2316 pr := testProvisioner() 2317 2318 Providers := map[addrs.Provider]providers.Factory{ 2319 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2320 } 2321 2322 provisioners := map[string]provisioners.Factory{ 2323 "local-exec": testProvisionerFuncFixed(pr), 2324 } 2325 ctx := testContext2(t, &ContextOpts{ 2326 Providers: Providers, 2327 Provisioners: provisioners, 2328 }) 2329 2330 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2331 assertNoErrors(t, diags) 2332 2333 // We'll marshal and unmarshal the plan here, to ensure that we have 2334 // a clean new context as would be created if we separately ran 2335 // terraform plan -out=tfplan && terraform apply tfplan 2336 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 2337 if err != nil { 2338 t.Fatal(err) 2339 } 2340 ctxOpts.Providers = Providers 2341 ctxOpts.Provisioners = provisioners 2342 ctx, diags = NewContext(ctxOpts) 2343 if diags.HasErrors() { 2344 t.Fatalf("failed to create context for plan: %s", diags.Err()) 2345 } 2346 2347 // Applying the plan should now succeed 2348 _, diags = ctx.Apply(plan, m) 2349 if diags.HasErrors() { 2350 t.Fatalf("apply failed unexpectedly: %s", diags.Err()) 2351 } 2352 2353 // Verify apply was invoked 2354 if !pr.ProvisionResourceCalled { 2355 t.Fatalf("provisioner was not called") 2356 } 2357 } 2358 2359 func TestContext2Apply_foreachVariable(t *testing.T) { 2360 m := testModule(t, "plan-for-each-unknown-value") 2361 p := testProvider("aws") 2362 p.PlanResourceChangeFn = testDiffFn 2363 p.ApplyResourceChangeFn = testApplyFn 2364 ctx := testContext2(t, &ContextOpts{ 2365 Providers: map[addrs.Provider]providers.Factory{ 2366 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2367 }, 2368 }) 2369 2370 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 2371 Mode: plans.NormalMode, 2372 SetVariables: InputValues{ 2373 "foo": &InputValue{ 2374 Value: cty.StringVal("hello"), 2375 }, 2376 }, 2377 }) 2378 assertNoErrors(t, diags) 2379 2380 state, diags := ctx.Apply(plan, m) 2381 if diags.HasErrors() { 2382 t.Fatalf("diags: %s", diags.Err()) 2383 } 2384 2385 actual := strings.TrimSpace(state.String()) 2386 expected := strings.TrimSpace(testTerraformApplyForEachVariableStr) 2387 if actual != expected { 2388 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2389 } 2390 } 2391 2392 func TestContext2Apply_moduleBasic(t *testing.T) { 2393 m := testModule(t, "apply-module") 2394 p := testProvider("aws") 2395 p.PlanResourceChangeFn = testDiffFn 2396 p.ApplyResourceChangeFn = testApplyFn 2397 ctx := testContext2(t, &ContextOpts{ 2398 Providers: map[addrs.Provider]providers.Factory{ 2399 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2400 }, 2401 }) 2402 2403 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2404 assertNoErrors(t, diags) 2405 2406 state, diags := ctx.Apply(plan, m) 2407 if diags.HasErrors() { 2408 t.Fatalf("diags: %s", diags.Err()) 2409 } 2410 2411 actual := strings.TrimSpace(state.String()) 2412 expected := strings.TrimSpace(testTerraformApplyModuleStr) 2413 if actual != expected { 2414 t.Fatalf("bad, expected:\n%s\n\nactual:\n%s", expected, actual) 2415 } 2416 } 2417 2418 func TestContext2Apply_moduleDestroyOrder(t *testing.T) { 2419 m := testModule(t, "apply-module-destroy-order") 2420 p := testProvider("aws") 2421 p.PlanResourceChangeFn = testDiffFn 2422 2423 // Create a custom apply function to track the order they were destroyed 2424 var order []string 2425 var orderLock sync.Mutex 2426 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 2427 id := req.PriorState.GetAttr("id").AsString() 2428 2429 if id == "b" { 2430 // Pause briefly to make any race conditions more visible, since 2431 // missing edges here can cause undeterministic ordering. 2432 time.Sleep(100 * time.Millisecond) 2433 } 2434 2435 orderLock.Lock() 2436 defer orderLock.Unlock() 2437 2438 order = append(order, id) 2439 resp.NewState = req.PlannedState 2440 return resp 2441 } 2442 2443 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 2444 ResourceTypes: map[string]*configschema.Block{ 2445 "aws_instance": { 2446 Attributes: map[string]*configschema.Attribute{ 2447 "id": {Type: cty.String, Required: true}, 2448 "blah": {Type: cty.String, Optional: true}, 2449 "value": {Type: cty.String, Optional: true}, 2450 }, 2451 }, 2452 }, 2453 }) 2454 2455 state := states.NewState() 2456 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 2457 child.SetResourceInstanceCurrent( 2458 mustResourceInstanceAddr("aws_instance.a").Resource, 2459 &states.ResourceInstanceObjectSrc{ 2460 Status: states.ObjectReady, 2461 AttrsJSON: []byte(`{"id":"a"}`), 2462 }, 2463 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2464 ) 2465 root := state.EnsureModule(addrs.RootModuleInstance) 2466 root.SetResourceInstanceCurrent( 2467 mustResourceInstanceAddr("aws_instance.b").Resource, 2468 &states.ResourceInstanceObjectSrc{ 2469 Status: states.ObjectReady, 2470 AttrsJSON: []byte(`{"id":"b"}`), 2471 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.aws_instance.a")}, 2472 }, 2473 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2474 ) 2475 2476 ctx := testContext2(t, &ContextOpts{ 2477 Providers: map[addrs.Provider]providers.Factory{ 2478 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2479 }, 2480 }) 2481 2482 plan, diags := ctx.Plan(m, state, &PlanOpts{ 2483 Mode: plans.DestroyMode, 2484 }) 2485 assertNoErrors(t, diags) 2486 2487 state, diags = ctx.Apply(plan, m) 2488 if diags.HasErrors() { 2489 t.Fatalf("diags: %s", diags.Err()) 2490 } 2491 2492 expected := []string{"b", "a"} 2493 if !reflect.DeepEqual(order, expected) { 2494 t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) 2495 } 2496 2497 { 2498 actual := strings.TrimSpace(state.String()) 2499 expected := strings.TrimSpace(testTerraformApplyModuleDestroyOrderStr) 2500 if actual != expected { 2501 t.Errorf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2502 } 2503 } 2504 } 2505 2506 func TestContext2Apply_moduleInheritAlias(t *testing.T) { 2507 m := testModule(t, "apply-module-provider-inherit-alias") 2508 p := testProvider("aws") 2509 p.PlanResourceChangeFn = testDiffFn 2510 p.ApplyResourceChangeFn = testApplyFn 2511 2512 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2513 val := req.Config.GetAttr("value") 2514 if val.IsNull() { 2515 return 2516 } 2517 2518 root := req.Config.GetAttr("root") 2519 if !root.IsNull() { 2520 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("child should not get root")) 2521 } 2522 2523 return 2524 } 2525 2526 ctx := testContext2(t, &ContextOpts{ 2527 Providers: map[addrs.Provider]providers.Factory{ 2528 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2529 }, 2530 }) 2531 2532 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2533 assertNoErrors(t, diags) 2534 2535 state, diags := ctx.Apply(plan, m) 2536 if diags.HasErrors() { 2537 t.Fatalf("diags: %s", diags.Err()) 2538 } 2539 2540 checkStateString(t, state, ` 2541 <no state> 2542 module.child: 2543 aws_instance.foo: 2544 ID = foo 2545 provider = provider["registry.terraform.io/hashicorp/aws"].eu 2546 type = aws_instance 2547 `) 2548 } 2549 2550 func TestContext2Apply_orphanResource(t *testing.T) { 2551 // This is a two-step test: 2552 // 1. Apply a configuration with resources that have count set. 2553 // This should place the empty resource object in the state to record 2554 // that each exists, and record any instances. 2555 // 2. Apply an empty configuration against the same state, which should 2556 // then clean up both the instances and the containing resource objects. 2557 p := testProvider("test") 2558 p.PlanResourceChangeFn = testDiffFn 2559 p.ApplyResourceChangeFn = testApplyFn 2560 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 2561 ResourceTypes: map[string]*configschema.Block{ 2562 "test_thing": { 2563 Attributes: map[string]*configschema.Attribute{ 2564 "id": {Type: cty.String, Computed: true}, 2565 "foo": {Type: cty.String, Optional: true}, 2566 }, 2567 }, 2568 }, 2569 }) 2570 2571 // Step 1: create the resources and instances 2572 m := testModule(t, "apply-orphan-resource") 2573 ctx := testContext2(t, &ContextOpts{ 2574 Providers: map[addrs.Provider]providers.Factory{ 2575 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 2576 }, 2577 }) 2578 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2579 assertNoErrors(t, diags) 2580 state, diags := ctx.Apply(plan, m) 2581 assertNoErrors(t, diags) 2582 2583 // At this point both resources should be recorded in the state, along 2584 // with the single instance associated with test_thing.one. 2585 want := states.BuildState(func(s *states.SyncState) { 2586 providerAddr := addrs.AbsProviderConfig{ 2587 Provider: addrs.NewDefaultProvider("test"), 2588 Module: addrs.RootModule, 2589 } 2590 oneAddr := addrs.Resource{ 2591 Mode: addrs.ManagedResourceMode, 2592 Type: "test_thing", 2593 Name: "one", 2594 }.Absolute(addrs.RootModuleInstance) 2595 s.SetResourceProvider(oneAddr, providerAddr) 2596 s.SetResourceInstanceCurrent(oneAddr.Instance(addrs.IntKey(0)), &states.ResourceInstanceObjectSrc{ 2597 Status: states.ObjectReady, 2598 AttrsJSON: []byte(`{"id":"foo"}`), 2599 }, providerAddr) 2600 }) 2601 2602 if state.String() != want.String() { 2603 t.Fatalf("wrong state after step 1\n%s", cmp.Diff(want, state)) 2604 } 2605 2606 // Step 2: update with an empty config, to destroy everything 2607 m = testModule(t, "empty") 2608 ctx = testContext2(t, &ContextOpts{ 2609 Providers: map[addrs.Provider]providers.Factory{ 2610 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 2611 }, 2612 }) 2613 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 2614 assertNoErrors(t, diags) 2615 { 2616 addr := mustResourceInstanceAddr("test_thing.one[0]") 2617 change := plan.Changes.ResourceInstance(addr) 2618 if change == nil { 2619 t.Fatalf("no planned change for %s", addr) 2620 } 2621 if got, want := change.Action, plans.Delete; got != want { 2622 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 2623 } 2624 if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseNoResourceConfig; got != want { 2625 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 2626 } 2627 } 2628 2629 state, diags = ctx.Apply(plan, m) 2630 assertNoErrors(t, diags) 2631 2632 // The state should now be _totally_ empty, with just an empty root module 2633 // (since that always exists) and no resources at all. 2634 want = states.NewState() 2635 if !cmp.Equal(state, want) { 2636 t.Fatalf("wrong state after step 2\ngot: %swant: %s", spew.Sdump(state), spew.Sdump(want)) 2637 } 2638 2639 } 2640 2641 func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) { 2642 m := testModule(t, "apply-module-provider-inherit-alias-orphan") 2643 p := testProvider("aws") 2644 p.PlanResourceChangeFn = testDiffFn 2645 2646 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2647 val := req.Config.GetAttr("value") 2648 if val.IsNull() { 2649 return 2650 } 2651 2652 root := req.Config.GetAttr("root") 2653 if !root.IsNull() { 2654 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("child should not get root")) 2655 } 2656 2657 return 2658 } 2659 2660 // Create a state with an orphan module 2661 state := states.NewState() 2662 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 2663 child.SetResourceInstanceCurrent( 2664 mustResourceInstanceAddr("aws_instance.bar").Resource, 2665 &states.ResourceInstanceObjectSrc{ 2666 Status: states.ObjectReady, 2667 AttrsJSON: []byte(`{"id":"bar"}`), 2668 }, 2669 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2670 ) 2671 2672 ctx := testContext2(t, &ContextOpts{ 2673 Providers: map[addrs.Provider]providers.Factory{ 2674 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2675 }, 2676 }) 2677 2678 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2679 assertNoErrors(t, diags) 2680 { 2681 addr := mustResourceInstanceAddr("module.child.aws_instance.bar") 2682 change := plan.Changes.ResourceInstance(addr) 2683 if change == nil { 2684 t.Fatalf("no planned change for %s", addr) 2685 } 2686 if got, want := change.Action, plans.Delete; got != want { 2687 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 2688 } 2689 // This should ideally be ResourceInstanceDeleteBecauseNoModule, but 2690 // the codepath deciding this doesn't currently have enough information 2691 // to differentiate, and so this is a compromise. 2692 if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseNoResourceConfig; got != want { 2693 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 2694 } 2695 } 2696 2697 state, diags = ctx.Apply(plan, m) 2698 if diags.HasErrors() { 2699 t.Fatalf("diags: %s", diags.Err()) 2700 } 2701 2702 if !p.ConfigureProviderCalled { 2703 t.Fatal("must call configure") 2704 } 2705 2706 checkStateString(t, state, "<no state>") 2707 } 2708 2709 func TestContext2Apply_moduleOrphanProvider(t *testing.T) { 2710 m := testModule(t, "apply-module-orphan-provider-inherit") 2711 p := testProvider("aws") 2712 p.PlanResourceChangeFn = testDiffFn 2713 2714 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2715 val := req.Config.GetAttr("value") 2716 if val.IsNull() { 2717 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 2718 } 2719 2720 return 2721 } 2722 2723 // Create a state with an orphan module 2724 state := states.NewState() 2725 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 2726 child.SetResourceInstanceCurrent( 2727 mustResourceInstanceAddr("aws_instance.bar").Resource, 2728 &states.ResourceInstanceObjectSrc{ 2729 Status: states.ObjectReady, 2730 AttrsJSON: []byte(`{"id":"bar"}`), 2731 }, 2732 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2733 ) 2734 2735 ctx := testContext2(t, &ContextOpts{ 2736 Providers: map[addrs.Provider]providers.Factory{ 2737 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2738 }, 2739 }) 2740 2741 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2742 assertNoErrors(t, diags) 2743 2744 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 2745 t.Fatalf("apply errors: %s", diags.Err()) 2746 } 2747 } 2748 2749 func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) { 2750 m := testModule(t, "apply-module-orphan-provider-inherit") 2751 p := testProvider("aws") 2752 p.PlanResourceChangeFn = testDiffFn 2753 2754 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2755 val := req.Config.GetAttr("value") 2756 if val.IsNull() { 2757 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 2758 } 2759 2760 return 2761 } 2762 2763 // Create a state with an orphan module that is nested (grandchild) 2764 state := states.NewState() 2765 child := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey).Child("child", addrs.NoKey)) 2766 child.SetResourceInstanceCurrent( 2767 mustResourceInstanceAddr("aws_instance.bar").Resource, 2768 &states.ResourceInstanceObjectSrc{ 2769 Status: states.ObjectReady, 2770 AttrsJSON: []byte(`{"id":"bar"}`), 2771 }, 2772 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2773 ) 2774 2775 ctx := testContext2(t, &ContextOpts{ 2776 Providers: map[addrs.Provider]providers.Factory{ 2777 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2778 }, 2779 }) 2780 2781 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2782 assertNoErrors(t, diags) 2783 2784 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 2785 t.Fatalf("apply errors: %s", diags.Err()) 2786 } 2787 } 2788 2789 func TestContext2Apply_moduleGrandchildProvider(t *testing.T) { 2790 m := testModule(t, "apply-module-grandchild-provider-inherit") 2791 p := testProvider("aws") 2792 p.PlanResourceChangeFn = testDiffFn 2793 2794 var callLock sync.Mutex 2795 called := false 2796 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2797 val := req.Config.GetAttr("value") 2798 if val.IsNull() { 2799 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 2800 } 2801 2802 callLock.Lock() 2803 called = true 2804 callLock.Unlock() 2805 2806 return 2807 } 2808 2809 ctx := testContext2(t, &ContextOpts{ 2810 Providers: map[addrs.Provider]providers.Factory{ 2811 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2812 }, 2813 }) 2814 2815 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2816 assertNoErrors(t, diags) 2817 2818 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 2819 t.Fatalf("apply errors: %s", diags.Err()) 2820 } 2821 2822 callLock.Lock() 2823 defer callLock.Unlock() 2824 if called != true { 2825 t.Fatalf("err: configure never called") 2826 } 2827 } 2828 2829 // This tests an issue where all the providers in a module but not 2830 // in the root weren't being added to the root properly. In this test 2831 // case: aws is explicitly added to root, but "test" should be added to. 2832 // With the bug, it wasn't. 2833 func TestContext2Apply_moduleOnlyProvider(t *testing.T) { 2834 m := testModule(t, "apply-module-only-provider") 2835 p := testProvider("aws") 2836 p.PlanResourceChangeFn = testDiffFn 2837 p.ApplyResourceChangeFn = testApplyFn 2838 pTest := testProvider("test") 2839 pTest.ApplyResourceChangeFn = testApplyFn 2840 pTest.PlanResourceChangeFn = testDiffFn 2841 2842 ctx := testContext2(t, &ContextOpts{ 2843 Providers: map[addrs.Provider]providers.Factory{ 2844 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2845 addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest), 2846 }, 2847 }) 2848 2849 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2850 assertNoErrors(t, diags) 2851 2852 state, diags := ctx.Apply(plan, m) 2853 if diags.HasErrors() { 2854 t.Fatalf("diags: %s", diags.Err()) 2855 } 2856 2857 actual := strings.TrimSpace(state.String()) 2858 expected := strings.TrimSpace(testTerraformApplyModuleOnlyProviderStr) 2859 if actual != expected { 2860 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2861 } 2862 } 2863 2864 func TestContext2Apply_moduleProviderAlias(t *testing.T) { 2865 m := testModule(t, "apply-module-provider-alias") 2866 p := testProvider("aws") 2867 p.PlanResourceChangeFn = testDiffFn 2868 p.ApplyResourceChangeFn = testApplyFn 2869 ctx := testContext2(t, &ContextOpts{ 2870 Providers: map[addrs.Provider]providers.Factory{ 2871 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2872 }, 2873 }) 2874 2875 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 2876 assertNoErrors(t, diags) 2877 2878 state, diags := ctx.Apply(plan, m) 2879 if diags.HasErrors() { 2880 t.Fatalf("diags: %s", diags.Err()) 2881 } 2882 2883 actual := strings.TrimSpace(state.String()) 2884 expected := strings.TrimSpace(testTerraformApplyModuleProviderAliasStr) 2885 if actual != expected { 2886 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2887 } 2888 } 2889 2890 func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) { 2891 m := testModule(t, "apply-module-provider-alias") 2892 p := testProvider("aws") 2893 p.PlanResourceChangeFn = testDiffFn 2894 ctx := testContext2(t, &ContextOpts{ 2895 Providers: map[addrs.Provider]providers.Factory{ 2896 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2897 }, 2898 }) 2899 2900 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 2901 Mode: plans.NormalMode, 2902 Targets: []addrs.Targetable{ 2903 addrs.ConfigResource{ 2904 Module: addrs.RootModule, 2905 Resource: addrs.Resource{ 2906 Mode: addrs.ManagedResourceMode, 2907 Type: "nonexistent", 2908 Name: "thing", 2909 }, 2910 }, 2911 }, 2912 }) 2913 assertNoErrors(t, diags) 2914 2915 state, diags := ctx.Apply(plan, m) 2916 if diags.HasErrors() { 2917 t.Fatalf("diags: %s", diags.Err()) 2918 } 2919 2920 actual := strings.TrimSpace(state.String()) 2921 expected := strings.TrimSpace(` 2922 <no state> 2923 `) 2924 if actual != expected { 2925 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2926 } 2927 } 2928 2929 func TestContext2Apply_moduleProviderCloseNested(t *testing.T) { 2930 m := testModule(t, "apply-module-provider-close-nested") 2931 p := testProvider("aws") 2932 p.PlanResourceChangeFn = testDiffFn 2933 state := states.NewState() 2934 root := state.EnsureModule(addrs.RootModuleInstance) 2935 root.SetResourceInstanceCurrent( 2936 mustResourceInstanceAddr("aws_instance.foo").Resource, 2937 &states.ResourceInstanceObjectSrc{ 2938 Status: states.ObjectReady, 2939 AttrsJSON: []byte(`{"id":"bar"}`), 2940 }, 2941 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2942 ) 2943 2944 ctx := testContext2(t, &ContextOpts{ 2945 Providers: map[addrs.Provider]providers.Factory{ 2946 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2947 }, 2948 }) 2949 2950 plan, diags := ctx.Plan(m, state, &PlanOpts{ 2951 Mode: plans.DestroyMode, 2952 }) 2953 assertNoErrors(t, diags) 2954 2955 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 2956 t.Fatalf("apply errors: %s", diags.Err()) 2957 } 2958 } 2959 2960 // Tests that variables used as module vars that reference data that 2961 // already exists in the state and requires no diff works properly. This 2962 // fixes an issue faced where module variables were pruned because they were 2963 // accessing "non-existent" resources (they existed, just not in the graph 2964 // cause they weren't in the diff). 2965 func TestContext2Apply_moduleVarRefExisting(t *testing.T) { 2966 m := testModule(t, "apply-ref-existing") 2967 p := testProvider("aws") 2968 p.PlanResourceChangeFn = testDiffFn 2969 p.ApplyResourceChangeFn = testApplyFn 2970 state := states.NewState() 2971 root := state.EnsureModule(addrs.RootModuleInstance) 2972 root.SetResourceInstanceCurrent( 2973 mustResourceInstanceAddr("aws_instance.foo").Resource, 2974 &states.ResourceInstanceObjectSrc{ 2975 Status: states.ObjectReady, 2976 AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`), 2977 }, 2978 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2979 ) 2980 2981 ctx := testContext2(t, &ContextOpts{ 2982 Providers: map[addrs.Provider]providers.Factory{ 2983 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2984 }, 2985 }) 2986 2987 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 2988 assertNoErrors(t, diags) 2989 2990 state, diags = ctx.Apply(plan, m) 2991 if diags.HasErrors() { 2992 t.Fatalf("diags: %s", diags.Err()) 2993 } 2994 2995 actual := strings.TrimSpace(state.String()) 2996 expected := strings.TrimSpace(testTerraformApplyModuleVarRefExistingStr) 2997 if actual != expected { 2998 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2999 } 3000 } 3001 3002 func TestContext2Apply_moduleVarResourceCount(t *testing.T) { 3003 m := testModule(t, "apply-module-var-resource-count") 3004 p := testProvider("aws") 3005 p.PlanResourceChangeFn = testDiffFn 3006 ctx := testContext2(t, &ContextOpts{ 3007 Providers: map[addrs.Provider]providers.Factory{ 3008 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3009 }, 3010 }) 3011 3012 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 3013 Mode: plans.DestroyMode, 3014 SetVariables: InputValues{ 3015 "num": &InputValue{ 3016 Value: cty.NumberIntVal(2), 3017 SourceType: ValueFromCaller, 3018 }, 3019 }, 3020 }) 3021 assertNoErrors(t, diags) 3022 3023 state, diags := ctx.Apply(plan, m) 3024 assertNoErrors(t, diags) 3025 3026 ctx = testContext2(t, &ContextOpts{ 3027 Providers: map[addrs.Provider]providers.Factory{ 3028 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3029 }, 3030 }) 3031 3032 plan, diags = ctx.Plan(m, state, &PlanOpts{ 3033 Mode: plans.NormalMode, 3034 SetVariables: InputValues{ 3035 "num": &InputValue{ 3036 Value: cty.NumberIntVal(5), 3037 SourceType: ValueFromCaller, 3038 }, 3039 }, 3040 }) 3041 assertNoErrors(t, diags) 3042 3043 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 3044 t.Fatalf("apply errors: %s", diags.Err()) 3045 } 3046 } 3047 3048 // GH-819 3049 func TestContext2Apply_moduleBool(t *testing.T) { 3050 m := testModule(t, "apply-module-bool") 3051 p := testProvider("aws") 3052 p.PlanResourceChangeFn = testDiffFn 3053 p.ApplyResourceChangeFn = testApplyFn 3054 ctx := testContext2(t, &ContextOpts{ 3055 Providers: map[addrs.Provider]providers.Factory{ 3056 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3057 }, 3058 }) 3059 3060 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3061 assertNoErrors(t, diags) 3062 3063 state, diags := ctx.Apply(plan, m) 3064 if diags.HasErrors() { 3065 t.Fatalf("diags: %s", diags.Err()) 3066 } 3067 3068 actual := strings.TrimSpace(state.String()) 3069 expected := strings.TrimSpace(testTerraformApplyModuleBoolStr) 3070 if actual != expected { 3071 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3072 } 3073 } 3074 3075 // Tests that a module can be targeted and everything is properly created. 3076 // This adds to the plan test to also just verify that apply works. 3077 func TestContext2Apply_moduleTarget(t *testing.T) { 3078 m := testModule(t, "plan-targeted-cross-module") 3079 p := testProvider("aws") 3080 p.PlanResourceChangeFn = testDiffFn 3081 p.ApplyResourceChangeFn = testApplyFn 3082 ctx := testContext2(t, &ContextOpts{ 3083 Providers: map[addrs.Provider]providers.Factory{ 3084 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3085 }, 3086 }) 3087 3088 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 3089 Mode: plans.NormalMode, 3090 Targets: []addrs.Targetable{ 3091 addrs.RootModuleInstance.Child("B", addrs.NoKey), 3092 }, 3093 }) 3094 assertNoErrors(t, diags) 3095 3096 state, diags := ctx.Apply(plan, m) 3097 if diags.HasErrors() { 3098 t.Fatalf("diags: %s", diags.Err()) 3099 } 3100 3101 checkStateString(t, state, ` 3102 <no state> 3103 module.A: 3104 aws_instance.foo: 3105 ID = foo 3106 provider = provider["registry.terraform.io/hashicorp/aws"] 3107 foo = bar 3108 type = aws_instance 3109 3110 Outputs: 3111 3112 value = foo 3113 module.B: 3114 aws_instance.bar: 3115 ID = foo 3116 provider = provider["registry.terraform.io/hashicorp/aws"] 3117 foo = foo 3118 type = aws_instance 3119 3120 Dependencies: 3121 module.A.aws_instance.foo 3122 `) 3123 } 3124 3125 func TestContext2Apply_multiProvider(t *testing.T) { 3126 m := testModule(t, "apply-multi-provider") 3127 p := testProvider("aws") 3128 p.PlanResourceChangeFn = testDiffFn 3129 p.ApplyResourceChangeFn = testApplyFn 3130 3131 pDO := testProvider("do") 3132 pDO.ApplyResourceChangeFn = testApplyFn 3133 pDO.PlanResourceChangeFn = testDiffFn 3134 3135 ctx := testContext2(t, &ContextOpts{ 3136 Providers: map[addrs.Provider]providers.Factory{ 3137 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3138 addrs.NewDefaultProvider("do"): testProviderFuncFixed(pDO), 3139 }, 3140 }) 3141 3142 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3143 assertNoErrors(t, diags) 3144 3145 state, diags := ctx.Apply(plan, m) 3146 if diags.HasErrors() { 3147 t.Fatalf("diags: %s", diags.Err()) 3148 } 3149 3150 mod := state.RootModule() 3151 if len(mod.Resources) < 2 { 3152 t.Fatalf("bad: %#v", mod.Resources) 3153 } 3154 3155 actual := strings.TrimSpace(state.String()) 3156 expected := strings.TrimSpace(testTerraformApplyMultiProviderStr) 3157 if actual != expected { 3158 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3159 } 3160 } 3161 3162 func TestContext2Apply_multiProviderDestroy(t *testing.T) { 3163 m := testModule(t, "apply-multi-provider-destroy") 3164 p := testProvider("aws") 3165 p.PlanResourceChangeFn = testDiffFn 3166 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3167 Provider: &configschema.Block{ 3168 Attributes: map[string]*configschema.Attribute{ 3169 "addr": {Type: cty.String, Optional: true}, 3170 }, 3171 }, 3172 ResourceTypes: map[string]*configschema.Block{ 3173 "aws_instance": { 3174 Attributes: map[string]*configschema.Attribute{ 3175 "id": {Type: cty.String, Computed: true}, 3176 "foo": {Type: cty.String, Optional: true}, 3177 }, 3178 }, 3179 }, 3180 }) 3181 3182 p2 := testProvider("vault") 3183 p2.ApplyResourceChangeFn = testApplyFn 3184 p2.PlanResourceChangeFn = testDiffFn 3185 p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3186 ResourceTypes: map[string]*configschema.Block{ 3187 "vault_instance": { 3188 Attributes: map[string]*configschema.Attribute{ 3189 "id": {Type: cty.String, Computed: true}, 3190 }, 3191 }, 3192 }, 3193 }) 3194 3195 var state *states.State 3196 3197 // First, create the instances 3198 { 3199 ctx := testContext2(t, &ContextOpts{ 3200 Providers: map[addrs.Provider]providers.Factory{ 3201 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3202 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3203 }, 3204 }) 3205 3206 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3207 assertNoErrors(t, diags) 3208 3209 s, diags := ctx.Apply(plan, m) 3210 assertNoErrors(t, diags) 3211 3212 state = s 3213 } 3214 3215 // Destroy them 3216 { 3217 // Verify that aws_instance.bar is destroyed first 3218 var checked bool 3219 var called int32 3220 var lock sync.Mutex 3221 applyFn := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 3222 lock.Lock() 3223 defer lock.Unlock() 3224 3225 if req.TypeName == "aws_instance" { 3226 checked = true 3227 3228 // Sleep to allow parallel execution 3229 time.Sleep(50 * time.Millisecond) 3230 3231 // Verify that called is 0 (dep not called) 3232 if atomic.LoadInt32(&called) != 0 { 3233 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called")) 3234 return resp 3235 } 3236 } 3237 3238 atomic.AddInt32(&called, 1) 3239 return testApplyFn(req) 3240 } 3241 3242 // Set the apply functions 3243 p.ApplyResourceChangeFn = applyFn 3244 p2.ApplyResourceChangeFn = applyFn 3245 3246 ctx := testContext2(t, &ContextOpts{ 3247 Providers: map[addrs.Provider]providers.Factory{ 3248 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3249 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3250 }, 3251 }) 3252 3253 plan, diags := ctx.Plan(m, state, &PlanOpts{ 3254 Mode: plans.DestroyMode, 3255 }) 3256 assertNoErrors(t, diags) 3257 3258 s, diags := ctx.Apply(plan, m) 3259 assertNoErrors(t, diags) 3260 3261 if !checked { 3262 t.Fatal("should be checked") 3263 } 3264 3265 state = s 3266 } 3267 3268 checkStateString(t, state, `<no state>`) 3269 } 3270 3271 // This is like the multiProviderDestroy test except it tests that 3272 // dependent resources within a child module that inherit provider 3273 // configuration are still destroyed first. 3274 func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { 3275 m := testModule(t, "apply-multi-provider-destroy-child") 3276 p := testProvider("aws") 3277 p.PlanResourceChangeFn = testDiffFn 3278 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3279 Provider: &configschema.Block{ 3280 Attributes: map[string]*configschema.Attribute{ 3281 "value": {Type: cty.String, Optional: true}, 3282 }, 3283 }, 3284 ResourceTypes: map[string]*configschema.Block{ 3285 "aws_instance": { 3286 Attributes: map[string]*configschema.Attribute{ 3287 "id": {Type: cty.String, Computed: true}, 3288 "foo": {Type: cty.String, Optional: true}, 3289 }, 3290 }, 3291 }, 3292 }) 3293 3294 p2 := testProvider("vault") 3295 p2.ApplyResourceChangeFn = testApplyFn 3296 p2.PlanResourceChangeFn = testDiffFn 3297 p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3298 Provider: &configschema.Block{}, 3299 ResourceTypes: map[string]*configschema.Block{ 3300 "vault_instance": { 3301 Attributes: map[string]*configschema.Attribute{ 3302 "id": {Type: cty.String, Computed: true}, 3303 }, 3304 }, 3305 }, 3306 }) 3307 3308 var state *states.State 3309 3310 // First, create the instances 3311 { 3312 ctx := testContext2(t, &ContextOpts{ 3313 Providers: map[addrs.Provider]providers.Factory{ 3314 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3315 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3316 }, 3317 }) 3318 3319 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3320 assertNoErrors(t, diags) 3321 3322 s, diags := ctx.Apply(plan, m) 3323 if diags.HasErrors() { 3324 t.Fatalf("diags: %s", diags.Err()) 3325 } 3326 3327 state = s 3328 } 3329 3330 // Destroy them 3331 { 3332 // Verify that aws_instance.bar is destroyed first 3333 var checked bool 3334 var called int32 3335 var lock sync.Mutex 3336 applyFn := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 3337 lock.Lock() 3338 defer lock.Unlock() 3339 3340 if req.TypeName == "aws_instance" { 3341 checked = true 3342 3343 // Sleep to allow parallel execution 3344 time.Sleep(50 * time.Millisecond) 3345 3346 // Verify that called is 0 (dep not called) 3347 if atomic.LoadInt32(&called) != 0 { 3348 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called")) 3349 return resp 3350 } 3351 } 3352 3353 atomic.AddInt32(&called, 1) 3354 return testApplyFn(req) 3355 } 3356 3357 // Set the apply functions 3358 p.ApplyResourceChangeFn = applyFn 3359 p2.ApplyResourceChangeFn = applyFn 3360 3361 ctx := testContext2(t, &ContextOpts{ 3362 Providers: map[addrs.Provider]providers.Factory{ 3363 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3364 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3365 }, 3366 }) 3367 3368 plan, diags := ctx.Plan(m, state, &PlanOpts{ 3369 Mode: plans.DestroyMode, 3370 }) 3371 assertNoErrors(t, diags) 3372 3373 s, diags := ctx.Apply(plan, m) 3374 if diags.HasErrors() { 3375 t.Fatalf("diags: %s", diags.Err()) 3376 } 3377 3378 if !checked { 3379 t.Fatal("should be checked") 3380 } 3381 3382 state = s 3383 } 3384 3385 checkStateString(t, state, ` 3386 <no state> 3387 `) 3388 } 3389 3390 func TestContext2Apply_multiVar(t *testing.T) { 3391 m := testModule(t, "apply-multi-var") 3392 p := testProvider("aws") 3393 p.PlanResourceChangeFn = testDiffFn 3394 3395 // First, apply with a count of 3 3396 ctx := testContext2(t, &ContextOpts{ 3397 Providers: map[addrs.Provider]providers.Factory{ 3398 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3399 }, 3400 }) 3401 3402 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 3403 Mode: plans.NormalMode, 3404 SetVariables: InputValues{ 3405 "num": &InputValue{ 3406 Value: cty.NumberIntVal(3), 3407 SourceType: ValueFromCaller, 3408 }, 3409 }, 3410 }) 3411 assertNoErrors(t, diags) 3412 3413 state, diags := ctx.Apply(plan, m) 3414 if diags.HasErrors() { 3415 t.Fatalf("diags: %s", diags.Err()) 3416 } 3417 3418 actual := state.RootModule().OutputValues["output"] 3419 expected := cty.StringVal("bar0,bar1,bar2") 3420 if actual == nil || actual.Value != expected { 3421 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3422 } 3423 3424 t.Logf("Initial state: %s", state.String()) 3425 3426 // Apply again, reduce the count to 1 3427 { 3428 ctx := testContext2(t, &ContextOpts{ 3429 Providers: map[addrs.Provider]providers.Factory{ 3430 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3431 }, 3432 }) 3433 3434 plan, diags := ctx.Plan(m, state, &PlanOpts{ 3435 Mode: plans.NormalMode, 3436 SetVariables: InputValues{ 3437 "num": &InputValue{ 3438 Value: cty.NumberIntVal(1), 3439 SourceType: ValueFromCaller, 3440 }, 3441 }, 3442 }) 3443 assertNoErrors(t, diags) 3444 3445 state, diags := ctx.Apply(plan, m) 3446 if diags.HasErrors() { 3447 t.Fatalf("diags: %s", diags.Err()) 3448 } 3449 3450 t.Logf("End state: %s", state.String()) 3451 3452 actual := state.RootModule().OutputValues["output"] 3453 if actual == nil { 3454 t.Fatal("missing output") 3455 } 3456 3457 expected := cty.StringVal("bar0") 3458 if actual.Value != expected { 3459 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3460 } 3461 } 3462 } 3463 3464 // This is a holistic test of multi-var (aka "splat variable") handling 3465 // across several different Terraform subsystems. This is here because 3466 // historically there were quirky differences in handling across different 3467 // parts of Terraform and so here we want to assert the expected behavior and 3468 // ensure that it remains consistent in future. 3469 func TestContext2Apply_multiVarComprehensive(t *testing.T) { 3470 m := testModule(t, "apply-multi-var-comprehensive") 3471 p := testProvider("test") 3472 3473 configs := map[string]cty.Value{} 3474 var configsLock sync.Mutex 3475 3476 p.ApplyResourceChangeFn = testApplyFn 3477 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 3478 proposed := req.ProposedNewState 3479 configsLock.Lock() 3480 defer configsLock.Unlock() 3481 key := proposed.GetAttr("key").AsString() 3482 // This test was originally written using the legacy p.PlanResourceChangeFn interface, 3483 // and so the assertions below expect an old-style ResourceConfig, which 3484 // we'll construct via our shim for now to avoid rewriting all of the 3485 // assertions. 3486 configs[key] = req.ProposedNewState 3487 3488 retVals := make(map[string]cty.Value) 3489 for it := proposed.ElementIterator(); it.Next(); { 3490 idxVal, val := it.Element() 3491 idx := idxVal.AsString() 3492 3493 switch idx { 3494 case "id": 3495 retVals[idx] = cty.UnknownVal(cty.String) 3496 case "name": 3497 retVals[idx] = cty.StringVal(key) 3498 default: 3499 retVals[idx] = val 3500 } 3501 } 3502 3503 return providers.PlanResourceChangeResponse{ 3504 PlannedState: cty.ObjectVal(retVals), 3505 } 3506 } 3507 3508 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3509 ResourceTypes: map[string]*configschema.Block{ 3510 "test_thing": { 3511 Attributes: map[string]*configschema.Attribute{ 3512 "key": {Type: cty.String, Required: true}, 3513 3514 "source_id": {Type: cty.String, Optional: true}, 3515 "source_name": {Type: cty.String, Optional: true}, 3516 "first_source_id": {Type: cty.String, Optional: true}, 3517 "first_source_name": {Type: cty.String, Optional: true}, 3518 "source_ids": {Type: cty.List(cty.String), Optional: true}, 3519 "source_names": {Type: cty.List(cty.String), Optional: true}, 3520 "source_ids_from_func": {Type: cty.List(cty.String), Optional: true}, 3521 "source_names_from_func": {Type: cty.List(cty.String), Optional: true}, 3522 "source_ids_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true}, 3523 "source_names_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true}, 3524 3525 "id": {Type: cty.String, Computed: true}, 3526 "name": {Type: cty.String, Computed: true}, 3527 }, 3528 }, 3529 }, 3530 }) 3531 3532 // First, apply with a count of 3 3533 ctx := testContext2(t, &ContextOpts{ 3534 Providers: map[addrs.Provider]providers.Factory{ 3535 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 3536 }, 3537 }) 3538 3539 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 3540 Mode: plans.NormalMode, 3541 SetVariables: InputValues{ 3542 "num": &InputValue{ 3543 Value: cty.NumberIntVal(3), 3544 SourceType: ValueFromCaller, 3545 }, 3546 }, 3547 }) 3548 assertNoErrors(t, diags) 3549 3550 checkConfig := func(key string, want cty.Value) { 3551 configsLock.Lock() 3552 defer configsLock.Unlock() 3553 3554 got, ok := configs[key] 3555 if !ok { 3556 t.Errorf("no config recorded for %s; expected a configuration", key) 3557 return 3558 } 3559 3560 t.Run("config for "+key, func(t *testing.T) { 3561 for _, problem := range deep.Equal(got, want) { 3562 t.Errorf(problem) 3563 } 3564 }) 3565 } 3566 3567 checkConfig("multi_count_var.0", cty.ObjectVal(map[string]cty.Value{ 3568 "source_id": cty.UnknownVal(cty.String), 3569 "source_name": cty.StringVal("source.0"), 3570 })) 3571 checkConfig("multi_count_var.2", cty.ObjectVal(map[string]cty.Value{ 3572 "source_id": cty.UnknownVal(cty.String), 3573 "source_name": cty.StringVal("source.2"), 3574 })) 3575 checkConfig("multi_count_derived.0", cty.ObjectVal(map[string]cty.Value{ 3576 "source_id": cty.UnknownVal(cty.String), 3577 "source_name": cty.StringVal("source.0"), 3578 })) 3579 checkConfig("multi_count_derived.2", cty.ObjectVal(map[string]cty.Value{ 3580 "source_id": cty.UnknownVal(cty.String), 3581 "source_name": cty.StringVal("source.2"), 3582 })) 3583 checkConfig("whole_splat", cty.ObjectVal(map[string]cty.Value{ 3584 "source_ids": cty.ListVal([]cty.Value{ 3585 cty.UnknownVal(cty.String), 3586 cty.UnknownVal(cty.String), 3587 cty.UnknownVal(cty.String), 3588 }), 3589 "source_names": cty.ListVal([]cty.Value{ 3590 cty.StringVal("source.0"), 3591 cty.StringVal("source.1"), 3592 cty.StringVal("source.2"), 3593 }), 3594 "source_ids_from_func": cty.UnknownVal(cty.String), 3595 "source_names_from_func": cty.ListVal([]cty.Value{ 3596 cty.StringVal("source.0"), 3597 cty.StringVal("source.1"), 3598 cty.StringVal("source.2"), 3599 }), 3600 "source_ids_wrapped": cty.ListVal([]cty.Value{ 3601 cty.ListVal([]cty.Value{ 3602 cty.UnknownVal(cty.String), 3603 cty.UnknownVal(cty.String), 3604 cty.UnknownVal(cty.String), 3605 }), 3606 }), 3607 "source_names_wrapped": cty.ListVal([]cty.Value{ 3608 cty.ListVal([]cty.Value{ 3609 cty.StringVal("source.0"), 3610 cty.StringVal("source.1"), 3611 cty.StringVal("source.2"), 3612 }), 3613 }), 3614 "first_source_id": cty.UnknownVal(cty.String), 3615 "first_source_name": cty.StringVal("source.0"), 3616 })) 3617 checkConfig("child.whole_splat", cty.ObjectVal(map[string]cty.Value{ 3618 "source_ids": cty.ListVal([]cty.Value{ 3619 cty.UnknownVal(cty.String), 3620 cty.UnknownVal(cty.String), 3621 cty.UnknownVal(cty.String), 3622 }), 3623 "source_names": cty.ListVal([]cty.Value{ 3624 cty.StringVal("source.0"), 3625 cty.StringVal("source.1"), 3626 cty.StringVal("source.2"), 3627 }), 3628 "source_ids_wrapped": cty.ListVal([]cty.Value{ 3629 cty.ListVal([]cty.Value{ 3630 cty.UnknownVal(cty.String), 3631 cty.UnknownVal(cty.String), 3632 cty.UnknownVal(cty.String), 3633 }), 3634 }), 3635 "source_names_wrapped": cty.ListVal([]cty.Value{ 3636 cty.ListVal([]cty.Value{ 3637 cty.StringVal("source.0"), 3638 cty.StringVal("source.1"), 3639 cty.StringVal("source.2"), 3640 }), 3641 }), 3642 })) 3643 3644 t.Run("apply", func(t *testing.T) { 3645 state, diags := ctx.Apply(plan, m) 3646 if diags.HasErrors() { 3647 t.Fatalf("error during apply: %s", diags.Err()) 3648 } 3649 3650 want := map[string]interface{}{ 3651 "source_ids": []interface{}{"foo", "foo", "foo"}, 3652 "source_names": []interface{}{ 3653 "source.0", 3654 "source.1", 3655 "source.2", 3656 }, 3657 } 3658 got := map[string]interface{}{} 3659 for k, s := range state.RootModule().OutputValues { 3660 got[k] = hcl2shim.ConfigValueFromHCL2(s.Value) 3661 } 3662 if !reflect.DeepEqual(got, want) { 3663 t.Errorf( 3664 "wrong outputs\ngot: %s\nwant: %s", 3665 spew.Sdump(got), spew.Sdump(want), 3666 ) 3667 } 3668 }) 3669 } 3670 3671 // Test that multi-var (splat) access is ordered by count, not by 3672 // value. 3673 func TestContext2Apply_multiVarOrder(t *testing.T) { 3674 m := testModule(t, "apply-multi-var-order") 3675 p := testProvider("aws") 3676 p.PlanResourceChangeFn = testDiffFn 3677 3678 // First, apply with a count of 3 3679 ctx := testContext2(t, &ContextOpts{ 3680 Providers: map[addrs.Provider]providers.Factory{ 3681 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3682 }, 3683 }) 3684 3685 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3686 assertNoErrors(t, diags) 3687 3688 state, diags := ctx.Apply(plan, m) 3689 if diags.HasErrors() { 3690 t.Fatalf("diags: %s", diags.Err()) 3691 } 3692 3693 t.Logf("State: %s", state.String()) 3694 3695 actual := state.RootModule().OutputValues["should-be-11"] 3696 expected := cty.StringVal("index-11") 3697 if actual == nil || actual.Value != expected { 3698 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3699 } 3700 } 3701 3702 // Test that multi-var (splat) access is ordered by count, not by 3703 // value, through interpolations. 3704 func TestContext2Apply_multiVarOrderInterp(t *testing.T) { 3705 m := testModule(t, "apply-multi-var-order-interp") 3706 p := testProvider("aws") 3707 p.PlanResourceChangeFn = testDiffFn 3708 3709 // First, apply with a count of 3 3710 ctx := testContext2(t, &ContextOpts{ 3711 Providers: map[addrs.Provider]providers.Factory{ 3712 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3713 }, 3714 }) 3715 3716 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3717 assertNoErrors(t, diags) 3718 3719 state, diags := ctx.Apply(plan, m) 3720 if diags.HasErrors() { 3721 t.Fatalf("diags: %s", diags.Err()) 3722 } 3723 3724 t.Logf("State: %s", state.String()) 3725 3726 actual := state.RootModule().OutputValues["should-be-11"] 3727 expected := cty.StringVal("baz-index-11") 3728 if actual == nil || actual.Value != expected { 3729 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3730 } 3731 } 3732 3733 // Based on GH-10440 where a graph edge wasn't properly being created 3734 // between a modified resource and a count instance being destroyed. 3735 func TestContext2Apply_multiVarCountDec(t *testing.T) { 3736 var s *states.State 3737 3738 // First create resources. Nothing sneaky here. 3739 { 3740 m := testModule(t, "apply-multi-var-count-dec") 3741 p := testProvider("aws") 3742 p.PlanResourceChangeFn = testDiffFn 3743 p.ApplyResourceChangeFn = testApplyFn 3744 ctx := testContext2(t, &ContextOpts{ 3745 Providers: map[addrs.Provider]providers.Factory{ 3746 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3747 }, 3748 }) 3749 3750 log.Print("\n========\nStep 1 Plan\n========") 3751 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 3752 Mode: plans.NormalMode, 3753 SetVariables: InputValues{ 3754 "num": &InputValue{ 3755 Value: cty.NumberIntVal(2), 3756 SourceType: ValueFromCaller, 3757 }, 3758 }, 3759 }) 3760 assertNoErrors(t, diags) 3761 3762 log.Print("\n========\nStep 1 Apply\n========") 3763 state, diags := ctx.Apply(plan, m) 3764 if diags.HasErrors() { 3765 t.Fatalf("diags: %s", diags.Err()) 3766 } 3767 3768 t.Logf("Step 1 state:\n%s", state) 3769 3770 s = state 3771 } 3772 3773 // Decrease the count by 1 and verify that everything happens in the 3774 // right order. 3775 m := testModule(t, "apply-multi-var-count-dec") 3776 p := testProvider("aws") 3777 p.PlanResourceChangeFn = testDiffFn 3778 3779 // Verify that aws_instance.bar is modified first and nothing 3780 // else happens at the same time. 3781 { 3782 var checked bool 3783 var called int32 3784 var lock sync.Mutex 3785 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 3786 lock.Lock() 3787 defer lock.Unlock() 3788 3789 if !req.PlannedState.IsNull() { 3790 s := req.PlannedState.AsValueMap() 3791 if ami, ok := s["ami"]; ok && !ami.IsNull() && ami.AsString() == "special" { 3792 checked = true 3793 3794 // Sleep to allow parallel execution 3795 time.Sleep(50 * time.Millisecond) 3796 3797 // Verify that called is 0 (dep not called) 3798 if atomic.LoadInt32(&called) != 1 { 3799 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called")) 3800 return 3801 } 3802 } 3803 } 3804 atomic.AddInt32(&called, 1) 3805 return testApplyFn(req) 3806 } 3807 3808 ctx := testContext2(t, &ContextOpts{ 3809 Providers: map[addrs.Provider]providers.Factory{ 3810 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3811 }, 3812 }) 3813 3814 log.Print("\n========\nStep 2 Plan\n========") 3815 plan, diags := ctx.Plan(m, s, &PlanOpts{ 3816 Mode: plans.NormalMode, 3817 SetVariables: InputValues{ 3818 "num": &InputValue{ 3819 Value: cty.NumberIntVal(1), 3820 SourceType: ValueFromCaller, 3821 }, 3822 }, 3823 }) 3824 assertNoErrors(t, diags) 3825 3826 t.Logf("Step 2 plan:\n%s", legacyDiffComparisonString(plan.Changes)) 3827 3828 log.Print("\n========\nStep 2 Apply\n========") 3829 _, diags = ctx.Apply(plan, m) 3830 if diags.HasErrors() { 3831 t.Fatalf("apply errors: %s", diags.Err()) 3832 } 3833 3834 if !checked { 3835 t.Error("apply never called") 3836 } 3837 } 3838 } 3839 3840 // Test that we can resolve a multi-var (splat) for the first resource 3841 // created in a non-root module, which happens when the module state doesn't 3842 // exist yet. 3843 // https://github.com/muratcelep/terraform/issues/14438 3844 func TestContext2Apply_multiVarMissingState(t *testing.T) { 3845 m := testModule(t, "apply-multi-var-missing-state") 3846 p := testProvider("test") 3847 p.PlanResourceChangeFn = testDiffFn 3848 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3849 ResourceTypes: map[string]*configschema.Block{ 3850 "test_thing": { 3851 Attributes: map[string]*configschema.Attribute{ 3852 "a_ids": {Type: cty.String, Optional: true}, 3853 "id": {Type: cty.String, Computed: true}, 3854 }, 3855 }, 3856 }, 3857 }) 3858 3859 // First, apply with a count of 3 3860 ctx := testContext2(t, &ContextOpts{ 3861 Providers: map[addrs.Provider]providers.Factory{ 3862 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 3863 }, 3864 }) 3865 3866 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3867 assertNoErrors(t, diags) 3868 3869 // Before the relevant bug was fixed, Terraform would panic during apply. 3870 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 3871 t.Fatalf("apply failed: %s", diags.Err()) 3872 } 3873 3874 // If we get here with no errors or panics then our test was successful. 3875 } 3876 3877 func TestContext2Apply_outputOrphan(t *testing.T) { 3878 m := testModule(t, "apply-output-orphan") 3879 p := testProvider("aws") 3880 p.PlanResourceChangeFn = testDiffFn 3881 3882 state := states.NewState() 3883 root := state.EnsureModule(addrs.RootModuleInstance) 3884 root.SetOutputValue("foo", cty.StringVal("bar"), false) 3885 root.SetOutputValue("bar", cty.StringVal("baz"), false) 3886 3887 ctx := testContext2(t, &ContextOpts{ 3888 Providers: map[addrs.Provider]providers.Factory{ 3889 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3890 }, 3891 }) 3892 3893 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 3894 assertNoErrors(t, diags) 3895 3896 state, diags = ctx.Apply(plan, m) 3897 if diags.HasErrors() { 3898 t.Fatalf("diags: %s", diags.Err()) 3899 } 3900 3901 actual := strings.TrimSpace(state.String()) 3902 expected := strings.TrimSpace(testTerraformApplyOutputOrphanStr) 3903 if actual != expected { 3904 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3905 } 3906 } 3907 3908 func TestContext2Apply_outputOrphanModule(t *testing.T) { 3909 m := testModule(t, "apply-output-orphan-module") 3910 p := testProvider("aws") 3911 p.PlanResourceChangeFn = testDiffFn 3912 3913 state := states.NewState() 3914 3915 ctx := testContext2(t, &ContextOpts{ 3916 Providers: map[addrs.Provider]providers.Factory{ 3917 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3918 }, 3919 }) 3920 3921 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 3922 assertNoErrors(t, diags) 3923 3924 s, diags := ctx.Apply(plan, m) 3925 if diags.HasErrors() { 3926 t.Fatalf("diags: %s", diags.Err()) 3927 } 3928 3929 actual := strings.TrimSpace(s.String()) 3930 expected := strings.TrimSpace(testTerraformApplyOutputOrphanModuleStr) 3931 if actual != expected { 3932 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 3933 } 3934 3935 // now apply with no module in the config, which should remove the 3936 // remaining output 3937 ctx = testContext2(t, &ContextOpts{ 3938 Providers: map[addrs.Provider]providers.Factory{ 3939 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3940 }, 3941 }) 3942 3943 emptyConfig := configs.NewEmptyConfig() 3944 3945 // NOTE: While updating this test to pass the state in as a Plan argument, 3946 // rather than into the testContext2 call above, it previously said 3947 // State: state.DeepCopy(), which is a little weird since we just 3948 // created "s" above as the result of the previous apply, but I've preserved 3949 // it to avoid changing the flow of this test in case that's important 3950 // for some reason. 3951 plan, diags = ctx.Plan(emptyConfig, state.DeepCopy(), DefaultPlanOpts) 3952 assertNoErrors(t, diags) 3953 3954 state, diags = ctx.Apply(plan, emptyConfig) 3955 if diags.HasErrors() { 3956 t.Fatalf("diags: %s", diags.Err()) 3957 } 3958 3959 if !state.Empty() { 3960 t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(state)) 3961 } 3962 } 3963 3964 func TestContext2Apply_providerComputedVar(t *testing.T) { 3965 m := testModule(t, "apply-provider-computed") 3966 p := testProvider("aws") 3967 p.PlanResourceChangeFn = testDiffFn 3968 3969 pTest := testProvider("test") 3970 pTest.ApplyResourceChangeFn = testApplyFn 3971 pTest.PlanResourceChangeFn = testDiffFn 3972 3973 ctx := testContext2(t, &ContextOpts{ 3974 Providers: map[addrs.Provider]providers.Factory{ 3975 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3976 addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest), 3977 }, 3978 }) 3979 3980 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 3981 val := req.Config.GetAttr("value") 3982 if val.IsNull() { 3983 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 3984 return 3985 } 3986 return 3987 } 3988 3989 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 3990 assertNoErrors(t, diags) 3991 3992 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 3993 t.Fatalf("apply errors: %s", diags.Err()) 3994 } 3995 } 3996 3997 func TestContext2Apply_providerConfigureDisabled(t *testing.T) { 3998 m := testModule(t, "apply-provider-configure-disabled") 3999 p := testProvider("aws") 4000 p.PlanResourceChangeFn = testDiffFn 4001 4002 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 4003 val := req.Config.GetAttr("value") 4004 if val.IsNull() { 4005 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 4006 } 4007 4008 return 4009 } 4010 4011 ctx := testContext2(t, &ContextOpts{ 4012 Providers: map[addrs.Provider]providers.Factory{ 4013 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4014 }, 4015 }) 4016 4017 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4018 assertNoErrors(t, diags) 4019 4020 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 4021 t.Fatalf("apply errors: %s", diags.Err()) 4022 } 4023 4024 if !p.ConfigureProviderCalled { 4025 t.Fatal("configure never called") 4026 } 4027 } 4028 4029 func TestContext2Apply_provisionerModule(t *testing.T) { 4030 m := testModule(t, "apply-provisioner-module") 4031 4032 p := testProvider("aws") 4033 p.PlanResourceChangeFn = testDiffFn 4034 p.ApplyResourceChangeFn = testApplyFn 4035 4036 pr := testProvisioner() 4037 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 4038 Provisioner: &configschema.Block{ 4039 Attributes: map[string]*configschema.Attribute{ 4040 "foo": {Type: cty.String, Optional: true}, 4041 }, 4042 }, 4043 } 4044 4045 ctx := testContext2(t, &ContextOpts{ 4046 Providers: map[addrs.Provider]providers.Factory{ 4047 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4048 }, 4049 Provisioners: map[string]provisioners.Factory{ 4050 "shell": testProvisionerFuncFixed(pr), 4051 }, 4052 }) 4053 4054 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4055 assertNoErrors(t, diags) 4056 4057 state, diags := ctx.Apply(plan, m) 4058 if diags.HasErrors() { 4059 t.Fatalf("diags: %s", diags.Err()) 4060 } 4061 4062 actual := strings.TrimSpace(state.String()) 4063 expected := strings.TrimSpace(testTerraformApplyProvisionerModuleStr) 4064 if actual != expected { 4065 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4066 } 4067 4068 // Verify apply was invoked 4069 if !pr.ProvisionResourceCalled { 4070 t.Fatalf("provisioner not invoked") 4071 } 4072 } 4073 4074 func TestContext2Apply_Provisioner_compute(t *testing.T) { 4075 m := testModule(t, "apply-provisioner-compute") 4076 p := testProvider("aws") 4077 pr := testProvisioner() 4078 p.PlanResourceChangeFn = testDiffFn 4079 p.ApplyResourceChangeFn = testApplyFn 4080 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4081 4082 val := req.Config.GetAttr("command").AsString() 4083 if val != "computed_value" { 4084 t.Fatalf("bad value for foo: %q", val) 4085 } 4086 req.UIOutput.Output(fmt.Sprintf("Executing: %q", val)) 4087 4088 return 4089 } 4090 h := new(MockHook) 4091 ctx := testContext2(t, &ContextOpts{ 4092 Hooks: []Hook{h}, 4093 Providers: map[addrs.Provider]providers.Factory{ 4094 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4095 }, 4096 Provisioners: map[string]provisioners.Factory{ 4097 "shell": testProvisionerFuncFixed(pr), 4098 }, 4099 }) 4100 4101 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 4102 Mode: plans.NormalMode, 4103 SetVariables: InputValues{ 4104 "value": &InputValue{ 4105 Value: cty.NumberIntVal(1), 4106 SourceType: ValueFromCaller, 4107 }, 4108 }, 4109 }) 4110 assertNoErrors(t, diags) 4111 4112 state, diags := ctx.Apply(plan, m) 4113 if diags.HasErrors() { 4114 t.Fatalf("diags: %s", diags.Err()) 4115 } 4116 4117 actual := strings.TrimSpace(state.String()) 4118 expected := strings.TrimSpace(testTerraformApplyProvisionerStr) 4119 if actual != expected { 4120 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4121 } 4122 4123 // Verify apply was invoked 4124 if !pr.ProvisionResourceCalled { 4125 t.Fatalf("provisioner not invoked") 4126 } 4127 4128 // Verify output was rendered 4129 if !h.ProvisionOutputCalled { 4130 t.Fatalf("ProvisionOutput hook not called") 4131 } 4132 if got, want := h.ProvisionOutputMessage, `Executing: "computed_value"`; got != want { 4133 t.Errorf("expected output to be %q, but was %q", want, got) 4134 } 4135 } 4136 4137 func TestContext2Apply_provisionerCreateFail(t *testing.T) { 4138 m := testModule(t, "apply-provisioner-fail-create") 4139 p := testProvider("aws") 4140 pr := testProvisioner() 4141 p.PlanResourceChangeFn = testDiffFn 4142 4143 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 4144 resp := testApplyFn(req) 4145 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 4146 4147 return resp 4148 } 4149 4150 ctx := testContext2(t, &ContextOpts{ 4151 Providers: map[addrs.Provider]providers.Factory{ 4152 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4153 }, 4154 Provisioners: map[string]provisioners.Factory{ 4155 "shell": testProvisionerFuncFixed(pr), 4156 }, 4157 }) 4158 4159 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4160 assertNoErrors(t, diags) 4161 4162 state, diags := ctx.Apply(plan, m) 4163 if diags == nil { 4164 t.Fatal("should error") 4165 } 4166 4167 got := strings.TrimSpace(state.String()) 4168 want := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr) 4169 if got != want { 4170 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want) 4171 } 4172 } 4173 4174 func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) { 4175 m := testModule(t, "apply-provisioner-fail-create") 4176 p := testProvider("aws") 4177 pr := testProvisioner() 4178 p.PlanResourceChangeFn = testDiffFn 4179 4180 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4181 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 4182 return 4183 } 4184 4185 ctx := testContext2(t, &ContextOpts{ 4186 Providers: map[addrs.Provider]providers.Factory{ 4187 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4188 }, 4189 Provisioners: map[string]provisioners.Factory{ 4190 "shell": testProvisionerFuncFixed(pr), 4191 }, 4192 }) 4193 4194 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4195 assertNoErrors(t, diags) 4196 4197 state, diags := ctx.Apply(plan, m) 4198 if diags == nil { 4199 t.Fatal("should error") 4200 } 4201 4202 actual := strings.TrimSpace(state.String()) 4203 expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr) 4204 if actual != expected { 4205 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4206 } 4207 } 4208 4209 func TestContext2Apply_provisionerFail(t *testing.T) { 4210 m := testModule(t, "apply-provisioner-fail") 4211 p := testProvider("aws") 4212 p.PlanResourceChangeFn = testDiffFn 4213 p.ApplyResourceChangeFn = testApplyFn 4214 pr := testProvisioner() 4215 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4216 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION")) 4217 return 4218 } 4219 4220 ctx := testContext2(t, &ContextOpts{ 4221 Providers: map[addrs.Provider]providers.Factory{ 4222 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4223 }, 4224 Provisioners: map[string]provisioners.Factory{ 4225 "shell": testProvisionerFuncFixed(pr), 4226 }, 4227 }) 4228 4229 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4230 assertNoErrors(t, diags) 4231 4232 state, diags := ctx.Apply(plan, m) 4233 if diags == nil { 4234 t.Fatal("should error") 4235 } 4236 4237 actual := strings.TrimSpace(state.String()) 4238 expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr) 4239 if actual != expected { 4240 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4241 } 4242 } 4243 4244 func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) { 4245 m := testModule(t, "apply-provisioner-fail-create-before") 4246 p := testProvider("aws") 4247 pr := testProvisioner() 4248 p.PlanResourceChangeFn = testDiffFn 4249 p.ApplyResourceChangeFn = testApplyFn 4250 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4251 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION")) 4252 return 4253 } 4254 4255 state := states.NewState() 4256 root := state.EnsureModule(addrs.RootModuleInstance) 4257 root.SetResourceInstanceCurrent( 4258 mustResourceInstanceAddr("aws_instance.bar").Resource, 4259 &states.ResourceInstanceObjectSrc{ 4260 Status: states.ObjectReady, 4261 AttrsJSON: []byte(`{"id":"bar","require_new":"abc"}`), 4262 }, 4263 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4264 ) 4265 4266 ctx := testContext2(t, &ContextOpts{ 4267 Providers: map[addrs.Provider]providers.Factory{ 4268 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4269 }, 4270 Provisioners: map[string]provisioners.Factory{ 4271 "shell": testProvisionerFuncFixed(pr), 4272 }, 4273 }) 4274 4275 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 4276 assertNoErrors(t, diags) 4277 4278 state, diags = ctx.Apply(plan, m) 4279 if !diags.HasErrors() { 4280 t.Fatal("should error") 4281 } 4282 4283 actual := strings.TrimSpace(state.String()) 4284 expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr) 4285 if actual != expected { 4286 t.Fatalf("expected:\n%s\n:got\n%s", expected, actual) 4287 } 4288 } 4289 4290 func TestContext2Apply_error_createBeforeDestroy(t *testing.T) { 4291 m := testModule(t, "apply-error-create-before") 4292 p := testProvider("aws") 4293 4294 state := states.NewState() 4295 root := state.EnsureModule(addrs.RootModuleInstance) 4296 root.SetResourceInstanceCurrent( 4297 mustResourceInstanceAddr("aws_instance.bar").Resource, 4298 &states.ResourceInstanceObjectSrc{ 4299 Status: states.ObjectReady, 4300 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc","type":"aws_instance"}`), 4301 }, 4302 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4303 ) 4304 4305 ctx := testContext2(t, &ContextOpts{ 4306 Providers: map[addrs.Provider]providers.Factory{ 4307 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4308 }, 4309 }) 4310 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4311 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("placeholder error from ApplyFn")) 4312 return 4313 } 4314 p.PlanResourceChangeFn = testDiffFn 4315 4316 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 4317 assertNoErrors(t, diags) 4318 4319 state, diags = ctx.Apply(plan, m) 4320 if !diags.HasErrors() { 4321 t.Fatal("should have error") 4322 } 4323 if got, want := diags.Err().Error(), "placeholder error from ApplyFn"; got != want { 4324 // We're looking for our artificial error from ApplyFn above, whose 4325 // message is literally "placeholder error from ApplyFn". 4326 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 4327 } 4328 4329 actual := strings.TrimSpace(state.String()) 4330 expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr) 4331 if actual != expected { 4332 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected) 4333 } 4334 } 4335 4336 func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) { 4337 m := testModule(t, "apply-error-create-before") 4338 p := testProvider("aws") 4339 4340 state := states.NewState() 4341 root := state.EnsureModule(addrs.RootModuleInstance) 4342 root.SetResourceInstanceCurrent( 4343 mustResourceInstanceAddr("aws_instance.bar").Resource, 4344 &states.ResourceInstanceObjectSrc{ 4345 Status: states.ObjectReady, 4346 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 4347 }, 4348 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4349 ) 4350 4351 ctx := testContext2(t, &ContextOpts{ 4352 Providers: map[addrs.Provider]providers.Factory{ 4353 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4354 }, 4355 }) 4356 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4357 // Fail the destroy! 4358 if req.PlannedState.IsNull() { 4359 resp.NewState = req.PriorState 4360 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 4361 return 4362 } 4363 4364 return testApplyFn(req) 4365 } 4366 p.PlanResourceChangeFn = testDiffFn 4367 4368 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 4369 assertNoErrors(t, diags) 4370 4371 state, diags = ctx.Apply(plan, m) 4372 if !diags.HasErrors() { 4373 t.Fatal("should have error") 4374 } 4375 4376 actual := strings.TrimSpace(state.String()) 4377 expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr) 4378 if actual != expected { 4379 t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected) 4380 } 4381 } 4382 4383 func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) { 4384 m := testModule(t, "apply-multi-depose-create-before-destroy") 4385 p := testProvider("aws") 4386 ps := map[addrs.Provider]providers.Factory{addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p)} 4387 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 4388 ResourceTypes: map[string]*configschema.Block{ 4389 "aws_instance": { 4390 Attributes: map[string]*configschema.Attribute{ 4391 "require_new": {Type: cty.String, Optional: true}, 4392 "id": {Type: cty.String, Computed: true}, 4393 }, 4394 }, 4395 }, 4396 }) 4397 4398 state := states.NewState() 4399 root := state.EnsureModule(addrs.RootModuleInstance) 4400 root.SetResourceInstanceCurrent( 4401 mustResourceInstanceAddr("aws_instance.web").Resource, 4402 &states.ResourceInstanceObjectSrc{ 4403 Status: states.ObjectReady, 4404 AttrsJSON: []byte(`{"id":"foo"}`), 4405 }, 4406 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4407 ) 4408 4409 p.PlanResourceChangeFn = testDiffFn 4410 4411 ctx := testContext2(t, &ContextOpts{ 4412 Providers: ps, 4413 }) 4414 createdInstanceId := "bar" 4415 // Create works 4416 createFunc := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4417 s := req.PlannedState.AsValueMap() 4418 s["id"] = cty.StringVal(createdInstanceId) 4419 resp.NewState = cty.ObjectVal(s) 4420 return 4421 } 4422 4423 // Destroy starts broken 4424 destroyFunc := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4425 resp.NewState = req.PriorState 4426 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("destroy failed")) 4427 return 4428 } 4429 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4430 if req.PlannedState.IsNull() { 4431 return destroyFunc(req) 4432 } else { 4433 return createFunc(req) 4434 } 4435 } 4436 4437 plan, diags := ctx.Plan(m, state, &PlanOpts{ 4438 Mode: plans.NormalMode, 4439 SetVariables: InputValues{ 4440 "require_new": &InputValue{ 4441 Value: cty.StringVal("yes"), 4442 }, 4443 }, 4444 }) 4445 assertNoErrors(t, diags) 4446 4447 // Destroy is broken, so even though CBD successfully replaces the instance, 4448 // we'll have to save the Deposed instance to destroy later 4449 state, diags = ctx.Apply(plan, m) 4450 if !diags.HasErrors() { 4451 t.Fatal("should have error") 4452 } 4453 4454 checkStateString(t, state, ` 4455 aws_instance.web: (1 deposed) 4456 ID = bar 4457 provider = provider["registry.terraform.io/hashicorp/aws"] 4458 require_new = yes 4459 Deposed ID 1 = foo 4460 `) 4461 4462 createdInstanceId = "baz" 4463 ctx = testContext2(t, &ContextOpts{ 4464 Providers: ps, 4465 }) 4466 4467 plan, diags = ctx.Plan(m, state, &PlanOpts{ 4468 Mode: plans.NormalMode, 4469 SetVariables: InputValues{ 4470 "require_new": &InputValue{ 4471 Value: cty.StringVal("baz"), 4472 }, 4473 }, 4474 }) 4475 assertNoErrors(t, diags) 4476 4477 // We're replacing the primary instance once again. Destroy is _still_ 4478 // broken, so the Deposed list gets longer 4479 state, diags = ctx.Apply(plan, m) 4480 if !diags.HasErrors() { 4481 t.Fatal("should have error") 4482 } 4483 4484 // For this one we can't rely on checkStateString because its result is 4485 // not deterministic when multiple deposed objects are present. Instead, 4486 // we will probe the state object directly. 4487 { 4488 is := state.RootModule().Resources["aws_instance.web"].Instances[addrs.NoKey] 4489 if is.Current == nil { 4490 t.Fatalf("no current object for aws_instance web; should have one") 4491 } 4492 if !bytes.Contains(is.Current.AttrsJSON, []byte("baz")) { 4493 t.Fatalf("incorrect current object attrs %s; want id=baz", is.Current.AttrsJSON) 4494 } 4495 if got, want := len(is.Deposed), 2; got != want { 4496 t.Fatalf("wrong number of deposed instances %d; want %d", got, want) 4497 } 4498 var foos, bars int 4499 for _, obj := range is.Deposed { 4500 if bytes.Contains(obj.AttrsJSON, []byte("foo")) { 4501 foos++ 4502 } 4503 if bytes.Contains(obj.AttrsJSON, []byte("bar")) { 4504 bars++ 4505 } 4506 } 4507 if got, want := foos, 1; got != want { 4508 t.Fatalf("wrong number of deposed instances with id=foo %d; want %d", got, want) 4509 } 4510 if got, want := bars, 1; got != want { 4511 t.Fatalf("wrong number of deposed instances with id=bar %d; want %d", got, want) 4512 } 4513 } 4514 4515 // Destroy partially fixed! 4516 destroyFunc = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4517 s := req.PriorState.AsValueMap() 4518 id := s["id"].AsString() 4519 if id == "foo" || id == "baz" { 4520 resp.NewState = cty.NullVal(req.PriorState.Type()) 4521 } else { 4522 resp.NewState = req.PriorState 4523 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("destroy partially failed")) 4524 } 4525 return 4526 } 4527 4528 createdInstanceId = "qux" 4529 ctx = testContext2(t, &ContextOpts{ 4530 Providers: ps, 4531 }) 4532 plan, diags = ctx.Plan(m, state, &PlanOpts{ 4533 Mode: plans.NormalMode, 4534 SetVariables: InputValues{ 4535 "require_new": &InputValue{ 4536 Value: cty.StringVal("qux"), 4537 }, 4538 }, 4539 }) 4540 assertNoErrors(t, diags) 4541 4542 state, diags = ctx.Apply(plan, m) 4543 // Expect error because 1/2 of Deposed destroys failed 4544 if !diags.HasErrors() { 4545 t.Fatal("should have error") 4546 } 4547 4548 // foo and baz are now gone, bar sticks around 4549 checkStateString(t, state, ` 4550 aws_instance.web: (1 deposed) 4551 ID = qux 4552 provider = provider["registry.terraform.io/hashicorp/aws"] 4553 require_new = qux 4554 Deposed ID 1 = bar 4555 `) 4556 4557 // Destroy working fully! 4558 destroyFunc = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4559 resp.NewState = cty.NullVal(req.PriorState.Type()) 4560 return 4561 } 4562 4563 createdInstanceId = "quux" 4564 ctx = testContext2(t, &ContextOpts{ 4565 Providers: ps, 4566 }) 4567 plan, diags = ctx.Plan(m, state, &PlanOpts{ 4568 Mode: plans.NormalMode, 4569 SetVariables: InputValues{ 4570 "require_new": &InputValue{ 4571 Value: cty.StringVal("quux"), 4572 }, 4573 }, 4574 }) 4575 assertNoErrors(t, diags) 4576 state, diags = ctx.Apply(plan, m) 4577 if diags.HasErrors() { 4578 t.Fatal("should not have error:", diags.Err()) 4579 } 4580 4581 // And finally the state is clean 4582 checkStateString(t, state, ` 4583 aws_instance.web: 4584 ID = quux 4585 provider = provider["registry.terraform.io/hashicorp/aws"] 4586 require_new = quux 4587 `) 4588 } 4589 4590 // Verify that a normal provisioner with on_failure "continue" set won't 4591 // taint the resource and continues executing. 4592 func TestContext2Apply_provisionerFailContinue(t *testing.T) { 4593 m := testModule(t, "apply-provisioner-fail-continue") 4594 p := testProvider("aws") 4595 pr := testProvisioner() 4596 p.PlanResourceChangeFn = testDiffFn 4597 p.ApplyResourceChangeFn = testApplyFn 4598 4599 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4600 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4601 return 4602 } 4603 4604 ctx := testContext2(t, &ContextOpts{ 4605 Providers: map[addrs.Provider]providers.Factory{ 4606 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4607 }, 4608 Provisioners: map[string]provisioners.Factory{ 4609 "shell": testProvisionerFuncFixed(pr), 4610 }, 4611 }) 4612 4613 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4614 assertNoErrors(t, diags) 4615 4616 state, diags := ctx.Apply(plan, m) 4617 if diags.HasErrors() { 4618 t.Fatalf("diags: %s", diags.Err()) 4619 } 4620 4621 checkStateString(t, state, ` 4622 aws_instance.foo: 4623 ID = foo 4624 provider = provider["registry.terraform.io/hashicorp/aws"] 4625 foo = bar 4626 type = aws_instance 4627 `) 4628 4629 // Verify apply was invoked 4630 if !pr.ProvisionResourceCalled { 4631 t.Fatalf("provisioner not invoked") 4632 } 4633 } 4634 4635 // Verify that a normal provisioner with on_failure "continue" records 4636 // the error with the hook. 4637 func TestContext2Apply_provisionerFailContinueHook(t *testing.T) { 4638 h := new(MockHook) 4639 m := testModule(t, "apply-provisioner-fail-continue") 4640 p := testProvider("aws") 4641 pr := testProvisioner() 4642 p.PlanResourceChangeFn = testDiffFn 4643 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4644 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4645 return 4646 } 4647 4648 ctx := testContext2(t, &ContextOpts{ 4649 Hooks: []Hook{h}, 4650 Providers: map[addrs.Provider]providers.Factory{ 4651 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4652 }, 4653 Provisioners: map[string]provisioners.Factory{ 4654 "shell": testProvisionerFuncFixed(pr), 4655 }, 4656 }) 4657 4658 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 4659 assertNoErrors(t, diags) 4660 4661 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 4662 t.Fatalf("apply errors: %s", diags.Err()) 4663 } 4664 4665 if !h.PostProvisionInstanceStepCalled { 4666 t.Fatal("PostProvisionInstanceStep not called") 4667 } 4668 if h.PostProvisionInstanceStepErrorArg == nil { 4669 t.Fatal("should have error") 4670 } 4671 } 4672 4673 func TestContext2Apply_provisionerDestroy(t *testing.T) { 4674 m := testModule(t, "apply-provisioner-destroy") 4675 p := testProvider("aws") 4676 pr := testProvisioner() 4677 p.PlanResourceChangeFn = testDiffFn 4678 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4679 val := req.Config.GetAttr("command").AsString() 4680 if val != "destroy a bar" { 4681 t.Fatalf("bad value for foo: %q", val) 4682 } 4683 4684 return 4685 } 4686 4687 state := states.NewState() 4688 root := state.RootModule() 4689 root.SetResourceInstanceCurrent( 4690 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4691 &states.ResourceInstanceObjectSrc{ 4692 Status: states.ObjectReady, 4693 AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), 4694 }, 4695 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4696 ) 4697 4698 ctx := testContext2(t, &ContextOpts{ 4699 Providers: map[addrs.Provider]providers.Factory{ 4700 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4701 }, 4702 Provisioners: map[string]provisioners.Factory{ 4703 "shell": testProvisionerFuncFixed(pr), 4704 }, 4705 }) 4706 4707 plan, diags := ctx.Plan(m, state, &PlanOpts{ 4708 Mode: plans.DestroyMode, 4709 }) 4710 assertNoErrors(t, diags) 4711 4712 state, diags = ctx.Apply(plan, m) 4713 if diags.HasErrors() { 4714 t.Fatalf("diags: %s", diags.Err()) 4715 } 4716 4717 checkStateString(t, state, `<no state>`) 4718 4719 // Verify apply was invoked 4720 if !pr.ProvisionResourceCalled { 4721 t.Fatalf("provisioner not invoked") 4722 } 4723 } 4724 4725 // Verify that on destroy provisioner failure, nothing happens to the instance 4726 func TestContext2Apply_provisionerDestroyFail(t *testing.T) { 4727 m := testModule(t, "apply-provisioner-destroy") 4728 p := testProvider("aws") 4729 pr := testProvisioner() 4730 p.PlanResourceChangeFn = testDiffFn 4731 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4732 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4733 return 4734 } 4735 4736 state := states.NewState() 4737 root := state.RootModule() 4738 root.SetResourceInstanceCurrent( 4739 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4740 &states.ResourceInstanceObjectSrc{ 4741 Status: states.ObjectReady, 4742 AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), 4743 }, 4744 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4745 ) 4746 4747 ctx := testContext2(t, &ContextOpts{ 4748 Providers: map[addrs.Provider]providers.Factory{ 4749 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4750 }, 4751 Provisioners: map[string]provisioners.Factory{ 4752 "shell": testProvisionerFuncFixed(pr), 4753 }, 4754 }) 4755 4756 plan, diags := ctx.Plan(m, state, &PlanOpts{ 4757 Mode: plans.DestroyMode, 4758 }) 4759 assertNoErrors(t, diags) 4760 4761 state, diags = ctx.Apply(plan, m) 4762 if diags == nil { 4763 t.Fatal("should error") 4764 } 4765 4766 checkStateString(t, state, ` 4767 aws_instance.foo["a"]: 4768 ID = bar 4769 provider = provider["registry.terraform.io/hashicorp/aws"] 4770 foo = bar 4771 `) 4772 4773 // Verify apply was invoked 4774 if !pr.ProvisionResourceCalled { 4775 t.Fatalf("provisioner not invoked") 4776 } 4777 } 4778 4779 // Verify that on destroy provisioner failure with "continue" that 4780 // we continue to the next provisioner. 4781 func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) { 4782 m := testModule(t, "apply-provisioner-destroy-continue") 4783 p := testProvider("aws") 4784 pr := testProvisioner() 4785 p.PlanResourceChangeFn = testDiffFn 4786 4787 var l sync.Mutex 4788 var calls []string 4789 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4790 val := req.Config.GetAttr("command") 4791 if val.IsNull() { 4792 t.Fatalf("bad value for foo: %#v", val) 4793 } 4794 4795 l.Lock() 4796 defer l.Unlock() 4797 calls = append(calls, val.AsString()) 4798 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4799 return 4800 } 4801 4802 state := states.NewState() 4803 root := state.RootModule() 4804 root.SetResourceInstanceCurrent( 4805 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4806 &states.ResourceInstanceObjectSrc{ 4807 Status: states.ObjectReady, 4808 AttrsJSON: []byte(`{"id":"bar"}`), 4809 }, 4810 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4811 ) 4812 4813 ctx := testContext2(t, &ContextOpts{ 4814 Providers: map[addrs.Provider]providers.Factory{ 4815 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4816 }, 4817 Provisioners: map[string]provisioners.Factory{ 4818 "shell": testProvisionerFuncFixed(pr), 4819 }, 4820 }) 4821 4822 plan, diags := ctx.Plan(m, state, &PlanOpts{ 4823 Mode: plans.DestroyMode, 4824 }) 4825 assertNoErrors(t, diags) 4826 4827 state, diags = ctx.Apply(plan, m) 4828 if diags.HasErrors() { 4829 t.Fatalf("diags: %s", diags.Err()) 4830 } 4831 4832 checkStateString(t, state, `<no state>`) 4833 4834 // Verify apply was invoked 4835 if !pr.ProvisionResourceCalled { 4836 t.Fatalf("provisioner not invoked") 4837 } 4838 4839 expected := []string{"one", "two"} 4840 if !reflect.DeepEqual(calls, expected) { 4841 t.Fatalf("wrong commands\ngot: %#v\nwant: %#v", calls, expected) 4842 } 4843 } 4844 4845 // Verify that on destroy provisioner failure with "continue" that 4846 // we continue to the next provisioner. But if the next provisioner defines 4847 // to fail, then we fail after running it. 4848 func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) { 4849 m := testModule(t, "apply-provisioner-destroy-fail") 4850 p := testProvider("aws") 4851 pr := testProvisioner() 4852 p.PlanResourceChangeFn = testDiffFn 4853 4854 var l sync.Mutex 4855 var calls []string 4856 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4857 val := req.Config.GetAttr("command") 4858 if val.IsNull() { 4859 t.Fatalf("bad value for foo: %#v", val) 4860 } 4861 4862 l.Lock() 4863 defer l.Unlock() 4864 calls = append(calls, val.AsString()) 4865 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4866 return 4867 } 4868 4869 state := states.NewState() 4870 root := state.EnsureModule(addrs.RootModuleInstance) 4871 root.SetResourceInstanceCurrent( 4872 mustResourceInstanceAddr("aws_instance.foo").Resource, 4873 &states.ResourceInstanceObjectSrc{ 4874 Status: states.ObjectReady, 4875 AttrsJSON: []byte(`{"id":"bar"}`), 4876 }, 4877 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4878 ) 4879 4880 ctx := testContext2(t, &ContextOpts{ 4881 Providers: map[addrs.Provider]providers.Factory{ 4882 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4883 }, 4884 Provisioners: map[string]provisioners.Factory{ 4885 "shell": testProvisionerFuncFixed(pr), 4886 }, 4887 }) 4888 4889 plan, diags := ctx.Plan(m, state, &PlanOpts{ 4890 Mode: plans.DestroyMode, 4891 }) 4892 assertNoErrors(t, diags) 4893 4894 state, diags = ctx.Apply(plan, m) 4895 if diags == nil { 4896 t.Fatal("apply succeeded; wanted error from second provisioner") 4897 } 4898 4899 checkStateString(t, state, ` 4900 aws_instance.foo: 4901 ID = bar 4902 provider = provider["registry.terraform.io/hashicorp/aws"] 4903 `) 4904 4905 // Verify apply was invoked 4906 if !pr.ProvisionResourceCalled { 4907 t.Fatalf("provisioner not invoked") 4908 } 4909 4910 expected := []string{"one", "two"} 4911 if !reflect.DeepEqual(calls, expected) { 4912 t.Fatalf("bad: %#v", calls) 4913 } 4914 } 4915 4916 // Verify destroy provisioners are not run for tainted instances. 4917 func TestContext2Apply_provisionerDestroyTainted(t *testing.T) { 4918 m := testModule(t, "apply-provisioner-destroy") 4919 p := testProvider("aws") 4920 pr := testProvisioner() 4921 p.PlanResourceChangeFn = testDiffFn 4922 p.ApplyResourceChangeFn = testApplyFn 4923 4924 destroyCalled := false 4925 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4926 expected := "create a b" 4927 val := req.Config.GetAttr("command") 4928 if val.AsString() != expected { 4929 t.Fatalf("bad value for command: %#v", val) 4930 } 4931 4932 return 4933 } 4934 4935 state := states.NewState() 4936 root := state.RootModule() 4937 root.SetResourceInstanceCurrent( 4938 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4939 &states.ResourceInstanceObjectSrc{ 4940 Status: states.ObjectTainted, 4941 AttrsJSON: []byte(`{"id":"bar"}`), 4942 }, 4943 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4944 ) 4945 4946 ctx := testContext2(t, &ContextOpts{ 4947 Providers: map[addrs.Provider]providers.Factory{ 4948 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4949 }, 4950 Provisioners: map[string]provisioners.Factory{ 4951 "shell": testProvisionerFuncFixed(pr), 4952 }, 4953 }) 4954 4955 plan, diags := ctx.Plan(m, state, &PlanOpts{ 4956 Mode: plans.NormalMode, 4957 SetVariables: InputValues{ 4958 "input": &InputValue{ 4959 Value: cty.MapVal(map[string]cty.Value{ 4960 "a": cty.StringVal("b"), 4961 }), 4962 SourceType: ValueFromInput, 4963 }, 4964 }, 4965 }) 4966 assertNoErrors(t, diags) 4967 4968 state, diags = ctx.Apply(plan, m) 4969 if diags.HasErrors() { 4970 t.Fatalf("diags: %s", diags.Err()) 4971 } 4972 4973 checkStateString(t, state, ` 4974 aws_instance.foo["a"]: 4975 ID = foo 4976 provider = provider["registry.terraform.io/hashicorp/aws"] 4977 foo = bar 4978 type = aws_instance 4979 `) 4980 4981 // Verify apply was invoked 4982 if !pr.ProvisionResourceCalled { 4983 t.Fatalf("provisioner not invoked") 4984 } 4985 4986 if destroyCalled { 4987 t.Fatal("destroy should not be called") 4988 } 4989 } 4990 4991 func TestContext2Apply_provisionerResourceRef(t *testing.T) { 4992 m := testModule(t, "apply-provisioner-resource-ref") 4993 p := testProvider("aws") 4994 p.PlanResourceChangeFn = testDiffFn 4995 p.ApplyResourceChangeFn = testApplyFn 4996 4997 pr := testProvisioner() 4998 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4999 val := req.Config.GetAttr("command") 5000 if val.AsString() != "2" { 5001 t.Fatalf("bad value for command: %#v", val) 5002 } 5003 5004 return 5005 } 5006 5007 ctx := testContext2(t, &ContextOpts{ 5008 Providers: map[addrs.Provider]providers.Factory{ 5009 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5010 }, 5011 Provisioners: map[string]provisioners.Factory{ 5012 "shell": testProvisionerFuncFixed(pr), 5013 }, 5014 }) 5015 5016 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5017 assertNoErrors(t, diags) 5018 5019 state, diags := ctx.Apply(plan, m) 5020 if diags.HasErrors() { 5021 t.Fatalf("diags: %s", diags.Err()) 5022 } 5023 5024 actual := strings.TrimSpace(state.String()) 5025 expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr) 5026 if actual != expected { 5027 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5028 } 5029 5030 // Verify apply was invoked 5031 if !pr.ProvisionResourceCalled { 5032 t.Fatalf("provisioner not invoked") 5033 } 5034 } 5035 5036 func TestContext2Apply_provisionerSelfRef(t *testing.T) { 5037 m := testModule(t, "apply-provisioner-self-ref") 5038 p := testProvider("aws") 5039 pr := testProvisioner() 5040 p.PlanResourceChangeFn = testDiffFn 5041 p.ApplyResourceChangeFn = testApplyFn 5042 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5043 val := req.Config.GetAttr("command") 5044 if val.AsString() != "bar" { 5045 t.Fatalf("bad value for command: %#v", val) 5046 } 5047 5048 return 5049 } 5050 5051 ctx := testContext2(t, &ContextOpts{ 5052 Providers: map[addrs.Provider]providers.Factory{ 5053 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5054 }, 5055 Provisioners: map[string]provisioners.Factory{ 5056 "shell": testProvisionerFuncFixed(pr), 5057 }, 5058 }) 5059 5060 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5061 assertNoErrors(t, diags) 5062 5063 state, diags := ctx.Apply(plan, m) 5064 if diags.HasErrors() { 5065 t.Fatalf("diags: %s", diags.Err()) 5066 } 5067 5068 actual := strings.TrimSpace(state.String()) 5069 expected := strings.TrimSpace(testTerraformApplyProvisionerSelfRefStr) 5070 if actual != expected { 5071 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5072 } 5073 5074 // Verify apply was invoked 5075 if !pr.ProvisionResourceCalled { 5076 t.Fatalf("provisioner not invoked") 5077 } 5078 } 5079 5080 func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { 5081 var lock sync.Mutex 5082 commands := make([]string, 0, 5) 5083 5084 m := testModule(t, "apply-provisioner-multi-self-ref") 5085 p := testProvider("aws") 5086 pr := testProvisioner() 5087 p.PlanResourceChangeFn = testDiffFn 5088 p.ApplyResourceChangeFn = testApplyFn 5089 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5090 lock.Lock() 5091 defer lock.Unlock() 5092 5093 val := req.Config.GetAttr("command") 5094 if val.IsNull() { 5095 t.Fatalf("bad value for command: %#v", val) 5096 } 5097 5098 commands = append(commands, val.AsString()) 5099 return 5100 } 5101 5102 ctx := testContext2(t, &ContextOpts{ 5103 Providers: map[addrs.Provider]providers.Factory{ 5104 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5105 }, 5106 Provisioners: map[string]provisioners.Factory{ 5107 "shell": testProvisionerFuncFixed(pr), 5108 }, 5109 }) 5110 5111 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5112 assertNoErrors(t, diags) 5113 5114 state, diags := ctx.Apply(plan, m) 5115 if diags.HasErrors() { 5116 t.Fatalf("diags: %s", diags.Err()) 5117 } 5118 5119 actual := strings.TrimSpace(state.String()) 5120 expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefStr) 5121 if actual != expected { 5122 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5123 } 5124 5125 // Verify apply was invoked 5126 if !pr.ProvisionResourceCalled { 5127 t.Fatalf("provisioner not invoked") 5128 } 5129 5130 // Verify our result 5131 sort.Strings(commands) 5132 expectedCommands := []string{"number 0", "number 1", "number 2"} 5133 if !reflect.DeepEqual(commands, expectedCommands) { 5134 t.Fatalf("bad: %#v", commands) 5135 } 5136 } 5137 5138 func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) { 5139 var lock sync.Mutex 5140 order := make([]string, 0, 5) 5141 5142 m := testModule(t, "apply-provisioner-multi-self-ref-single") 5143 p := testProvider("aws") 5144 pr := testProvisioner() 5145 p.PlanResourceChangeFn = testDiffFn 5146 p.ApplyResourceChangeFn = testApplyFn 5147 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5148 lock.Lock() 5149 defer lock.Unlock() 5150 5151 val := req.Config.GetAttr("order") 5152 if val.IsNull() { 5153 t.Fatalf("no val for order") 5154 } 5155 5156 order = append(order, val.AsString()) 5157 return 5158 } 5159 5160 ctx := testContext2(t, &ContextOpts{ 5161 Providers: map[addrs.Provider]providers.Factory{ 5162 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5163 }, 5164 Provisioners: map[string]provisioners.Factory{ 5165 "shell": testProvisionerFuncFixed(pr), 5166 }, 5167 }) 5168 5169 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5170 assertNoErrors(t, diags) 5171 5172 state, diags := ctx.Apply(plan, m) 5173 if diags.HasErrors() { 5174 t.Fatalf("diags: %s", diags.Err()) 5175 } 5176 5177 actual := strings.TrimSpace(state.String()) 5178 expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefSingleStr) 5179 if actual != expected { 5180 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5181 } 5182 5183 // Verify apply was invoked 5184 if !pr.ProvisionResourceCalled { 5185 t.Fatalf("provisioner not invoked") 5186 } 5187 5188 // Verify our result 5189 sort.Strings(order) 5190 expectedOrder := []string{"0", "1", "2"} 5191 if !reflect.DeepEqual(order, expectedOrder) { 5192 t.Fatalf("bad: %#v", order) 5193 } 5194 } 5195 5196 func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) { 5197 m := testModule(t, "apply-provisioner-explicit-self-ref") 5198 p := testProvider("aws") 5199 pr := testProvisioner() 5200 p.PlanResourceChangeFn = testDiffFn 5201 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5202 val := req.Config.GetAttr("command") 5203 if val.IsNull() || val.AsString() != "bar" { 5204 t.Fatalf("bad value for command: %#v", val) 5205 } 5206 5207 return 5208 } 5209 5210 var state *states.State 5211 { 5212 ctx := testContext2(t, &ContextOpts{ 5213 Providers: map[addrs.Provider]providers.Factory{ 5214 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5215 }, 5216 Provisioners: map[string]provisioners.Factory{ 5217 "shell": testProvisionerFuncFixed(pr), 5218 }, 5219 }) 5220 5221 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5222 if diags.HasErrors() { 5223 t.Fatalf("diags: %s", diags.Err()) 5224 } 5225 5226 state, diags = ctx.Apply(plan, m) 5227 if diags.HasErrors() { 5228 t.Fatalf("diags: %s", diags.Err()) 5229 } 5230 5231 // Verify apply was invoked 5232 if !pr.ProvisionResourceCalled { 5233 t.Fatalf("provisioner not invoked") 5234 } 5235 } 5236 5237 { 5238 ctx := testContext2(t, &ContextOpts{ 5239 Providers: map[addrs.Provider]providers.Factory{ 5240 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5241 }, 5242 Provisioners: map[string]provisioners.Factory{ 5243 "shell": testProvisionerFuncFixed(pr), 5244 }, 5245 }) 5246 5247 plan, diags := ctx.Plan(m, state, &PlanOpts{ 5248 Mode: plans.DestroyMode, 5249 }) 5250 if diags.HasErrors() { 5251 t.Fatalf("diags: %s", diags.Err()) 5252 } 5253 5254 state, diags = ctx.Apply(plan, m) 5255 if diags.HasErrors() { 5256 t.Fatalf("diags: %s", diags.Err()) 5257 } 5258 5259 checkStateString(t, state, `<no state>`) 5260 } 5261 } 5262 5263 func TestContext2Apply_provisionerForEachSelfRef(t *testing.T) { 5264 m := testModule(t, "apply-provisioner-for-each-self") 5265 p := testProvider("aws") 5266 pr := testProvisioner() 5267 p.PlanResourceChangeFn = testDiffFn 5268 5269 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5270 val := req.Config.GetAttr("command") 5271 if val.IsNull() { 5272 t.Fatalf("bad value for command: %#v", val) 5273 } 5274 5275 return resp 5276 } 5277 5278 ctx := testContext2(t, &ContextOpts{ 5279 Providers: map[addrs.Provider]providers.Factory{ 5280 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5281 }, 5282 Provisioners: map[string]provisioners.Factory{ 5283 "shell": testProvisionerFuncFixed(pr), 5284 }, 5285 }) 5286 5287 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5288 assertNoErrors(t, diags) 5289 5290 _, diags = ctx.Apply(plan, m) 5291 if diags.HasErrors() { 5292 t.Fatalf("diags: %s", diags.Err()) 5293 } 5294 } 5295 5296 // Provisioner should NOT run on a diff, only create 5297 func TestContext2Apply_Provisioner_Diff(t *testing.T) { 5298 m := testModule(t, "apply-provisioner-diff") 5299 p := testProvider("aws") 5300 pr := testProvisioner() 5301 p.PlanResourceChangeFn = testDiffFn 5302 p.ApplyResourceChangeFn = testApplyFn 5303 ctx := testContext2(t, &ContextOpts{ 5304 Providers: map[addrs.Provider]providers.Factory{ 5305 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5306 }, 5307 Provisioners: map[string]provisioners.Factory{ 5308 "shell": testProvisionerFuncFixed(pr), 5309 }, 5310 }) 5311 5312 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5313 assertNoErrors(t, diags) 5314 5315 state, diags := ctx.Apply(plan, m) 5316 if diags.HasErrors() { 5317 logDiagnostics(t, diags) 5318 t.Fatal("apply failed") 5319 } 5320 5321 actual := strings.TrimSpace(state.String()) 5322 expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr) 5323 if actual != expected { 5324 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5325 } 5326 5327 // Verify apply was invoked 5328 if !pr.ProvisionResourceCalled { 5329 t.Fatalf("provisioner was not called on first apply") 5330 } 5331 pr.ProvisionResourceCalled = false 5332 5333 // Change the state to force a diff 5334 mod := state.RootModule() 5335 obj := mod.Resources["aws_instance.bar"].Instances[addrs.NoKey].Current 5336 var attrs map[string]interface{} 5337 err := json.Unmarshal(obj.AttrsJSON, &attrs) 5338 if err != nil { 5339 t.Fatal(err) 5340 } 5341 attrs["foo"] = "baz" 5342 obj.AttrsJSON, err = json.Marshal(attrs) 5343 if err != nil { 5344 t.Fatal(err) 5345 } 5346 5347 // Re-create context with state 5348 ctx = testContext2(t, &ContextOpts{ 5349 Providers: map[addrs.Provider]providers.Factory{ 5350 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5351 }, 5352 Provisioners: map[string]provisioners.Factory{ 5353 "shell": testProvisionerFuncFixed(pr), 5354 }, 5355 }) 5356 5357 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 5358 assertNoErrors(t, diags) 5359 5360 state2, diags := ctx.Apply(plan, m) 5361 if diags.HasErrors() { 5362 logDiagnostics(t, diags) 5363 t.Fatal("apply failed") 5364 } 5365 5366 actual = strings.TrimSpace(state2.String()) 5367 if actual != expected { 5368 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5369 } 5370 5371 // Verify apply was NOT invoked 5372 if pr.ProvisionResourceCalled { 5373 t.Fatalf("provisioner was called on second apply; should not have been") 5374 } 5375 } 5376 5377 func TestContext2Apply_outputDiffVars(t *testing.T) { 5378 m := testModule(t, "apply-good") 5379 p := testProvider("aws") 5380 5381 state := states.NewState() 5382 root := state.EnsureModule(addrs.RootModuleInstance) 5383 root.SetResourceInstanceCurrent( 5384 mustResourceInstanceAddr("aws_instance.baz").Resource, 5385 &states.ResourceInstanceObjectSrc{ 5386 Status: states.ObjectReady, 5387 AttrsJSON: []byte(`{"id":"bar"}`), 5388 }, 5389 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5390 ) 5391 5392 ctx := testContext2(t, &ContextOpts{ 5393 Providers: map[addrs.Provider]providers.Factory{ 5394 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5395 }, 5396 }) 5397 5398 p.PlanResourceChangeFn = testDiffFn 5399 //func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 5400 // d := &InstanceDiff{ 5401 // Attributes: map[string]*ResourceAttrDiff{}, 5402 // } 5403 // if new, ok := rc.Get("value"); ok { 5404 // d.Attributes["value"] = &ResourceAttrDiff{ 5405 // New: new.(string), 5406 // } 5407 // } 5408 // if new, ok := rc.Get("foo"); ok { 5409 // d.Attributes["foo"] = &ResourceAttrDiff{ 5410 // New: new.(string), 5411 // } 5412 // } else if rc.IsComputed("foo") { 5413 // d.Attributes["foo"] = &ResourceAttrDiff{ 5414 // NewComputed: true, 5415 // Type: DiffAttrOutput, // This doesn't actually really do anything anymore, but this test originally set it. 5416 // } 5417 // } 5418 // if new, ok := rc.Get("num"); ok { 5419 // d.Attributes["num"] = &ResourceAttrDiff{ 5420 // New: fmt.Sprintf("%#v", new), 5421 // } 5422 // } 5423 // return d, nil 5424 //} 5425 5426 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 5427 assertNoErrors(t, diags) 5428 5429 _, diags = ctx.Apply(plan, m) 5430 assertNoErrors(t, diags) 5431 } 5432 5433 func TestContext2Apply_destroyX(t *testing.T) { 5434 m := testModule(t, "apply-destroy") 5435 h := new(HookRecordApplyOrder) 5436 p := testProvider("aws") 5437 p.PlanResourceChangeFn = testDiffFn 5438 ctx := testContext2(t, &ContextOpts{ 5439 Hooks: []Hook{h}, 5440 Providers: map[addrs.Provider]providers.Factory{ 5441 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5442 }, 5443 }) 5444 5445 // First plan and apply a create operation 5446 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5447 assertNoErrors(t, diags) 5448 5449 state, diags := ctx.Apply(plan, m) 5450 if diags.HasErrors() { 5451 t.Fatalf("diags: %s", diags.Err()) 5452 } 5453 5454 // Next, plan and apply a destroy operation 5455 h.Active = true 5456 ctx = testContext2(t, &ContextOpts{ 5457 Hooks: []Hook{h}, 5458 Providers: map[addrs.Provider]providers.Factory{ 5459 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5460 }, 5461 }) 5462 5463 plan, diags = ctx.Plan(m, state, &PlanOpts{ 5464 Mode: plans.DestroyMode, 5465 }) 5466 assertNoErrors(t, diags) 5467 5468 state, diags = ctx.Apply(plan, m) 5469 if diags.HasErrors() { 5470 t.Fatalf("diags: %s", diags.Err()) 5471 } 5472 5473 // Test that things were destroyed 5474 actual := strings.TrimSpace(state.String()) 5475 expected := strings.TrimSpace(testTerraformApplyDestroyStr) 5476 if actual != expected { 5477 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5478 } 5479 5480 // Test that things were destroyed _in the right order_ 5481 expected2 := []string{"aws_instance.bar", "aws_instance.foo"} 5482 actual2 := h.IDs 5483 if !reflect.DeepEqual(actual2, expected2) { 5484 t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) 5485 } 5486 } 5487 5488 func TestContext2Apply_destroyOrder(t *testing.T) { 5489 m := testModule(t, "apply-destroy") 5490 h := new(HookRecordApplyOrder) 5491 p := testProvider("aws") 5492 p.PlanResourceChangeFn = testDiffFn 5493 ctx := testContext2(t, &ContextOpts{ 5494 Hooks: []Hook{h}, 5495 Providers: map[addrs.Provider]providers.Factory{ 5496 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5497 }, 5498 }) 5499 5500 // First plan and apply a create operation 5501 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5502 assertNoErrors(t, diags) 5503 5504 state, diags := ctx.Apply(plan, m) 5505 if diags.HasErrors() { 5506 t.Fatalf("diags: %s", diags.Err()) 5507 } 5508 5509 t.Logf("State 1: %s", state) 5510 5511 // Next, plan and apply a destroy 5512 h.Active = true 5513 ctx = testContext2(t, &ContextOpts{ 5514 Hooks: []Hook{h}, 5515 Providers: map[addrs.Provider]providers.Factory{ 5516 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5517 }, 5518 }) 5519 5520 plan, diags = ctx.Plan(m, state, &PlanOpts{ 5521 Mode: plans.DestroyMode, 5522 }) 5523 assertNoErrors(t, diags) 5524 5525 state, diags = ctx.Apply(plan, m) 5526 if diags.HasErrors() { 5527 t.Fatalf("diags: %s", diags.Err()) 5528 } 5529 5530 // Test that things were destroyed 5531 actual := strings.TrimSpace(state.String()) 5532 expected := strings.TrimSpace(testTerraformApplyDestroyStr) 5533 if actual != expected { 5534 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5535 } 5536 5537 // Test that things were destroyed _in the right order_ 5538 expected2 := []string{"aws_instance.bar", "aws_instance.foo"} 5539 actual2 := h.IDs 5540 if !reflect.DeepEqual(actual2, expected2) { 5541 t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) 5542 } 5543 } 5544 5545 // https://github.com/muratcelep/terraform/issues/2767 5546 func TestContext2Apply_destroyModulePrefix(t *testing.T) { 5547 m := testModule(t, "apply-destroy-module-resource-prefix") 5548 h := new(MockHook) 5549 p := testProvider("aws") 5550 p.PlanResourceChangeFn = testDiffFn 5551 ctx := testContext2(t, &ContextOpts{ 5552 Hooks: []Hook{h}, 5553 Providers: map[addrs.Provider]providers.Factory{ 5554 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5555 }, 5556 }) 5557 5558 // First plan and apply a create operation 5559 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5560 assertNoErrors(t, diags) 5561 5562 state, diags := ctx.Apply(plan, m) 5563 if diags.HasErrors() { 5564 t.Fatalf("diags: %s", diags.Err()) 5565 } 5566 5567 // Verify that we got the apply info correct 5568 if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" { 5569 t.Fatalf("bad: %s", v) 5570 } 5571 5572 // Next, plan and apply a destroy operation and reset the hook 5573 h = new(MockHook) 5574 ctx = testContext2(t, &ContextOpts{ 5575 Hooks: []Hook{h}, 5576 Providers: map[addrs.Provider]providers.Factory{ 5577 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5578 }, 5579 }) 5580 5581 plan, diags = ctx.Plan(m, state, &PlanOpts{ 5582 Mode: plans.DestroyMode, 5583 }) 5584 assertNoErrors(t, diags) 5585 5586 _, diags = ctx.Apply(plan, m) 5587 if diags.HasErrors() { 5588 t.Fatalf("diags: %s", diags.Err()) 5589 } 5590 5591 // Test that things were destroyed 5592 if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" { 5593 t.Fatalf("bad: %s", v) 5594 } 5595 } 5596 5597 func TestContext2Apply_destroyNestedModule(t *testing.T) { 5598 m := testModule(t, "apply-destroy-nested-module") 5599 p := testProvider("aws") 5600 p.PlanResourceChangeFn = testDiffFn 5601 5602 state := states.NewState() 5603 root := state.EnsureModule(addrs.RootModuleInstance) 5604 root.SetResourceInstanceCurrent( 5605 mustResourceInstanceAddr("aws_instance.bar").Resource, 5606 &states.ResourceInstanceObjectSrc{ 5607 Status: states.ObjectReady, 5608 AttrsJSON: []byte(`{"id":"bar"}`), 5609 }, 5610 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5611 ) 5612 5613 ctx := testContext2(t, &ContextOpts{ 5614 Providers: map[addrs.Provider]providers.Factory{ 5615 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5616 }, 5617 }) 5618 5619 // First plan and apply a create operation 5620 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 5621 assertNoErrors(t, diags) 5622 5623 s, diags := ctx.Apply(plan, m) 5624 if diags.HasErrors() { 5625 t.Fatalf("diags: %s", diags.Err()) 5626 } 5627 5628 // Test that things were destroyed 5629 actual := strings.TrimSpace(s.String()) 5630 if actual != "<no state>" { 5631 t.Fatalf("expected no state, got: %s", actual) 5632 } 5633 } 5634 5635 func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) { 5636 m := testModule(t, "apply-destroy-deeply-nested-module") 5637 p := testProvider("aws") 5638 p.PlanResourceChangeFn = testDiffFn 5639 5640 state := states.NewState() 5641 root := state.EnsureModule(addrs.RootModuleInstance) 5642 root.SetResourceInstanceCurrent( 5643 mustResourceInstanceAddr("aws_instance.bar").Resource, 5644 &states.ResourceInstanceObjectSrc{ 5645 Status: states.ObjectReady, 5646 AttrsJSON: []byte(`{"id":"bar"}`), 5647 }, 5648 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5649 ) 5650 5651 ctx := testContext2(t, &ContextOpts{ 5652 Providers: map[addrs.Provider]providers.Factory{ 5653 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5654 }, 5655 }) 5656 5657 // First plan and apply a create operation 5658 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 5659 assertNoErrors(t, diags) 5660 5661 s, diags := ctx.Apply(plan, m) 5662 if diags.HasErrors() { 5663 t.Fatalf("diags: %s", diags.Err()) 5664 } 5665 5666 // Test that things were destroyed 5667 if !s.Empty() { 5668 t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(s)) 5669 } 5670 } 5671 5672 // https://github.com/muratcelep/terraform/issues/5440 5673 func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) { 5674 m, snap := testModuleWithSnapshot(t, "apply-destroy-module-with-attrs") 5675 p := testProvider("aws") 5676 p.PlanResourceChangeFn = testDiffFn 5677 5678 var state *states.State 5679 { 5680 ctx := testContext2(t, &ContextOpts{ 5681 Providers: map[addrs.Provider]providers.Factory{ 5682 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5683 }, 5684 }) 5685 5686 // First plan and apply a create operation 5687 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5688 if diags.HasErrors() { 5689 t.Fatalf("plan diags: %s", diags.Err()) 5690 } else { 5691 t.Logf("Step 1 plan: %s", legacyDiffComparisonString(plan.Changes)) 5692 } 5693 5694 state, diags = ctx.Apply(plan, m) 5695 if diags.HasErrors() { 5696 t.Fatalf("apply errs: %s", diags.Err()) 5697 } 5698 5699 t.Logf("Step 1 state: %s", state) 5700 } 5701 5702 h := new(HookRecordApplyOrder) 5703 h.Active = true 5704 5705 { 5706 ctx := testContext2(t, &ContextOpts{ 5707 Hooks: []Hook{h}, 5708 Providers: map[addrs.Provider]providers.Factory{ 5709 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5710 }, 5711 }) 5712 5713 // First plan and apply a create operation 5714 plan, diags := ctx.Plan(m, state, &PlanOpts{ 5715 Mode: plans.DestroyMode, 5716 }) 5717 if diags.HasErrors() { 5718 t.Fatalf("destroy plan err: %s", diags.Err()) 5719 } 5720 5721 t.Logf("Step 2 plan: %s", legacyDiffComparisonString(plan.Changes)) 5722 5723 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 5724 if err != nil { 5725 t.Fatalf("failed to round-trip through planfile: %s", err) 5726 } 5727 5728 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 5729 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5730 } 5731 5732 ctx, diags = NewContext(ctxOpts) 5733 if diags.HasErrors() { 5734 t.Fatalf("err: %s", diags.Err()) 5735 } 5736 5737 state, diags = ctx.Apply(plan, m) 5738 if diags.HasErrors() { 5739 t.Fatalf("destroy apply err: %s", diags.Err()) 5740 } 5741 5742 t.Logf("Step 2 state: %s", state) 5743 } 5744 5745 //Test that things were destroyed 5746 if state.HasManagedResourceInstanceObjects() { 5747 t.Fatal("expected empty state, got:", state) 5748 } 5749 } 5750 5751 func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { 5752 m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count") 5753 p := testProvider("aws") 5754 p.PlanResourceChangeFn = testDiffFn 5755 5756 var state *states.State 5757 { 5758 ctx := testContext2(t, &ContextOpts{ 5759 Providers: map[addrs.Provider]providers.Factory{ 5760 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5761 }, 5762 }) 5763 5764 // First plan and apply a create operation 5765 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5766 assertNoErrors(t, diags) 5767 5768 state, diags = ctx.Apply(plan, m) 5769 if diags.HasErrors() { 5770 t.Fatalf("apply err: %s", diags.Err()) 5771 } 5772 } 5773 5774 h := new(HookRecordApplyOrder) 5775 h.Active = true 5776 5777 { 5778 ctx := testContext2(t, &ContextOpts{ 5779 Hooks: []Hook{h}, 5780 Providers: map[addrs.Provider]providers.Factory{ 5781 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5782 }, 5783 }) 5784 5785 // First plan and apply a create operation 5786 plan, diags := ctx.Plan(m, state, &PlanOpts{ 5787 Mode: plans.DestroyMode, 5788 }) 5789 if diags.HasErrors() { 5790 t.Fatalf("destroy plan err: %s", diags.Err()) 5791 } 5792 5793 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 5794 if err != nil { 5795 t.Fatalf("failed to round-trip through planfile: %s", err) 5796 } 5797 5798 ctxOpts.Providers = 5799 map[addrs.Provider]providers.Factory{ 5800 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5801 } 5802 5803 ctx, diags = NewContext(ctxOpts) 5804 if diags.HasErrors() { 5805 t.Fatalf("err: %s", diags.Err()) 5806 } 5807 5808 state, diags = ctx.Apply(plan, m) 5809 if diags.HasErrors() { 5810 t.Fatalf("destroy apply err: %s", diags.Err()) 5811 } 5812 } 5813 5814 //Test that things were destroyed 5815 actual := strings.TrimSpace(state.String()) 5816 expected := strings.TrimSpace(` 5817 <no state>`) 5818 if actual != expected { 5819 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 5820 } 5821 } 5822 5823 func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) { 5824 m := testModule(t, "apply-destroy-mod-var-and-count") 5825 p := testProvider("aws") 5826 p.PlanResourceChangeFn = testDiffFn 5827 5828 var state *states.State 5829 { 5830 ctx := testContext2(t, &ContextOpts{ 5831 Providers: map[addrs.Provider]providers.Factory{ 5832 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5833 }, 5834 }) 5835 5836 // First plan and apply a create operation 5837 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5838 assertNoErrors(t, diags) 5839 5840 state, diags = ctx.Apply(plan, m) 5841 if diags.HasErrors() { 5842 t.Fatalf("apply err: %s", diags.Err()) 5843 } 5844 } 5845 5846 { 5847 ctx := testContext2(t, &ContextOpts{ 5848 Providers: map[addrs.Provider]providers.Factory{ 5849 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5850 }, 5851 }) 5852 5853 plan, diags := ctx.Plan(m, state, &PlanOpts{ 5854 Mode: plans.DestroyMode, 5855 Targets: []addrs.Targetable{ 5856 addrs.RootModuleInstance.Child("child", addrs.NoKey), 5857 }, 5858 }) 5859 if diags.HasErrors() { 5860 t.Fatalf("plan err: %s", diags) 5861 } 5862 if len(diags) != 1 { 5863 // Should have one warning that -target is in effect. 5864 t.Fatalf("got %d diagnostics in plan; want 1", len(diags)) 5865 } 5866 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 5867 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 5868 } 5869 if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want { 5870 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 5871 } 5872 5873 // Destroy, targeting the module explicitly 5874 state, diags = ctx.Apply(plan, m) 5875 if diags.HasErrors() { 5876 t.Fatalf("destroy apply err: %s", diags) 5877 } 5878 if len(diags) != 1 { 5879 t.Fatalf("got %d diagnostics; want 1", len(diags)) 5880 } 5881 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 5882 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 5883 } 5884 if got, want := diags[0].Description().Summary, "Applied changes may be incomplete"; got != want { 5885 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 5886 } 5887 } 5888 5889 //Test that things were destroyed 5890 actual := strings.TrimSpace(state.String()) 5891 expected := strings.TrimSpace(`<no state>`) 5892 if actual != expected { 5893 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 5894 } 5895 } 5896 5897 func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { 5898 m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count-nested") 5899 p := testProvider("aws") 5900 p.PlanResourceChangeFn = testDiffFn 5901 5902 var state *states.State 5903 { 5904 ctx := testContext2(t, &ContextOpts{ 5905 Providers: map[addrs.Provider]providers.Factory{ 5906 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5907 }, 5908 }) 5909 5910 // First plan and apply a create operation 5911 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5912 assertNoErrors(t, diags) 5913 5914 state, diags = ctx.Apply(plan, m) 5915 if diags.HasErrors() { 5916 t.Fatalf("apply err: %s", diags.Err()) 5917 } 5918 } 5919 5920 h := new(HookRecordApplyOrder) 5921 h.Active = true 5922 5923 { 5924 ctx := testContext2(t, &ContextOpts{ 5925 Hooks: []Hook{h}, 5926 Providers: map[addrs.Provider]providers.Factory{ 5927 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5928 }, 5929 }) 5930 5931 // First plan and apply a create operation 5932 plan, diags := ctx.Plan(m, state, &PlanOpts{ 5933 Mode: plans.DestroyMode, 5934 }) 5935 if diags.HasErrors() { 5936 t.Fatalf("destroy plan err: %s", diags.Err()) 5937 } 5938 5939 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 5940 if err != nil { 5941 t.Fatalf("failed to round-trip through planfile: %s", err) 5942 } 5943 5944 ctxOpts.Providers = 5945 map[addrs.Provider]providers.Factory{ 5946 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5947 } 5948 5949 ctx, diags = NewContext(ctxOpts) 5950 if diags.HasErrors() { 5951 t.Fatalf("err: %s", diags.Err()) 5952 } 5953 5954 state, diags = ctx.Apply(plan, m) 5955 if diags.HasErrors() { 5956 t.Fatalf("destroy apply err: %s", diags.Err()) 5957 } 5958 } 5959 5960 //Test that things were destroyed 5961 actual := strings.TrimSpace(state.String()) 5962 expected := strings.TrimSpace(` 5963 <no state>`) 5964 if actual != expected { 5965 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 5966 } 5967 } 5968 5969 func TestContext2Apply_destroyOutputs(t *testing.T) { 5970 m := testModule(t, "apply-destroy-outputs") 5971 p := testProvider("test") 5972 p.PlanResourceChangeFn = testDiffFn 5973 5974 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 5975 // add the required id 5976 m := req.Config.AsValueMap() 5977 m["id"] = cty.StringVal("foo") 5978 5979 return providers.ReadDataSourceResponse{ 5980 State: cty.ObjectVal(m), 5981 } 5982 } 5983 5984 ctx := testContext2(t, &ContextOpts{ 5985 Providers: map[addrs.Provider]providers.Factory{ 5986 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 5987 }, 5988 }) 5989 5990 // First plan and apply a create operation 5991 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 5992 assertNoErrors(t, diags) 5993 5994 state, diags := ctx.Apply(plan, m) 5995 5996 if diags.HasErrors() { 5997 t.Fatalf("diags: %s", diags.Err()) 5998 } 5999 6000 // Next, plan and apply a destroy operation 6001 ctx = testContext2(t, &ContextOpts{ 6002 Providers: map[addrs.Provider]providers.Factory{ 6003 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6004 }, 6005 }) 6006 6007 plan, diags = ctx.Plan(m, state, &PlanOpts{ 6008 Mode: plans.DestroyMode, 6009 }) 6010 assertNoErrors(t, diags) 6011 6012 state, diags = ctx.Apply(plan, m) 6013 if diags.HasErrors() { 6014 t.Fatalf("diags: %s", diags.Err()) 6015 } 6016 6017 mod := state.RootModule() 6018 if len(mod.Resources) > 0 { 6019 t.Fatalf("expected no resources, got: %#v", mod) 6020 } 6021 6022 // destroying again should produce no errors 6023 ctx = testContext2(t, &ContextOpts{ 6024 Providers: map[addrs.Provider]providers.Factory{ 6025 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6026 }, 6027 }) 6028 plan, diags = ctx.Plan(m, state, &PlanOpts{ 6029 Mode: plans.DestroyMode, 6030 }) 6031 assertNoErrors(t, diags) 6032 6033 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 6034 t.Fatal(diags.Err()) 6035 } 6036 } 6037 6038 func TestContext2Apply_destroyOrphan(t *testing.T) { 6039 m := testModule(t, "apply-error") 6040 p := testProvider("aws") 6041 state := states.NewState() 6042 root := state.EnsureModule(addrs.RootModuleInstance) 6043 root.SetResourceInstanceCurrent( 6044 mustResourceInstanceAddr("aws_instance.baz").Resource, 6045 &states.ResourceInstanceObjectSrc{ 6046 Status: states.ObjectReady, 6047 AttrsJSON: []byte(`{"id":"bar"}`), 6048 }, 6049 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6050 ) 6051 ctx := testContext2(t, &ContextOpts{ 6052 Providers: map[addrs.Provider]providers.Factory{ 6053 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6054 }, 6055 }) 6056 6057 p.PlanResourceChangeFn = testDiffFn 6058 6059 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6060 assertNoErrors(t, diags) 6061 6062 s, diags := ctx.Apply(plan, m) 6063 if diags.HasErrors() { 6064 t.Fatalf("diags: %s", diags.Err()) 6065 } 6066 6067 mod := s.RootModule() 6068 if _, ok := mod.Resources["aws_instance.baz"]; ok { 6069 t.Fatalf("bad: %#v", mod.Resources) 6070 } 6071 } 6072 6073 func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) { 6074 m := testModule(t, "apply-destroy-provisioner") 6075 p := testProvider("aws") 6076 pr := testProvisioner() 6077 p.PlanResourceChangeFn = testDiffFn 6078 6079 state := states.NewState() 6080 root := state.EnsureModule(addrs.RootModuleInstance) 6081 root.SetResourceInstanceCurrent( 6082 mustResourceInstanceAddr("aws_instance.foo").Resource, 6083 &states.ResourceInstanceObjectSrc{ 6084 Status: states.ObjectReady, 6085 AttrsJSON: []byte(`{"id":"bar"}`), 6086 }, 6087 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6088 ) 6089 6090 ctx := testContext2(t, &ContextOpts{ 6091 Providers: map[addrs.Provider]providers.Factory{ 6092 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6093 }, 6094 Provisioners: map[string]provisioners.Factory{ 6095 "shell": testProvisionerFuncFixed(pr), 6096 }, 6097 }) 6098 6099 plan, diags := ctx.Plan(m, state, &PlanOpts{ 6100 Mode: plans.DestroyMode, 6101 }) 6102 assertNoErrors(t, diags) 6103 6104 s, diags := ctx.Apply(plan, m) 6105 if diags.HasErrors() { 6106 t.Fatalf("diags: %s", diags.Err()) 6107 } 6108 6109 if pr.ProvisionResourceCalled { 6110 t.Fatal("provisioner should not be called") 6111 } 6112 6113 actual := strings.TrimSpace(s.String()) 6114 expected := strings.TrimSpace("<no state>") 6115 if actual != expected { 6116 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6117 } 6118 } 6119 6120 func TestContext2Apply_error(t *testing.T) { 6121 errored := false 6122 6123 m := testModule(t, "apply-error") 6124 p := testProvider("aws") 6125 ctx := testContext2(t, &ContextOpts{ 6126 Providers: map[addrs.Provider]providers.Factory{ 6127 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6128 }, 6129 }) 6130 6131 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 6132 if errored { 6133 resp.NewState = req.PlannedState 6134 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 6135 return 6136 } 6137 errored = true 6138 6139 return testApplyFn(req) 6140 } 6141 p.PlanResourceChangeFn = testDiffFn 6142 6143 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6144 assertNoErrors(t, diags) 6145 6146 state, diags := ctx.Apply(plan, m) 6147 if diags == nil { 6148 t.Fatal("should have error") 6149 } 6150 6151 actual := strings.TrimSpace(state.String()) 6152 expected := strings.TrimSpace(testTerraformApplyErrorStr) 6153 if actual != expected { 6154 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 6155 } 6156 } 6157 6158 func TestContext2Apply_errorDestroy(t *testing.T) { 6159 m := testModule(t, "empty") 6160 p := testProvider("test") 6161 6162 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 6163 ResourceTypes: map[string]*configschema.Block{ 6164 "test_thing": { 6165 Attributes: map[string]*configschema.Attribute{ 6166 "id": {Type: cty.String, Optional: true}, 6167 }, 6168 }, 6169 }, 6170 }) 6171 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 6172 // Should actually be called for this test, because Terraform Core 6173 // constructs the plan for a destroy operation itself. 6174 return providers.PlanResourceChangeResponse{ 6175 PlannedState: req.ProposedNewState, 6176 } 6177 } 6178 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6179 // The apply (in this case, a destroy) always fails, so we can verify 6180 // that the object stays in the state after a destroy fails even though 6181 // we aren't returning a new state object here. 6182 return providers.ApplyResourceChangeResponse{ 6183 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("failed")), 6184 } 6185 } 6186 6187 ctx := testContext2(t, &ContextOpts{ 6188 Providers: map[addrs.Provider]providers.Factory{ 6189 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6190 }, 6191 }) 6192 6193 state := states.BuildState(func(ss *states.SyncState) { 6194 ss.SetResourceInstanceCurrent( 6195 addrs.Resource{ 6196 Mode: addrs.ManagedResourceMode, 6197 Type: "test_thing", 6198 Name: "foo", 6199 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 6200 &states.ResourceInstanceObjectSrc{ 6201 Status: states.ObjectReady, 6202 AttrsJSON: []byte(`{"id":"baz"}`), 6203 }, 6204 addrs.AbsProviderConfig{ 6205 Provider: addrs.NewDefaultProvider("test"), 6206 Module: addrs.RootModule, 6207 }, 6208 ) 6209 }) 6210 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6211 assertNoErrors(t, diags) 6212 6213 state, diags = ctx.Apply(plan, m) 6214 if !diags.HasErrors() { 6215 t.Fatal("should have error") 6216 } 6217 6218 actual := strings.TrimSpace(state.String()) 6219 expected := strings.TrimSpace(` 6220 test_thing.foo: 6221 ID = baz 6222 provider = provider["registry.terraform.io/hashicorp/test"] 6223 `) // test_thing.foo is still here, even though provider returned no new state along with its error 6224 if actual != expected { 6225 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 6226 } 6227 } 6228 6229 func TestContext2Apply_errorCreateInvalidNew(t *testing.T) { 6230 m := testModule(t, "apply-error") 6231 6232 p := testProvider("aws") 6233 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 6234 ResourceTypes: map[string]*configschema.Block{ 6235 "aws_instance": { 6236 Attributes: map[string]*configschema.Attribute{ 6237 "value": {Type: cty.String, Optional: true}, 6238 "foo": {Type: cty.String, Optional: true}, 6239 }, 6240 }, 6241 }, 6242 }) 6243 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 6244 return providers.PlanResourceChangeResponse{ 6245 PlannedState: req.ProposedNewState, 6246 } 6247 } 6248 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6249 // We're intentionally returning an inconsistent new state here 6250 // because we want to test that Terraform ignores the inconsistency 6251 // when accompanied by another error. 6252 return providers.ApplyResourceChangeResponse{ 6253 NewState: cty.ObjectVal(map[string]cty.Value{ 6254 "value": cty.StringVal("wrong wrong wrong wrong"), 6255 "foo": cty.StringVal("absolutely brimming over with wrongability"), 6256 }), 6257 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")), 6258 } 6259 } 6260 6261 ctx := testContext2(t, &ContextOpts{ 6262 Providers: map[addrs.Provider]providers.Factory{ 6263 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6264 }, 6265 }) 6266 6267 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6268 assertNoErrors(t, diags) 6269 6270 state, diags := ctx.Apply(plan, m) 6271 if diags == nil { 6272 t.Fatal("should have error") 6273 } 6274 if got, want := len(diags), 1; got != want { 6275 // There should be no additional diagnostics generated by Terraform's own eval logic, 6276 // because the provider's own error supersedes them. 6277 t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err()) 6278 } 6279 if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) { 6280 t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err()) 6281 } 6282 if got, want := len(state.RootModule().Resources), 2; got != want { 6283 t.Errorf("%d resources in state before prune; should have %d\n%s", got, want, spew.Sdump(state)) 6284 } 6285 state.PruneResourceHusks() // aws_instance.bar with no instances gets left behind when we bail out, but that's okay 6286 if got, want := len(state.RootModule().Resources), 1; got != want { 6287 t.Errorf("%d resources in state after prune; should have only one (aws_instance.foo, tainted)\n%s", got, spew.Sdump(state)) 6288 } 6289 } 6290 6291 func TestContext2Apply_errorUpdateNullNew(t *testing.T) { 6292 m := testModule(t, "apply-error") 6293 6294 p := testProvider("aws") 6295 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 6296 ResourceTypes: map[string]*configschema.Block{ 6297 "aws_instance": { 6298 Attributes: map[string]*configschema.Attribute{ 6299 "value": {Type: cty.String, Optional: true}, 6300 "foo": {Type: cty.String, Optional: true}, 6301 }, 6302 }, 6303 }, 6304 }) 6305 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 6306 return providers.PlanResourceChangeResponse{ 6307 PlannedState: req.ProposedNewState, 6308 } 6309 } 6310 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6311 // We're intentionally returning no NewState here because we want to 6312 // test that Terraform retains the prior state, rather than treating 6313 // the returned null as "no state" (object deleted). 6314 return providers.ApplyResourceChangeResponse{ 6315 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")), 6316 } 6317 } 6318 6319 ctx := testContext2(t, &ContextOpts{ 6320 Providers: map[addrs.Provider]providers.Factory{ 6321 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6322 }, 6323 }) 6324 6325 state := states.BuildState(func(ss *states.SyncState) { 6326 ss.SetResourceInstanceCurrent( 6327 addrs.Resource{ 6328 Mode: addrs.ManagedResourceMode, 6329 Type: "aws_instance", 6330 Name: "foo", 6331 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 6332 &states.ResourceInstanceObjectSrc{ 6333 Status: states.ObjectReady, 6334 AttrsJSON: []byte(`{"value":"old"}`), 6335 }, 6336 addrs.AbsProviderConfig{ 6337 Provider: addrs.NewDefaultProvider("aws"), 6338 Module: addrs.RootModule, 6339 }, 6340 ) 6341 }) 6342 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6343 assertNoErrors(t, diags) 6344 6345 state, diags = ctx.Apply(plan, m) 6346 if !diags.HasErrors() { 6347 t.Fatal("should have error") 6348 } 6349 if got, want := len(diags), 1; got != want { 6350 // There should be no additional diagnostics generated by Terraform's own eval logic, 6351 // because the provider's own error supersedes them. 6352 t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err()) 6353 } 6354 if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) { 6355 t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err()) 6356 } 6357 state.PruneResourceHusks() 6358 if got, want := len(state.RootModule().Resources), 1; got != want { 6359 t.Fatalf("%d resources in state; should have only one (aws_instance.foo, unmodified)\n%s", got, spew.Sdump(state)) 6360 } 6361 6362 is := state.ResourceInstance(addrs.Resource{ 6363 Mode: addrs.ManagedResourceMode, 6364 Type: "aws_instance", 6365 Name: "foo", 6366 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 6367 if is == nil { 6368 t.Fatalf("aws_instance.foo is not in the state after apply") 6369 } 6370 if got, want := is.Current.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) { 6371 t.Fatalf("incorrect attributes for aws_instance.foo\ngot: %s\nwant: JSON containing %s\n\n%s", got, want, spew.Sdump(is)) 6372 } 6373 } 6374 6375 func TestContext2Apply_errorPartial(t *testing.T) { 6376 errored := false 6377 6378 m := testModule(t, "apply-error") 6379 p := testProvider("aws") 6380 6381 state := states.NewState() 6382 root := state.EnsureModule(addrs.RootModuleInstance) 6383 root.SetResourceInstanceCurrent( 6384 mustResourceInstanceAddr("aws_instance.bar").Resource, 6385 &states.ResourceInstanceObjectSrc{ 6386 Status: states.ObjectReady, 6387 AttrsJSON: []byte(`{"id":"bar","type":"aws_instance"}`), 6388 }, 6389 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6390 ) 6391 6392 ctx := testContext2(t, &ContextOpts{ 6393 Providers: map[addrs.Provider]providers.Factory{ 6394 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6395 }, 6396 }) 6397 6398 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 6399 if errored { 6400 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 6401 return 6402 } 6403 errored = true 6404 6405 return testApplyFn(req) 6406 } 6407 p.PlanResourceChangeFn = testDiffFn 6408 6409 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6410 assertNoErrors(t, diags) 6411 6412 s, diags := ctx.Apply(plan, m) 6413 if diags == nil { 6414 t.Fatal("should have error") 6415 } 6416 6417 mod := s.RootModule() 6418 if len(mod.Resources) != 2 { 6419 t.Fatalf("bad: %#v", mod.Resources) 6420 } 6421 6422 actual := strings.TrimSpace(s.String()) 6423 expected := strings.TrimSpace(testTerraformApplyErrorPartialStr) 6424 if actual != expected { 6425 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 6426 } 6427 } 6428 6429 func TestContext2Apply_hook(t *testing.T) { 6430 m := testModule(t, "apply-good") 6431 h := new(MockHook) 6432 p := testProvider("aws") 6433 p.PlanResourceChangeFn = testDiffFn 6434 ctx := testContext2(t, &ContextOpts{ 6435 Hooks: []Hook{h}, 6436 Providers: map[addrs.Provider]providers.Factory{ 6437 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6438 }, 6439 }) 6440 6441 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6442 assertNoErrors(t, diags) 6443 6444 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 6445 t.Fatalf("apply errors: %s", diags.Err()) 6446 } 6447 6448 if !h.PreApplyCalled { 6449 t.Fatal("should be called") 6450 } 6451 if !h.PostApplyCalled { 6452 t.Fatal("should be called") 6453 } 6454 if !h.PostStateUpdateCalled { 6455 t.Fatalf("should call post state update") 6456 } 6457 } 6458 6459 func TestContext2Apply_hookOrphan(t *testing.T) { 6460 m := testModule(t, "apply-blank") 6461 h := new(MockHook) 6462 p := testProvider("aws") 6463 p.PlanResourceChangeFn = testDiffFn 6464 6465 state := states.NewState() 6466 root := state.EnsureModule(addrs.RootModuleInstance) 6467 root.SetResourceInstanceCurrent( 6468 mustResourceInstanceAddr("aws_instance.bar").Resource, 6469 &states.ResourceInstanceObjectSrc{ 6470 Status: states.ObjectReady, 6471 AttrsJSON: []byte(`{"id":"bar"}`), 6472 }, 6473 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6474 ) 6475 6476 ctx := testContext2(t, &ContextOpts{ 6477 Hooks: []Hook{h}, 6478 Providers: map[addrs.Provider]providers.Factory{ 6479 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6480 }, 6481 }) 6482 6483 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6484 assertNoErrors(t, diags) 6485 6486 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 6487 t.Fatalf("apply errors: %s", diags.Err()) 6488 } 6489 6490 if !h.PreApplyCalled { 6491 t.Fatal("should be called") 6492 } 6493 if !h.PostApplyCalled { 6494 t.Fatal("should be called") 6495 } 6496 if !h.PostStateUpdateCalled { 6497 t.Fatalf("should call post state update") 6498 } 6499 } 6500 6501 func TestContext2Apply_idAttr(t *testing.T) { 6502 m := testModule(t, "apply-idattr") 6503 p := testProvider("aws") 6504 ctx := testContext2(t, &ContextOpts{ 6505 Providers: map[addrs.Provider]providers.Factory{ 6506 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6507 }, 6508 }) 6509 6510 p.PlanResourceChangeFn = testDiffFn 6511 p.ApplyResourceChangeFn = testApplyFn 6512 6513 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6514 assertNoErrors(t, diags) 6515 6516 state, diags := ctx.Apply(plan, m) 6517 if diags.HasErrors() { 6518 t.Fatalf("apply errors: %s", diags.Err()) 6519 } 6520 6521 mod := state.RootModule() 6522 rs, ok := mod.Resources["aws_instance.foo"] 6523 if !ok { 6524 t.Fatal("not in state") 6525 } 6526 var attrs map[string]interface{} 6527 err := json.Unmarshal(rs.Instances[addrs.NoKey].Current.AttrsJSON, &attrs) 6528 if err != nil { 6529 t.Fatal(err) 6530 } 6531 if got, want := attrs["id"], "foo"; got != want { 6532 t.Fatalf("wrong id\ngot: %#v\nwant: %#v", got, want) 6533 } 6534 } 6535 6536 func TestContext2Apply_outputBasic(t *testing.T) { 6537 m := testModule(t, "apply-output") 6538 p := testProvider("aws") 6539 p.PlanResourceChangeFn = testDiffFn 6540 p.ApplyResourceChangeFn = testApplyFn 6541 ctx := testContext2(t, &ContextOpts{ 6542 Providers: map[addrs.Provider]providers.Factory{ 6543 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6544 }, 6545 }) 6546 6547 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6548 assertNoErrors(t, diags) 6549 6550 state, diags := ctx.Apply(plan, m) 6551 if diags.HasErrors() { 6552 t.Fatalf("diags: %s", diags.Err()) 6553 } 6554 6555 actual := strings.TrimSpace(state.String()) 6556 expected := strings.TrimSpace(testTerraformApplyOutputStr) 6557 if actual != expected { 6558 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6559 } 6560 } 6561 6562 func TestContext2Apply_outputAdd(t *testing.T) { 6563 m1 := testModule(t, "apply-output-add-before") 6564 p1 := testProvider("aws") 6565 p1.ApplyResourceChangeFn = testApplyFn 6566 p1.PlanResourceChangeFn = testDiffFn 6567 ctx1 := testContext2(t, &ContextOpts{ 6568 Providers: map[addrs.Provider]providers.Factory{ 6569 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p1), 6570 }, 6571 }) 6572 6573 plan1, diags := ctx1.Plan(m1, states.NewState(), DefaultPlanOpts) 6574 assertNoErrors(t, diags) 6575 6576 state1, diags := ctx1.Apply(plan1, m1) 6577 if diags.HasErrors() { 6578 t.Fatalf("diags: %s", diags.Err()) 6579 } 6580 6581 m2 := testModule(t, "apply-output-add-after") 6582 p2 := testProvider("aws") 6583 p2.ApplyResourceChangeFn = testApplyFn 6584 p2.PlanResourceChangeFn = testDiffFn 6585 ctx2 := testContext2(t, &ContextOpts{ 6586 Providers: map[addrs.Provider]providers.Factory{ 6587 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p2), 6588 }, 6589 }) 6590 6591 plan2, diags := ctx1.Plan(m2, state1, DefaultPlanOpts) 6592 assertNoErrors(t, diags) 6593 6594 state2, diags := ctx2.Apply(plan2, m2) 6595 if diags.HasErrors() { 6596 t.Fatalf("diags: %s", diags.Err()) 6597 } 6598 6599 actual := strings.TrimSpace(state2.String()) 6600 expected := strings.TrimSpace(testTerraformApplyOutputAddStr) 6601 if actual != expected { 6602 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6603 } 6604 } 6605 6606 func TestContext2Apply_outputList(t *testing.T) { 6607 m := testModule(t, "apply-output-list") 6608 p := testProvider("aws") 6609 p.PlanResourceChangeFn = testDiffFn 6610 p.ApplyResourceChangeFn = testApplyFn 6611 ctx := testContext2(t, &ContextOpts{ 6612 Providers: map[addrs.Provider]providers.Factory{ 6613 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6614 }, 6615 }) 6616 6617 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6618 assertNoErrors(t, diags) 6619 6620 state, diags := ctx.Apply(plan, m) 6621 if diags.HasErrors() { 6622 t.Fatalf("diags: %s", diags.Err()) 6623 } 6624 6625 actual := strings.TrimSpace(state.String()) 6626 expected := strings.TrimSpace(testTerraformApplyOutputListStr) 6627 if actual != expected { 6628 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6629 } 6630 } 6631 6632 func TestContext2Apply_outputMulti(t *testing.T) { 6633 m := testModule(t, "apply-output-multi") 6634 p := testProvider("aws") 6635 p.PlanResourceChangeFn = testDiffFn 6636 p.ApplyResourceChangeFn = testApplyFn 6637 ctx := testContext2(t, &ContextOpts{ 6638 Providers: map[addrs.Provider]providers.Factory{ 6639 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6640 }, 6641 }) 6642 6643 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6644 assertNoErrors(t, diags) 6645 6646 state, diags := ctx.Apply(plan, m) 6647 if diags.HasErrors() { 6648 t.Fatalf("diags: %s", diags.Err()) 6649 } 6650 6651 actual := strings.TrimSpace(state.String()) 6652 expected := strings.TrimSpace(testTerraformApplyOutputMultiStr) 6653 if actual != expected { 6654 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6655 } 6656 } 6657 6658 func TestContext2Apply_outputMultiIndex(t *testing.T) { 6659 m := testModule(t, "apply-output-multi-index") 6660 p := testProvider("aws") 6661 p.PlanResourceChangeFn = testDiffFn 6662 p.ApplyResourceChangeFn = testApplyFn 6663 ctx := testContext2(t, &ContextOpts{ 6664 Providers: map[addrs.Provider]providers.Factory{ 6665 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6666 }, 6667 }) 6668 6669 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 6670 assertNoErrors(t, diags) 6671 6672 state, diags := ctx.Apply(plan, m) 6673 if diags.HasErrors() { 6674 t.Fatalf("diags: %s", diags.Err()) 6675 } 6676 6677 actual := strings.TrimSpace(state.String()) 6678 expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr) 6679 if actual != expected { 6680 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6681 } 6682 } 6683 6684 func TestContext2Apply_taintX(t *testing.T) { 6685 m := testModule(t, "apply-taint") 6686 p := testProvider("aws") 6687 // destroyCount tests against regression of 6688 // https://github.com/muratcelep/terraform/issues/1056 6689 var destroyCount = int32(0) 6690 var once sync.Once 6691 simulateProviderDelay := func() { 6692 time.Sleep(10 * time.Millisecond) 6693 } 6694 6695 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6696 once.Do(simulateProviderDelay) 6697 if req.PlannedState.IsNull() { 6698 atomic.AddInt32(&destroyCount, 1) 6699 } 6700 return testApplyFn(req) 6701 } 6702 p.PlanResourceChangeFn = testDiffFn 6703 6704 state := states.NewState() 6705 root := state.EnsureModule(addrs.RootModuleInstance) 6706 root.SetResourceInstanceCurrent( 6707 mustResourceInstanceAddr("aws_instance.bar").Resource, 6708 &states.ResourceInstanceObjectSrc{ 6709 Status: states.ObjectTainted, 6710 AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`), 6711 }, 6712 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6713 ) 6714 6715 ctx := testContext2(t, &ContextOpts{ 6716 Providers: map[addrs.Provider]providers.Factory{ 6717 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6718 }, 6719 }) 6720 6721 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6722 if diags.HasErrors() { 6723 t.Fatalf("diags: %s", diags.Err()) 6724 } else { 6725 t.Logf("plan: %s", legacyDiffComparisonString(plan.Changes)) 6726 } 6727 6728 s, diags := ctx.Apply(plan, m) 6729 if diags.HasErrors() { 6730 t.Fatalf("diags: %s", diags.Err()) 6731 } 6732 6733 actual := strings.TrimSpace(s.String()) 6734 expected := strings.TrimSpace(testTerraformApplyTaintStr) 6735 if actual != expected { 6736 t.Fatalf("bad:\n%s", actual) 6737 } 6738 6739 if destroyCount != 1 { 6740 t.Fatalf("Expected 1 destroy, got %d", destroyCount) 6741 } 6742 } 6743 6744 func TestContext2Apply_taintDep(t *testing.T) { 6745 m := testModule(t, "apply-taint-dep") 6746 p := testProvider("aws") 6747 p.PlanResourceChangeFn = testDiffFn 6748 p.ApplyResourceChangeFn = testApplyFn 6749 6750 state := states.NewState() 6751 root := state.EnsureModule(addrs.RootModuleInstance) 6752 root.SetResourceInstanceCurrent( 6753 mustResourceInstanceAddr("aws_instance.foo").Resource, 6754 &states.ResourceInstanceObjectSrc{ 6755 Status: states.ObjectTainted, 6756 AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`), 6757 }, 6758 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6759 ) 6760 root.SetResourceInstanceCurrent( 6761 mustResourceInstanceAddr("aws_instance.bar").Resource, 6762 &states.ResourceInstanceObjectSrc{ 6763 Status: states.ObjectReady, 6764 AttrsJSON: []byte(`{"id":"bar","num": "2", "type": "aws_instance", "foo": "baz"}`), 6765 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")}, 6766 }, 6767 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6768 ) 6769 6770 ctx := testContext2(t, &ContextOpts{ 6771 Providers: map[addrs.Provider]providers.Factory{ 6772 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6773 }, 6774 }) 6775 6776 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6777 if diags.HasErrors() { 6778 t.Fatalf("diags: %s", diags.Err()) 6779 } else { 6780 t.Logf("plan: %s", legacyDiffComparisonString(plan.Changes)) 6781 } 6782 6783 s, diags := ctx.Apply(plan, m) 6784 if diags.HasErrors() { 6785 t.Fatalf("diags: %s", diags.Err()) 6786 } 6787 6788 actual := strings.TrimSpace(s.String()) 6789 expected := strings.TrimSpace(testTerraformApplyTaintDepStr) 6790 if actual != expected { 6791 t.Fatalf("bad:\n%s", actual) 6792 } 6793 } 6794 6795 func TestContext2Apply_taintDepRequiresNew(t *testing.T) { 6796 m := testModule(t, "apply-taint-dep-requires-new") 6797 p := testProvider("aws") 6798 p.PlanResourceChangeFn = testDiffFn 6799 p.ApplyResourceChangeFn = testApplyFn 6800 6801 state := states.NewState() 6802 root := state.EnsureModule(addrs.RootModuleInstance) 6803 root.SetResourceInstanceCurrent( 6804 mustResourceInstanceAddr("aws_instance.foo").Resource, 6805 &states.ResourceInstanceObjectSrc{ 6806 Status: states.ObjectTainted, 6807 AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`), 6808 }, 6809 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6810 ) 6811 root.SetResourceInstanceCurrent( 6812 mustResourceInstanceAddr("aws_instance.bar").Resource, 6813 &states.ResourceInstanceObjectSrc{ 6814 Status: states.ObjectReady, 6815 AttrsJSON: []byte(`{"id":"bar","num": "2", "type": "aws_instance", "foo": "baz"}`), 6816 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")}, 6817 }, 6818 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6819 ) 6820 6821 ctx := testContext2(t, &ContextOpts{ 6822 Providers: map[addrs.Provider]providers.Factory{ 6823 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6824 }, 6825 }) 6826 6827 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 6828 if diags.HasErrors() { 6829 t.Fatalf("diags: %s", diags.Err()) 6830 } else { 6831 t.Logf("plan: %s", legacyDiffComparisonString(plan.Changes)) 6832 } 6833 6834 s, diags := ctx.Apply(plan, m) 6835 if diags.HasErrors() { 6836 t.Fatalf("diags: %s", diags.Err()) 6837 } 6838 6839 actual := strings.TrimSpace(s.String()) 6840 expected := strings.TrimSpace(testTerraformApplyTaintDepRequireNewStr) 6841 if actual != expected { 6842 t.Fatalf("bad:\n%s", actual) 6843 } 6844 } 6845 6846 func TestContext2Apply_targeted(t *testing.T) { 6847 m := testModule(t, "apply-targeted") 6848 p := testProvider("aws") 6849 p.PlanResourceChangeFn = testDiffFn 6850 p.ApplyResourceChangeFn = testApplyFn 6851 ctx := testContext2(t, &ContextOpts{ 6852 Providers: map[addrs.Provider]providers.Factory{ 6853 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6854 }, 6855 }) 6856 6857 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 6858 Mode: plans.NormalMode, 6859 Targets: []addrs.Targetable{ 6860 addrs.RootModuleInstance.Resource( 6861 addrs.ManagedResourceMode, "aws_instance", "foo", 6862 ), 6863 }, 6864 }) 6865 assertNoErrors(t, diags) 6866 6867 state, diags := ctx.Apply(plan, m) 6868 if diags.HasErrors() { 6869 t.Fatalf("diags: %s", diags.Err()) 6870 } 6871 6872 mod := state.RootModule() 6873 if len(mod.Resources) != 1 { 6874 t.Fatalf("expected 1 resource, got: %#v", mod.Resources) 6875 } 6876 6877 checkStateString(t, state, ` 6878 aws_instance.foo: 6879 ID = foo 6880 provider = provider["registry.terraform.io/hashicorp/aws"] 6881 num = 2 6882 type = aws_instance 6883 `) 6884 } 6885 6886 func TestContext2Apply_targetedCount(t *testing.T) { 6887 m := testModule(t, "apply-targeted-count") 6888 p := testProvider("aws") 6889 p.PlanResourceChangeFn = testDiffFn 6890 p.ApplyResourceChangeFn = testApplyFn 6891 ctx := testContext2(t, &ContextOpts{ 6892 Providers: map[addrs.Provider]providers.Factory{ 6893 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6894 }, 6895 }) 6896 6897 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 6898 Mode: plans.NormalMode, 6899 Targets: []addrs.Targetable{ 6900 addrs.RootModuleInstance.Resource( 6901 addrs.ManagedResourceMode, "aws_instance", "foo", 6902 ), 6903 }, 6904 }) 6905 assertNoErrors(t, diags) 6906 6907 state, diags := ctx.Apply(plan, m) 6908 if diags.HasErrors() { 6909 t.Fatalf("diags: %s", diags.Err()) 6910 } 6911 6912 checkStateString(t, state, ` 6913 aws_instance.foo.0: 6914 ID = foo 6915 provider = provider["registry.terraform.io/hashicorp/aws"] 6916 type = aws_instance 6917 aws_instance.foo.1: 6918 ID = foo 6919 provider = provider["registry.terraform.io/hashicorp/aws"] 6920 type = aws_instance 6921 aws_instance.foo.2: 6922 ID = foo 6923 provider = provider["registry.terraform.io/hashicorp/aws"] 6924 type = aws_instance 6925 `) 6926 } 6927 6928 func TestContext2Apply_targetedCountIndex(t *testing.T) { 6929 m := testModule(t, "apply-targeted-count") 6930 p := testProvider("aws") 6931 p.PlanResourceChangeFn = testDiffFn 6932 p.ApplyResourceChangeFn = testApplyFn 6933 ctx := testContext2(t, &ContextOpts{ 6934 Providers: map[addrs.Provider]providers.Factory{ 6935 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6936 }, 6937 }) 6938 6939 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 6940 Mode: plans.NormalMode, 6941 Targets: []addrs.Targetable{ 6942 addrs.RootModuleInstance.ResourceInstance( 6943 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1), 6944 ), 6945 }, 6946 }) 6947 assertNoErrors(t, diags) 6948 6949 state, diags := ctx.Apply(plan, m) 6950 if diags.HasErrors() { 6951 t.Fatalf("diags: %s", diags.Err()) 6952 } 6953 6954 checkStateString(t, state, ` 6955 aws_instance.foo.1: 6956 ID = foo 6957 provider = provider["registry.terraform.io/hashicorp/aws"] 6958 type = aws_instance 6959 `) 6960 } 6961 6962 func TestContext2Apply_targetedDestroy(t *testing.T) { 6963 m := testModule(t, "destroy-targeted") 6964 p := testProvider("aws") 6965 p.PlanResourceChangeFn = testDiffFn 6966 6967 state := states.NewState() 6968 root := state.EnsureModule(addrs.RootModuleInstance) 6969 root.SetResourceInstanceCurrent( 6970 mustResourceInstanceAddr("aws_instance.a").Resource, 6971 &states.ResourceInstanceObjectSrc{ 6972 Status: states.ObjectReady, 6973 AttrsJSON: []byte(`{"id":"bar"}`), 6974 }, 6975 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6976 ) 6977 root.SetOutputValue("out", cty.StringVal("bar"), false) 6978 6979 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 6980 child.SetResourceInstanceCurrent( 6981 mustResourceInstanceAddr("aws_instance.b").Resource, 6982 &states.ResourceInstanceObjectSrc{ 6983 Status: states.ObjectReady, 6984 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 6985 }, 6986 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6987 ) 6988 6989 ctx := testContext2(t, &ContextOpts{ 6990 Providers: map[addrs.Provider]providers.Factory{ 6991 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6992 }, 6993 }) 6994 6995 if diags := ctx.Validate(m); diags.HasErrors() { 6996 t.Fatalf("validate errors: %s", diags.Err()) 6997 } 6998 6999 plan, diags := ctx.Plan(m, state, &PlanOpts{ 7000 Mode: plans.DestroyMode, 7001 Targets: []addrs.Targetable{ 7002 addrs.RootModuleInstance.Resource( 7003 addrs.ManagedResourceMode, "aws_instance", "a", 7004 ), 7005 }, 7006 }) 7007 assertNoErrors(t, diags) 7008 7009 state, diags = ctx.Apply(plan, m) 7010 if diags.HasErrors() { 7011 t.Fatalf("diags: %s", diags.Err()) 7012 } 7013 7014 mod := state.RootModule() 7015 if len(mod.Resources) != 0 { 7016 t.Fatalf("expected 0 resources, got: %#v", mod.Resources) 7017 } 7018 7019 // the root output should not get removed; only the targeted resource. 7020 // 7021 // Note: earlier versions of this test expected 0 outputs, but it turns out 7022 // that was because Validate - not apply or destroy - removed the output 7023 // (which depends on the targeted resource) from state. That version of this 7024 // test did not match actual terraform behavior: the output remains in 7025 // state. 7026 // 7027 // TODO: Future refactoring may enable us to remove the output from state in 7028 // this case, and that would be Just Fine - this test can be modified to 7029 // expect 0 outputs. 7030 if len(mod.OutputValues) != 1 { 7031 t.Fatalf("expected 1 outputs, got: %#v", mod.OutputValues) 7032 } 7033 7034 // the module instance should remain 7035 mod = state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7036 if len(mod.Resources) != 1 { 7037 t.Fatalf("expected 1 resources, got: %#v", mod.Resources) 7038 } 7039 } 7040 7041 func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) { 7042 m := testModule(t, "apply-destroy-targeted-count") 7043 p := testProvider("aws") 7044 p.PlanResourceChangeFn = testDiffFn 7045 7046 state := states.NewState() 7047 root := state.EnsureModule(addrs.RootModuleInstance) 7048 root.SetResourceInstanceCurrent( 7049 mustResourceInstanceAddr("aws_instance.foo").Resource, 7050 &states.ResourceInstanceObjectSrc{ 7051 Status: states.ObjectReady, 7052 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7053 }, 7054 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7055 ) 7056 root.SetResourceInstanceCurrent( 7057 mustResourceInstanceAddr("aws_instance.bar").Resource, 7058 &states.ResourceInstanceObjectSrc{ 7059 Status: states.ObjectReady, 7060 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7061 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")}, 7062 }, 7063 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7064 ) 7065 7066 ctx := testContext2(t, &ContextOpts{ 7067 Providers: map[addrs.Provider]providers.Factory{ 7068 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7069 }, 7070 }) 7071 7072 plan, diags := ctx.Plan(m, state, &PlanOpts{ 7073 Mode: plans.DestroyMode, 7074 Targets: []addrs.Targetable{ 7075 addrs.RootModuleInstance.Resource( 7076 addrs.ManagedResourceMode, "aws_instance", "foo", 7077 ), 7078 }, 7079 }) 7080 assertNoErrors(t, diags) 7081 7082 state, diags = ctx.Apply(plan, m) 7083 if diags.HasErrors() { 7084 t.Fatalf("diags: %s", diags.Err()) 7085 } 7086 7087 checkStateString(t, state, `<no state>`) 7088 } 7089 7090 // https://github.com/muratcelep/terraform/issues/4462 7091 func TestContext2Apply_targetedDestroyModule(t *testing.T) { 7092 m := testModule(t, "apply-targeted-module") 7093 p := testProvider("aws") 7094 p.PlanResourceChangeFn = testDiffFn 7095 7096 state := states.NewState() 7097 root := state.EnsureModule(addrs.RootModuleInstance) 7098 root.SetResourceInstanceCurrent( 7099 mustResourceInstanceAddr("aws_instance.foo").Resource, 7100 &states.ResourceInstanceObjectSrc{ 7101 Status: states.ObjectReady, 7102 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7103 }, 7104 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7105 ) 7106 root.SetResourceInstanceCurrent( 7107 mustResourceInstanceAddr("aws_instance.bar").Resource, 7108 &states.ResourceInstanceObjectSrc{ 7109 Status: states.ObjectReady, 7110 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7111 }, 7112 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7113 ) 7114 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7115 child.SetResourceInstanceCurrent( 7116 mustResourceInstanceAddr("aws_instance.foo").Resource, 7117 &states.ResourceInstanceObjectSrc{ 7118 Status: states.ObjectReady, 7119 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7120 }, 7121 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7122 ) 7123 child.SetResourceInstanceCurrent( 7124 mustResourceInstanceAddr("aws_instance.bar").Resource, 7125 &states.ResourceInstanceObjectSrc{ 7126 Status: states.ObjectReady, 7127 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7128 }, 7129 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7130 ) 7131 7132 ctx := testContext2(t, &ContextOpts{ 7133 Providers: map[addrs.Provider]providers.Factory{ 7134 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7135 }, 7136 }) 7137 7138 plan, diags := ctx.Plan(m, state, &PlanOpts{ 7139 Mode: plans.DestroyMode, 7140 Targets: []addrs.Targetable{ 7141 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 7142 addrs.ManagedResourceMode, "aws_instance", "foo", 7143 ), 7144 }, 7145 }) 7146 assertNoErrors(t, diags) 7147 7148 state, diags = ctx.Apply(plan, m) 7149 if diags.HasErrors() { 7150 t.Fatalf("diags: %s", diags.Err()) 7151 } 7152 7153 checkStateString(t, state, ` 7154 aws_instance.bar: 7155 ID = i-abc123 7156 provider = provider["registry.terraform.io/hashicorp/aws"] 7157 aws_instance.foo: 7158 ID = i-bcd345 7159 provider = provider["registry.terraform.io/hashicorp/aws"] 7160 7161 module.child: 7162 aws_instance.bar: 7163 ID = i-abc123 7164 provider = provider["registry.terraform.io/hashicorp/aws"] 7165 `) 7166 } 7167 7168 func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) { 7169 m := testModule(t, "apply-targeted-count") 7170 p := testProvider("aws") 7171 p.PlanResourceChangeFn = testDiffFn 7172 7173 foo := &states.ResourceInstanceObjectSrc{ 7174 Status: states.ObjectReady, 7175 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7176 } 7177 bar := &states.ResourceInstanceObjectSrc{ 7178 Status: states.ObjectReady, 7179 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7180 } 7181 7182 state := states.NewState() 7183 root := state.EnsureModule(addrs.RootModuleInstance) 7184 root.SetResourceInstanceCurrent( 7185 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 7186 foo, 7187 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7188 ) 7189 root.SetResourceInstanceCurrent( 7190 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 7191 foo, 7192 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7193 ) 7194 root.SetResourceInstanceCurrent( 7195 mustResourceInstanceAddr("aws_instance.foo[2]").Resource, 7196 foo, 7197 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7198 ) 7199 root.SetResourceInstanceCurrent( 7200 mustResourceInstanceAddr("aws_instance.bar[0]").Resource, 7201 bar, 7202 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7203 ) 7204 root.SetResourceInstanceCurrent( 7205 mustResourceInstanceAddr("aws_instance.bar[1]").Resource, 7206 bar, 7207 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7208 ) 7209 root.SetResourceInstanceCurrent( 7210 mustResourceInstanceAddr("aws_instance.bar[2]").Resource, 7211 bar, 7212 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7213 ) 7214 7215 ctx := testContext2(t, &ContextOpts{ 7216 Providers: map[addrs.Provider]providers.Factory{ 7217 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7218 }, 7219 }) 7220 7221 plan, diags := ctx.Plan(m, state, &PlanOpts{ 7222 Mode: plans.DestroyMode, 7223 Targets: []addrs.Targetable{ 7224 addrs.RootModuleInstance.ResourceInstance( 7225 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(2), 7226 ), 7227 addrs.RootModuleInstance.ResourceInstance( 7228 addrs.ManagedResourceMode, "aws_instance", "bar", addrs.IntKey(1), 7229 ), 7230 }, 7231 }) 7232 assertNoErrors(t, diags) 7233 7234 state, diags = ctx.Apply(plan, m) 7235 if diags.HasErrors() { 7236 t.Fatalf("diags: %s", diags.Err()) 7237 } 7238 7239 checkStateString(t, state, ` 7240 aws_instance.bar.0: 7241 ID = i-abc123 7242 provider = provider["registry.terraform.io/hashicorp/aws"] 7243 aws_instance.bar.2: 7244 ID = i-abc123 7245 provider = provider["registry.terraform.io/hashicorp/aws"] 7246 aws_instance.foo.0: 7247 ID = i-bcd345 7248 provider = provider["registry.terraform.io/hashicorp/aws"] 7249 aws_instance.foo.1: 7250 ID = i-bcd345 7251 provider = provider["registry.terraform.io/hashicorp/aws"] 7252 `) 7253 } 7254 7255 func TestContext2Apply_targetedModule(t *testing.T) { 7256 m := testModule(t, "apply-targeted-module") 7257 p := testProvider("aws") 7258 p.PlanResourceChangeFn = testDiffFn 7259 p.ApplyResourceChangeFn = testApplyFn 7260 ctx := testContext2(t, &ContextOpts{ 7261 Providers: map[addrs.Provider]providers.Factory{ 7262 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7263 }, 7264 }) 7265 7266 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 7267 Mode: plans.NormalMode, 7268 Targets: []addrs.Targetable{ 7269 addrs.RootModuleInstance.Child("child", addrs.NoKey), 7270 }, 7271 }) 7272 assertNoErrors(t, diags) 7273 7274 state, diags := ctx.Apply(plan, m) 7275 if diags.HasErrors() { 7276 t.Fatalf("diags: %s", diags.Err()) 7277 } 7278 7279 mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7280 if mod == nil { 7281 t.Fatalf("no child module found in the state!\n\n%#v", state) 7282 } 7283 if len(mod.Resources) != 2 { 7284 t.Fatalf("expected 2 resources, got: %#v", mod.Resources) 7285 } 7286 7287 checkStateString(t, state, ` 7288 <no state> 7289 module.child: 7290 aws_instance.bar: 7291 ID = foo 7292 provider = provider["registry.terraform.io/hashicorp/aws"] 7293 num = 2 7294 type = aws_instance 7295 aws_instance.foo: 7296 ID = foo 7297 provider = provider["registry.terraform.io/hashicorp/aws"] 7298 num = 2 7299 type = aws_instance 7300 `) 7301 } 7302 7303 // GH-1858 7304 func TestContext2Apply_targetedModuleDep(t *testing.T) { 7305 m := testModule(t, "apply-targeted-module-dep") 7306 p := testProvider("aws") 7307 p.PlanResourceChangeFn = testDiffFn 7308 p.ApplyResourceChangeFn = testApplyFn 7309 ctx := testContext2(t, &ContextOpts{ 7310 Providers: map[addrs.Provider]providers.Factory{ 7311 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7312 }, 7313 }) 7314 7315 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 7316 Mode: plans.NormalMode, 7317 Targets: []addrs.Targetable{ 7318 addrs.RootModuleInstance.Resource( 7319 addrs.ManagedResourceMode, "aws_instance", "foo", 7320 ), 7321 }, 7322 }) 7323 if diags.HasErrors() { 7324 t.Fatalf("diags: %s", diags.Err()) 7325 } else { 7326 t.Logf("Diff: %s", legacyDiffComparisonString(plan.Changes)) 7327 } 7328 7329 state, diags := ctx.Apply(plan, m) 7330 if diags.HasErrors() { 7331 t.Fatalf("diags: %s", diags.Err()) 7332 } 7333 7334 checkStateString(t, state, ` 7335 aws_instance.foo: 7336 ID = foo 7337 provider = provider["registry.terraform.io/hashicorp/aws"] 7338 foo = foo 7339 type = aws_instance 7340 7341 Dependencies: 7342 module.child.aws_instance.mod 7343 7344 module.child: 7345 aws_instance.mod: 7346 ID = foo 7347 provider = provider["registry.terraform.io/hashicorp/aws"] 7348 type = aws_instance 7349 7350 Outputs: 7351 7352 output = foo 7353 `) 7354 } 7355 7356 // GH-10911 untargeted outputs should not be in the graph, and therefore 7357 // not execute. 7358 func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) { 7359 m := testModule(t, "apply-targeted-module-unrelated-outputs") 7360 p := testProvider("aws") 7361 p.PlanResourceChangeFn = testDiffFn 7362 p.ApplyResourceChangeFn = testApplyFn 7363 7364 state := states.NewState() 7365 _ = state.EnsureModule(addrs.RootModuleInstance.Child("child2", addrs.NoKey)) 7366 7367 ctx := testContext2(t, &ContextOpts{ 7368 Providers: map[addrs.Provider]providers.Factory{ 7369 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7370 }, 7371 }) 7372 7373 plan, diags := ctx.Plan(m, state, &PlanOpts{ 7374 Mode: plans.NormalMode, 7375 Targets: []addrs.Targetable{ 7376 addrs.RootModuleInstance.Child("child2", addrs.NoKey), 7377 }, 7378 }) 7379 assertNoErrors(t, diags) 7380 7381 s, diags := ctx.Apply(plan, m) 7382 if diags.HasErrors() { 7383 t.Fatalf("diags: %s", diags.Err()) 7384 } 7385 7386 // - module.child1's instance_id output is dropped because we don't preserve 7387 // non-root module outputs between runs (they can be recalculated from config) 7388 // - module.child2's instance_id is updated because its dependency is updated 7389 // - child2_id is updated because if its transitive dependency via module.child2 7390 checkStateString(t, s, ` 7391 <no state> 7392 Outputs: 7393 7394 child2_id = foo 7395 7396 module.child2: 7397 aws_instance.foo: 7398 ID = foo 7399 provider = provider["registry.terraform.io/hashicorp/aws"] 7400 type = aws_instance 7401 7402 Outputs: 7403 7404 instance_id = foo 7405 `) 7406 } 7407 7408 func TestContext2Apply_targetedModuleResource(t *testing.T) { 7409 m := testModule(t, "apply-targeted-module-resource") 7410 p := testProvider("aws") 7411 p.PlanResourceChangeFn = testDiffFn 7412 p.ApplyResourceChangeFn = testApplyFn 7413 ctx := testContext2(t, &ContextOpts{ 7414 Providers: map[addrs.Provider]providers.Factory{ 7415 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7416 }, 7417 }) 7418 7419 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 7420 Mode: plans.NormalMode, 7421 Targets: []addrs.Targetable{ 7422 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 7423 addrs.ManagedResourceMode, "aws_instance", "foo", 7424 ), 7425 }, 7426 }) 7427 assertNoErrors(t, diags) 7428 7429 state, diags := ctx.Apply(plan, m) 7430 if diags.HasErrors() { 7431 t.Fatalf("diags: %s", diags.Err()) 7432 } 7433 7434 mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7435 if mod == nil || len(mod.Resources) != 1 { 7436 t.Fatalf("expected 1 resource, got: %#v", mod) 7437 } 7438 7439 checkStateString(t, state, ` 7440 <no state> 7441 module.child: 7442 aws_instance.foo: 7443 ID = foo 7444 provider = provider["registry.terraform.io/hashicorp/aws"] 7445 num = 2 7446 type = aws_instance 7447 `) 7448 } 7449 7450 func TestContext2Apply_targetedResourceOrphanModule(t *testing.T) { 7451 m := testModule(t, "apply-targeted-resource-orphan-module") 7452 p := testProvider("aws") 7453 p.PlanResourceChangeFn = testDiffFn 7454 7455 state := states.NewState() 7456 child := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey)) 7457 child.SetResourceInstanceCurrent( 7458 mustResourceInstanceAddr("aws_instance.bar").Resource, 7459 &states.ResourceInstanceObjectSrc{ 7460 Status: states.ObjectReady, 7461 AttrsJSON: []byte(`{"type":"aws_instance"}`), 7462 }, 7463 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7464 ) 7465 7466 ctx := testContext2(t, &ContextOpts{ 7467 Providers: map[addrs.Provider]providers.Factory{ 7468 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7469 }, 7470 }) 7471 7472 plan, diags := ctx.Plan(m, state, &PlanOpts{ 7473 Mode: plans.NormalMode, 7474 Targets: []addrs.Targetable{ 7475 addrs.RootModuleInstance.Resource( 7476 addrs.ManagedResourceMode, "aws_instance", "foo", 7477 ), 7478 }, 7479 }) 7480 assertNoErrors(t, diags) 7481 7482 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 7483 t.Fatalf("apply errors: %s", diags.Err()) 7484 } 7485 } 7486 7487 func TestContext2Apply_unknownAttribute(t *testing.T) { 7488 m := testModule(t, "apply-unknown") 7489 p := testProvider("aws") 7490 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 7491 resp = testDiffFn(req) 7492 planned := resp.PlannedState.AsValueMap() 7493 planned["unknown"] = cty.UnknownVal(cty.String) 7494 resp.PlannedState = cty.ObjectVal(planned) 7495 return resp 7496 } 7497 p.ApplyResourceChangeFn = testApplyFn 7498 7499 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 7500 ResourceTypes: map[string]*configschema.Block{ 7501 "aws_instance": { 7502 Attributes: map[string]*configschema.Attribute{ 7503 "id": {Type: cty.String, Computed: true}, 7504 "num": {Type: cty.Number, Optional: true}, 7505 "unknown": {Type: cty.String, Computed: true}, 7506 "type": {Type: cty.String, Computed: true}, 7507 }, 7508 }, 7509 }, 7510 }) 7511 7512 ctx := testContext2(t, &ContextOpts{ 7513 Providers: map[addrs.Provider]providers.Factory{ 7514 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7515 }, 7516 }) 7517 7518 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 7519 assertNoErrors(t, diags) 7520 7521 state, diags := ctx.Apply(plan, m) 7522 if !diags.HasErrors() { 7523 t.Error("should error, because attribute 'unknown' is still unknown after apply") 7524 } 7525 7526 actual := strings.TrimSpace(state.String()) 7527 expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr) 7528 if actual != expected { 7529 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7530 } 7531 } 7532 7533 func TestContext2Apply_unknownAttributeInterpolate(t *testing.T) { 7534 m := testModule(t, "apply-unknown-interpolate") 7535 p := testProvider("aws") 7536 p.PlanResourceChangeFn = testDiffFn 7537 ctx := testContext2(t, &ContextOpts{ 7538 Providers: map[addrs.Provider]providers.Factory{ 7539 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7540 }, 7541 }) 7542 7543 if _, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts); diags == nil { 7544 t.Fatal("should error") 7545 } 7546 } 7547 7548 func TestContext2Apply_vars(t *testing.T) { 7549 fixture := contextFixtureApplyVars(t) 7550 opts := fixture.ContextOpts() 7551 ctx := testContext2(t, opts) 7552 m := fixture.Config 7553 7554 diags := ctx.Validate(m) 7555 if len(diags) != 0 { 7556 t.Fatalf("bad: %s", diags.ErrWithWarnings()) 7557 } 7558 7559 variables := InputValues{ 7560 "foo": &InputValue{ 7561 Value: cty.StringVal("us-east-1"), 7562 SourceType: ValueFromCaller, 7563 }, 7564 "test_list": &InputValue{ 7565 Value: cty.ListVal([]cty.Value{ 7566 cty.StringVal("Hello"), 7567 cty.StringVal("World"), 7568 }), 7569 SourceType: ValueFromCaller, 7570 }, 7571 "test_map": &InputValue{ 7572 Value: cty.MapVal(map[string]cty.Value{ 7573 "Hello": cty.StringVal("World"), 7574 "Foo": cty.StringVal("Bar"), 7575 "Baz": cty.StringVal("Foo"), 7576 }), 7577 SourceType: ValueFromCaller, 7578 }, 7579 "amis": &InputValue{ 7580 Value: cty.MapVal(map[string]cty.Value{ 7581 "us-east-1": cty.StringVal("override"), 7582 }), 7583 SourceType: ValueFromCaller, 7584 }, 7585 } 7586 7587 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 7588 Mode: plans.NormalMode, 7589 SetVariables: variables, 7590 }) 7591 assertNoErrors(t, diags) 7592 7593 state, diags := ctx.Apply(plan, m) 7594 if diags.HasErrors() { 7595 t.Fatalf("err: %s", diags.Err()) 7596 } 7597 7598 got := strings.TrimSpace(state.String()) 7599 want := strings.TrimSpace(testTerraformApplyVarsStr) 7600 if got != want { 7601 t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want) 7602 } 7603 } 7604 7605 func TestContext2Apply_varsEnv(t *testing.T) { 7606 fixture := contextFixtureApplyVarsEnv(t) 7607 opts := fixture.ContextOpts() 7608 ctx := testContext2(t, opts) 7609 m := fixture.Config 7610 7611 diags := ctx.Validate(m) 7612 if len(diags) != 0 { 7613 t.Fatalf("bad: %s", diags.ErrWithWarnings()) 7614 } 7615 7616 variables := InputValues{ 7617 "string": &InputValue{ 7618 Value: cty.StringVal("baz"), 7619 SourceType: ValueFromEnvVar, 7620 }, 7621 "list": &InputValue{ 7622 Value: cty.ListVal([]cty.Value{ 7623 cty.StringVal("Hello"), 7624 cty.StringVal("World"), 7625 }), 7626 SourceType: ValueFromEnvVar, 7627 }, 7628 "map": &InputValue{ 7629 Value: cty.MapVal(map[string]cty.Value{ 7630 "Hello": cty.StringVal("World"), 7631 "Foo": cty.StringVal("Bar"), 7632 "Baz": cty.StringVal("Foo"), 7633 }), 7634 SourceType: ValueFromEnvVar, 7635 }, 7636 } 7637 7638 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 7639 Mode: plans.NormalMode, 7640 SetVariables: variables, 7641 }) 7642 assertNoErrors(t, diags) 7643 7644 state, diags := ctx.Apply(plan, m) 7645 if diags.HasErrors() { 7646 t.Fatalf("err: %s", diags.Err()) 7647 } 7648 7649 actual := strings.TrimSpace(state.String()) 7650 expected := strings.TrimSpace(testTerraformApplyVarsEnvStr) 7651 if actual != expected { 7652 t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7653 } 7654 } 7655 7656 func TestContext2Apply_createBefore_depends(t *testing.T) { 7657 m := testModule(t, "apply-depends-create-before") 7658 h := new(HookRecordApplyOrder) 7659 p := testProvider("aws") 7660 p.PlanResourceChangeFn = testDiffFn 7661 p.ApplyResourceChangeFn = testApplyFn 7662 state := states.NewState() 7663 root := state.EnsureModule(addrs.RootModuleInstance) 7664 root.SetResourceInstanceCurrent( 7665 addrs.Resource{ 7666 Mode: addrs.ManagedResourceMode, 7667 Type: "aws_instance", 7668 Name: "web", 7669 }.Instance(addrs.NoKey), 7670 &states.ResourceInstanceObjectSrc{ 7671 Status: states.ObjectReady, 7672 AttrsJSON: []byte(`{"id":"bar","require_new":"ami-old"}`), 7673 }, 7674 addrs.AbsProviderConfig{ 7675 Provider: addrs.NewDefaultProvider("aws"), 7676 Module: addrs.RootModule, 7677 }, 7678 ) 7679 7680 root.SetResourceInstanceCurrent( 7681 addrs.Resource{ 7682 Mode: addrs.ManagedResourceMode, 7683 Type: "aws_instance", 7684 Name: "lb", 7685 }.Instance(addrs.NoKey), 7686 &states.ResourceInstanceObjectSrc{ 7687 Status: states.ObjectReady, 7688 AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`), 7689 Dependencies: []addrs.ConfigResource{ 7690 { 7691 Resource: addrs.Resource{ 7692 Mode: addrs.ManagedResourceMode, 7693 Type: "aws_instance", 7694 Name: "web", 7695 }, 7696 Module: addrs.RootModule, 7697 }, 7698 }, 7699 }, 7700 addrs.AbsProviderConfig{ 7701 Provider: addrs.NewDefaultProvider("aws"), 7702 Module: addrs.RootModule, 7703 }, 7704 ) 7705 7706 ctx := testContext2(t, &ContextOpts{ 7707 Hooks: []Hook{h}, 7708 Providers: map[addrs.Provider]providers.Factory{ 7709 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7710 }, 7711 }) 7712 7713 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 7714 if diags.HasErrors() { 7715 logDiagnostics(t, diags) 7716 t.Fatal("plan failed") 7717 } else { 7718 t.Logf("plan:\n%s", legacyDiffComparisonString(plan.Changes)) 7719 } 7720 7721 h.Active = true 7722 state, diags = ctx.Apply(plan, m) 7723 if diags.HasErrors() { 7724 logDiagnostics(t, diags) 7725 t.Fatal("apply failed") 7726 } 7727 7728 mod := state.RootModule() 7729 if len(mod.Resources) < 2 { 7730 t.Logf("state after apply:\n%s", state.String()) 7731 t.Fatalf("only %d resources in root module; want at least 2", len(mod.Resources)) 7732 } 7733 7734 got := strings.TrimSpace(state.String()) 7735 want := strings.TrimSpace(testTerraformApplyDependsCreateBeforeStr) 7736 if got != want { 7737 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", got, want) 7738 } 7739 7740 // Test that things were managed _in the right order_ 7741 order := h.States 7742 7743 diffs := h.Diffs 7744 if !order[0].IsNull() || diffs[0].Action == plans.Delete { 7745 t.Fatalf("should create new instance first: %#v", order) 7746 } 7747 7748 if order[1].GetAttr("id").AsString() != "baz" { 7749 t.Fatalf("update must happen after create: %#v", order[1]) 7750 } 7751 7752 if order[2].GetAttr("id").AsString() != "bar" || diffs[2].Action != plans.Delete { 7753 t.Fatalf("destroy must happen after update: %#v", order[2]) 7754 } 7755 } 7756 7757 func TestContext2Apply_singleDestroy(t *testing.T) { 7758 m := testModule(t, "apply-depends-create-before") 7759 h := new(HookRecordApplyOrder) 7760 p := testProvider("aws") 7761 invokeCount := 0 7762 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 7763 invokeCount++ 7764 switch invokeCount { 7765 case 1: 7766 if req.PlannedState.IsNull() { 7767 t.Fatalf("should not destroy") 7768 } 7769 if id := req.PlannedState.GetAttr("id"); id.IsKnown() { 7770 t.Fatalf("should not have ID") 7771 } 7772 case 2: 7773 if req.PlannedState.IsNull() { 7774 t.Fatalf("should not destroy") 7775 } 7776 if id := req.PlannedState.GetAttr("id"); id.AsString() != "baz" { 7777 t.Fatalf("should have id") 7778 } 7779 case 3: 7780 if !req.PlannedState.IsNull() { 7781 t.Fatalf("should destroy") 7782 } 7783 default: 7784 t.Fatalf("bad invoke count %d", invokeCount) 7785 } 7786 return testApplyFn(req) 7787 } 7788 7789 p.PlanResourceChangeFn = testDiffFn 7790 state := states.NewState() 7791 root := state.EnsureModule(addrs.RootModuleInstance) 7792 root.SetResourceInstanceCurrent( 7793 addrs.Resource{ 7794 Mode: addrs.ManagedResourceMode, 7795 Type: "aws_instance", 7796 Name: "web", 7797 }.Instance(addrs.NoKey), 7798 &states.ResourceInstanceObjectSrc{ 7799 Status: states.ObjectReady, 7800 AttrsJSON: []byte(`{"id":"bar","require_new":"ami-old"}`), 7801 }, 7802 addrs.AbsProviderConfig{ 7803 Provider: addrs.NewDefaultProvider("aws"), 7804 Module: addrs.RootModule, 7805 }, 7806 ) 7807 7808 root.SetResourceInstanceCurrent( 7809 addrs.Resource{ 7810 Mode: addrs.ManagedResourceMode, 7811 Type: "aws_instance", 7812 Name: "lb", 7813 }.Instance(addrs.NoKey), 7814 &states.ResourceInstanceObjectSrc{ 7815 Status: states.ObjectReady, 7816 AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`), 7817 Dependencies: []addrs.ConfigResource{ 7818 { 7819 Resource: addrs.Resource{ 7820 Mode: addrs.ManagedResourceMode, 7821 Type: "aws_instance", 7822 Name: "web", 7823 }, 7824 Module: addrs.RootModule, 7825 }, 7826 }, 7827 }, 7828 addrs.AbsProviderConfig{ 7829 Provider: addrs.NewDefaultProvider("aws"), 7830 Module: addrs.RootModule, 7831 }, 7832 ) 7833 7834 ctx := testContext2(t, &ContextOpts{ 7835 Hooks: []Hook{h}, 7836 Providers: map[addrs.Provider]providers.Factory{ 7837 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7838 }, 7839 }) 7840 7841 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 7842 assertNoErrors(t, diags) 7843 7844 h.Active = true 7845 _, diags = ctx.Apply(plan, m) 7846 if diags.HasErrors() { 7847 t.Fatalf("diags: %s", diags.Err()) 7848 } 7849 7850 if invokeCount != 3 { 7851 t.Fatalf("bad: %d", invokeCount) 7852 } 7853 } 7854 7855 // GH-7824 7856 func TestContext2Apply_issue7824(t *testing.T) { 7857 p := testProvider("template") 7858 p.PlanResourceChangeFn = testDiffFn 7859 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 7860 ResourceTypes: map[string]*configschema.Block{ 7861 "template_file": { 7862 Attributes: map[string]*configschema.Attribute{ 7863 "template": {Type: cty.String, Optional: true}, 7864 "__template_requires_new": {Type: cty.Bool, Optional: true}, 7865 }, 7866 }, 7867 }, 7868 }) 7869 7870 m, snap := testModuleWithSnapshot(t, "issue-7824") 7871 7872 // Apply cleanly step 0 7873 ctx := testContext2(t, &ContextOpts{ 7874 Providers: map[addrs.Provider]providers.Factory{ 7875 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 7876 }, 7877 }) 7878 7879 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 7880 if diags.HasErrors() { 7881 t.Fatalf("err: %s", diags.Err()) 7882 } 7883 7884 // Write / Read plan to simulate running it through a Plan file 7885 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 7886 if err != nil { 7887 t.Fatalf("failed to round-trip through planfile: %s", err) 7888 } 7889 7890 ctxOpts.Providers = 7891 map[addrs.Provider]providers.Factory{ 7892 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 7893 } 7894 7895 ctx, diags = NewContext(ctxOpts) 7896 if diags.HasErrors() { 7897 t.Fatalf("err: %s", diags.Err()) 7898 } 7899 7900 _, diags = ctx.Apply(plan, m) 7901 if diags.HasErrors() { 7902 t.Fatalf("err: %s", diags.Err()) 7903 } 7904 } 7905 7906 // This deals with the situation where a splat expression is used referring 7907 // to another resource whose count is non-constant. 7908 func TestContext2Apply_issue5254(t *testing.T) { 7909 // Create a provider. We use "template" here just to match the repro 7910 // we got from the issue itself. 7911 p := testProvider("template") 7912 p.PlanResourceChangeFn = testDiffFn 7913 p.ApplyResourceChangeFn = testApplyFn 7914 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 7915 ResourceTypes: map[string]*configschema.Block{ 7916 "template_file": { 7917 Attributes: map[string]*configschema.Attribute{ 7918 "template": {Type: cty.String, Optional: true}, 7919 "__template_requires_new": {Type: cty.Bool, Optional: true}, 7920 "id": {Type: cty.String, Computed: true}, 7921 "type": {Type: cty.String, Computed: true}, 7922 }, 7923 }, 7924 }, 7925 }) 7926 7927 // Apply cleanly step 0 7928 m := testModule(t, "issue-5254/step-0") 7929 ctx := testContext2(t, &ContextOpts{ 7930 Providers: map[addrs.Provider]providers.Factory{ 7931 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 7932 }, 7933 }) 7934 7935 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 7936 if diags.HasErrors() { 7937 t.Fatalf("err: %s", diags.Err()) 7938 } 7939 7940 state, diags := ctx.Apply(plan, m) 7941 if diags.HasErrors() { 7942 t.Fatalf("err: %s", diags.Err()) 7943 } 7944 7945 m, snap := testModuleWithSnapshot(t, "issue-5254/step-1") 7946 7947 // Application success. Now make the modification and store a plan 7948 ctx = testContext2(t, &ContextOpts{ 7949 Providers: map[addrs.Provider]providers.Factory{ 7950 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 7951 }, 7952 }) 7953 7954 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 7955 if diags.HasErrors() { 7956 t.Fatalf("err: %s", diags.Err()) 7957 } 7958 7959 // Write / Read plan to simulate running it through a Plan file 7960 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 7961 if err != nil { 7962 t.Fatalf("failed to round-trip through planfile: %s", err) 7963 } 7964 7965 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 7966 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 7967 } 7968 7969 ctx, diags = NewContext(ctxOpts) 7970 if diags.HasErrors() { 7971 t.Fatalf("err: %s", diags.Err()) 7972 } 7973 7974 state, diags = ctx.Apply(plan, m) 7975 if diags.HasErrors() { 7976 t.Fatalf("err: %s", diags.Err()) 7977 } 7978 7979 actual := strings.TrimSpace(state.String()) 7980 expected := strings.TrimSpace(` 7981 template_file.child: 7982 ID = foo 7983 provider = provider["registry.terraform.io/hashicorp/template"] 7984 __template_requires_new = true 7985 template = Hi 7986 type = template_file 7987 7988 Dependencies: 7989 template_file.parent 7990 template_file.parent.0: 7991 ID = foo 7992 provider = provider["registry.terraform.io/hashicorp/template"] 7993 template = Hi 7994 type = template_file 7995 `) 7996 if actual != expected { 7997 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected) 7998 } 7999 } 8000 8001 func TestContext2Apply_targetedWithTaintedInState(t *testing.T) { 8002 p := testProvider("aws") 8003 p.PlanResourceChangeFn = testDiffFn 8004 p.ApplyResourceChangeFn = testApplyFn 8005 m, snap := testModuleWithSnapshot(t, "apply-tainted-targets") 8006 8007 state := states.NewState() 8008 root := state.EnsureModule(addrs.RootModuleInstance) 8009 root.SetResourceInstanceCurrent( 8010 mustResourceInstanceAddr("aws_instance.ifailedprovisioners").Resource, 8011 &states.ResourceInstanceObjectSrc{ 8012 Status: states.ObjectTainted, 8013 AttrsJSON: []byte(`{"id":"ifailedprovisioners"}`), 8014 }, 8015 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8016 ) 8017 8018 ctx := testContext2(t, &ContextOpts{ 8019 Providers: map[addrs.Provider]providers.Factory{ 8020 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8021 }, 8022 }) 8023 8024 plan, diags := ctx.Plan(m, state, &PlanOpts{ 8025 Mode: plans.NormalMode, 8026 Targets: []addrs.Targetable{ 8027 addrs.RootModuleInstance.Resource( 8028 addrs.ManagedResourceMode, "aws_instance", "iambeingadded", 8029 ), 8030 }, 8031 }) 8032 if diags.HasErrors() { 8033 t.Fatalf("err: %s", diags.Err()) 8034 } 8035 8036 // Write / Read plan to simulate running it through a Plan file 8037 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 8038 if err != nil { 8039 t.Fatalf("failed to round-trip through planfile: %s", err) 8040 } 8041 8042 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 8043 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8044 } 8045 8046 ctx, diags = NewContext(ctxOpts) 8047 if diags.HasErrors() { 8048 t.Fatalf("err: %s", diags.Err()) 8049 } 8050 8051 s, diags := ctx.Apply(plan, m) 8052 if diags.HasErrors() { 8053 t.Fatalf("err: %s", diags.Err()) 8054 } 8055 8056 actual := strings.TrimSpace(s.String()) 8057 expected := strings.TrimSpace(` 8058 aws_instance.iambeingadded: 8059 ID = foo 8060 provider = provider["registry.terraform.io/hashicorp/aws"] 8061 type = aws_instance 8062 aws_instance.ifailedprovisioners: (tainted) 8063 ID = ifailedprovisioners 8064 provider = provider["registry.terraform.io/hashicorp/aws"] 8065 `) 8066 if actual != expected { 8067 t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual) 8068 } 8069 } 8070 8071 // Higher level test exposing the bug this covers in 8072 // TestResource_ignoreChangesRequired 8073 func TestContext2Apply_ignoreChangesCreate(t *testing.T) { 8074 m := testModule(t, "apply-ignore-changes-create") 8075 p := testProvider("aws") 8076 p.PlanResourceChangeFn = testDiffFn 8077 p.ApplyResourceChangeFn = testApplyFn 8078 8079 instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block 8080 instanceSchema.Attributes["required_field"] = &configschema.Attribute{ 8081 Type: cty.String, 8082 Required: true, 8083 } 8084 8085 ctx := testContext2(t, &ContextOpts{ 8086 Providers: map[addrs.Provider]providers.Factory{ 8087 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8088 }, 8089 }) 8090 8091 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8092 if diags.HasErrors() { 8093 t.Fatalf("diags: %s", diags.Err()) 8094 } else { 8095 t.Logf(legacyDiffComparisonString(plan.Changes)) 8096 } 8097 8098 state, diags := ctx.Apply(plan, m) 8099 if diags.HasErrors() { 8100 t.Fatalf("diags: %s", diags.Err()) 8101 } 8102 8103 mod := state.RootModule() 8104 if len(mod.Resources) != 1 { 8105 t.Fatalf("bad: %s", state) 8106 } 8107 8108 actual := strings.TrimSpace(state.String()) 8109 // Expect no changes from original state 8110 expected := strings.TrimSpace(` 8111 aws_instance.foo: 8112 ID = foo 8113 provider = provider["registry.terraform.io/hashicorp/aws"] 8114 required_field = set 8115 type = aws_instance 8116 `) 8117 if actual != expected { 8118 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 8119 } 8120 } 8121 8122 func TestContext2Apply_ignoreChangesWithDep(t *testing.T) { 8123 m := testModule(t, "apply-ignore-changes-dep") 8124 p := testProvider("aws") 8125 8126 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 8127 resp.PlannedState = req.ProposedNewState 8128 8129 switch req.TypeName { 8130 case "aws_instance": 8131 resp.RequiresReplace = append(resp.RequiresReplace, cty.Path{cty.GetAttrStep{Name: "ami"}}) 8132 case "aws_eip": 8133 return testDiffFn(req) 8134 default: 8135 t.Fatalf("Unexpected type: %s", req.TypeName) 8136 } 8137 return 8138 } 8139 8140 state := states.NewState() 8141 root := state.EnsureModule(addrs.RootModuleInstance) 8142 root.SetResourceInstanceCurrent( 8143 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 8144 &states.ResourceInstanceObjectSrc{ 8145 Status: states.ObjectReady, 8146 AttrsJSON: []byte(`{"id":"i-abc123","ami":"ami-abcd1234"}`), 8147 }, 8148 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8149 ) 8150 root.SetResourceInstanceCurrent( 8151 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 8152 &states.ResourceInstanceObjectSrc{ 8153 Status: states.ObjectReady, 8154 AttrsJSON: []byte(`{"id":"i-bcd234","ami":"i-bcd234"}`), 8155 }, 8156 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8157 ) 8158 root.SetResourceInstanceCurrent( 8159 mustResourceInstanceAddr("aws_eip.foo[0]").Resource, 8160 &states.ResourceInstanceObjectSrc{ 8161 Status: states.ObjectReady, 8162 AttrsJSON: []byte(`{"id":"eip-abc123","instance":"i-abc123"}`), 8163 Dependencies: []addrs.ConfigResource{ 8164 { 8165 Resource: addrs.Resource{ 8166 Mode: addrs.ManagedResourceMode, 8167 Type: "aws_instance", 8168 Name: "foo", 8169 }, 8170 Module: addrs.RootModule, 8171 }, 8172 }, 8173 }, 8174 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8175 ) 8176 root.SetResourceInstanceCurrent( 8177 mustResourceInstanceAddr("aws_eip.foo[1]").Resource, 8178 &states.ResourceInstanceObjectSrc{ 8179 Status: states.ObjectReady, 8180 AttrsJSON: []byte(`{"id":"eip-bcd234","instance":"i-bcd234"}`), 8181 Dependencies: []addrs.ConfigResource{ 8182 { 8183 Resource: addrs.Resource{ 8184 Mode: addrs.ManagedResourceMode, 8185 Type: "aws_instance", 8186 Name: "foo", 8187 }, 8188 Module: addrs.RootModule, 8189 }, 8190 }, 8191 }, 8192 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8193 ) 8194 8195 ctx := testContext2(t, &ContextOpts{ 8196 Providers: map[addrs.Provider]providers.Factory{ 8197 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8198 }, 8199 }) 8200 8201 plan, diags := ctx.Plan(m, state.DeepCopy(), DefaultPlanOpts) 8202 assertNoErrors(t, diags) 8203 8204 s, diags := ctx.Apply(plan, m) 8205 assertNoErrors(t, diags) 8206 8207 actual := strings.TrimSpace(s.String()) 8208 expected := strings.TrimSpace(state.String()) 8209 if actual != expected { 8210 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 8211 } 8212 } 8213 8214 func TestContext2Apply_ignoreChangesAll(t *testing.T) { 8215 m := testModule(t, "apply-ignore-changes-all") 8216 p := testProvider("aws") 8217 p.PlanResourceChangeFn = testDiffFn 8218 p.ApplyResourceChangeFn = testApplyFn 8219 8220 instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block 8221 instanceSchema.Attributes["required_field"] = &configschema.Attribute{ 8222 Type: cty.String, 8223 Required: true, 8224 } 8225 8226 ctx := testContext2(t, &ContextOpts{ 8227 Providers: map[addrs.Provider]providers.Factory{ 8228 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8229 }, 8230 }) 8231 8232 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8233 if diags.HasErrors() { 8234 logDiagnostics(t, diags) 8235 t.Fatal("plan failed") 8236 } else { 8237 t.Logf(legacyDiffComparisonString(plan.Changes)) 8238 } 8239 8240 state, diags := ctx.Apply(plan, m) 8241 assertNoErrors(t, diags) 8242 8243 mod := state.RootModule() 8244 if len(mod.Resources) != 1 { 8245 t.Fatalf("bad: %s", state) 8246 } 8247 8248 actual := strings.TrimSpace(state.String()) 8249 // Expect no changes from original state 8250 expected := strings.TrimSpace(` 8251 aws_instance.foo: 8252 ID = foo 8253 provider = provider["registry.terraform.io/hashicorp/aws"] 8254 required_field = set 8255 type = aws_instance 8256 `) 8257 if actual != expected { 8258 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 8259 } 8260 } 8261 8262 // https://github.com/muratcelep/terraform/issues/7378 8263 func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testing.T) { 8264 m, snap := testModuleWithSnapshot(t, "apply-destroy-nested-module-with-attrs") 8265 p := testProvider("null") 8266 p.PlanResourceChangeFn = testDiffFn 8267 8268 var state *states.State 8269 { 8270 ctx := testContext2(t, &ContextOpts{ 8271 Providers: map[addrs.Provider]providers.Factory{ 8272 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8273 }, 8274 }) 8275 8276 // First plan and apply a create operation 8277 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8278 assertNoErrors(t, diags) 8279 8280 state, diags = ctx.Apply(plan, m) 8281 if diags.HasErrors() { 8282 t.Fatalf("apply err: %s", diags.Err()) 8283 } 8284 } 8285 8286 { 8287 ctx := testContext2(t, &ContextOpts{ 8288 Providers: map[addrs.Provider]providers.Factory{ 8289 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8290 }, 8291 }) 8292 8293 plan, diags := ctx.Plan(m, state, &PlanOpts{ 8294 Mode: plans.DestroyMode, 8295 }) 8296 if diags.HasErrors() { 8297 t.Fatalf("destroy plan err: %s", diags.Err()) 8298 } 8299 8300 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 8301 if err != nil { 8302 t.Fatalf("failed to round-trip through planfile: %s", err) 8303 } 8304 8305 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 8306 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8307 } 8308 8309 ctx, diags = NewContext(ctxOpts) 8310 if diags.HasErrors() { 8311 t.Fatalf("err: %s", diags.Err()) 8312 } 8313 8314 state, diags = ctx.Apply(plan, m) 8315 if diags.HasErrors() { 8316 t.Fatalf("destroy apply err: %s", diags.Err()) 8317 } 8318 } 8319 8320 if !state.Empty() { 8321 t.Fatalf("state after apply: %s\nwant empty state", spew.Sdump(state)) 8322 } 8323 } 8324 8325 // If a data source explicitly depends on another resource, it's because we need 8326 // that resource to be applied first. 8327 func TestContext2Apply_dataDependsOn(t *testing.T) { 8328 p := testProvider("null") 8329 m := testModuleInline(t, map[string]string{ 8330 "main.tf": ` 8331 resource "null_instance" "write" { 8332 foo = "attribute" 8333 } 8334 8335 data "null_data_source" "read" { 8336 count = 1 8337 depends_on = ["null_instance.write"] 8338 } 8339 8340 resource "null_instance" "depends" { 8341 foo = data.null_data_source.read[0].foo 8342 } 8343 `}) 8344 8345 ctx := testContext2(t, &ContextOpts{ 8346 Providers: map[addrs.Provider]providers.Factory{ 8347 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8348 }, 8349 }) 8350 8351 // the "provisioner" here writes to this variable, because the intent is to 8352 // create a dependency which can't be viewed through the graph, and depends 8353 // solely on the configuration providing "depends_on" 8354 provisionerOutput := "" 8355 8356 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 8357 // the side effect of the resource being applied 8358 provisionerOutput = "APPLIED" 8359 return testApplyFn(req) 8360 } 8361 8362 p.PlanResourceChangeFn = testDiffFn 8363 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 8364 return providers.ReadDataSourceResponse{ 8365 State: cty.ObjectVal(map[string]cty.Value{ 8366 "id": cty.StringVal("boop"), 8367 "foo": cty.StringVal(provisionerOutput), 8368 }), 8369 } 8370 } 8371 8372 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8373 assertNoErrors(t, diags) 8374 8375 state, diags := ctx.Apply(plan, m) 8376 assertNoErrors(t, diags) 8377 8378 root := state.Module(addrs.RootModuleInstance) 8379 is := root.ResourceInstance(addrs.Resource{ 8380 Mode: addrs.DataResourceMode, 8381 Type: "null_data_source", 8382 Name: "read", 8383 }.Instance(addrs.IntKey(0))) 8384 if is == nil { 8385 t.Fatal("data resource instance is not present in state; should be") 8386 } 8387 var attrs map[string]interface{} 8388 err := json.Unmarshal(is.Current.AttrsJSON, &attrs) 8389 if err != nil { 8390 t.Fatal(err) 8391 } 8392 actual := attrs["foo"] 8393 expected := "APPLIED" 8394 if actual != expected { 8395 t.Fatalf("bad:\n%s", strings.TrimSpace(state.String())) 8396 } 8397 8398 // run another plan to make sure the data source doesn't show as a change 8399 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 8400 assertNoErrors(t, diags) 8401 8402 for _, c := range plan.Changes.Resources { 8403 if c.Action != plans.NoOp { 8404 t.Fatalf("unexpected change for %s", c.Addr) 8405 } 8406 } 8407 8408 // now we cause a change in the first resource, which should trigger a plan 8409 // in the data source, and the resource that depends on the data source 8410 // must plan a change as well. 8411 m = testModuleInline(t, map[string]string{ 8412 "main.tf": ` 8413 resource "null_instance" "write" { 8414 foo = "new" 8415 } 8416 8417 data "null_data_source" "read" { 8418 depends_on = ["null_instance.write"] 8419 } 8420 8421 resource "null_instance" "depends" { 8422 foo = data.null_data_source.read.foo 8423 } 8424 `}) 8425 8426 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 8427 // the side effect of the resource being applied 8428 provisionerOutput = "APPLIED_AGAIN" 8429 return testApplyFn(req) 8430 } 8431 8432 ctx = testContext2(t, &ContextOpts{ 8433 Providers: map[addrs.Provider]providers.Factory{ 8434 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8435 }, 8436 }) 8437 8438 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 8439 assertNoErrors(t, diags) 8440 8441 expectedChanges := map[string]plans.Action{ 8442 "null_instance.write": plans.Update, 8443 "data.null_data_source.read": plans.Read, 8444 "null_instance.depends": plans.Update, 8445 } 8446 8447 for _, c := range plan.Changes.Resources { 8448 if c.Action != expectedChanges[c.Addr.String()] { 8449 t.Errorf("unexpected %s for %s", c.Action, c.Addr) 8450 } 8451 } 8452 } 8453 8454 func TestContext2Apply_terraformWorkspace(t *testing.T) { 8455 m := testModule(t, "apply-terraform-workspace") 8456 p := testProvider("aws") 8457 p.PlanResourceChangeFn = testDiffFn 8458 8459 ctx := testContext2(t, &ContextOpts{ 8460 Meta: &ContextMeta{Env: "foo"}, 8461 Providers: map[addrs.Provider]providers.Factory{ 8462 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8463 }, 8464 }) 8465 8466 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8467 assertNoErrors(t, diags) 8468 8469 state, diags := ctx.Apply(plan, m) 8470 if diags.HasErrors() { 8471 t.Fatalf("diags: %s", diags.Err()) 8472 } 8473 8474 actual := state.RootModule().OutputValues["output"] 8475 expected := cty.StringVal("foo") 8476 if actual == nil || actual.Value != expected { 8477 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 8478 } 8479 } 8480 8481 // verify that multiple config references only create a single depends_on entry 8482 func TestContext2Apply_multiRef(t *testing.T) { 8483 m := testModule(t, "apply-multi-ref") 8484 p := testProvider("aws") 8485 p.PlanResourceChangeFn = testDiffFn 8486 ctx := testContext2(t, &ContextOpts{ 8487 Providers: map[addrs.Provider]providers.Factory{ 8488 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8489 }, 8490 }) 8491 8492 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8493 assertNoErrors(t, diags) 8494 8495 state, diags := ctx.Apply(plan, m) 8496 if diags.HasErrors() { 8497 t.Fatalf("err: %s", diags.Err()) 8498 } 8499 8500 deps := state.Modules[""].Resources["aws_instance.other"].Instances[addrs.NoKey].Current.Dependencies 8501 if len(deps) != 1 || deps[0].String() != "aws_instance.create" { 8502 t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps) 8503 } 8504 } 8505 8506 func TestContext2Apply_targetedModuleRecursive(t *testing.T) { 8507 m := testModule(t, "apply-targeted-module-recursive") 8508 p := testProvider("aws") 8509 p.PlanResourceChangeFn = testDiffFn 8510 p.ApplyResourceChangeFn = testApplyFn 8511 ctx := testContext2(t, &ContextOpts{ 8512 Providers: map[addrs.Provider]providers.Factory{ 8513 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8514 }, 8515 }) 8516 8517 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 8518 Mode: plans.NormalMode, 8519 Targets: []addrs.Targetable{ 8520 addrs.RootModuleInstance.Child("child", addrs.NoKey), 8521 }, 8522 }) 8523 assertNoErrors(t, diags) 8524 8525 state, diags := ctx.Apply(plan, m) 8526 if diags.HasErrors() { 8527 t.Fatalf("err: %s", diags.Err()) 8528 } 8529 8530 mod := state.Module( 8531 addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("subchild", addrs.NoKey), 8532 ) 8533 if mod == nil { 8534 t.Fatalf("no subchild module found in the state!\n\n%#v", state) 8535 } 8536 if len(mod.Resources) != 1 { 8537 t.Fatalf("expected 1 resources, got: %#v", mod.Resources) 8538 } 8539 8540 checkStateString(t, state, ` 8541 <no state> 8542 module.child.subchild: 8543 aws_instance.foo: 8544 ID = foo 8545 provider = provider["registry.terraform.io/hashicorp/aws"] 8546 num = 2 8547 type = aws_instance 8548 `) 8549 } 8550 8551 func TestContext2Apply_localVal(t *testing.T) { 8552 m := testModule(t, "apply-local-val") 8553 ctx := testContext2(t, &ContextOpts{ 8554 Providers: map[addrs.Provider]providers.Factory{}, 8555 }) 8556 8557 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8558 assertNoErrors(t, diags) 8559 8560 state, diags := ctx.Apply(plan, m) 8561 if diags.HasErrors() { 8562 t.Fatalf("error during apply: %s", diags.Err()) 8563 } 8564 8565 got := strings.TrimSpace(state.String()) 8566 want := strings.TrimSpace(` 8567 <no state> 8568 Outputs: 8569 8570 result_1 = hello 8571 result_3 = hello world 8572 `) 8573 if got != want { 8574 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 8575 } 8576 } 8577 8578 func TestContext2Apply_destroyWithLocals(t *testing.T) { 8579 m := testModule(t, "apply-destroy-with-locals") 8580 p := testProvider("aws") 8581 p.PlanResourceChangeFn = testDiffFn 8582 8583 state := states.NewState() 8584 root := state.EnsureModule(addrs.RootModuleInstance) 8585 root.SetResourceInstanceCurrent( 8586 mustResourceInstanceAddr("aws_instance.foo").Resource, 8587 &states.ResourceInstanceObjectSrc{ 8588 Status: states.ObjectReady, 8589 AttrsJSON: []byte(`{"id":"foo"}`), 8590 }, 8591 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8592 ) 8593 root.SetOutputValue("name", cty.StringVal("test-bar"), false) 8594 8595 ctx := testContext2(t, &ContextOpts{ 8596 Providers: map[addrs.Provider]providers.Factory{ 8597 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8598 }, 8599 }) 8600 8601 plan, diags := ctx.Plan(m, state, &PlanOpts{ 8602 Mode: plans.DestroyMode, 8603 }) 8604 assertNoErrors(t, diags) 8605 8606 s, diags := ctx.Apply(plan, m) 8607 if diags.HasErrors() { 8608 t.Fatalf("error during apply: %s", diags.Err()) 8609 } 8610 8611 got := strings.TrimSpace(s.String()) 8612 want := strings.TrimSpace(`<no state>`) 8613 if got != want { 8614 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 8615 } 8616 } 8617 8618 func TestContext2Apply_providerWithLocals(t *testing.T) { 8619 m := testModule(t, "provider-with-locals") 8620 p := testProvider("aws") 8621 8622 providerRegion := "" 8623 // this should not be overridden during destroy 8624 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 8625 val := req.Config.GetAttr("region") 8626 if !val.IsNull() { 8627 providerRegion = val.AsString() 8628 } 8629 8630 return 8631 } 8632 8633 p.PlanResourceChangeFn = testDiffFn 8634 ctx := testContext2(t, &ContextOpts{ 8635 Providers: map[addrs.Provider]providers.Factory{ 8636 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8637 }, 8638 }) 8639 8640 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 8641 assertNoErrors(t, diags) 8642 8643 state, diags := ctx.Apply(plan, m) 8644 if diags.HasErrors() { 8645 t.Fatalf("err: %s", diags.Err()) 8646 } 8647 8648 ctx = testContext2(t, &ContextOpts{ 8649 Providers: map[addrs.Provider]providers.Factory{ 8650 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8651 }, 8652 }) 8653 8654 plan, diags = ctx.Plan(m, state, &PlanOpts{ 8655 Mode: plans.DestroyMode, 8656 }) 8657 assertNoErrors(t, diags) 8658 8659 state, diags = ctx.Apply(plan, m) 8660 if diags.HasErrors() { 8661 t.Fatalf("err: %s", diags.Err()) 8662 } 8663 8664 if state.HasManagedResourceInstanceObjects() { 8665 t.Fatal("expected no state, got:", state) 8666 } 8667 8668 if providerRegion != "bar" { 8669 t.Fatalf("expected region %q, got: %q", "bar", providerRegion) 8670 } 8671 } 8672 8673 func TestContext2Apply_destroyWithProviders(t *testing.T) { 8674 m := testModule(t, "destroy-module-with-provider") 8675 p := testProvider("aws") 8676 p.PlanResourceChangeFn = testDiffFn 8677 8678 state := states.NewState() 8679 removed := state.EnsureModule(addrs.RootModuleInstance.Child("mod", addrs.NoKey).Child("removed", addrs.NoKey)) 8680 removed.SetResourceInstanceCurrent( 8681 mustResourceInstanceAddr("aws_instance.child").Resource, 8682 &states.ResourceInstanceObjectSrc{ 8683 Status: states.ObjectReady, 8684 AttrsJSON: []byte(`{"id":"bar"}`), 8685 }, 8686 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].baz`), 8687 ) 8688 8689 ctx := testContext2(t, &ContextOpts{ 8690 Providers: map[addrs.Provider]providers.Factory{ 8691 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8692 }, 8693 }) 8694 8695 // test that we can't destroy if the provider is missing 8696 if _, diags := ctx.Plan(m, state, &PlanOpts{Mode: plans.DestroyMode}); diags == nil { 8697 t.Fatal("expected plan error, provider.aws.baz doesn't exist") 8698 } 8699 8700 // correct the state 8701 state.Modules["module.mod.module.removed"].Resources["aws_instance.child"].ProviderConfig = mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].bar`) 8702 8703 ctx = testContext2(t, &ContextOpts{ 8704 Providers: map[addrs.Provider]providers.Factory{ 8705 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8706 }, 8707 }) 8708 8709 plan, diags := ctx.Plan(m, state, &PlanOpts{ 8710 Mode: plans.DestroyMode, 8711 }) 8712 assertNoErrors(t, diags) 8713 8714 state, diags = ctx.Apply(plan, m) 8715 if diags.HasErrors() { 8716 t.Fatalf("error during apply: %s", diags.Err()) 8717 } 8718 8719 got := strings.TrimSpace(state.String()) 8720 8721 want := strings.TrimSpace("<no state>") 8722 if got != want { 8723 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 8724 } 8725 } 8726 8727 func TestContext2Apply_providersFromState(t *testing.T) { 8728 m := configs.NewEmptyConfig() 8729 p := testProvider("aws") 8730 p.PlanResourceChangeFn = testDiffFn 8731 8732 implicitProviderState := states.NewState() 8733 impRoot := implicitProviderState.EnsureModule(addrs.RootModuleInstance) 8734 impRoot.SetResourceInstanceCurrent( 8735 mustResourceInstanceAddr("aws_instance.a").Resource, 8736 &states.ResourceInstanceObjectSrc{ 8737 Status: states.ObjectReady, 8738 AttrsJSON: []byte(`{"id":"bar"}`), 8739 }, 8740 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8741 ) 8742 8743 aliasedProviderState := states.NewState() 8744 aliasRoot := aliasedProviderState.EnsureModule(addrs.RootModuleInstance) 8745 aliasRoot.SetResourceInstanceCurrent( 8746 mustResourceInstanceAddr("aws_instance.a").Resource, 8747 &states.ResourceInstanceObjectSrc{ 8748 Status: states.ObjectReady, 8749 AttrsJSON: []byte(`{"id":"bar"}`), 8750 }, 8751 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].bar`), 8752 ) 8753 8754 moduleProviderState := states.NewState() 8755 moduleProviderRoot := moduleProviderState.EnsureModule(addrs.RootModuleInstance) 8756 moduleProviderRoot.SetResourceInstanceCurrent( 8757 mustResourceInstanceAddr("aws_instance.a").Resource, 8758 &states.ResourceInstanceObjectSrc{ 8759 Status: states.ObjectReady, 8760 AttrsJSON: []byte(`{"id":"bar"}`), 8761 }, 8762 mustProviderConfig(`module.child.provider["registry.terraform.io/hashicorp/aws"]`), 8763 ) 8764 8765 for _, tc := range []struct { 8766 name string 8767 state *states.State 8768 output string 8769 err bool 8770 }{ 8771 { 8772 name: "add implicit provider", 8773 state: implicitProviderState, 8774 err: false, 8775 output: "<no state>", 8776 }, 8777 8778 // an aliased provider must be in the config to remove a resource 8779 { 8780 name: "add aliased provider", 8781 state: aliasedProviderState, 8782 err: true, 8783 }, 8784 8785 // a provider in a module implies some sort of config, so this isn't 8786 // allowed even without an alias 8787 { 8788 name: "add unaliased module provider", 8789 state: moduleProviderState, 8790 err: true, 8791 }, 8792 } { 8793 t.Run(tc.name, func(t *testing.T) { 8794 ctx := testContext2(t, &ContextOpts{ 8795 Providers: map[addrs.Provider]providers.Factory{ 8796 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8797 }, 8798 }) 8799 8800 plan, diags := ctx.Plan(m, tc.state, DefaultPlanOpts) 8801 if tc.err { 8802 if diags == nil { 8803 t.Fatal("expected error") 8804 } else { 8805 return 8806 } 8807 } 8808 if !tc.err && diags.HasErrors() { 8809 t.Fatal(diags.Err()) 8810 } 8811 8812 state, diags := ctx.Apply(plan, m) 8813 if diags.HasErrors() { 8814 t.Fatalf("diags: %s", diags.Err()) 8815 } 8816 8817 checkStateString(t, state, "<no state>") 8818 8819 }) 8820 } 8821 } 8822 8823 func TestContext2Apply_plannedInterpolatedCount(t *testing.T) { 8824 m, snap := testModuleWithSnapshot(t, "apply-interpolated-count") 8825 8826 p := testProvider("aws") 8827 p.PlanResourceChangeFn = testDiffFn 8828 8829 Providers := map[addrs.Provider]providers.Factory{ 8830 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8831 } 8832 8833 state := states.NewState() 8834 root := state.EnsureModule(addrs.RootModuleInstance) 8835 root.SetResourceInstanceCurrent( 8836 mustResourceInstanceAddr("aws_instance.test").Resource, 8837 &states.ResourceInstanceObjectSrc{ 8838 Status: states.ObjectReady, 8839 AttrsJSON: []byte(`{"id":"foo"}`), 8840 }, 8841 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8842 ) 8843 8844 ctx := testContext2(t, &ContextOpts{ 8845 Providers: Providers, 8846 }) 8847 8848 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 8849 if diags.HasErrors() { 8850 t.Fatalf("plan failed: %s", diags.Err()) 8851 } 8852 8853 // We'll marshal and unmarshal the plan here, to ensure that we have 8854 // a clean new context as would be created if we separately ran 8855 // terraform plan -out=tfplan && terraform apply tfplan 8856 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 8857 if err != nil { 8858 t.Fatalf("failed to round-trip through planfile: %s", err) 8859 } 8860 8861 ctxOpts.Providers = Providers 8862 ctx, diags = NewContext(ctxOpts) 8863 if diags.HasErrors() { 8864 t.Fatalf("err: %s", diags.Err()) 8865 } 8866 8867 // Applying the plan should now succeed 8868 _, diags = ctx.Apply(plan, m) 8869 if diags.HasErrors() { 8870 t.Fatalf("apply failed: %s", diags.Err()) 8871 } 8872 } 8873 8874 func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) { 8875 m, snap := testModuleWithSnapshot(t, "plan-destroy-interpolated-count") 8876 8877 p := testProvider("aws") 8878 p.PlanResourceChangeFn = testDiffFn 8879 providers := map[addrs.Provider]providers.Factory{ 8880 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8881 } 8882 8883 state := states.NewState() 8884 root := state.EnsureModule(addrs.RootModuleInstance) 8885 root.SetResourceInstanceCurrent( 8886 mustResourceInstanceAddr("aws_instance.a[0]").Resource, 8887 &states.ResourceInstanceObjectSrc{ 8888 Status: states.ObjectReady, 8889 AttrsJSON: []byte(`{"id":"foo"}`), 8890 }, 8891 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8892 ) 8893 root.SetResourceInstanceCurrent( 8894 mustResourceInstanceAddr("aws_instance.a[1]").Resource, 8895 &states.ResourceInstanceObjectSrc{ 8896 Status: states.ObjectReady, 8897 AttrsJSON: []byte(`{"id":"foo"}`), 8898 }, 8899 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8900 ) 8901 root.SetOutputValue("out", cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("foo")}), false) 8902 8903 ctx := testContext2(t, &ContextOpts{ 8904 Providers: providers, 8905 }) 8906 8907 plan, diags := ctx.Plan(m, state, &PlanOpts{ 8908 Mode: plans.DestroyMode, 8909 }) 8910 if diags.HasErrors() { 8911 t.Fatalf("plan failed: %s", diags.Err()) 8912 } 8913 8914 // We'll marshal and unmarshal the plan here, to ensure that we have 8915 // a clean new context as would be created if we separately ran 8916 // terraform plan -out=tfplan && terraform apply tfplan 8917 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 8918 if err != nil { 8919 t.Fatalf("failed to round-trip through planfile: %s", err) 8920 } 8921 8922 ctxOpts.Providers = providers 8923 ctx, diags = NewContext(ctxOpts) 8924 if diags.HasErrors() { 8925 t.Fatalf("err: %s", diags.Err()) 8926 } 8927 8928 // Applying the plan should now succeed 8929 state, diags = ctx.Apply(plan, m) 8930 if diags.HasErrors() { 8931 t.Fatalf("apply failed: %s", diags.Err()) 8932 } 8933 if !state.Empty() { 8934 t.Fatalf("state not empty: %s\n", state) 8935 } 8936 } 8937 8938 func TestContext2Apply_scaleInMultivarRef(t *testing.T) { 8939 m := testModule(t, "apply-resource-scale-in") 8940 8941 p := testProvider("aws") 8942 p.PlanResourceChangeFn = testDiffFn 8943 8944 Providers := map[addrs.Provider]providers.Factory{ 8945 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8946 } 8947 8948 state := states.NewState() 8949 root := state.EnsureModule(addrs.RootModuleInstance) 8950 root.SetResourceInstanceCurrent( 8951 mustResourceInstanceAddr("aws_instance.one").Resource, 8952 &states.ResourceInstanceObjectSrc{ 8953 Status: states.ObjectReady, 8954 AttrsJSON: []byte(`{"id":"foo"}`), 8955 }, 8956 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8957 ) 8958 root.SetResourceInstanceCurrent( 8959 mustResourceInstanceAddr("aws_instance.two").Resource, 8960 &states.ResourceInstanceObjectSrc{ 8961 Status: states.ObjectReady, 8962 AttrsJSON: []byte(`{"id":"foo"}`), 8963 }, 8964 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8965 ) 8966 8967 ctx := testContext2(t, &ContextOpts{ 8968 Providers: Providers, 8969 }) 8970 8971 plan, diags := ctx.Plan(m, state, &PlanOpts{ 8972 Mode: plans.NormalMode, 8973 SetVariables: InputValues{ 8974 "instance_count": { 8975 Value: cty.NumberIntVal(0), 8976 SourceType: ValueFromCaller, 8977 }, 8978 }, 8979 }) 8980 assertNoErrors(t, diags) 8981 { 8982 addr := mustResourceInstanceAddr("aws_instance.one[0]") 8983 change := plan.Changes.ResourceInstance(addr) 8984 if change == nil { 8985 t.Fatalf("no planned change for %s", addr) 8986 } 8987 // This test was originally written with Terraform v0.11 and earlier 8988 // in mind, so it declares a no-key instance of aws_instance.one, 8989 // but its configuration sets count (to zero) and so we end up first 8990 // moving the no-key instance to the zero key and then planning to 8991 // destroy the zero key. 8992 if got, want := change.PrevRunAddr, mustResourceInstanceAddr("aws_instance.one"); !want.Equal(got) { 8993 t.Errorf("wrong previous run address for %s %s; want %s", addr, got, want) 8994 } 8995 if got, want := change.Action, plans.Delete; got != want { 8996 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 8997 } 8998 if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseCountIndex; got != want { 8999 t.Errorf("wrong action reason for %s %s; want %s", addr, got, want) 9000 } 9001 } 9002 { 9003 addr := mustResourceInstanceAddr("aws_instance.two") 9004 change := plan.Changes.ResourceInstance(addr) 9005 if change == nil { 9006 t.Fatalf("no planned change for %s", addr) 9007 } 9008 if got, want := change.PrevRunAddr, mustResourceInstanceAddr("aws_instance.two"); !want.Equal(got) { 9009 t.Errorf("wrong previous run address for %s %s; want %s", addr, got, want) 9010 } 9011 if got, want := change.Action, plans.Update; got != want { 9012 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 9013 } 9014 if got, want := change.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { 9015 t.Errorf("wrong action reason for %s %s; want %s", addr, got, want) 9016 } 9017 } 9018 9019 // Applying the plan should now succeed 9020 _, diags = ctx.Apply(plan, m) 9021 assertNoErrors(t, diags) 9022 } 9023 9024 func TestContext2Apply_inconsistentWithPlan(t *testing.T) { 9025 m := testModule(t, "apply-inconsistent-with-plan") 9026 p := testProvider("test") 9027 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9028 ResourceTypes: map[string]*configschema.Block{ 9029 "test": { 9030 Attributes: map[string]*configschema.Attribute{ 9031 "id": {Type: cty.String, Computed: true}, 9032 }, 9033 }, 9034 }, 9035 }) 9036 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 9037 return providers.PlanResourceChangeResponse{ 9038 PlannedState: cty.ObjectVal(map[string]cty.Value{ 9039 "id": cty.StringVal("before"), 9040 }), 9041 } 9042 } 9043 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 9044 return providers.ApplyResourceChangeResponse{ 9045 NewState: cty.ObjectVal(map[string]cty.Value{ 9046 // This is intentionally incorrect: because id was fixed at "before" 9047 // during plan, it must not change during apply. 9048 "id": cty.StringVal("after"), 9049 }), 9050 } 9051 } 9052 ctx := testContext2(t, &ContextOpts{ 9053 Providers: map[addrs.Provider]providers.Factory{ 9054 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9055 }, 9056 }) 9057 9058 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 9059 assertNoErrors(t, diags) 9060 9061 _, diags = ctx.Apply(plan, m) 9062 if !diags.HasErrors() { 9063 t.Fatalf("apply succeeded; want error") 9064 } 9065 if got, want := diags.Err().Error(), "Provider produced inconsistent result after apply"; !strings.Contains(got, want) { 9066 t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want) 9067 } 9068 } 9069 9070 // Issue 19908 was about retaining an existing object in the state when an 9071 // update to it fails and the provider does not return a partially-updated 9072 // value for it. Previously we were incorrectly removing it from the state 9073 // in that case, but instead it should be retained so the update can be 9074 // retried. 9075 func TestContext2Apply_issue19908(t *testing.T) { 9076 m := testModule(t, "apply-issue19908") 9077 p := testProvider("test") 9078 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9079 ResourceTypes: map[string]*configschema.Block{ 9080 "test": { 9081 Attributes: map[string]*configschema.Attribute{ 9082 "baz": {Type: cty.String, Required: true}, 9083 }, 9084 }, 9085 }, 9086 }) 9087 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 9088 return providers.PlanResourceChangeResponse{ 9089 PlannedState: req.ProposedNewState, 9090 } 9091 } 9092 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 9093 var diags tfdiags.Diagnostics 9094 diags = diags.Append(fmt.Errorf("update failed")) 9095 return providers.ApplyResourceChangeResponse{ 9096 Diagnostics: diags, 9097 } 9098 } 9099 ctx := testContext2(t, &ContextOpts{ 9100 Providers: map[addrs.Provider]providers.Factory{ 9101 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9102 }, 9103 }) 9104 9105 state := states.BuildState(func(s *states.SyncState) { 9106 s.SetResourceInstanceCurrent( 9107 addrs.Resource{ 9108 Mode: addrs.ManagedResourceMode, 9109 Type: "test", 9110 Name: "foo", 9111 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 9112 &states.ResourceInstanceObjectSrc{ 9113 AttrsJSON: []byte(`{"baz":"old"}`), 9114 Status: states.ObjectReady, 9115 }, 9116 addrs.AbsProviderConfig{ 9117 Provider: addrs.NewDefaultProvider("test"), 9118 Module: addrs.RootModule, 9119 }, 9120 ) 9121 }) 9122 9123 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 9124 assertNoErrors(t, diags) 9125 9126 state, diags = ctx.Apply(plan, m) 9127 if !diags.HasErrors() { 9128 t.Fatalf("apply succeeded; want error") 9129 } 9130 if got, want := diags.Err().Error(), "update failed"; !strings.Contains(got, want) { 9131 t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want) 9132 } 9133 9134 mod := state.RootModule() 9135 rs := mod.Resources["test.foo"] 9136 if rs == nil { 9137 t.Fatalf("test.foo not in state after apply, but should be") 9138 } 9139 is := rs.Instances[addrs.NoKey] 9140 if is == nil { 9141 t.Fatalf("test.foo not in state after apply, but should be") 9142 } 9143 obj := is.Current 9144 if obj == nil { 9145 t.Fatalf("test.foo has no current object in state after apply, but should do") 9146 } 9147 9148 if got, want := obj.Status, states.ObjectReady; got != want { 9149 t.Errorf("test.foo has wrong status %s after apply; want %s", got, want) 9150 } 9151 if got, want := obj.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) { 9152 t.Errorf("test.foo attributes JSON doesn't contain %s after apply\ngot: %s", want, got) 9153 } 9154 } 9155 9156 func TestContext2Apply_invalidIndexRef(t *testing.T) { 9157 p := testProvider("test") 9158 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9159 ResourceTypes: map[string]*configschema.Block{ 9160 "test_instance": { 9161 Attributes: map[string]*configschema.Attribute{ 9162 "value": {Type: cty.String, Optional: true, Computed: true}, 9163 }, 9164 }, 9165 }, 9166 }) 9167 p.PlanResourceChangeFn = testDiffFn 9168 9169 m := testModule(t, "apply-invalid-index") 9170 c := testContext2(t, &ContextOpts{ 9171 Providers: map[addrs.Provider]providers.Factory{ 9172 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9173 }, 9174 }) 9175 diags := c.Validate(m) 9176 if diags.HasErrors() { 9177 t.Fatalf("unexpected validation failure: %s", diags.Err()) 9178 } 9179 9180 wantErr := `The given key does not identify an element in this collection value` 9181 _, diags = c.Plan(m, states.NewState(), DefaultPlanOpts) 9182 9183 if !diags.HasErrors() { 9184 t.Fatalf("plan succeeded; want error") 9185 } 9186 gotErr := diags.Err().Error() 9187 9188 if !strings.Contains(gotErr, wantErr) { 9189 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErr, wantErr) 9190 } 9191 } 9192 9193 func TestContext2Apply_moduleReplaceCycle(t *testing.T) { 9194 for _, mode := range []string{"normal", "cbd"} { 9195 var m *configs.Config 9196 9197 switch mode { 9198 case "normal": 9199 m = testModule(t, "apply-module-replace-cycle") 9200 case "cbd": 9201 m = testModule(t, "apply-module-replace-cycle-cbd") 9202 } 9203 9204 p := testProvider("aws") 9205 p.PlanResourceChangeFn = testDiffFn 9206 9207 instanceSchema := &configschema.Block{ 9208 Attributes: map[string]*configschema.Attribute{ 9209 "id": {Type: cty.String, Computed: true}, 9210 "require_new": {Type: cty.String, Optional: true}, 9211 }, 9212 } 9213 9214 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9215 ResourceTypes: map[string]*configschema.Block{ 9216 "aws_instance": instanceSchema, 9217 }, 9218 }) 9219 9220 state := states.NewState() 9221 modA := state.EnsureModule(addrs.RootModuleInstance.Child("a", addrs.NoKey)) 9222 modA.SetResourceInstanceCurrent( 9223 addrs.Resource{ 9224 Mode: addrs.ManagedResourceMode, 9225 Type: "aws_instance", 9226 Name: "a", 9227 }.Instance(addrs.NoKey), 9228 &states.ResourceInstanceObjectSrc{ 9229 Status: states.ObjectReady, 9230 AttrsJSON: []byte(`{"id":"a","require_new":"old"}`), 9231 CreateBeforeDestroy: mode == "cbd", 9232 }, 9233 addrs.AbsProviderConfig{ 9234 Provider: addrs.NewDefaultProvider("aws"), 9235 Module: addrs.RootModule, 9236 }, 9237 ) 9238 9239 modB := state.EnsureModule(addrs.RootModuleInstance.Child("b", addrs.NoKey)) 9240 modB.SetResourceInstanceCurrent( 9241 addrs.Resource{ 9242 Mode: addrs.ManagedResourceMode, 9243 Type: "aws_instance", 9244 Name: "b", 9245 }.Instance(addrs.IntKey(0)), 9246 &states.ResourceInstanceObjectSrc{ 9247 Status: states.ObjectReady, 9248 AttrsJSON: []byte(`{"id":"b","require_new":"old"}`), 9249 }, 9250 addrs.AbsProviderConfig{ 9251 Provider: addrs.NewDefaultProvider("aws"), 9252 Module: addrs.RootModule, 9253 }, 9254 ) 9255 9256 aBefore, _ := plans.NewDynamicValue( 9257 cty.ObjectVal(map[string]cty.Value{ 9258 "id": cty.StringVal("a"), 9259 "require_new": cty.StringVal("old"), 9260 }), instanceSchema.ImpliedType()) 9261 aAfter, _ := plans.NewDynamicValue( 9262 cty.ObjectVal(map[string]cty.Value{ 9263 "id": cty.UnknownVal(cty.String), 9264 "require_new": cty.StringVal("new"), 9265 }), instanceSchema.ImpliedType()) 9266 bBefore, _ := plans.NewDynamicValue( 9267 cty.ObjectVal(map[string]cty.Value{ 9268 "id": cty.StringVal("b"), 9269 "require_new": cty.StringVal("old"), 9270 }), instanceSchema.ImpliedType()) 9271 bAfter, _ := plans.NewDynamicValue( 9272 cty.ObjectVal(map[string]cty.Value{ 9273 "id": cty.UnknownVal(cty.String), 9274 "require_new": cty.UnknownVal(cty.String), 9275 }), instanceSchema.ImpliedType()) 9276 9277 var aAction plans.Action 9278 switch mode { 9279 case "normal": 9280 aAction = plans.DeleteThenCreate 9281 case "cbd": 9282 aAction = plans.CreateThenDelete 9283 } 9284 9285 ctx := testContext2(t, &ContextOpts{ 9286 Providers: map[addrs.Provider]providers.Factory{ 9287 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 9288 }, 9289 }) 9290 9291 changes := &plans.Changes{ 9292 Resources: []*plans.ResourceInstanceChangeSrc{ 9293 { 9294 Addr: addrs.Resource{ 9295 Mode: addrs.ManagedResourceMode, 9296 Type: "aws_instance", 9297 Name: "a", 9298 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("a", addrs.NoKey)), 9299 ProviderAddr: addrs.AbsProviderConfig{ 9300 Provider: addrs.NewDefaultProvider("aws"), 9301 Module: addrs.RootModule, 9302 }, 9303 ChangeSrc: plans.ChangeSrc{ 9304 Action: aAction, 9305 Before: aBefore, 9306 After: aAfter, 9307 }, 9308 }, 9309 { 9310 Addr: addrs.Resource{ 9311 Mode: addrs.ManagedResourceMode, 9312 Type: "aws_instance", 9313 Name: "b", 9314 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("b", addrs.NoKey)), 9315 ProviderAddr: addrs.AbsProviderConfig{ 9316 Provider: addrs.NewDefaultProvider("aws"), 9317 Module: addrs.RootModule, 9318 }, 9319 ChangeSrc: plans.ChangeSrc{ 9320 Action: plans.DeleteThenCreate, 9321 Before: bBefore, 9322 After: bAfter, 9323 }, 9324 }, 9325 }, 9326 } 9327 9328 plan := &plans.Plan{ 9329 UIMode: plans.NormalMode, 9330 Changes: changes, 9331 PriorState: state.DeepCopy(), 9332 PrevRunState: state.DeepCopy(), 9333 } 9334 9335 t.Run(mode, func(t *testing.T) { 9336 _, diags := ctx.Apply(plan, m) 9337 if diags.HasErrors() { 9338 t.Fatal(diags.Err()) 9339 } 9340 }) 9341 } 9342 } 9343 9344 func TestContext2Apply_destroyDataCycle(t *testing.T) { 9345 m, snap := testModuleWithSnapshot(t, "apply-destroy-data-cycle") 9346 p := testProvider("null") 9347 p.PlanResourceChangeFn = testDiffFn 9348 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 9349 return providers.ReadDataSourceResponse{ 9350 State: cty.ObjectVal(map[string]cty.Value{ 9351 "id": cty.StringVal("new"), 9352 "foo": cty.NullVal(cty.String), 9353 }), 9354 } 9355 } 9356 9357 tp := testProvider("test") 9358 tp.PlanResourceChangeFn = testDiffFn 9359 9360 state := states.NewState() 9361 root := state.EnsureModule(addrs.RootModuleInstance) 9362 root.SetResourceInstanceCurrent( 9363 addrs.Resource{ 9364 Mode: addrs.ManagedResourceMode, 9365 Type: "null_resource", 9366 Name: "a", 9367 }.Instance(addrs.IntKey(0)), 9368 &states.ResourceInstanceObjectSrc{ 9369 Status: states.ObjectReady, 9370 AttrsJSON: []byte(`{"id":"a"}`), 9371 }, 9372 addrs.AbsProviderConfig{ 9373 Provider: addrs.NewDefaultProvider("null"), 9374 Module: addrs.RootModule, 9375 }, 9376 ) 9377 root.SetResourceInstanceCurrent( 9378 addrs.Resource{ 9379 Mode: addrs.ManagedResourceMode, 9380 Type: "test_resource", 9381 Name: "a", 9382 }.Instance(addrs.IntKey(0)), 9383 &states.ResourceInstanceObjectSrc{ 9384 Status: states.ObjectReady, 9385 AttrsJSON: []byte(`{"id":"a"}`), 9386 Dependencies: []addrs.ConfigResource{ 9387 { 9388 Resource: addrs.Resource{ 9389 Mode: addrs.DataResourceMode, 9390 Type: "null_data_source", 9391 Name: "d", 9392 }, 9393 Module: addrs.RootModule, 9394 }, 9395 }, 9396 }, 9397 addrs.AbsProviderConfig{ 9398 Provider: addrs.NewDefaultProvider("test"), 9399 Module: addrs.RootModule, 9400 }, 9401 ) 9402 root.SetResourceInstanceCurrent( 9403 addrs.Resource{ 9404 Mode: addrs.DataResourceMode, 9405 Type: "null_data_source", 9406 Name: "d", 9407 }.Instance(addrs.NoKey), 9408 &states.ResourceInstanceObjectSrc{ 9409 Status: states.ObjectReady, 9410 AttrsJSON: []byte(`{"id":"old"}`), 9411 }, 9412 addrs.AbsProviderConfig{ 9413 Provider: addrs.NewDefaultProvider("null"), 9414 Module: addrs.RootModule, 9415 }, 9416 ) 9417 9418 Providers := map[addrs.Provider]providers.Factory{ 9419 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 9420 addrs.NewDefaultProvider("test"): testProviderFuncFixed(tp), 9421 } 9422 9423 ctx := testContext2(t, &ContextOpts{ 9424 Providers: Providers, 9425 }) 9426 9427 plan, diags := ctx.Plan(m, state, &PlanOpts{ 9428 Mode: plans.DestroyMode, 9429 }) 9430 diags.HasErrors() 9431 if diags.HasErrors() { 9432 t.Fatalf("diags: %s", diags.Err()) 9433 } 9434 9435 // We'll marshal and unmarshal the plan here, to ensure that we have 9436 // a clean new context as would be created if we separately ran 9437 // terraform plan -out=tfplan && terraform apply tfplan 9438 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 9439 if err != nil { 9440 t.Fatal(err) 9441 } 9442 ctxOpts.Providers = Providers 9443 ctx, diags = NewContext(ctxOpts) 9444 if diags.HasErrors() { 9445 t.Fatalf("failed to create context for plan: %s", diags.Err()) 9446 } 9447 9448 tp.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 9449 foo := req.Config.GetAttr("foo") 9450 if !foo.IsKnown() { 9451 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown config value foo")) 9452 return resp 9453 } 9454 9455 if foo.AsString() != "new" { 9456 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("wrong config value: %q", foo.AsString())) 9457 } 9458 return resp 9459 } 9460 9461 _, diags = ctx.Apply(plan, m) 9462 if diags.HasErrors() { 9463 t.Fatalf("diags: %s", diags.Err()) 9464 } 9465 } 9466 9467 func TestContext2Apply_taintedDestroyFailure(t *testing.T) { 9468 m := testModule(t, "apply-destroy-tainted") 9469 p := testProvider("test") 9470 p.PlanResourceChangeFn = testDiffFn 9471 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 9472 // All destroys fail. 9473 if req.PlannedState.IsNull() { 9474 resp.Diagnostics = resp.Diagnostics.Append(errors.New("failure")) 9475 return 9476 } 9477 9478 // c will also fail to create, meaning the existing tainted instance 9479 // becomes deposed, ans is then promoted back to current. 9480 // only C has a foo attribute 9481 planned := req.PlannedState.AsValueMap() 9482 foo, ok := planned["foo"] 9483 if ok && !foo.IsNull() && foo.AsString() == "c" { 9484 resp.Diagnostics = resp.Diagnostics.Append(errors.New("failure")) 9485 return 9486 } 9487 9488 return testApplyFn(req) 9489 } 9490 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9491 ResourceTypes: map[string]*configschema.Block{ 9492 "test_instance": { 9493 Attributes: map[string]*configschema.Attribute{ 9494 "id": { 9495 Type: cty.String, 9496 Computed: true, 9497 }, 9498 "foo": { 9499 Type: cty.String, 9500 Optional: true, 9501 }, 9502 }, 9503 }, 9504 }, 9505 }) 9506 9507 state := states.NewState() 9508 root := state.EnsureModule(addrs.RootModuleInstance) 9509 root.SetResourceInstanceCurrent( 9510 addrs.Resource{ 9511 Mode: addrs.ManagedResourceMode, 9512 Type: "test_instance", 9513 Name: "a", 9514 }.Instance(addrs.NoKey), 9515 &states.ResourceInstanceObjectSrc{ 9516 Status: states.ObjectTainted, 9517 AttrsJSON: []byte(`{"id":"a","foo":"a"}`), 9518 }, 9519 addrs.AbsProviderConfig{ 9520 Provider: addrs.NewDefaultProvider("test"), 9521 Module: addrs.RootModule, 9522 }, 9523 ) 9524 root.SetResourceInstanceCurrent( 9525 addrs.Resource{ 9526 Mode: addrs.ManagedResourceMode, 9527 Type: "test_instance", 9528 Name: "b", 9529 }.Instance(addrs.NoKey), 9530 &states.ResourceInstanceObjectSrc{ 9531 Status: states.ObjectTainted, 9532 AttrsJSON: []byte(`{"id":"b","foo":"b"}`), 9533 }, 9534 addrs.AbsProviderConfig{ 9535 Provider: addrs.NewDefaultProvider("test"), 9536 Module: addrs.RootModule, 9537 }, 9538 ) 9539 root.SetResourceInstanceCurrent( 9540 addrs.Resource{ 9541 Mode: addrs.ManagedResourceMode, 9542 Type: "test_instance", 9543 Name: "c", 9544 }.Instance(addrs.NoKey), 9545 &states.ResourceInstanceObjectSrc{ 9546 Status: states.ObjectTainted, 9547 AttrsJSON: []byte(`{"id":"c","foo":"old"}`), 9548 }, 9549 addrs.AbsProviderConfig{ 9550 Provider: addrs.NewDefaultProvider("test"), 9551 Module: addrs.RootModule, 9552 }, 9553 ) 9554 9555 Providers := map[addrs.Provider]providers.Factory{ 9556 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9557 } 9558 9559 ctx := testContext2(t, &ContextOpts{ 9560 Providers: Providers, 9561 Hooks: []Hook{&testHook{}}, 9562 }) 9563 9564 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 9565 diags.HasErrors() 9566 if diags.HasErrors() { 9567 t.Fatalf("diags: %s", diags.Err()) 9568 } 9569 9570 state, diags = ctx.Apply(plan, m) 9571 if !diags.HasErrors() { 9572 t.Fatal("expected error") 9573 } 9574 9575 root = state.Module(addrs.RootModuleInstance) 9576 9577 // the instance that failed to destroy should remain tainted 9578 a := root.ResourceInstance(addrs.Resource{ 9579 Mode: addrs.ManagedResourceMode, 9580 Type: "test_instance", 9581 Name: "a", 9582 }.Instance(addrs.NoKey)) 9583 9584 if a.Current.Status != states.ObjectTainted { 9585 t.Fatal("test_instance.a should be tainted") 9586 } 9587 9588 // b is create_before_destroy, and the destroy failed, so there should be 1 9589 // deposed instance. 9590 b := root.ResourceInstance(addrs.Resource{ 9591 Mode: addrs.ManagedResourceMode, 9592 Type: "test_instance", 9593 Name: "b", 9594 }.Instance(addrs.NoKey)) 9595 9596 if b.Current.Status != states.ObjectReady { 9597 t.Fatal("test_instance.b should be Ready") 9598 } 9599 9600 if len(b.Deposed) != 1 { 9601 t.Fatal("test_instance.b failed to keep deposed instance") 9602 } 9603 9604 // the desposed c instance should be promoted back to Current, and remain 9605 // tainted 9606 c := root.ResourceInstance(addrs.Resource{ 9607 Mode: addrs.ManagedResourceMode, 9608 Type: "test_instance", 9609 Name: "c", 9610 }.Instance(addrs.NoKey)) 9611 9612 if c.Current == nil { 9613 t.Fatal("test_instance.c has no current instance, but it should") 9614 } 9615 9616 if c.Current.Status != states.ObjectTainted { 9617 t.Fatal("test_instance.c should be tainted") 9618 } 9619 9620 if len(c.Deposed) != 0 { 9621 t.Fatal("test_instance.c should have no deposed instances") 9622 } 9623 9624 if string(c.Current.AttrsJSON) != `{"foo":"old","id":"c"}` { 9625 t.Fatalf("unexpected attrs for c: %q\n", c.Current.AttrsJSON) 9626 } 9627 } 9628 9629 func TestContext2Apply_plannedConnectionRefs(t *testing.T) { 9630 m := testModule(t, "apply-plan-connection-refs") 9631 p := testProvider("test") 9632 p.PlanResourceChangeFn = testDiffFn 9633 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 9634 s := req.PlannedState.AsValueMap() 9635 // delay "a" slightly, so if the reference edge is missing the "b" 9636 // provisioner will see an unknown value. 9637 if s["foo"].AsString() == "a" { 9638 time.Sleep(500 * time.Millisecond) 9639 } 9640 9641 s["id"] = cty.StringVal("ID") 9642 if ty, ok := s["type"]; ok && !ty.IsKnown() { 9643 s["type"] = cty.StringVal(req.TypeName) 9644 } 9645 resp.NewState = cty.ObjectVal(s) 9646 return resp 9647 } 9648 9649 provisionerFactory := func() (provisioners.Interface, error) { 9650 pr := testProvisioner() 9651 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 9652 host := req.Connection.GetAttr("host") 9653 if host.IsNull() || !host.IsKnown() { 9654 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("invalid host value: %#v", host)) 9655 } 9656 9657 return resp 9658 } 9659 return pr, nil 9660 } 9661 9662 Providers := map[addrs.Provider]providers.Factory{ 9663 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9664 } 9665 9666 provisioners := map[string]provisioners.Factory{ 9667 "shell": provisionerFactory, 9668 } 9669 9670 hook := &testHook{} 9671 ctx := testContext2(t, &ContextOpts{ 9672 Providers: Providers, 9673 Provisioners: provisioners, 9674 Hooks: []Hook{hook}, 9675 }) 9676 9677 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 9678 diags.HasErrors() 9679 if diags.HasErrors() { 9680 t.Fatalf("diags: %s", diags.Err()) 9681 } 9682 9683 _, diags = ctx.Apply(plan, m) 9684 if diags.HasErrors() { 9685 t.Fatalf("diags: %s", diags.Err()) 9686 } 9687 } 9688 9689 func TestContext2Apply_cbdCycle(t *testing.T) { 9690 m, snap := testModuleWithSnapshot(t, "apply-cbd-cycle") 9691 p := testProvider("test") 9692 p.PlanResourceChangeFn = testDiffFn 9693 9694 state := states.NewState() 9695 root := state.EnsureModule(addrs.RootModuleInstance) 9696 root.SetResourceInstanceCurrent( 9697 addrs.Resource{ 9698 Mode: addrs.ManagedResourceMode, 9699 Type: "test_instance", 9700 Name: "a", 9701 }.Instance(addrs.NoKey), 9702 &states.ResourceInstanceObjectSrc{ 9703 Status: states.ObjectReady, 9704 AttrsJSON: []byte(`{"id":"a","require_new":"old","foo":"b"}`), 9705 Dependencies: []addrs.ConfigResource{ 9706 { 9707 Resource: addrs.Resource{ 9708 Mode: addrs.ManagedResourceMode, 9709 Type: "test_instance", 9710 Name: "b", 9711 }, 9712 Module: addrs.RootModule, 9713 }, 9714 { 9715 Resource: addrs.Resource{ 9716 Mode: addrs.ManagedResourceMode, 9717 Type: "test_instance", 9718 Name: "c", 9719 }, 9720 Module: addrs.RootModule, 9721 }, 9722 }, 9723 }, 9724 addrs.AbsProviderConfig{ 9725 Provider: addrs.NewDefaultProvider("test"), 9726 Module: addrs.RootModule, 9727 }, 9728 ) 9729 root.SetResourceInstanceCurrent( 9730 addrs.Resource{ 9731 Mode: addrs.ManagedResourceMode, 9732 Type: "test_instance", 9733 Name: "b", 9734 }.Instance(addrs.NoKey), 9735 &states.ResourceInstanceObjectSrc{ 9736 Status: states.ObjectReady, 9737 AttrsJSON: []byte(`{"id":"b","require_new":"old","foo":"c"}`), 9738 Dependencies: []addrs.ConfigResource{ 9739 { 9740 Resource: addrs.Resource{ 9741 Mode: addrs.ManagedResourceMode, 9742 Type: "test_instance", 9743 Name: "c", 9744 }, 9745 Module: addrs.RootModule, 9746 }, 9747 }, 9748 }, 9749 addrs.AbsProviderConfig{ 9750 Provider: addrs.NewDefaultProvider("test"), 9751 Module: addrs.RootModule, 9752 }, 9753 ) 9754 root.SetResourceInstanceCurrent( 9755 addrs.Resource{ 9756 Mode: addrs.ManagedResourceMode, 9757 Type: "test_instance", 9758 Name: "c", 9759 }.Instance(addrs.NoKey), 9760 &states.ResourceInstanceObjectSrc{ 9761 Status: states.ObjectReady, 9762 AttrsJSON: []byte(`{"id":"c","require_new":"old"}`), 9763 }, 9764 addrs.AbsProviderConfig{ 9765 Provider: addrs.NewDefaultProvider("test"), 9766 Module: addrs.RootModule, 9767 }, 9768 ) 9769 9770 Providers := map[addrs.Provider]providers.Factory{ 9771 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9772 } 9773 9774 hook := &testHook{} 9775 ctx := testContext2(t, &ContextOpts{ 9776 Providers: Providers, 9777 Hooks: []Hook{hook}, 9778 }) 9779 9780 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 9781 diags.HasErrors() 9782 if diags.HasErrors() { 9783 t.Fatalf("diags: %s", diags.Err()) 9784 } 9785 9786 // We'll marshal and unmarshal the plan here, to ensure that we have 9787 // a clean new context as would be created if we separately ran 9788 // terraform plan -out=tfplan && terraform apply tfplan 9789 ctxOpts, m, plan, err := contextOptsForPlanViaFile(snap, plan) 9790 if err != nil { 9791 t.Fatal(err) 9792 } 9793 ctxOpts.Providers = Providers 9794 ctx, diags = NewContext(ctxOpts) 9795 if diags.HasErrors() { 9796 t.Fatalf("failed to create context for plan: %s", diags.Err()) 9797 } 9798 9799 _, diags = ctx.Apply(plan, m) 9800 if diags.HasErrors() { 9801 t.Fatalf("diags: %s", diags.Err()) 9802 } 9803 } 9804 9805 func TestContext2Apply_ProviderMeta_apply_set(t *testing.T) { 9806 m := testModule(t, "provider-meta-set") 9807 p := testProvider("test") 9808 p.PlanResourceChangeFn = testDiffFn 9809 schema := p.ProviderSchema() 9810 schema.ProviderMeta = &configschema.Block{ 9811 Attributes: map[string]*configschema.Attribute{ 9812 "baz": { 9813 Type: cty.String, 9814 Required: true, 9815 }, 9816 }, 9817 } 9818 9819 var pmMu sync.Mutex 9820 arcPMs := map[string]cty.Value{} 9821 9822 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 9823 pmMu.Lock() 9824 defer pmMu.Unlock() 9825 arcPMs[req.TypeName] = req.ProviderMeta 9826 9827 s := req.PlannedState.AsValueMap() 9828 s["id"] = cty.StringVal("ID") 9829 if ty, ok := s["type"]; ok && !ty.IsKnown() { 9830 s["type"] = cty.StringVal(req.TypeName) 9831 } 9832 return providers.ApplyResourceChangeResponse{ 9833 NewState: cty.ObjectVal(s), 9834 } 9835 } 9836 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 9837 ctx := testContext2(t, &ContextOpts{ 9838 Providers: map[addrs.Provider]providers.Factory{ 9839 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9840 }, 9841 }) 9842 9843 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 9844 assertNoErrors(t, diags) 9845 9846 _, diags = ctx.Apply(plan, m) 9847 assertNoErrors(t, diags) 9848 9849 if !p.ApplyResourceChangeCalled { 9850 t.Fatalf("ApplyResourceChange not called") 9851 } 9852 9853 expectations := map[string]cty.Value{} 9854 9855 if pm, ok := arcPMs["test_resource"]; !ok { 9856 t.Fatalf("sub-module ApplyResourceChange not called") 9857 } else if pm.IsNull() { 9858 t.Fatalf("null ProviderMeta in sub-module ApplyResourceChange") 9859 } else { 9860 expectations["quux-submodule"] = pm 9861 } 9862 9863 if pm, ok := arcPMs["test_instance"]; !ok { 9864 t.Fatalf("root module ApplyResourceChange not called") 9865 } else if pm.IsNull() { 9866 t.Fatalf("null ProviderMeta in root module ApplyResourceChange") 9867 } else { 9868 expectations["quux"] = pm 9869 } 9870 9871 type metaStruct struct { 9872 Baz string `cty:"baz"` 9873 } 9874 9875 for expected, v := range expectations { 9876 var meta metaStruct 9877 err := gocty.FromCtyValue(v, &meta) 9878 if err != nil { 9879 t.Fatalf("Error parsing cty value: %s", err) 9880 } 9881 if meta.Baz != expected { 9882 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 9883 } 9884 } 9885 } 9886 9887 func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) { 9888 m := testModule(t, "provider-meta-unset") 9889 p := testProvider("test") 9890 p.PlanResourceChangeFn = testDiffFn 9891 schema := p.ProviderSchema() 9892 schema.ProviderMeta = &configschema.Block{ 9893 Attributes: map[string]*configschema.Attribute{ 9894 "baz": { 9895 Type: cty.String, 9896 Required: true, 9897 }, 9898 }, 9899 } 9900 var pmMu sync.Mutex 9901 arcPMs := map[string]cty.Value{} 9902 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 9903 pmMu.Lock() 9904 defer pmMu.Unlock() 9905 arcPMs[req.TypeName] = req.ProviderMeta 9906 9907 s := req.PlannedState.AsValueMap() 9908 s["id"] = cty.StringVal("ID") 9909 if ty, ok := s["type"]; ok && !ty.IsKnown() { 9910 s["type"] = cty.StringVal(req.TypeName) 9911 } 9912 return providers.ApplyResourceChangeResponse{ 9913 NewState: cty.ObjectVal(s), 9914 } 9915 } 9916 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 9917 ctx := testContext2(t, &ContextOpts{ 9918 Providers: map[addrs.Provider]providers.Factory{ 9919 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9920 }, 9921 }) 9922 9923 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 9924 assertNoErrors(t, diags) 9925 9926 _, diags = ctx.Apply(plan, m) 9927 assertNoErrors(t, diags) 9928 9929 if !p.ApplyResourceChangeCalled { 9930 t.Fatalf("ApplyResourceChange not called") 9931 } 9932 9933 if pm, ok := arcPMs["test_resource"]; !ok { 9934 t.Fatalf("sub-module ApplyResourceChange not called") 9935 } else if !pm.IsNull() { 9936 t.Fatalf("non-null ProviderMeta in sub-module ApplyResourceChange: %+v", pm) 9937 } 9938 9939 if pm, ok := arcPMs["test_instance"]; !ok { 9940 t.Fatalf("root module ApplyResourceChange not called") 9941 } else if !pm.IsNull() { 9942 t.Fatalf("non-null ProviderMeta in root module ApplyResourceChange: %+v", pm) 9943 } 9944 } 9945 9946 func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) { 9947 m := testModule(t, "provider-meta-set") 9948 p := testProvider("test") 9949 schema := p.ProviderSchema() 9950 schema.ProviderMeta = &configschema.Block{ 9951 Attributes: map[string]*configschema.Attribute{ 9952 "baz": { 9953 Type: cty.String, 9954 Required: true, 9955 }, 9956 }, 9957 } 9958 prcPMs := map[string]cty.Value{} 9959 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 9960 prcPMs[req.TypeName] = req.ProviderMeta 9961 return providers.PlanResourceChangeResponse{ 9962 PlannedState: req.ProposedNewState, 9963 } 9964 } 9965 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 9966 ctx := testContext2(t, &ContextOpts{ 9967 Providers: map[addrs.Provider]providers.Factory{ 9968 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9969 }, 9970 }) 9971 9972 _, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 9973 assertNoErrors(t, diags) 9974 9975 if !p.PlanResourceChangeCalled { 9976 t.Fatalf("PlanResourceChange not called") 9977 } 9978 9979 expectations := map[string]cty.Value{} 9980 9981 if pm, ok := prcPMs["test_resource"]; !ok { 9982 t.Fatalf("sub-module PlanResourceChange not called") 9983 } else if pm.IsNull() { 9984 t.Fatalf("null ProviderMeta in sub-module PlanResourceChange") 9985 } else { 9986 expectations["quux-submodule"] = pm 9987 } 9988 9989 if pm, ok := prcPMs["test_instance"]; !ok { 9990 t.Fatalf("root module PlanResourceChange not called") 9991 } else if pm.IsNull() { 9992 t.Fatalf("null ProviderMeta in root module PlanResourceChange") 9993 } else { 9994 expectations["quux"] = pm 9995 } 9996 9997 type metaStruct struct { 9998 Baz string `cty:"baz"` 9999 } 10000 10001 for expected, v := range expectations { 10002 var meta metaStruct 10003 err := gocty.FromCtyValue(v, &meta) 10004 if err != nil { 10005 t.Fatalf("Error parsing cty value: %s", err) 10006 } 10007 if meta.Baz != expected { 10008 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10009 } 10010 } 10011 } 10012 10013 func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) { 10014 m := testModule(t, "provider-meta-unset") 10015 p := testProvider("test") 10016 schema := p.ProviderSchema() 10017 schema.ProviderMeta = &configschema.Block{ 10018 Attributes: map[string]*configschema.Attribute{ 10019 "baz": { 10020 Type: cty.String, 10021 Required: true, 10022 }, 10023 }, 10024 } 10025 prcPMs := map[string]cty.Value{} 10026 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 10027 prcPMs[req.TypeName] = req.ProviderMeta 10028 return providers.PlanResourceChangeResponse{ 10029 PlannedState: req.ProposedNewState, 10030 } 10031 } 10032 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10033 ctx := testContext2(t, &ContextOpts{ 10034 Providers: map[addrs.Provider]providers.Factory{ 10035 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10036 }, 10037 }) 10038 10039 _, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10040 assertNoErrors(t, diags) 10041 10042 if !p.PlanResourceChangeCalled { 10043 t.Fatalf("PlanResourceChange not called") 10044 } 10045 10046 if pm, ok := prcPMs["test_resource"]; !ok { 10047 t.Fatalf("sub-module PlanResourceChange not called") 10048 } else if !pm.IsNull() { 10049 t.Fatalf("non-null ProviderMeta in sub-module PlanResourceChange: %+v", pm) 10050 } 10051 10052 if pm, ok := prcPMs["test_instance"]; !ok { 10053 t.Fatalf("root module PlanResourceChange not called") 10054 } else if !pm.IsNull() { 10055 t.Fatalf("non-null ProviderMeta in root module PlanResourceChange: %+v", pm) 10056 } 10057 } 10058 10059 func TestContext2Apply_ProviderMeta_plan_setNoSchema(t *testing.T) { 10060 m := testModule(t, "provider-meta-set") 10061 p := testProvider("test") 10062 p.PlanResourceChangeFn = testDiffFn 10063 ctx := testContext2(t, &ContextOpts{ 10064 Providers: map[addrs.Provider]providers.Factory{ 10065 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10066 }, 10067 }) 10068 10069 _, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10070 if !diags.HasErrors() { 10071 t.Fatalf("plan supposed to error, has no errors") 10072 } 10073 10074 var rootErr, subErr bool 10075 errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks" 10076 for _, diag := range diags { 10077 if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" { 10078 t.Errorf("Unexpected error: %+v", diag.Description()) 10079 } 10080 switch diag.Description().Detail { 10081 case fmt.Sprintf(errorSummary, "instance"): 10082 rootErr = true 10083 case fmt.Sprintf(errorSummary, "resource"): 10084 subErr = true 10085 default: 10086 t.Errorf("Unexpected error: %s", diag.Description()) 10087 } 10088 } 10089 if !rootErr { 10090 t.Errorf("Expected unsupported provider_meta block error for root module, none received") 10091 } 10092 if !subErr { 10093 t.Errorf("Expected unsupported provider_meta block error for sub-module, none received") 10094 } 10095 } 10096 10097 func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) { 10098 m := testModule(t, "provider-meta-set") 10099 p := testProvider("test") 10100 p.PlanResourceChangeFn = testDiffFn 10101 schema := p.ProviderSchema() 10102 schema.ProviderMeta = &configschema.Block{ 10103 Attributes: map[string]*configschema.Attribute{ 10104 "quux": { 10105 Type: cty.String, 10106 Required: true, 10107 }, 10108 }, 10109 } 10110 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10111 ctx := testContext2(t, &ContextOpts{ 10112 Providers: map[addrs.Provider]providers.Factory{ 10113 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10114 }, 10115 }) 10116 10117 _, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10118 if !diags.HasErrors() { 10119 t.Fatalf("plan supposed to error, has no errors") 10120 } 10121 10122 var reqErr, invalidErr bool 10123 for _, diag := range diags { 10124 switch diag.Description().Summary { 10125 case "Missing required argument": 10126 if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` { 10127 reqErr = true 10128 } else { 10129 t.Errorf("Unexpected error %+v", diag.Description()) 10130 } 10131 case "Unsupported argument": 10132 if diag.Description().Detail == `An argument named "baz" is not expected here.` { 10133 invalidErr = true 10134 } else { 10135 t.Errorf("Unexpected error %+v", diag.Description()) 10136 } 10137 default: 10138 t.Errorf("Unexpected error %+v", diag.Description()) 10139 } 10140 } 10141 if !reqErr { 10142 t.Errorf("Expected missing required argument error, none received") 10143 } 10144 if !invalidErr { 10145 t.Errorf("Expected unsupported argument error, none received") 10146 } 10147 } 10148 10149 func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) { 10150 m := testModule(t, "provider-meta-set") 10151 p := testProvider("test") 10152 p.PlanResourceChangeFn = testDiffFn 10153 schema := p.ProviderSchema() 10154 schema.ProviderMeta = &configschema.Block{ 10155 Attributes: map[string]*configschema.Attribute{ 10156 "baz": { 10157 Type: cty.String, 10158 Required: true, 10159 }, 10160 }, 10161 } 10162 rrcPMs := map[string]cty.Value{} 10163 p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { 10164 rrcPMs[req.TypeName] = req.ProviderMeta 10165 newState, err := p.GetProviderSchemaResponse.ResourceTypes[req.TypeName].Block.CoerceValue(req.PriorState) 10166 if err != nil { 10167 panic(err) 10168 } 10169 resp.NewState = newState 10170 return resp 10171 } 10172 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10173 ctx := testContext2(t, &ContextOpts{ 10174 Providers: map[addrs.Provider]providers.Factory{ 10175 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10176 }, 10177 }) 10178 10179 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10180 assertNoErrors(t, diags) 10181 10182 state, diags := ctx.Apply(plan, m) 10183 assertNoErrors(t, diags) 10184 10185 _, diags = ctx.Refresh(m, state, DefaultPlanOpts) 10186 assertNoErrors(t, diags) 10187 10188 if !p.ReadResourceCalled { 10189 t.Fatalf("ReadResource not called") 10190 } 10191 10192 expectations := map[string]cty.Value{} 10193 10194 if pm, ok := rrcPMs["test_resource"]; !ok { 10195 t.Fatalf("sub-module ReadResource not called") 10196 } else if pm.IsNull() { 10197 t.Fatalf("null ProviderMeta in sub-module ReadResource") 10198 } else { 10199 expectations["quux-submodule"] = pm 10200 } 10201 10202 if pm, ok := rrcPMs["test_instance"]; !ok { 10203 t.Fatalf("root module ReadResource not called") 10204 } else if pm.IsNull() { 10205 t.Fatalf("null ProviderMeta in root module ReadResource") 10206 } else { 10207 expectations["quux"] = pm 10208 } 10209 10210 type metaStruct struct { 10211 Baz string `cty:"baz"` 10212 } 10213 10214 for expected, v := range expectations { 10215 var meta metaStruct 10216 err := gocty.FromCtyValue(v, &meta) 10217 if err != nil { 10218 t.Fatalf("Error parsing cty value: %s", err) 10219 } 10220 if meta.Baz != expected { 10221 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10222 } 10223 } 10224 } 10225 10226 func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) { 10227 m := testModule(t, "provider-meta-set") 10228 p := testProvider("test") 10229 p.PlanResourceChangeFn = testDiffFn 10230 10231 // we need a schema for plan/apply so they don't error 10232 schema := p.ProviderSchema() 10233 schema.ProviderMeta = &configschema.Block{ 10234 Attributes: map[string]*configschema.Attribute{ 10235 "baz": { 10236 Type: cty.String, 10237 Required: true, 10238 }, 10239 }, 10240 } 10241 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10242 ctx := testContext2(t, &ContextOpts{ 10243 Providers: map[addrs.Provider]providers.Factory{ 10244 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10245 }, 10246 }) 10247 10248 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10249 assertNoErrors(t, diags) 10250 10251 state, diags := ctx.Apply(plan, m) 10252 assertNoErrors(t, diags) 10253 10254 // drop the schema before refresh, to test that it errors 10255 schema.ProviderMeta = nil 10256 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10257 ctx = testContext2(t, &ContextOpts{ 10258 Providers: map[addrs.Provider]providers.Factory{ 10259 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10260 }, 10261 }) 10262 10263 _, diags = ctx.Refresh(m, state, DefaultPlanOpts) 10264 if !diags.HasErrors() { 10265 t.Fatalf("refresh supposed to error, has no errors") 10266 } 10267 10268 var rootErr, subErr bool 10269 errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks" 10270 for _, diag := range diags { 10271 if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" { 10272 t.Errorf("Unexpected error: %+v", diag.Description()) 10273 } 10274 switch diag.Description().Detail { 10275 case fmt.Sprintf(errorSummary, "instance"): 10276 rootErr = true 10277 case fmt.Sprintf(errorSummary, "resource"): 10278 subErr = true 10279 default: 10280 t.Errorf("Unexpected error: %s", diag.Description()) 10281 } 10282 } 10283 if !rootErr { 10284 t.Errorf("Expected unsupported provider_meta block error for root module, none received") 10285 } 10286 if !subErr { 10287 t.Errorf("Expected unsupported provider_meta block error for sub-module, none received") 10288 } 10289 } 10290 10291 func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) { 10292 m := testModule(t, "provider-meta-set") 10293 p := testProvider("test") 10294 p.PlanResourceChangeFn = testDiffFn 10295 10296 // we need a matching schema for plan/apply so they don't error 10297 schema := p.ProviderSchema() 10298 schema.ProviderMeta = &configschema.Block{ 10299 Attributes: map[string]*configschema.Attribute{ 10300 "baz": { 10301 Type: cty.String, 10302 Required: true, 10303 }, 10304 }, 10305 } 10306 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10307 ctx := testContext2(t, &ContextOpts{ 10308 Providers: map[addrs.Provider]providers.Factory{ 10309 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10310 }, 10311 }) 10312 10313 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10314 assertNoErrors(t, diags) 10315 10316 state, diags := ctx.Apply(plan, m) 10317 assertNoErrors(t, diags) 10318 10319 // change the schema before refresh, to test that it errors 10320 schema.ProviderMeta = &configschema.Block{ 10321 Attributes: map[string]*configschema.Attribute{ 10322 "quux": { 10323 Type: cty.String, 10324 Required: true, 10325 }, 10326 }, 10327 } 10328 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10329 ctx = testContext2(t, &ContextOpts{ 10330 Providers: map[addrs.Provider]providers.Factory{ 10331 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10332 }, 10333 }) 10334 10335 _, diags = ctx.Refresh(m, state, DefaultPlanOpts) 10336 if !diags.HasErrors() { 10337 t.Fatalf("refresh supposed to error, has no errors") 10338 } 10339 10340 var reqErr, invalidErr bool 10341 for _, diag := range diags { 10342 switch diag.Description().Summary { 10343 case "Missing required argument": 10344 if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` { 10345 reqErr = true 10346 } else { 10347 t.Errorf("Unexpected error %+v", diag.Description()) 10348 } 10349 case "Unsupported argument": 10350 if diag.Description().Detail == `An argument named "baz" is not expected here.` { 10351 invalidErr = true 10352 } else { 10353 t.Errorf("Unexpected error %+v", diag.Description()) 10354 } 10355 default: 10356 t.Errorf("Unexpected error %+v", diag.Description()) 10357 } 10358 } 10359 if !reqErr { 10360 t.Errorf("Expected missing required argument error, none received") 10361 } 10362 if !invalidErr { 10363 t.Errorf("Expected unsupported argument error, none received") 10364 } 10365 } 10366 10367 func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) { 10368 m := testModule(t, "provider-meta-data-set") 10369 p := testProvider("test") 10370 p.PlanResourceChangeFn = testDiffFn 10371 schema := p.ProviderSchema() 10372 schema.ProviderMeta = &configschema.Block{ 10373 Attributes: map[string]*configschema.Attribute{ 10374 "baz": { 10375 Type: cty.String, 10376 Required: true, 10377 }, 10378 }, 10379 } 10380 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10381 ctx := testContext2(t, &ContextOpts{ 10382 Providers: map[addrs.Provider]providers.Factory{ 10383 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10384 }, 10385 }) 10386 rdsPMs := map[string]cty.Value{} 10387 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 10388 rdsPMs[req.TypeName] = req.ProviderMeta 10389 switch req.TypeName { 10390 case "test_data_source": 10391 log.Printf("[TRACE] test_data_source RDSR returning") 10392 return providers.ReadDataSourceResponse{ 10393 State: cty.ObjectVal(map[string]cty.Value{ 10394 "id": cty.StringVal("yo"), 10395 "foo": cty.StringVal("bar"), 10396 }), 10397 } 10398 case "test_file": 10399 log.Printf("[TRACE] test_file RDSR returning") 10400 return providers.ReadDataSourceResponse{ 10401 State: cty.ObjectVal(map[string]cty.Value{ 10402 "id": cty.StringVal("bar"), 10403 "rendered": cty.StringVal("baz"), 10404 "template": cty.StringVal(""), 10405 }), 10406 } 10407 default: 10408 // config drift, oops 10409 log.Printf("[TRACE] unknown request TypeName: %q", req.TypeName) 10410 return providers.ReadDataSourceResponse{} 10411 } 10412 } 10413 10414 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10415 assertNoErrors(t, diags) 10416 10417 state, diags := ctx.Apply(plan, m) 10418 assertNoErrors(t, diags) 10419 10420 _, diags = ctx.Refresh(m, state, DefaultPlanOpts) 10421 assertNoErrors(t, diags) 10422 10423 if !p.ReadDataSourceCalled { 10424 t.Fatalf("ReadDataSource not called") 10425 } 10426 10427 expectations := map[string]cty.Value{} 10428 10429 if pm, ok := rdsPMs["test_file"]; !ok { 10430 t.Fatalf("sub-module ReadDataSource not called") 10431 } else if pm.IsNull() { 10432 t.Fatalf("null ProviderMeta in sub-module ReadDataSource") 10433 } else { 10434 expectations["quux-submodule"] = pm 10435 } 10436 10437 if pm, ok := rdsPMs["test_data_source"]; !ok { 10438 t.Fatalf("root module ReadDataSource not called") 10439 } else if pm.IsNull() { 10440 t.Fatalf("null ProviderMeta in root module ReadDataSource") 10441 } else { 10442 expectations["quux"] = pm 10443 } 10444 10445 type metaStruct struct { 10446 Baz string `cty:"baz"` 10447 } 10448 10449 for expected, v := range expectations { 10450 var meta metaStruct 10451 err := gocty.FromCtyValue(v, &meta) 10452 if err != nil { 10453 t.Fatalf("Error parsing cty value: %s", err) 10454 } 10455 if meta.Baz != expected { 10456 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10457 } 10458 } 10459 } 10460 10461 func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) { 10462 m := testModule(t, "provider-meta-data-unset") 10463 p := testProvider("test") 10464 p.PlanResourceChangeFn = testDiffFn 10465 schema := p.ProviderSchema() 10466 schema.ProviderMeta = &configschema.Block{ 10467 Attributes: map[string]*configschema.Attribute{ 10468 "baz": { 10469 Type: cty.String, 10470 Required: true, 10471 }, 10472 }, 10473 } 10474 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10475 ctx := testContext2(t, &ContextOpts{ 10476 Providers: map[addrs.Provider]providers.Factory{ 10477 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10478 }, 10479 }) 10480 rdsPMs := map[string]cty.Value{} 10481 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 10482 rdsPMs[req.TypeName] = req.ProviderMeta 10483 switch req.TypeName { 10484 case "test_data_source": 10485 return providers.ReadDataSourceResponse{ 10486 State: cty.ObjectVal(map[string]cty.Value{ 10487 "id": cty.StringVal("yo"), 10488 "foo": cty.StringVal("bar"), 10489 }), 10490 } 10491 case "test_file": 10492 return providers.ReadDataSourceResponse{ 10493 State: cty.ObjectVal(map[string]cty.Value{ 10494 "id": cty.StringVal("bar"), 10495 "rendered": cty.StringVal("baz"), 10496 "template": cty.StringVal(""), 10497 }), 10498 } 10499 default: 10500 // config drift, oops 10501 return providers.ReadDataSourceResponse{} 10502 } 10503 } 10504 10505 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10506 assertNoErrors(t, diags) 10507 10508 _, diags = ctx.Apply(plan, m) 10509 assertNoErrors(t, diags) 10510 10511 if !p.ReadDataSourceCalled { 10512 t.Fatalf("ReadDataSource not called") 10513 } 10514 10515 if pm, ok := rdsPMs["test_file"]; !ok { 10516 t.Fatalf("sub-module ReadDataSource not called") 10517 } else if !pm.IsNull() { 10518 t.Fatalf("non-null ProviderMeta in sub-module ReadDataSource") 10519 } 10520 10521 if pm, ok := rdsPMs["test_data_source"]; !ok { 10522 t.Fatalf("root module ReadDataSource not called") 10523 } else if !pm.IsNull() { 10524 t.Fatalf("non-null ProviderMeta in root module ReadDataSource") 10525 } 10526 } 10527 10528 func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) { 10529 m := testModule(t, "provider-meta-data-set") 10530 p := testProvider("test") 10531 p.PlanResourceChangeFn = testDiffFn 10532 ctx := testContext2(t, &ContextOpts{ 10533 Providers: map[addrs.Provider]providers.Factory{ 10534 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10535 }, 10536 }) 10537 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 10538 State: cty.ObjectVal(map[string]cty.Value{ 10539 "id": cty.StringVal("yo"), 10540 "foo": cty.StringVal("bar"), 10541 }), 10542 } 10543 10544 _, diags := ctx.Refresh(m, states.NewState(), DefaultPlanOpts) 10545 if !diags.HasErrors() { 10546 t.Fatalf("refresh supposed to error, has no errors") 10547 } 10548 10549 var rootErr, subErr bool 10550 errorSummary := "The resource data.test_%s.foo belongs to a provider that doesn't support provider_meta blocks" 10551 for _, diag := range diags { 10552 if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" { 10553 t.Errorf("Unexpected error: %+v", diag.Description()) 10554 } 10555 switch diag.Description().Detail { 10556 case fmt.Sprintf(errorSummary, "data_source"): 10557 rootErr = true 10558 case fmt.Sprintf(errorSummary, "file"): 10559 subErr = true 10560 default: 10561 t.Errorf("Unexpected error: %s", diag.Description()) 10562 } 10563 } 10564 if !rootErr { 10565 t.Errorf("Expected unsupported provider_meta block error for root module, none received") 10566 } 10567 if !subErr { 10568 t.Errorf("Expected unsupported provider_meta block error for sub-module, none received") 10569 } 10570 } 10571 10572 func TestContext2Apply_ProviderMeta_refreshdata_setInvalid(t *testing.T) { 10573 m := testModule(t, "provider-meta-data-set") 10574 p := testProvider("test") 10575 p.PlanResourceChangeFn = testDiffFn 10576 schema := p.ProviderSchema() 10577 schema.ProviderMeta = &configschema.Block{ 10578 Attributes: map[string]*configschema.Attribute{ 10579 "quux": { 10580 Type: cty.String, 10581 Required: true, 10582 }, 10583 }, 10584 } 10585 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10586 ctx := testContext2(t, &ContextOpts{ 10587 Providers: map[addrs.Provider]providers.Factory{ 10588 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10589 }, 10590 }) 10591 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 10592 State: cty.ObjectVal(map[string]cty.Value{ 10593 "id": cty.StringVal("yo"), 10594 "foo": cty.StringVal("bar"), 10595 }), 10596 } 10597 10598 _, diags := ctx.Refresh(m, states.NewState(), DefaultPlanOpts) 10599 if !diags.HasErrors() { 10600 t.Fatalf("refresh supposed to error, has no errors") 10601 } 10602 10603 var reqErr, invalidErr bool 10604 for _, diag := range diags { 10605 switch diag.Description().Summary { 10606 case "Missing required argument": 10607 if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` { 10608 reqErr = true 10609 } else { 10610 t.Errorf("Unexpected error %+v", diag.Description()) 10611 } 10612 case "Unsupported argument": 10613 if diag.Description().Detail == `An argument named "baz" is not expected here.` { 10614 invalidErr = true 10615 } else { 10616 t.Errorf("Unexpected error %+v", diag.Description()) 10617 } 10618 default: 10619 t.Errorf("Unexpected error %+v", diag.Description()) 10620 } 10621 } 10622 if !reqErr { 10623 t.Errorf("Expected missing required argument error, none received") 10624 } 10625 if !invalidErr { 10626 t.Errorf("Expected unsupported argument error, none received") 10627 } 10628 } 10629 10630 func TestContext2Apply_expandModuleVariables(t *testing.T) { 10631 m := testModuleInline(t, map[string]string{ 10632 "main.tf": ` 10633 module "mod1" { 10634 for_each = toset(["a"]) 10635 source = "./mod" 10636 } 10637 10638 module "mod2" { 10639 source = "./mod" 10640 in = module.mod1["a"].out 10641 } 10642 `, 10643 "mod/main.tf": ` 10644 resource "aws_instance" "foo" { 10645 foo = var.in 10646 } 10647 10648 variable "in" { 10649 type = string 10650 default = "default" 10651 } 10652 10653 output "out" { 10654 value = aws_instance.foo.id 10655 } 10656 `, 10657 }) 10658 10659 p := testProvider("aws") 10660 p.PlanResourceChangeFn = testDiffFn 10661 p.ApplyResourceChangeFn = testApplyFn 10662 ctx := testContext2(t, &ContextOpts{ 10663 Providers: map[addrs.Provider]providers.Factory{ 10664 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 10665 }, 10666 }) 10667 10668 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10669 if diags.HasErrors() { 10670 t.Fatal(diags.ErrWithWarnings()) 10671 } 10672 10673 state, diags := ctx.Apply(plan, m) 10674 if diags.HasErrors() { 10675 t.Fatal(diags.ErrWithWarnings()) 10676 } 10677 10678 expected := `<no state> 10679 module.mod1["a"]: 10680 aws_instance.foo: 10681 ID = foo 10682 provider = provider["registry.terraform.io/hashicorp/aws"] 10683 foo = default 10684 type = aws_instance 10685 10686 Outputs: 10687 10688 out = foo 10689 module.mod2: 10690 aws_instance.foo: 10691 ID = foo 10692 provider = provider["registry.terraform.io/hashicorp/aws"] 10693 foo = foo 10694 type = aws_instance 10695 10696 Dependencies: 10697 module.mod1.aws_instance.foo` 10698 10699 if state.String() != expected { 10700 t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, state) 10701 } 10702 } 10703 10704 func TestContext2Apply_inheritAndStoreCBD(t *testing.T) { 10705 m := testModuleInline(t, map[string]string{ 10706 "main.tf": ` 10707 resource "aws_instance" "foo" { 10708 } 10709 10710 resource "aws_instance" "cbd" { 10711 foo = aws_instance.foo.id 10712 lifecycle { 10713 create_before_destroy = true 10714 } 10715 } 10716 `, 10717 }) 10718 10719 p := testProvider("aws") 10720 p.PlanResourceChangeFn = testDiffFn 10721 ctx := testContext2(t, &ContextOpts{ 10722 Providers: map[addrs.Provider]providers.Factory{ 10723 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 10724 }, 10725 }) 10726 10727 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10728 if diags.HasErrors() { 10729 t.Fatal(diags.ErrWithWarnings()) 10730 } 10731 10732 state, diags := ctx.Apply(plan, m) 10733 if diags.HasErrors() { 10734 t.Fatal(diags.ErrWithWarnings()) 10735 } 10736 10737 foo := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.foo")) 10738 if !foo.Current.CreateBeforeDestroy { 10739 t.Fatal("aws_instance.foo should also be create_before_destroy") 10740 } 10741 } 10742 10743 func TestContext2Apply_moduleDependsOn(t *testing.T) { 10744 m := testModule(t, "apply-module-depends-on") 10745 10746 p := testProvider("test") 10747 10748 // each instance being applied should happen in sequential order 10749 applied := int64(0) 10750 10751 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 10752 cfg := req.Config.AsValueMap() 10753 foo := cfg["foo"].AsString() 10754 ord := atomic.LoadInt64(&applied) 10755 10756 resp := providers.ReadDataSourceResponse{ 10757 State: cty.ObjectVal(map[string]cty.Value{ 10758 "id": cty.StringVal("data"), 10759 "foo": cfg["foo"], 10760 }), 10761 } 10762 10763 if foo == "a" && ord < 4 { 10764 // due to data source "a"'s module depending on instance 4, this 10765 // should not be less than 4 10766 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source a read too early")) 10767 } 10768 if foo == "b" && ord < 1 { 10769 // due to data source "b"'s module depending on instance 1, this 10770 // should not be less than 1 10771 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source b read too early")) 10772 } 10773 return resp 10774 } 10775 p.PlanResourceChangeFn = testDiffFn 10776 10777 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 10778 state := req.PlannedState.AsValueMap() 10779 num, _ := state["num"].AsBigFloat().Float64() 10780 ord := int64(num) 10781 if !atomic.CompareAndSwapInt64(&applied, ord-1, ord) { 10782 actual := atomic.LoadInt64(&applied) 10783 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("instance %d was applied after %d", ord, actual)) 10784 } 10785 10786 state["id"] = cty.StringVal(fmt.Sprintf("test_%d", ord)) 10787 state["type"] = cty.StringVal("test_instance") 10788 resp.NewState = cty.ObjectVal(state) 10789 10790 return resp 10791 } 10792 10793 ctx := testContext2(t, &ContextOpts{ 10794 Providers: map[addrs.Provider]providers.Factory{ 10795 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10796 }, 10797 }) 10798 10799 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10800 if diags.HasErrors() { 10801 t.Fatal(diags.ErrWithWarnings()) 10802 } 10803 10804 state, diags := ctx.Apply(plan, m) 10805 if diags.HasErrors() { 10806 t.Fatal(diags.ErrWithWarnings()) 10807 } 10808 10809 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 10810 if diags.HasErrors() { 10811 t.Fatal(diags.ErrWithWarnings()) 10812 } 10813 10814 for _, res := range plan.Changes.Resources { 10815 if res.Action != plans.NoOp { 10816 t.Fatalf("expected NoOp, got %s for %s", res.Action, res.Addr) 10817 } 10818 } 10819 } 10820 10821 func TestContext2Apply_moduleSelfReference(t *testing.T) { 10822 m := testModuleInline(t, map[string]string{ 10823 "main.tf": ` 10824 module "test" { 10825 source = "./test" 10826 10827 a = module.test.b 10828 } 10829 10830 output "c" { 10831 value = module.test.c 10832 } 10833 `, 10834 "test/main.tf": ` 10835 variable "a" {} 10836 10837 resource "test_instance" "test" { 10838 } 10839 10840 output "b" { 10841 value = test_instance.test.id 10842 } 10843 10844 output "c" { 10845 value = var.a 10846 }`}) 10847 10848 p := testProvider("test") 10849 p.PlanResourceChangeFn = testDiffFn 10850 ctx := testContext2(t, &ContextOpts{ 10851 Providers: map[addrs.Provider]providers.Factory{ 10852 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10853 }, 10854 }) 10855 10856 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10857 if diags.HasErrors() { 10858 t.Fatal(diags.ErrWithWarnings()) 10859 } 10860 10861 state, diags := ctx.Apply(plan, m) 10862 if diags.HasErrors() { 10863 t.Fatal(diags.ErrWithWarnings()) 10864 } 10865 10866 ctx = testContext2(t, &ContextOpts{ 10867 Providers: map[addrs.Provider]providers.Factory{ 10868 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10869 }, 10870 }) 10871 10872 plan, diags = ctx.Plan(m, state, &PlanOpts{ 10873 Mode: plans.DestroyMode, 10874 }) 10875 if diags.HasErrors() { 10876 t.Fatal(diags.ErrWithWarnings()) 10877 } 10878 10879 state, diags = ctx.Apply(plan, m) 10880 if diags.HasErrors() { 10881 t.Fatal(diags.ErrWithWarnings()) 10882 } 10883 10884 if !state.Empty() { 10885 t.Fatal("expected empty state, got:", state) 10886 } 10887 } 10888 10889 func TestContext2Apply_moduleExpandDependsOn(t *testing.T) { 10890 m := testModuleInline(t, map[string]string{ 10891 "main.tf": ` 10892 module "child" { 10893 count = 1 10894 source = "./child" 10895 10896 depends_on = [test_instance.a, test_instance.b] 10897 } 10898 10899 resource "test_instance" "a" { 10900 } 10901 10902 10903 resource "test_instance" "b" { 10904 } 10905 `, 10906 "child/main.tf": ` 10907 resource "test_instance" "foo" { 10908 } 10909 10910 output "myoutput" { 10911 value = "literal string" 10912 } 10913 `}) 10914 10915 p := testProvider("test") 10916 p.PlanResourceChangeFn = testDiffFn 10917 ctx := testContext2(t, &ContextOpts{ 10918 Providers: map[addrs.Provider]providers.Factory{ 10919 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10920 }, 10921 }) 10922 10923 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 10924 if diags.HasErrors() { 10925 t.Fatal(diags.ErrWithWarnings()) 10926 } 10927 10928 state, diags := ctx.Apply(plan, m) 10929 if diags.HasErrors() { 10930 t.Fatal(diags.ErrWithWarnings()) 10931 } 10932 10933 ctx = testContext2(t, &ContextOpts{ 10934 Providers: map[addrs.Provider]providers.Factory{ 10935 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10936 }, 10937 }) 10938 10939 plan, diags = ctx.Plan(m, state, &PlanOpts{ 10940 Mode: plans.DestroyMode, 10941 }) 10942 if diags.HasErrors() { 10943 t.Fatal(diags.ErrWithWarnings()) 10944 } 10945 10946 state, diags = ctx.Apply(plan, m) 10947 if diags.HasErrors() { 10948 t.Fatal(diags.ErrWithWarnings()) 10949 } 10950 10951 if !state.Empty() { 10952 t.Fatal("expected empty state, got:", state) 10953 } 10954 } 10955 10956 func TestContext2Apply_scaleInCBD(t *testing.T) { 10957 m := testModuleInline(t, map[string]string{ 10958 "main.tf": ` 10959 variable "ct" { 10960 type = number 10961 } 10962 10963 resource "test_instance" "a" { 10964 count = var.ct 10965 } 10966 10967 resource "test_instance" "b" { 10968 require_new = local.removable 10969 lifecycle { 10970 create_before_destroy = true 10971 } 10972 } 10973 10974 resource "test_instance" "c" { 10975 require_new = test_instance.b.id 10976 lifecycle { 10977 create_before_destroy = true 10978 } 10979 } 10980 10981 output "out" { 10982 value = join(".", test_instance.a[*].id) 10983 } 10984 10985 locals { 10986 removable = join(".", test_instance.a[*].id) 10987 } 10988 `}) 10989 10990 state := states.NewState() 10991 root := state.EnsureModule(addrs.RootModuleInstance) 10992 root.SetResourceInstanceCurrent( 10993 mustResourceInstanceAddr("test_instance.a[0]").Resource, 10994 &states.ResourceInstanceObjectSrc{ 10995 Status: states.ObjectReady, 10996 AttrsJSON: []byte(`{"id":"a0"}`), 10997 Dependencies: []addrs.ConfigResource{}, 10998 CreateBeforeDestroy: true, 10999 }, 11000 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11001 ) 11002 root.SetResourceInstanceCurrent( 11003 mustResourceInstanceAddr("test_instance.a[1]").Resource, 11004 &states.ResourceInstanceObjectSrc{ 11005 Status: states.ObjectReady, 11006 AttrsJSON: []byte(`{"id":"a1"}`), 11007 Dependencies: []addrs.ConfigResource{}, 11008 CreateBeforeDestroy: true, 11009 }, 11010 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11011 ) 11012 root.SetResourceInstanceCurrent( 11013 mustResourceInstanceAddr("test_instance.b").Resource, 11014 &states.ResourceInstanceObjectSrc{ 11015 Status: states.ObjectReady, 11016 AttrsJSON: []byte(`{"id":"b", "require_new":"old.old"}`), 11017 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_instance.a")}, 11018 CreateBeforeDestroy: true, 11019 }, 11020 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11021 ) 11022 root.SetResourceInstanceCurrent( 11023 mustResourceInstanceAddr("test_instance.c").Resource, 11024 &states.ResourceInstanceObjectSrc{ 11025 Status: states.ObjectReady, 11026 AttrsJSON: []byte(`{"id":"c", "require_new":"b"}`), 11027 Dependencies: []addrs.ConfigResource{ 11028 mustConfigResourceAddr("test_instance.a"), 11029 mustConfigResourceAddr("test_instance.b"), 11030 }, 11031 CreateBeforeDestroy: true, 11032 }, 11033 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11034 ) 11035 11036 p := testProvider("test") 11037 11038 p.PlanResourceChangeFn = func(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 11039 n := r.ProposedNewState.AsValueMap() 11040 11041 if r.PriorState.IsNull() { 11042 n["id"] = cty.UnknownVal(cty.String) 11043 resp.PlannedState = cty.ObjectVal(n) 11044 return resp 11045 } 11046 11047 p := r.PriorState.AsValueMap() 11048 11049 priorRN := p["require_new"] 11050 newRN := n["require_new"] 11051 11052 if eq := priorRN.Equals(newRN); !eq.IsKnown() || eq.False() { 11053 resp.RequiresReplace = []cty.Path{{cty.GetAttrStep{Name: "require_new"}}} 11054 n["id"] = cty.UnknownVal(cty.String) 11055 } 11056 11057 resp.PlannedState = cty.ObjectVal(n) 11058 return resp 11059 } 11060 11061 // reduce the count to 1 11062 ctx := testContext2(t, &ContextOpts{ 11063 Providers: map[addrs.Provider]providers.Factory{ 11064 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11065 }, 11066 }) 11067 11068 plan, diags := ctx.Plan(m, state, &PlanOpts{ 11069 Mode: plans.NormalMode, 11070 SetVariables: InputValues{ 11071 "ct": &InputValue{ 11072 Value: cty.NumberIntVal(1), 11073 SourceType: ValueFromCaller, 11074 }, 11075 }, 11076 }) 11077 if diags.HasErrors() { 11078 t.Fatal(diags.ErrWithWarnings()) 11079 } 11080 { 11081 addr := mustResourceInstanceAddr("test_instance.a[0]") 11082 change := plan.Changes.ResourceInstance(addr) 11083 if change == nil { 11084 t.Fatalf("no planned change for %s", addr) 11085 } 11086 if got, want := change.PrevRunAddr, mustResourceInstanceAddr("test_instance.a[0]"); !want.Equal(got) { 11087 t.Errorf("wrong previous run address for %s %s; want %s", addr, got, want) 11088 } 11089 if got, want := change.Action, plans.NoOp; got != want { 11090 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 11091 } 11092 if got, want := change.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { 11093 t.Errorf("wrong action reason for %s %s; want %s", addr, got, want) 11094 } 11095 } 11096 { 11097 addr := mustResourceInstanceAddr("test_instance.a[1]") 11098 change := plan.Changes.ResourceInstance(addr) 11099 if change == nil { 11100 t.Fatalf("no planned change for %s", addr) 11101 } 11102 if got, want := change.PrevRunAddr, mustResourceInstanceAddr("test_instance.a[1]"); !want.Equal(got) { 11103 t.Errorf("wrong previous run address for %s %s; want %s", addr, got, want) 11104 } 11105 if got, want := change.Action, plans.Delete; got != want { 11106 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 11107 } 11108 if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseCountIndex; got != want { 11109 t.Errorf("wrong action reason for %s %s; want %s", addr, got, want) 11110 } 11111 } 11112 11113 state, diags = ctx.Apply(plan, m) 11114 if diags.HasErrors() { 11115 log.Fatal(diags.ErrWithWarnings()) 11116 } 11117 11118 // check the output, as those can't cause an error planning the value 11119 out := state.RootModule().OutputValues["out"].Value.AsString() 11120 if out != "a0" { 11121 t.Fatalf(`expected output "a0", got: %q`, out) 11122 } 11123 11124 // reduce the count to 0 11125 ctx = testContext2(t, &ContextOpts{ 11126 Providers: map[addrs.Provider]providers.Factory{ 11127 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11128 }, 11129 }) 11130 11131 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11132 Mode: plans.NormalMode, 11133 SetVariables: InputValues{ 11134 "ct": &InputValue{ 11135 Value: cty.NumberIntVal(0), 11136 SourceType: ValueFromCaller, 11137 }, 11138 }, 11139 }) 11140 if diags.HasErrors() { 11141 t.Fatal(diags.ErrWithWarnings()) 11142 } 11143 { 11144 addr := mustResourceInstanceAddr("test_instance.a[0]") 11145 change := plan.Changes.ResourceInstance(addr) 11146 if change == nil { 11147 t.Fatalf("no planned change for %s", addr) 11148 } 11149 if got, want := change.PrevRunAddr, mustResourceInstanceAddr("test_instance.a[0]"); !want.Equal(got) { 11150 t.Errorf("wrong previous run address for %s %s; want %s", addr, got, want) 11151 } 11152 if got, want := change.Action, plans.Delete; got != want { 11153 t.Errorf("wrong action for %s %s; want %s", addr, got, want) 11154 } 11155 if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseCountIndex; got != want { 11156 t.Errorf("wrong action reason for %s %s; want %s", addr, got, want) 11157 } 11158 } 11159 { 11160 addr := mustResourceInstanceAddr("test_instance.a[1]") 11161 change := plan.Changes.ResourceInstance(addr) 11162 if change != nil { 11163 // It was already removed in the previous plan/apply 11164 t.Errorf("unexpected planned change for %s", addr) 11165 } 11166 } 11167 11168 state, diags = ctx.Apply(plan, m) 11169 if diags.HasErrors() { 11170 t.Fatal(diags.ErrWithWarnings()) 11171 } 11172 11173 // check the output, as those can't cause an error planning the value 11174 out = state.RootModule().OutputValues["out"].Value.AsString() 11175 if out != "" { 11176 t.Fatalf(`expected output "", got: %q`, out) 11177 } 11178 } 11179 11180 // Ensure that we can destroy when a provider references a resource that will 11181 // also be destroyed 11182 func TestContext2Apply_destroyProviderReference(t *testing.T) { 11183 m := testModuleInline(t, map[string]string{ 11184 "main.tf": ` 11185 provider "null" { 11186 value = "" 11187 } 11188 11189 module "mod" { 11190 source = "./mod" 11191 } 11192 11193 provider "test" { 11194 value = module.mod.output 11195 } 11196 11197 resource "test_instance" "bar" { 11198 } 11199 `, 11200 "mod/main.tf": ` 11201 data "null_data_source" "foo" { 11202 count = 1 11203 } 11204 11205 11206 output "output" { 11207 value = data.null_data_source.foo[0].output 11208 } 11209 `}) 11210 11211 schemaFn := func(name string) *ProviderSchema { 11212 return &ProviderSchema{ 11213 Provider: &configschema.Block{ 11214 Attributes: map[string]*configschema.Attribute{ 11215 "value": { 11216 Type: cty.String, 11217 Required: true, 11218 }, 11219 }, 11220 }, 11221 ResourceTypes: map[string]*configschema.Block{ 11222 name + "_instance": { 11223 Attributes: map[string]*configschema.Attribute{ 11224 "id": { 11225 Type: cty.String, 11226 Computed: true, 11227 }, 11228 "foo": { 11229 Type: cty.String, 11230 Optional: true, 11231 }, 11232 }, 11233 }, 11234 }, 11235 DataSources: map[string]*configschema.Block{ 11236 name + "_data_source": { 11237 Attributes: map[string]*configschema.Attribute{ 11238 "id": { 11239 Type: cty.String, 11240 Computed: true, 11241 }, 11242 "output": { 11243 Type: cty.String, 11244 Computed: true, 11245 }, 11246 }, 11247 }, 11248 }, 11249 } 11250 } 11251 11252 testP := new(MockProvider) 11253 testP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 11254 return providers.ReadResourceResponse{NewState: req.PriorState} 11255 } 11256 testP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("test")) 11257 11258 providerConfig := "" 11259 testP.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 11260 value := req.Config.GetAttr("value") 11261 if value.IsKnown() && !value.IsNull() { 11262 providerConfig = value.AsString() 11263 } else { 11264 providerConfig = "" 11265 } 11266 return resp 11267 } 11268 testP.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 11269 if providerConfig != "valid" { 11270 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provider config is %q", providerConfig)) 11271 return 11272 } 11273 return testApplyFn(req) 11274 } 11275 testP.PlanResourceChangeFn = testDiffFn 11276 11277 nullP := new(MockProvider) 11278 nullP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 11279 return providers.ReadResourceResponse{NewState: req.PriorState} 11280 } 11281 nullP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("null")) 11282 11283 nullP.ApplyResourceChangeFn = testApplyFn 11284 nullP.PlanResourceChangeFn = testDiffFn 11285 11286 nullP.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 11287 State: cty.ObjectVal(map[string]cty.Value{ 11288 "id": cty.StringVal("ID"), 11289 "output": cty.StringVal("valid"), 11290 }), 11291 } 11292 11293 ctx := testContext2(t, &ContextOpts{ 11294 Providers: map[addrs.Provider]providers.Factory{ 11295 addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), 11296 addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), 11297 }, 11298 }) 11299 11300 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 11301 assertNoErrors(t, diags) 11302 11303 state, diags := ctx.Apply(plan, m) 11304 if diags.HasErrors() { 11305 t.Fatalf("apply errors: %s", diags.Err()) 11306 } 11307 11308 ctx = testContext2(t, &ContextOpts{ 11309 Providers: map[addrs.Provider]providers.Factory{ 11310 addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), 11311 addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), 11312 }, 11313 }) 11314 11315 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11316 Mode: plans.DestroyMode, 11317 }) 11318 assertNoErrors(t, diags) 11319 11320 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 11321 t.Fatalf("destroy apply errors: %s", diags.Err()) 11322 } 11323 } 11324 11325 // Destroying properly requires pruning out all unneeded config nodes to 11326 // prevent incorrect expansion evaluation. 11327 func TestContext2Apply_destroyInterModuleExpansion(t *testing.T) { 11328 m := testModuleInline(t, map[string]string{ 11329 "main.tf": ` 11330 data "test_data_source" "a" { 11331 for_each = { 11332 one = "thing" 11333 } 11334 } 11335 11336 locals { 11337 module_input = { 11338 for k, v in data.test_data_source.a : k => v.id 11339 } 11340 } 11341 11342 module "mod1" { 11343 source = "./mod" 11344 input = local.module_input 11345 } 11346 11347 module "mod2" { 11348 source = "./mod" 11349 input = module.mod1.outputs 11350 } 11351 11352 resource "test_instance" "bar" { 11353 for_each = module.mod2.outputs 11354 } 11355 11356 output "module_output" { 11357 value = module.mod2.outputs 11358 } 11359 output "test_instances" { 11360 value = test_instance.bar 11361 } 11362 `, 11363 "mod/main.tf": ` 11364 variable "input" { 11365 } 11366 11367 data "test_data_source" "foo" { 11368 for_each = var.input 11369 } 11370 11371 output "outputs" { 11372 value = data.test_data_source.foo 11373 } 11374 `}) 11375 11376 p := testProvider("test") 11377 p.PlanResourceChangeFn = testDiffFn 11378 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 11379 return providers.ReadDataSourceResponse{ 11380 State: cty.ObjectVal(map[string]cty.Value{ 11381 "id": cty.StringVal("data_source"), 11382 "foo": cty.StringVal("output"), 11383 }), 11384 } 11385 } 11386 11387 ctx := testContext2(t, &ContextOpts{ 11388 Providers: map[addrs.Provider]providers.Factory{ 11389 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11390 }, 11391 }) 11392 11393 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 11394 assertNoErrors(t, diags) 11395 11396 state, diags := ctx.Apply(plan, m) 11397 if diags.HasErrors() { 11398 t.Fatalf("apply errors: %s", diags.Err()) 11399 } 11400 11401 destroy := func() { 11402 ctx = testContext2(t, &ContextOpts{ 11403 Providers: map[addrs.Provider]providers.Factory{ 11404 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11405 }, 11406 }) 11407 11408 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11409 Mode: plans.DestroyMode, 11410 }) 11411 assertNoErrors(t, diags) 11412 11413 state, diags = ctx.Apply(plan, m) 11414 if diags.HasErrors() { 11415 t.Fatalf("destroy apply errors: %s", diags.Err()) 11416 } 11417 } 11418 11419 destroy() 11420 // Destroying again from the empty state should not cause any errors either 11421 destroy() 11422 } 11423 11424 func TestContext2Apply_createBeforeDestroyWithModule(t *testing.T) { 11425 m := testModuleInline(t, map[string]string{ 11426 "main.tf": ` 11427 variable "v" {} 11428 11429 module "mod" { 11430 source = "./mod" 11431 in = var.v 11432 } 11433 11434 resource "test_resource" "a" { 11435 value = var.v 11436 depends_on = [module.mod] 11437 lifecycle { 11438 create_before_destroy = true 11439 } 11440 } 11441 `, 11442 "mod/main.tf": ` 11443 variable "in" {} 11444 11445 resource "test_resource" "a" { 11446 value = var.in 11447 } 11448 `}) 11449 11450 p := testProvider("test") 11451 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 11452 proposed := req.ProposedNewState.AsValueMap() 11453 proposed["id"] = cty.UnknownVal(cty.String) 11454 return providers.PlanResourceChangeResponse{ 11455 PlannedState: cty.ObjectVal(proposed), 11456 RequiresReplace: []cty.Path{{cty.GetAttrStep{Name: "value"}}}, 11457 } 11458 } 11459 11460 ctx := testContext2(t, &ContextOpts{ 11461 Providers: map[addrs.Provider]providers.Factory{ 11462 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11463 }, 11464 }) 11465 11466 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 11467 Mode: plans.NormalMode, 11468 SetVariables: InputValues{ 11469 "v": &InputValue{ 11470 Value: cty.StringVal("A"), 11471 }, 11472 }, 11473 }) 11474 assertNoErrors(t, diags) 11475 11476 state, diags := ctx.Apply(plan, m) 11477 if diags.HasErrors() { 11478 t.Fatalf("apply errors: %s", diags.Err()) 11479 } 11480 11481 ctx = testContext2(t, &ContextOpts{ 11482 Providers: map[addrs.Provider]providers.Factory{ 11483 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11484 }, 11485 }) 11486 11487 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11488 Mode: plans.NormalMode, 11489 SetVariables: InputValues{ 11490 "v": &InputValue{ 11491 Value: cty.StringVal("B"), 11492 }, 11493 }, 11494 }) 11495 assertNoErrors(t, diags) 11496 11497 _, diags = ctx.Apply(plan, m) 11498 if diags.HasErrors() { 11499 t.Fatalf("apply errors: %s", diags.Err()) 11500 } 11501 } 11502 11503 func TestContext2Apply_forcedCBD(t *testing.T) { 11504 m := testModuleInline(t, map[string]string{ 11505 "main.tf": ` 11506 variable "v" {} 11507 11508 resource "test_instance" "a" { 11509 require_new = var.v 11510 } 11511 11512 resource "test_instance" "b" { 11513 depends_on = [test_instance.a] 11514 lifecycle { 11515 create_before_destroy = true 11516 } 11517 } 11518 `}) 11519 11520 p := testProvider("test") 11521 p.PlanResourceChangeFn = testDiffFn 11522 11523 ctx := testContext2(t, &ContextOpts{ 11524 Providers: map[addrs.Provider]providers.Factory{ 11525 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11526 }, 11527 }) 11528 11529 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 11530 Mode: plans.NormalMode, 11531 SetVariables: InputValues{ 11532 "v": &InputValue{ 11533 Value: cty.StringVal("A"), 11534 }, 11535 }, 11536 }) 11537 assertNoErrors(t, diags) 11538 11539 state, diags := ctx.Apply(plan, m) 11540 if diags.HasErrors() { 11541 t.Fatalf("apply errors: %s", diags.Err()) 11542 } 11543 11544 ctx = testContext2(t, &ContextOpts{ 11545 Providers: map[addrs.Provider]providers.Factory{ 11546 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11547 }, 11548 }) 11549 11550 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11551 Mode: plans.NormalMode, 11552 SetVariables: InputValues{ 11553 "v": &InputValue{ 11554 Value: cty.StringVal("B"), 11555 }, 11556 }, 11557 }) 11558 assertNoErrors(t, diags) 11559 11560 _, diags = ctx.Apply(plan, m) 11561 if diags.HasErrors() { 11562 t.Fatalf("apply errors: %s", diags.Err()) 11563 } 11564 } 11565 11566 func TestContext2Apply_removeReferencedResource(t *testing.T) { 11567 m := testModuleInline(t, map[string]string{ 11568 "main.tf": ` 11569 variable "ct" { 11570 } 11571 11572 resource "test_resource" "to_remove" { 11573 count = var.ct 11574 } 11575 11576 resource "test_resource" "c" { 11577 value = join("", test_resource.to_remove[*].id) 11578 } 11579 `}) 11580 11581 p := testProvider("test") 11582 p.PlanResourceChangeFn = testDiffFn 11583 11584 ctx := testContext2(t, &ContextOpts{ 11585 Providers: map[addrs.Provider]providers.Factory{ 11586 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11587 }, 11588 }) 11589 11590 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 11591 Mode: plans.NormalMode, 11592 SetVariables: InputValues{ 11593 "ct": &InputValue{ 11594 Value: cty.NumberIntVal(1), 11595 }, 11596 }, 11597 }) 11598 assertNoErrors(t, diags) 11599 11600 state, diags := ctx.Apply(plan, m) 11601 if diags.HasErrors() { 11602 t.Fatalf("apply errors: %s", diags.Err()) 11603 } 11604 11605 ctx = testContext2(t, &ContextOpts{ 11606 Providers: map[addrs.Provider]providers.Factory{ 11607 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11608 }, 11609 }) 11610 11611 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11612 Mode: plans.NormalMode, 11613 SetVariables: InputValues{ 11614 "ct": &InputValue{ 11615 Value: cty.NumberIntVal(0), 11616 }, 11617 }, 11618 }) 11619 assertNoErrors(t, diags) 11620 11621 _, diags = ctx.Apply(plan, m) 11622 if diags.HasErrors() { 11623 t.Fatalf("apply errors: %s", diags.Err()) 11624 } 11625 } 11626 11627 func TestContext2Apply_variableSensitivity(t *testing.T) { 11628 m := testModuleInline(t, map[string]string{ 11629 "main.tf": ` 11630 variable "sensitive_var" { 11631 default = "foo" 11632 sensitive = true 11633 } 11634 11635 variable "sensitive_id" { 11636 default = "secret id" 11637 sensitive = true 11638 } 11639 11640 resource "test_resource" "foo" { 11641 value = var.sensitive_var 11642 11643 network_interface { 11644 network_interface_id = var.sensitive_id 11645 } 11646 }`, 11647 }) 11648 11649 p := new(MockProvider) 11650 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 11651 return providers.ReadResourceResponse{NewState: req.PriorState} 11652 } 11653 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 11654 Provider: &configschema.Block{}, 11655 ResourceTypes: map[string]*configschema.Block{ 11656 "test_resource": { 11657 Attributes: map[string]*configschema.Attribute{ 11658 "id": { 11659 Type: cty.String, 11660 Computed: true, 11661 }, 11662 "value": { 11663 Type: cty.String, 11664 Optional: true, 11665 Computed: true, 11666 }, 11667 }, 11668 BlockTypes: map[string]*configschema.NestedBlock{ 11669 "network_interface": { 11670 Block: configschema.Block{ 11671 Attributes: map[string]*configschema.Attribute{ 11672 "network_interface_id": {Type: cty.String, Optional: true}, 11673 "device_index": {Type: cty.Number, Optional: true}, 11674 }, 11675 }, 11676 Nesting: configschema.NestingSet, 11677 }, 11678 }, 11679 }, 11680 }, 11681 }) 11682 p.PlanResourceChangeFn = testDiffFn 11683 11684 ctx := testContext2(t, &ContextOpts{ 11685 Providers: map[addrs.Provider]providers.Factory{ 11686 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11687 }, 11688 }) 11689 11690 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 11691 assertNoErrors(t, diags) 11692 11693 state, diags := ctx.Apply(plan, m) 11694 if diags.HasErrors() { 11695 t.Fatalf("apply errors: %s", diags.Err()) 11696 } 11697 11698 // Run a second apply with no changes 11699 ctx = testContext2(t, &ContextOpts{ 11700 Providers: map[addrs.Provider]providers.Factory{ 11701 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11702 }, 11703 }) 11704 11705 plan, diags = ctx.Plan(m, state, DefaultPlanOpts) 11706 assertNoErrors(t, diags) 11707 11708 state, diags = ctx.Apply(plan, m) 11709 if diags.HasErrors() { 11710 t.Fatalf("apply errors: %s", diags.Err()) 11711 } 11712 11713 // Now change the variable value for sensitive_var 11714 ctx = testContext2(t, &ContextOpts{ 11715 Providers: map[addrs.Provider]providers.Factory{ 11716 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11717 }, 11718 }) 11719 11720 plan, diags = ctx.Plan(m, state, &PlanOpts{ 11721 Mode: plans.NormalMode, 11722 SetVariables: InputValues{ 11723 "sensitive_var": &InputValue{ 11724 Value: cty.StringVal("bar"), 11725 }, 11726 }, 11727 }) 11728 assertNoErrors(t, diags) 11729 11730 _, diags = ctx.Apply(plan, m) 11731 if diags.HasErrors() { 11732 t.Fatalf("apply errors: %s", diags.Err()) 11733 } 11734 } 11735 11736 func TestContext2Apply_variableSensitivityPropagation(t *testing.T) { 11737 m := testModuleInline(t, map[string]string{ 11738 "main.tf": ` 11739 variable "sensitive_map" { 11740 type = map(string) 11741 default = { 11742 "x" = "foo" 11743 } 11744 sensitive = true 11745 } 11746 11747 resource "test_resource" "foo" { 11748 value = var.sensitive_map.x 11749 } 11750 `, 11751 }) 11752 11753 p := testProvider("test") 11754 p.PlanResourceChangeFn = testDiffFn 11755 11756 ctx := testContext2(t, &ContextOpts{ 11757 Providers: map[addrs.Provider]providers.Factory{ 11758 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11759 }, 11760 }) 11761 11762 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 11763 if diags.HasErrors() { 11764 t.Fatalf("plan errors: %s", diags.Err()) 11765 } 11766 11767 verifySensitiveValue := func(pvms []cty.PathValueMarks) { 11768 if len(pvms) != 1 { 11769 t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) 11770 } 11771 pvm := pvms[0] 11772 if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) { 11773 t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) 11774 } 11775 if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks(marks.Sensitive); !gotMarks.Equal(wantMarks) { 11776 t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) 11777 } 11778 } 11779 11780 addr := mustResourceInstanceAddr("test_resource.foo") 11781 fooChangeSrc := plan.Changes.ResourceInstance(addr) 11782 verifySensitiveValue(fooChangeSrc.AfterValMarks) 11783 11784 state, diags := ctx.Apply(plan, m) 11785 if diags.HasErrors() { 11786 t.Fatalf("apply errors: %s", diags.Err()) 11787 } 11788 11789 fooState := state.ResourceInstance(addr) 11790 verifySensitiveValue(fooState.Current.AttrSensitivePaths) 11791 } 11792 11793 func TestContext2Apply_variableSensitivityProviders(t *testing.T) { 11794 m := testModuleInline(t, map[string]string{ 11795 "main.tf": ` 11796 resource "test_resource" "foo" { 11797 sensitive_value = "should get marked" 11798 } 11799 11800 resource "test_resource" "bar" { 11801 value = test_resource.foo.sensitive_value 11802 random = test_resource.foo.id # not sensitive 11803 11804 nesting_single { 11805 value = "abc" 11806 sensitive_value = "xyz" 11807 } 11808 } 11809 11810 resource "test_resource" "baz" { 11811 value = test_resource.bar.nesting_single.sensitive_value 11812 } 11813 `, 11814 }) 11815 11816 p := testProvider("test") 11817 p.PlanResourceChangeFn = testDiffFn 11818 11819 ctx := testContext2(t, &ContextOpts{ 11820 Providers: map[addrs.Provider]providers.Factory{ 11821 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11822 }, 11823 }) 11824 11825 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 11826 if diags.HasErrors() { 11827 t.Fatalf("plan errors: %s", diags.Err()) 11828 } 11829 11830 verifySensitiveValue := func(pvms []cty.PathValueMarks) { 11831 if len(pvms) != 1 { 11832 t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) 11833 } 11834 pvm := pvms[0] 11835 if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) { 11836 t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) 11837 } 11838 if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks(marks.Sensitive); !gotMarks.Equal(wantMarks) { 11839 t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) 11840 } 11841 } 11842 11843 // Sensitive attributes (defined by the provider) are marked 11844 // as sensitive when referenced from another resource 11845 // "bar" references sensitive resources in "foo" 11846 barAddr := mustResourceInstanceAddr("test_resource.bar") 11847 barChangeSrc := plan.Changes.ResourceInstance(barAddr) 11848 verifySensitiveValue(barChangeSrc.AfterValMarks) 11849 11850 bazAddr := mustResourceInstanceAddr("test_resource.baz") 11851 bazChangeSrc := plan.Changes.ResourceInstance(bazAddr) 11852 verifySensitiveValue(bazChangeSrc.AfterValMarks) 11853 11854 state, diags := ctx.Apply(plan, m) 11855 if diags.HasErrors() { 11856 t.Fatalf("apply errors: %s", diags.Err()) 11857 } 11858 11859 barState := state.ResourceInstance(barAddr) 11860 verifySensitiveValue(barState.Current.AttrSensitivePaths) 11861 11862 bazState := state.ResourceInstance(bazAddr) 11863 verifySensitiveValue(bazState.Current.AttrSensitivePaths) 11864 } 11865 11866 func TestContext2Apply_variableSensitivityChange(t *testing.T) { 11867 m := testModuleInline(t, map[string]string{ 11868 "main.tf": ` 11869 variable "sensitive_var" { 11870 default = "hello" 11871 sensitive = true 11872 } 11873 11874 resource "test_resource" "foo" { 11875 value = var.sensitive_var 11876 }`, 11877 }) 11878 11879 p := testProvider("test") 11880 p.PlanResourceChangeFn = testDiffFn 11881 11882 ctx := testContext2(t, &ContextOpts{ 11883 Providers: map[addrs.Provider]providers.Factory{ 11884 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11885 }, 11886 }) 11887 11888 state := states.BuildState(func(s *states.SyncState) { 11889 s.SetResourceInstanceCurrent( 11890 addrs.Resource{ 11891 Mode: addrs.ManagedResourceMode, 11892 Type: "test_resource", 11893 Name: "foo", 11894 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 11895 &states.ResourceInstanceObjectSrc{ 11896 Status: states.ObjectReady, 11897 AttrsJSON: []byte(`{"id":"foo", "value":"hello"}`), 11898 // No AttrSensitivePaths present 11899 }, 11900 addrs.AbsProviderConfig{ 11901 Provider: addrs.NewDefaultProvider("test"), 11902 Module: addrs.RootModule, 11903 }, 11904 ) 11905 }) 11906 11907 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 11908 assertNoErrors(t, diags) 11909 11910 addr := mustResourceInstanceAddr("test_resource.foo") 11911 11912 state, diags = ctx.Apply(plan, m) 11913 assertNoErrors(t, diags) 11914 11915 fooState := state.ResourceInstance(addr) 11916 11917 if len(fooState.Current.AttrSensitivePaths) != 1 { 11918 t.Fatalf("wrong number of sensitive paths, expected 1, got, %v", len(fooState.Current.AttrSensitivePaths)) 11919 } 11920 got := fooState.Current.AttrSensitivePaths[0] 11921 want := cty.PathValueMarks{ 11922 Path: cty.GetAttrPath("value"), 11923 Marks: cty.NewValueMarks(marks.Sensitive), 11924 } 11925 11926 if !got.Equal(want) { 11927 t.Fatalf("wrong value marks; got:\n%#v\n\nwant:\n%#v\n", got, want) 11928 } 11929 11930 m2 := testModuleInline(t, map[string]string{ 11931 "main.tf": ` 11932 variable "sensitive_var" { 11933 default = "hello" 11934 sensitive = false 11935 } 11936 11937 resource "test_resource" "foo" { 11938 value = var.sensitive_var 11939 }`, 11940 }) 11941 11942 ctx2 := testContext2(t, &ContextOpts{ 11943 Providers: map[addrs.Provider]providers.Factory{ 11944 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11945 }, 11946 }) 11947 11948 // NOTE: Prior to our refactoring to make the state an explicit argument 11949 // of Plan, as opposed to hidden state inside Context, this test was 11950 // calling ctx.Apply instead of ctx2.Apply and thus using the previous 11951 // plan instead of this new plan. "Fixing" it to use the new plan seems 11952 // to break the test, so we've preserved that oddity here by saving the 11953 // old plan as oldPlan and essentially discarding the new plan entirely, 11954 // but this seems rather suspicious and we should ideally figure out what 11955 // this test was originally intending to do and make it do that. 11956 oldPlan := plan 11957 _, diags = ctx2.Plan(m2, state, DefaultPlanOpts) 11958 assertNoErrors(t, diags) 11959 stateWithoutSensitive, diags := ctx.Apply(oldPlan, m) 11960 assertNoErrors(t, diags) 11961 11962 fooState2 := stateWithoutSensitive.ResourceInstance(addr) 11963 if len(fooState2.Current.AttrSensitivePaths) > 0 { 11964 t.Fatalf( 11965 "wrong number of sensitive paths, expected 0, got, %v\n%s", 11966 len(fooState2.Current.AttrSensitivePaths), 11967 spew.Sdump(fooState2.Current.AttrSensitivePaths), 11968 ) 11969 } 11970 } 11971 11972 func TestContext2Apply_moduleVariableOptionalAttributes(t *testing.T) { 11973 m := testModuleInline(t, map[string]string{ 11974 "main.tf": ` 11975 terraform { 11976 experiments = [module_variable_optional_attrs] 11977 } 11978 11979 variable "in" { 11980 type = object({ 11981 required = string 11982 optional = optional(string) 11983 }) 11984 } 11985 11986 output "out" { 11987 value = var.in 11988 } 11989 `}) 11990 11991 ctx := testContext2(t, &ContextOpts{}) 11992 11993 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 11994 Mode: plans.NormalMode, 11995 SetVariables: InputValues{ 11996 "in": &InputValue{ 11997 Value: cty.MapVal(map[string]cty.Value{ 11998 "required": cty.StringVal("boop"), 11999 }), 12000 SourceType: ValueFromCaller, 12001 }, 12002 }, 12003 }) 12004 if diags.HasErrors() { 12005 t.Fatal(diags.ErrWithWarnings()) 12006 } 12007 12008 state, diags := ctx.Apply(plan, m) 12009 if diags.HasErrors() { 12010 t.Fatal(diags.ErrWithWarnings()) 12011 } 12012 12013 got := state.RootModule().OutputValues["out"].Value 12014 want := cty.ObjectVal(map[string]cty.Value{ 12015 "required": cty.StringVal("boop"), 12016 12017 // Because "optional" was marked as optional, it got silently filled 12018 // in as a null value of string type rather than returning an error. 12019 "optional": cty.NullVal(cty.String), 12020 }) 12021 if !want.RawEquals(got) { 12022 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, want) 12023 } 12024 } 12025 12026 func TestContext2Apply_provisionerSensitive(t *testing.T) { 12027 m := testModule(t, "apply-provisioner-sensitive") 12028 p := testProvider("aws") 12029 12030 pr := testProvisioner() 12031 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 12032 if req.Config.ContainsMarked() { 12033 t.Fatalf("unexpectedly marked config value: %#v", req.Config) 12034 } 12035 command := req.Config.GetAttr("command") 12036 if command.IsMarked() { 12037 t.Fatalf("unexpectedly marked command argument: %#v", command.Marks()) 12038 } 12039 req.UIOutput.Output(fmt.Sprintf("Executing: %q", command.AsString())) 12040 return 12041 } 12042 p.PlanResourceChangeFn = testDiffFn 12043 p.ApplyResourceChangeFn = testApplyFn 12044 12045 h := new(MockHook) 12046 ctx := testContext2(t, &ContextOpts{ 12047 Hooks: []Hook{h}, 12048 Providers: map[addrs.Provider]providers.Factory{ 12049 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 12050 }, 12051 Provisioners: map[string]provisioners.Factory{ 12052 "shell": testProvisionerFuncFixed(pr), 12053 }, 12054 }) 12055 12056 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 12057 Mode: plans.NormalMode, 12058 SetVariables: InputValues{ 12059 "password": &InputValue{ 12060 Value: cty.StringVal("secret"), 12061 SourceType: ValueFromCaller, 12062 }, 12063 }, 12064 }) 12065 assertNoErrors(t, diags) 12066 12067 // "restart" provisioner 12068 pr.CloseCalled = false 12069 12070 state, diags := ctx.Apply(plan, m) 12071 if diags.HasErrors() { 12072 logDiagnostics(t, diags) 12073 t.Fatal("apply failed") 12074 } 12075 12076 actual := strings.TrimSpace(state.String()) 12077 expected := strings.TrimSpace(testTerraformApplyProvisionerSensitiveStr) 12078 if actual != expected { 12079 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 12080 } 12081 12082 // Verify apply was invoked 12083 if !pr.ProvisionResourceCalled { 12084 t.Fatalf("provisioner was not called on apply") 12085 } 12086 12087 // Verify output was suppressed 12088 if !h.ProvisionOutputCalled { 12089 t.Fatalf("ProvisionOutput hook not called") 12090 } 12091 if got, doNotWant := h.ProvisionOutputMessage, "secret"; strings.Contains(got, doNotWant) { 12092 t.Errorf("sensitive value %q included in output:\n%s", doNotWant, got) 12093 } 12094 if got, want := h.ProvisionOutputMessage, "output suppressed"; !strings.Contains(got, want) { 12095 t.Errorf("expected hook to be called with %q, but was:\n%s", want, got) 12096 } 12097 } 12098 12099 func TestContext2Apply_warnings(t *testing.T) { 12100 m := testModuleInline(t, map[string]string{ 12101 "main.tf": ` 12102 resource "test_resource" "foo" { 12103 }`, 12104 }) 12105 12106 p := testProvider("test") 12107 p.PlanResourceChangeFn = testDiffFn 12108 12109 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 12110 resp := testApplyFn(req) 12111 12112 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warning")) 12113 return resp 12114 } 12115 12116 ctx := testContext2(t, &ContextOpts{ 12117 Providers: map[addrs.Provider]providers.Factory{ 12118 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12119 }, 12120 }) 12121 12122 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 12123 assertNoErrors(t, diags) 12124 12125 state, diags := ctx.Apply(plan, m) 12126 if diags.HasErrors() { 12127 t.Fatalf("diags: %s", diags.Err()) 12128 } 12129 12130 inst := state.ResourceInstance(mustResourceInstanceAddr("test_resource.foo")) 12131 if inst == nil { 12132 t.Fatal("missing 'test_resource.foo' in state:", state) 12133 } 12134 } 12135 12136 func TestContext2Apply_rpcDiagnostics(t *testing.T) { 12137 m := testModuleInline(t, map[string]string{ 12138 "main.tf": ` 12139 resource "test_instance" "a" { 12140 } 12141 `, 12142 }) 12143 12144 p := testProvider("test") 12145 p.PlanResourceChangeFn = testDiffFn 12146 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 12147 resp = testApplyFn(req) 12148 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("don't frobble")) 12149 return resp 12150 } 12151 12152 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 12153 ResourceTypes: map[string]*configschema.Block{ 12154 "test_instance": { 12155 Attributes: map[string]*configschema.Attribute{ 12156 "id": {Type: cty.String, Computed: true}, 12157 }, 12158 }, 12159 }, 12160 }) 12161 12162 ctx := testContext2(t, &ContextOpts{ 12163 Providers: map[addrs.Provider]providers.Factory{ 12164 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12165 }, 12166 }) 12167 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 12168 if diags.HasErrors() { 12169 t.Fatal(diags.Err()) 12170 } 12171 12172 _, diags = ctx.Apply(plan, m) 12173 if diags.HasErrors() { 12174 t.Fatal(diags.Err()) 12175 } 12176 12177 if len(diags) == 0 { 12178 t.Fatal("expected warnings") 12179 } 12180 12181 for _, d := range diags { 12182 des := d.Description().Summary 12183 if !strings.Contains(des, "frobble") { 12184 t.Fatalf(`expected frobble, got %q`, des) 12185 } 12186 } 12187 } 12188 12189 func TestContext2Apply_dataSensitive(t *testing.T) { 12190 m := testModule(t, "apply-data-sensitive") 12191 p := testProvider("null") 12192 p.PlanResourceChangeFn = testDiffFn 12193 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 12194 // add the required id 12195 m := req.Config.AsValueMap() 12196 m["id"] = cty.StringVal("foo") 12197 12198 return providers.ReadDataSourceResponse{ 12199 State: cty.ObjectVal(m), 12200 } 12201 } 12202 12203 ctx := testContext2(t, &ContextOpts{ 12204 Providers: map[addrs.Provider]providers.Factory{ 12205 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 12206 }, 12207 }) 12208 12209 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 12210 if diags.HasErrors() { 12211 t.Fatalf("diags: %s", diags.Err()) 12212 } else { 12213 t.Logf(legacyDiffComparisonString(plan.Changes)) 12214 } 12215 12216 state, diags := ctx.Apply(plan, m) 12217 assertNoErrors(t, diags) 12218 12219 addr := mustResourceInstanceAddr("data.null_data_source.testing") 12220 12221 dataSourceState := state.ResourceInstance(addr) 12222 pvms := dataSourceState.Current.AttrSensitivePaths 12223 if len(pvms) != 1 { 12224 t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) 12225 } 12226 pvm := pvms[0] 12227 if gotPath, wantPath := pvm.Path, cty.GetAttrPath("foo"); !gotPath.Equals(wantPath) { 12228 t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) 12229 } 12230 if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks(marks.Sensitive); !gotMarks.Equal(wantMarks) { 12231 t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) 12232 } 12233 } 12234 12235 func TestContext2Apply_errorRestorePrivateData(t *testing.T) { 12236 // empty config to remove our resource 12237 m := testModuleInline(t, map[string]string{ 12238 "main.tf": "", 12239 }) 12240 12241 p := simpleMockProvider() 12242 p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ 12243 // we error during apply, which will trigger core to preserve the last 12244 // known state, including private data 12245 Diagnostics: tfdiags.Diagnostics(nil).Append(errors.New("oops")), 12246 } 12247 12248 addr := mustResourceInstanceAddr("test_object.a") 12249 12250 state := states.BuildState(func(s *states.SyncState) { 12251 s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ 12252 Status: states.ObjectReady, 12253 AttrsJSON: []byte(`{"id":"foo"}`), 12254 Private: []byte("private"), 12255 }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) 12256 }) 12257 12258 ctx := testContext2(t, &ContextOpts{ 12259 Providers: map[addrs.Provider]providers.Factory{ 12260 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12261 }, 12262 }) 12263 12264 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 12265 if diags.HasErrors() { 12266 t.Fatal(diags.Err()) 12267 } 12268 12269 state, _ = ctx.Apply(plan, m) 12270 if string(state.ResourceInstance(addr).Current.Private) != "private" { 12271 t.Fatal("missing private data in state") 12272 } 12273 } 12274 12275 func TestContext2Apply_errorRestoreStatus(t *testing.T) { 12276 // empty config to remove our resource 12277 m := testModuleInline(t, map[string]string{ 12278 "main.tf": "", 12279 }) 12280 12281 p := simpleMockProvider() 12282 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 12283 // We error during apply, but return the current object state. 12284 resp.Diagnostics = resp.Diagnostics.Append(errors.New("oops")) 12285 // return a warning too to make sure it isn't dropped 12286 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warned")) 12287 resp.NewState = req.PriorState 12288 resp.Private = req.PlannedPrivate 12289 return resp 12290 } 12291 12292 addr := mustResourceInstanceAddr("test_object.a") 12293 12294 state := states.BuildState(func(s *states.SyncState) { 12295 s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ 12296 Status: states.ObjectTainted, 12297 AttrsJSON: []byte(`{"test_string":"foo"}`), 12298 Private: []byte("private"), 12299 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.b")}, 12300 }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) 12301 }) 12302 12303 ctx := testContext2(t, &ContextOpts{ 12304 Providers: map[addrs.Provider]providers.Factory{ 12305 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12306 }, 12307 }) 12308 12309 plan, diags := ctx.Plan(m, state, DefaultPlanOpts) 12310 if diags.HasErrors() { 12311 t.Fatal(diags.Err()) 12312 } 12313 12314 state, diags = ctx.Apply(plan, m) 12315 12316 errString := diags.ErrWithWarnings().Error() 12317 if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") { 12318 t.Fatalf("error missing expected info: %q", errString) 12319 } 12320 12321 if len(diags) != 2 { 12322 t.Fatalf("expected 1 error and 1 warning, got: %q", errString) 12323 } 12324 12325 res := state.ResourceInstance(addr) 12326 if res == nil { 12327 t.Fatal("resource was removed from state") 12328 } 12329 12330 if res.Current.Status != states.ObjectTainted { 12331 t.Fatal("resource should still be tainted in the state") 12332 } 12333 12334 if len(res.Current.Dependencies) != 1 || !res.Current.Dependencies[0].Equal(mustConfigResourceAddr("test_object.b")) { 12335 t.Fatalf("incorrect dependencies, got %q", res.Current.Dependencies) 12336 } 12337 12338 if string(res.Current.Private) != "private" { 12339 t.Fatalf("incorrect private data, got %q", res.Current.Private) 12340 } 12341 } 12342 12343 func TestContext2Apply_nonConformingResponse(t *testing.T) { 12344 // empty config to remove our resource 12345 m := testModuleInline(t, map[string]string{ 12346 "main.tf": ` 12347 resource "test_object" "a" { 12348 test_string = "x" 12349 } 12350 `, 12351 }) 12352 12353 p := simpleMockProvider() 12354 respDiags := tfdiags.Diagnostics(nil).Append(tfdiags.SimpleWarning("warned")) 12355 respDiags = respDiags.Append(errors.New("oops")) 12356 p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ 12357 // Don't lose these diagnostics 12358 Diagnostics: respDiags, 12359 // This state is missing required attributes, and should produce an error 12360 NewState: cty.ObjectVal(map[string]cty.Value{ 12361 "test_string": cty.StringVal("x"), 12362 }), 12363 } 12364 12365 ctx := testContext2(t, &ContextOpts{ 12366 Providers: map[addrs.Provider]providers.Factory{ 12367 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12368 }, 12369 }) 12370 12371 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 12372 if diags.HasErrors() { 12373 t.Fatal(diags.Err()) 12374 } 12375 12376 _, diags = ctx.Apply(plan, m) 12377 errString := diags.ErrWithWarnings().Error() 12378 if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") { 12379 t.Fatalf("error missing expected info: %q", errString) 12380 } 12381 12382 // we should have more than the ones returned from the provider, and they 12383 // should not be coalesced into a single value 12384 if len(diags) < 3 { 12385 t.Fatalf("incorrect diagnostics, got %d values with %s", len(diags), diags.ErrWithWarnings()) 12386 } 12387 } 12388 12389 func TestContext2Apply_nilResponse(t *testing.T) { 12390 // empty config to remove our resource 12391 m := testModuleInline(t, map[string]string{ 12392 "main.tf": ` 12393 resource "test_object" "a" { 12394 } 12395 `, 12396 }) 12397 12398 p := simpleMockProvider() 12399 p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{} 12400 12401 ctx := testContext2(t, &ContextOpts{ 12402 Providers: map[addrs.Provider]providers.Factory{ 12403 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12404 }, 12405 }) 12406 12407 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 12408 if diags.HasErrors() { 12409 t.Fatal(diags.Err()) 12410 } 12411 12412 _, diags = ctx.Apply(plan, m) 12413 if !diags.HasErrors() { 12414 t.Fatal("expected and error") 12415 } 12416 12417 errString := diags.ErrWithWarnings().Error() 12418 if !strings.Contains(errString, "invalid nil value") { 12419 t.Fatalf("error missing expected info: %q", errString) 12420 } 12421 } 12422 12423 //////////////////////////////////////////////////////////////////////////////// 12424 // NOTE: Due to the size of this file, new tests should be added to 12425 // context_apply2_test.go. 12426 ////////////////////////////////////////////////////////////////////////////////