1package terraform 2 3import ( 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/hashicorp/terraform/internal/addrs" 22 "github.com/hashicorp/terraform/internal/configs" 23 "github.com/hashicorp/terraform/internal/configs/configschema" 24 "github.com/hashicorp/terraform/internal/configs/hcl2shim" 25 "github.com/hashicorp/terraform/internal/plans" 26 "github.com/hashicorp/terraform/internal/providers" 27 "github.com/hashicorp/terraform/internal/provisioners" 28 "github.com/hashicorp/terraform/internal/states" 29 "github.com/hashicorp/terraform/internal/tfdiags" 30 "github.com/zclconf/go-cty/cty" 31 "github.com/zclconf/go-cty/cty/gocty" 32) 33 34func TestContext2Apply_basic(t *testing.T) { 35 m := testModule(t, "apply-good") 36 p := testProvider("aws") 37 p.PlanResourceChangeFn = testDiffFn 38 p.ApplyResourceChangeFn = testApplyFn 39 ctx := testContext2(t, &ContextOpts{ 40 Config: m, 41 Providers: map[addrs.Provider]providers.Factory{ 42 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 43 }, 44 }) 45 46 if _, diags := ctx.Plan(); diags.HasErrors() { 47 t.Fatalf("plan errors: %s", diags.Err()) 48 } 49 50 state, diags := ctx.Apply() 51 if diags.HasErrors() { 52 t.Fatalf("diags: %s", diags.Err()) 53 } 54 55 mod := state.RootModule() 56 if len(mod.Resources) < 2 { 57 t.Fatalf("bad: %#v", mod.Resources) 58 } 59 60 actual := strings.TrimSpace(state.String()) 61 expected := strings.TrimSpace(testTerraformApplyStr) 62 if actual != expected { 63 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 64 } 65} 66 67func TestContext2Apply_unstable(t *testing.T) { 68 // This tests behavior when the configuration contains an unstable value, 69 // such as the result of uuid() or timestamp(), where each call produces 70 // a different result. 71 // 72 // This is an important case to test because we need to ensure that 73 // we don't re-call the function during the apply phase: the value should 74 // be fixed during plan 75 76 m := testModule(t, "apply-unstable") 77 p := testProvider("test") 78 p.PlanResourceChangeFn = testDiffFn 79 ctx := testContext2(t, &ContextOpts{ 80 Config: m, 81 Providers: map[addrs.Provider]providers.Factory{ 82 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 83 }, 84 }) 85 86 plan, diags := ctx.Plan() 87 if diags.HasErrors() { 88 t.Fatalf("unexpected error during Plan: %s", diags.Err()) 89 } 90 91 addr := addrs.Resource{ 92 Mode: addrs.ManagedResourceMode, 93 Type: "test_resource", 94 Name: "foo", 95 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) 96 schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block 97 rds := plan.Changes.ResourceInstance(addr) 98 rd, err := rds.Decode(schema.ImpliedType()) 99 if err != nil { 100 t.Fatal(err) 101 } 102 if rd.After.GetAttr("random").IsKnown() { 103 t.Fatalf("Attribute 'random' has known value %#v; should be unknown in plan", rd.After.GetAttr("random")) 104 } 105 106 state, diags := ctx.Apply() 107 if diags.HasErrors() { 108 t.Fatalf("unexpected error during Apply: %s", diags.Err()) 109 } 110 111 mod := state.Module(addr.Module) 112 rss := state.ResourceInstance(addr) 113 114 if len(mod.Resources) != 1 { 115 t.Fatalf("wrong number of resources %d; want 1", len(mod.Resources)) 116 } 117 118 rs, err := rss.Current.Decode(schema.ImpliedType()) 119 if err != nil { 120 t.Fatalf("decode error: %v", err) 121 } 122 got := rs.Value.GetAttr("random") 123 if !got.IsKnown() { 124 t.Fatalf("random is still unknown after apply") 125 } 126 if got, want := len(got.AsString()), 36; got != want { 127 t.Fatalf("random string has wrong length %d; want %d", got, want) 128 } 129} 130 131func TestContext2Apply_escape(t *testing.T) { 132 m := testModule(t, "apply-escape") 133 p := testProvider("aws") 134 p.PlanResourceChangeFn = testDiffFn 135 p.ApplyResourceChangeFn = testApplyFn 136 ctx := testContext2(t, &ContextOpts{ 137 Config: m, 138 Providers: map[addrs.Provider]providers.Factory{ 139 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 140 }, 141 }) 142 143 if _, diags := ctx.Plan(); diags.HasErrors() { 144 t.Fatalf("plan errors: %s", diags.Err()) 145 } 146 147 state, diags := ctx.Apply() 148 if diags.HasErrors() { 149 t.Fatalf("diags: %s", diags.Err()) 150 } 151 152 checkStateString(t, state, ` 153aws_instance.bar: 154 ID = foo 155 provider = provider["registry.terraform.io/hashicorp/aws"] 156 foo = "bar" 157 type = aws_instance 158`) 159} 160 161func TestContext2Apply_resourceCountOneList(t *testing.T) { 162 m := testModule(t, "apply-resource-count-one-list") 163 p := testProvider("null") 164 p.PlanResourceChangeFn = testDiffFn 165 p.ApplyResourceChangeFn = testApplyFn 166 ctx := testContext2(t, &ContextOpts{ 167 Config: m, 168 Providers: map[addrs.Provider]providers.Factory{ 169 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 170 }, 171 }) 172 173 if _, diags := ctx.Plan(); diags.HasErrors() { 174 t.Fatalf("plan errors: %s", diags.Err()) 175 } 176 177 state, diags := ctx.Apply() 178 assertNoDiagnostics(t, diags) 179 180 got := strings.TrimSpace(state.String()) 181 want := strings.TrimSpace(`null_resource.foo.0: 182 ID = foo 183 provider = provider["registry.terraform.io/hashicorp/null"] 184 185Outputs: 186 187test = [foo]`) 188 if got != want { 189 t.Fatalf("got:\n%s\n\nwant:\n%s\n", got, want) 190 } 191} 192func TestContext2Apply_resourceCountZeroList(t *testing.T) { 193 m := testModule(t, "apply-resource-count-zero-list") 194 p := testProvider("null") 195 p.PlanResourceChangeFn = testDiffFn 196 ctx := testContext2(t, &ContextOpts{ 197 Config: m, 198 Providers: map[addrs.Provider]providers.Factory{ 199 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 200 }, 201 }) 202 203 if _, diags := ctx.Plan(); diags.HasErrors() { 204 t.Fatalf("plan errors: %s", diags.Err()) 205 } 206 207 state, diags := ctx.Apply() 208 if diags.HasErrors() { 209 t.Fatalf("diags: %s", diags.Err()) 210 } 211 212 got := strings.TrimSpace(state.String()) 213 want := strings.TrimSpace(`<no state> 214Outputs: 215 216test = []`) 217 if got != want { 218 t.Fatalf("wrong state\n\ngot:\n%s\n\nwant:\n%s\n", got, want) 219 } 220} 221 222func TestContext2Apply_resourceDependsOnModule(t *testing.T) { 223 m := testModule(t, "apply-resource-depends-on-module") 224 p := testProvider("aws") 225 p.PlanResourceChangeFn = testDiffFn 226 227 // verify the apply happens in the correct order 228 var mu sync.Mutex 229 var order []string 230 231 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 232 ami := req.PlannedState.GetAttr("ami").AsString() 233 switch ami { 234 case "child": 235 236 // make the child slower than the parent 237 time.Sleep(50 * time.Millisecond) 238 239 mu.Lock() 240 order = append(order, "child") 241 mu.Unlock() 242 case "parent": 243 mu.Lock() 244 order = append(order, "parent") 245 mu.Unlock() 246 } 247 248 return testApplyFn(req) 249 } 250 251 ctx := testContext2(t, &ContextOpts{ 252 Config: m, 253 Providers: map[addrs.Provider]providers.Factory{ 254 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 255 }, 256 }) 257 258 if _, diags := ctx.Plan(); diags.HasErrors() { 259 t.Fatalf("plan errors: %s", diags.Err()) 260 } 261 262 state, diags := ctx.Apply() 263 if diags.HasErrors() { 264 t.Fatalf("diags: %s", diags.Err()) 265 } 266 267 if !reflect.DeepEqual(order, []string{"child", "parent"}) { 268 t.Fatal("resources applied out of order") 269 } 270 271 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleStr) 272} 273 274// Test that without a config, the Dependencies in the state are enough 275// to maintain proper ordering. 276func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) { 277 m := testModule(t, "apply-resource-depends-on-module-empty") 278 p := testProvider("aws") 279 p.PlanResourceChangeFn = testDiffFn 280 281 state := states.NewState() 282 root := state.EnsureModule(addrs.RootModuleInstance) 283 root.SetResourceInstanceCurrent( 284 mustResourceInstanceAddr("aws_instance.a").Resource, 285 &states.ResourceInstanceObjectSrc{ 286 Status: states.ObjectReady, 287 AttrsJSON: []byte(`{"id":"parent"}`), 288 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.aws_instance.child")}, 289 }, 290 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 291 ) 292 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 293 child.SetResourceInstanceCurrent( 294 mustResourceInstanceAddr("aws_instance.child").Resource, 295 &states.ResourceInstanceObjectSrc{ 296 Status: states.ObjectReady, 297 AttrsJSON: []byte(`{"id":"child"}`), 298 }, 299 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 300 ) 301 302 { 303 // verify the apply happens in the correct order 304 var mu sync.Mutex 305 var order []string 306 307 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 308 id := req.PriorState.GetAttr("id") 309 if id.IsKnown() && id.AsString() == "parent" { 310 // make the dep slower than the parent 311 time.Sleep(50 * time.Millisecond) 312 313 mu.Lock() 314 order = append(order, "child") 315 mu.Unlock() 316 } else { 317 mu.Lock() 318 order = append(order, "parent") 319 mu.Unlock() 320 } 321 322 return testApplyFn(req) 323 } 324 325 ctx := testContext2(t, &ContextOpts{ 326 Config: m, 327 Providers: map[addrs.Provider]providers.Factory{ 328 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 329 }, 330 State: state, 331 }) 332 333 if _, diags := ctx.Plan(); diags.HasErrors() { 334 t.Fatalf("diags: %s", diags.Err()) 335 } 336 337 state, diags := ctx.Apply() 338 assertNoErrors(t, diags) 339 340 if !reflect.DeepEqual(order, []string{"child", "parent"}) { 341 t.Fatal("resources applied out of order") 342 } 343 344 checkStateString(t, state, "<no state>") 345 } 346} 347 348func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) { 349 m := testModule(t, "apply-resource-depends-on-module") 350 p := testProvider("aws") 351 p.PlanResourceChangeFn = testDiffFn 352 353 var globalState *states.State 354 { 355 ctx := testContext2(t, &ContextOpts{ 356 Config: m, 357 Providers: map[addrs.Provider]providers.Factory{ 358 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 359 }, 360 }) 361 362 if _, diags := ctx.Plan(); diags.HasErrors() { 363 t.Fatalf("diags: %s", diags.Err()) 364 } 365 366 state, diags := ctx.Apply() 367 if diags.HasErrors() { 368 t.Fatalf("diags: %s", diags.Err()) 369 } 370 371 globalState = state 372 } 373 374 { 375 // Wait for the dependency, sleep, and verify the graph never 376 // called a child. 377 var called int32 378 var checked bool 379 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 380 ami := req.PriorState.GetAttr("ami").AsString() 381 if ami == "parent" { 382 checked = true 383 384 // Sleep to allow parallel execution 385 time.Sleep(50 * time.Millisecond) 386 387 // Verify that called is 0 (dep not called) 388 if atomic.LoadInt32(&called) != 0 { 389 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("module child should not be called")) 390 return resp 391 } 392 } 393 394 atomic.AddInt32(&called, 1) 395 return testApplyFn(req) 396 } 397 398 ctx := testContext2(t, &ContextOpts{ 399 Config: m, 400 Providers: map[addrs.Provider]providers.Factory{ 401 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 402 }, 403 State: globalState, 404 PlanMode: plans.DestroyMode, 405 }) 406 407 if _, diags := ctx.Plan(); diags.HasErrors() { 408 t.Fatalf("diags: %s", diags.Err()) 409 } 410 411 state, diags := ctx.Apply() 412 if diags.HasErrors() { 413 t.Fatalf("diags: %s", diags.Err()) 414 } 415 416 if !checked { 417 t.Fatal("should check") 418 } 419 420 checkStateString(t, state, `<no state>`) 421 } 422} 423 424func TestContext2Apply_resourceDependsOnModuleGrandchild(t *testing.T) { 425 m := testModule(t, "apply-resource-depends-on-module-deep") 426 p := testProvider("aws") 427 p.PlanResourceChangeFn = testDiffFn 428 429 { 430 // Wait for the dependency, sleep, and verify the graph never 431 // called a child. 432 var called int32 433 var checked bool 434 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 435 planned := req.PlannedState.AsValueMap() 436 if ami, ok := planned["ami"]; ok && ami.AsString() == "grandchild" { 437 checked = true 438 439 // Sleep to allow parallel execution 440 time.Sleep(50 * time.Millisecond) 441 442 // Verify that called is 0 (dep not called) 443 if atomic.LoadInt32(&called) != 0 { 444 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("aws_instance.a should not be called")) 445 return resp 446 } 447 } 448 449 atomic.AddInt32(&called, 1) 450 return testApplyFn(req) 451 } 452 453 ctx := testContext2(t, &ContextOpts{ 454 Config: m, 455 Providers: map[addrs.Provider]providers.Factory{ 456 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 457 }, 458 }) 459 460 if _, diags := ctx.Plan(); diags.HasErrors() { 461 t.Fatalf("diags: %s", diags.Err()) 462 } 463 464 state, diags := ctx.Apply() 465 if diags.HasErrors() { 466 t.Fatalf("diags: %s", diags.Err()) 467 } 468 469 if !checked { 470 t.Fatal("should check") 471 } 472 473 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleDeepStr) 474 } 475} 476 477func TestContext2Apply_resourceDependsOnModuleInModule(t *testing.T) { 478 m := testModule(t, "apply-resource-depends-on-module-in-module") 479 p := testProvider("aws") 480 p.PlanResourceChangeFn = testDiffFn 481 482 { 483 // Wait for the dependency, sleep, and verify the graph never 484 // called a child. 485 var called int32 486 var checked bool 487 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 488 planned := req.PlannedState.AsValueMap() 489 if ami, ok := planned["ami"]; ok && ami.AsString() == "grandchild" { 490 checked = true 491 492 // Sleep to allow parallel execution 493 time.Sleep(50 * time.Millisecond) 494 495 // Verify that called is 0 (dep not called) 496 if atomic.LoadInt32(&called) != 0 { 497 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("something else was applied before grandchild; grandchild should be first")) 498 return resp 499 } 500 } 501 502 atomic.AddInt32(&called, 1) 503 return testApplyFn(req) 504 } 505 506 ctx := testContext2(t, &ContextOpts{ 507 Config: m, 508 Providers: map[addrs.Provider]providers.Factory{ 509 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 510 }, 511 }) 512 513 if _, diags := ctx.Plan(); diags.HasErrors() { 514 t.Fatalf("diags: %s", diags.Err()) 515 } 516 517 state, diags := ctx.Apply() 518 if diags.HasErrors() { 519 t.Fatalf("diags: %s", diags.Err()) 520 } 521 522 if !checked { 523 t.Fatal("should check") 524 } 525 526 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleInModuleStr) 527 } 528} 529 530func TestContext2Apply_mapVarBetweenModules(t *testing.T) { 531 m := testModule(t, "apply-map-var-through-module") 532 p := testProvider("null") 533 p.PlanResourceChangeFn = testDiffFn 534 p.ApplyResourceChangeFn = testApplyFn 535 ctx := testContext2(t, &ContextOpts{ 536 Config: m, 537 Providers: map[addrs.Provider]providers.Factory{ 538 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 539 }, 540 }) 541 542 if _, diags := ctx.Plan(); diags.HasErrors() { 543 t.Fatalf("plan errors: %s", diags.Err()) 544 } 545 546 state, diags := ctx.Apply() 547 if diags.HasErrors() { 548 t.Fatalf("diags: %s", diags.Err()) 549 } 550 551 actual := strings.TrimSpace(state.String()) 552 expected := strings.TrimSpace(`<no state> 553Outputs: 554 555amis_from_module = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 } 556 557module.test: 558 null_resource.noop: 559 ID = foo 560 provider = provider["registry.terraform.io/hashicorp/null"] 561 562 Outputs: 563 564 amis_out = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }`) 565 if actual != expected { 566 t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual) 567 } 568} 569 570func TestContext2Apply_refCount(t *testing.T) { 571 m := testModule(t, "apply-ref-count") 572 p := testProvider("aws") 573 p.PlanResourceChangeFn = testDiffFn 574 p.ApplyResourceChangeFn = testApplyFn 575 ctx := testContext2(t, &ContextOpts{ 576 Config: m, 577 Providers: map[addrs.Provider]providers.Factory{ 578 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 579 }, 580 }) 581 582 if _, diags := ctx.Plan(); diags.HasErrors() { 583 t.Fatalf("plan errors: %s", diags.Err()) 584 } 585 586 state, diags := ctx.Apply() 587 if diags.HasErrors() { 588 t.Fatalf("diags: %s", diags.Err()) 589 } 590 591 mod := state.RootModule() 592 if len(mod.Resources) < 2 { 593 t.Fatalf("bad: %#v", mod.Resources) 594 } 595 596 actual := strings.TrimSpace(state.String()) 597 expected := strings.TrimSpace(testTerraformApplyRefCountStr) 598 if actual != expected { 599 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 600 } 601} 602 603func TestContext2Apply_providerAlias(t *testing.T) { 604 m := testModule(t, "apply-provider-alias") 605 p := testProvider("aws") 606 p.PlanResourceChangeFn = testDiffFn 607 p.ApplyResourceChangeFn = testApplyFn 608 ctx := testContext2(t, &ContextOpts{ 609 Config: m, 610 Providers: map[addrs.Provider]providers.Factory{ 611 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 612 }, 613 }) 614 615 if _, diags := ctx.Plan(); diags.HasErrors() { 616 t.Fatalf("plan errors: %s", diags.Err()) 617 } 618 619 state, diags := ctx.Apply() 620 if diags.HasErrors() { 621 t.Fatalf("diags: %s", diags.Err()) 622 } 623 624 mod := state.RootModule() 625 if len(mod.Resources) < 2 { 626 t.Fatalf("bad: %#v", mod.Resources) 627 } 628 629 actual := strings.TrimSpace(state.String()) 630 expected := strings.TrimSpace(testTerraformApplyProviderAliasStr) 631 if actual != expected { 632 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 633 } 634} 635 636// Two providers that are configured should both be configured prior to apply 637func TestContext2Apply_providerAliasConfigure(t *testing.T) { 638 m := testModule(t, "apply-provider-alias-configure") 639 640 p2 := testProvider("another") 641 p2.ApplyResourceChangeFn = testApplyFn 642 p2.PlanResourceChangeFn = testDiffFn 643 644 ctx := testContext2(t, &ContextOpts{ 645 Config: m, 646 Providers: map[addrs.Provider]providers.Factory{ 647 addrs.NewDefaultProvider("another"): testProviderFuncFixed(p2), 648 }, 649 }) 650 651 if p, diags := ctx.Plan(); diags.HasErrors() { 652 t.Fatalf("diags: %s", diags.Err()) 653 } else { 654 t.Logf(legacyDiffComparisonString(p.Changes)) 655 } 656 657 // Configure to record calls AFTER Plan above 658 var configCount int32 659 p2.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 660 atomic.AddInt32(&configCount, 1) 661 662 foo := req.Config.GetAttr("foo").AsString() 663 if foo != "bar" { 664 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("foo: %#v", foo)) 665 } 666 667 return 668 } 669 670 state, diags := ctx.Apply() 671 if diags.HasErrors() { 672 t.Fatalf("diags: %s", diags.Err()) 673 } 674 675 if configCount != 2 { 676 t.Fatalf("provider config expected 2 calls, got: %d", configCount) 677 } 678 679 actual := strings.TrimSpace(state.String()) 680 expected := strings.TrimSpace(testTerraformApplyProviderAliasConfigStr) 681 if actual != expected { 682 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 683 } 684} 685 686// GH-2870 687func TestContext2Apply_providerWarning(t *testing.T) { 688 m := testModule(t, "apply-provider-warning") 689 p := testProvider("aws") 690 p.PlanResourceChangeFn = testDiffFn 691 p.ApplyResourceChangeFn = testApplyFn 692 p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { 693 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("just a warning")) 694 return 695 } 696 ctx := testContext2(t, &ContextOpts{ 697 Config: m, 698 Providers: map[addrs.Provider]providers.Factory{ 699 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 700 }, 701 }) 702 703 if _, diags := ctx.Plan(); diags.HasErrors() { 704 t.Fatalf("plan errors: %s", diags.Err()) 705 } 706 707 state, diags := ctx.Apply() 708 if diags.HasErrors() { 709 t.Fatalf("diags: %s", diags.Err()) 710 } 711 712 actual := strings.TrimSpace(state.String()) 713 expected := strings.TrimSpace(` 714aws_instance.foo: 715 ID = foo 716 provider = provider["registry.terraform.io/hashicorp/aws"] 717 type = aws_instance 718 `) 719 if actual != expected { 720 t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected) 721 } 722 723 if !p.ConfigureProviderCalled { 724 t.Fatalf("provider Configure() was never called!") 725 } 726} 727 728func TestContext2Apply_emptyModule(t *testing.T) { 729 // A module with only outputs (no resources) 730 m := testModule(t, "apply-empty-module") 731 p := testProvider("aws") 732 p.PlanResourceChangeFn = testDiffFn 733 ctx := testContext2(t, &ContextOpts{ 734 Config: m, 735 Providers: map[addrs.Provider]providers.Factory{ 736 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 737 }, 738 }) 739 740 if _, diags := ctx.Plan(); diags.HasErrors() { 741 t.Fatalf("plan errors: %s", diags.Err()) 742 } 743 744 state, diags := ctx.Apply() 745 if diags.HasErrors() { 746 t.Fatalf("diags: %s", diags.Err()) 747 } 748 749 actual := strings.TrimSpace(state.String()) 750 actual = strings.Replace(actual, " ", "", -1) 751 expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr) 752 if actual != expected { 753 t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected) 754 } 755} 756 757func TestContext2Apply_createBeforeDestroy(t *testing.T) { 758 m := testModule(t, "apply-good-create-before") 759 p := testProvider("aws") 760 p.PlanResourceChangeFn = testDiffFn 761 p.ApplyResourceChangeFn = testApplyFn 762 state := states.NewState() 763 root := state.EnsureModule(addrs.RootModuleInstance) 764 root.SetResourceInstanceCurrent( 765 mustResourceInstanceAddr("aws_instance.bar").Resource, 766 &states.ResourceInstanceObjectSrc{ 767 Status: states.ObjectReady, 768 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 769 }, 770 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 771 ) 772 ctx := testContext2(t, &ContextOpts{ 773 Config: m, 774 Providers: map[addrs.Provider]providers.Factory{ 775 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 776 }, 777 State: state, 778 }) 779 780 if p, diags := ctx.Plan(); diags.HasErrors() { 781 t.Fatalf("diags: %s", diags.Err()) 782 } else { 783 t.Logf(legacyDiffComparisonString(p.Changes)) 784 } 785 786 state, diags := ctx.Apply() 787 if diags.HasErrors() { 788 t.Fatalf("diags: %s", diags.Err()) 789 } 790 791 mod := state.RootModule() 792 if got, want := len(mod.Resources), 1; got != want { 793 t.Logf("state:\n%s", state) 794 t.Fatalf("wrong number of resources %d; want %d", got, want) 795 } 796 797 actual := strings.TrimSpace(state.String()) 798 expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr) 799 if actual != expected { 800 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 801 } 802} 803 804func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) { 805 m := testModule(t, "apply-good-create-before-update") 806 p := testProvider("aws") 807 p.PlanResourceChangeFn = testDiffFn 808 809 // signal that resource foo has started applying 810 fooChan := make(chan struct{}) 811 812 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 813 id := req.PriorState.GetAttr("id").AsString() 814 switch id { 815 case "bar": 816 select { 817 case <-fooChan: 818 resp.Diagnostics = resp.Diagnostics.Append(errors.New("bar must be updated before foo is destroyed")) 819 return resp 820 case <-time.After(100 * time.Millisecond): 821 // wait a moment to ensure that foo is not going to be destroyed first 822 } 823 case "foo": 824 close(fooChan) 825 } 826 827 return testApplyFn(req) 828 } 829 830 state := states.NewState() 831 root := state.EnsureModule(addrs.RootModuleInstance) 832 fooAddr := mustResourceInstanceAddr("aws_instance.foo") 833 root.SetResourceInstanceCurrent( 834 fooAddr.Resource, 835 &states.ResourceInstanceObjectSrc{ 836 Status: states.ObjectReady, 837 AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`), 838 CreateBeforeDestroy: true, 839 }, 840 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 841 ) 842 root.SetResourceInstanceCurrent( 843 mustResourceInstanceAddr("aws_instance.bar").Resource, 844 &states.ResourceInstanceObjectSrc{ 845 Status: states.ObjectReady, 846 AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), 847 CreateBeforeDestroy: true, 848 Dependencies: []addrs.ConfigResource{fooAddr.ContainingResource().Config()}, 849 }, 850 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 851 ) 852 853 ctx := testContext2(t, &ContextOpts{ 854 Config: m, 855 Providers: map[addrs.Provider]providers.Factory{ 856 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 857 }, 858 State: state, 859 }) 860 861 if p, diags := ctx.Plan(); diags.HasErrors() { 862 t.Fatalf("diags: %s", diags.Err()) 863 } else { 864 t.Logf(legacyDiffComparisonString(p.Changes)) 865 } 866 867 state, diags := ctx.Apply() 868 if diags.HasErrors() { 869 t.Fatalf("diags: %s", diags.Err()) 870 } 871 872 mod := state.RootModule() 873 if len(mod.Resources) != 1 { 874 t.Fatalf("bad: %s", state) 875 } 876 877 actual := strings.TrimSpace(state.String()) 878 expected := strings.TrimSpace(testTerraformApplyCreateBeforeUpdateStr) 879 if actual != expected { 880 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 881 } 882} 883 884// This tests that when a CBD resource depends on a non-CBD resource, 885// we can still properly apply changes that require new for both. 886func TestContext2Apply_createBeforeDestroy_dependsNonCBD(t *testing.T) { 887 m := testModule(t, "apply-cbd-depends-non-cbd") 888 p := testProvider("aws") 889 p.PlanResourceChangeFn = testDiffFn 890 p.ApplyResourceChangeFn = testApplyFn 891 892 state := states.NewState() 893 root := state.EnsureModule(addrs.RootModuleInstance) 894 root.SetResourceInstanceCurrent( 895 mustResourceInstanceAddr("aws_instance.bar").Resource, 896 &states.ResourceInstanceObjectSrc{ 897 Status: states.ObjectReady, 898 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 899 }, 900 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 901 ) 902 root.SetResourceInstanceCurrent( 903 mustResourceInstanceAddr("aws_instance.foo").Resource, 904 &states.ResourceInstanceObjectSrc{ 905 Status: states.ObjectReady, 906 AttrsJSON: []byte(`{"id":"foo", "require_new": "abc"}`), 907 }, 908 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 909 ) 910 911 ctx := testContext2(t, &ContextOpts{ 912 Config: m, 913 Providers: map[addrs.Provider]providers.Factory{ 914 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 915 }, 916 State: state, 917 }) 918 919 if p, diags := ctx.Plan(); diags.HasErrors() { 920 t.Fatalf("diags: %s", diags.Err()) 921 } else { 922 t.Logf(legacyDiffComparisonString(p.Changes)) 923 } 924 925 state, diags := ctx.Apply() 926 if diags.HasErrors() { 927 t.Fatalf("diags: %s", diags.Err()) 928 } 929 930 checkStateString(t, state, ` 931aws_instance.bar: 932 ID = foo 933 provider = provider["registry.terraform.io/hashicorp/aws"] 934 require_new = yes 935 type = aws_instance 936 value = foo 937 938 Dependencies: 939 aws_instance.foo 940aws_instance.foo: 941 ID = foo 942 provider = provider["registry.terraform.io/hashicorp/aws"] 943 require_new = yes 944 type = aws_instance 945 `) 946} 947 948func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) { 949 h := new(MockHook) 950 m := testModule(t, "apply-good-create-before") 951 p := testProvider("aws") 952 p.PlanResourceChangeFn = testDiffFn 953 p.ApplyResourceChangeFn = testApplyFn 954 state := states.NewState() 955 root := state.EnsureModule(addrs.RootModuleInstance) 956 root.SetResourceInstanceCurrent( 957 mustResourceInstanceAddr("aws_instance.bar").Resource, 958 &states.ResourceInstanceObjectSrc{ 959 Status: states.ObjectReady, 960 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 961 }, 962 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 963 ) 964 965 var actual []cty.Value 966 var actualLock sync.Mutex 967 h.PostApplyFn = func(addr addrs.AbsResourceInstance, gen states.Generation, sv cty.Value, e error) (HookAction, error) { 968 actualLock.Lock() 969 970 defer actualLock.Unlock() 971 actual = append(actual, sv) 972 return HookActionContinue, nil 973 } 974 975 ctx := testContext2(t, &ContextOpts{ 976 Config: m, 977 Hooks: []Hook{h}, 978 Providers: map[addrs.Provider]providers.Factory{ 979 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 980 }, 981 State: state, 982 }) 983 984 if p, diags := ctx.Plan(); diags.HasErrors() { 985 t.Fatalf("diags: %s", diags.Err()) 986 } else { 987 t.Logf(legacyDiffComparisonString(p.Changes)) 988 } 989 990 if _, diags := ctx.Apply(); diags.HasErrors() { 991 t.Fatalf("apply errors: %s", diags.Err()) 992 } 993 994 expected := []cty.Value{ 995 cty.ObjectVal(map[string]cty.Value{ 996 "id": cty.StringVal("foo"), 997 "require_new": cty.StringVal("xyz"), 998 "type": cty.StringVal("aws_instance"), 999 }), 1000 cty.NullVal(cty.DynamicPseudoType), 1001 } 1002 1003 cmpOpt := cmp.Transformer("ctyshim", hcl2shim.ConfigValueFromHCL2) 1004 if !cmp.Equal(actual, expected, cmpOpt) { 1005 t.Fatalf("wrong state snapshot sequence\n%s", cmp.Diff(expected, actual, cmpOpt)) 1006 } 1007} 1008 1009// Test that we can perform an apply with CBD in a count with deposed instances. 1010func TestContext2Apply_createBeforeDestroy_deposedCount(t *testing.T) { 1011 m := testModule(t, "apply-cbd-count") 1012 p := testProvider("aws") 1013 p.PlanResourceChangeFn = testDiffFn 1014 p.ApplyResourceChangeFn = testApplyFn 1015 1016 state := states.NewState() 1017 root := state.EnsureModule(addrs.RootModuleInstance) 1018 root.SetResourceInstanceCurrent( 1019 mustResourceInstanceAddr("aws_instance.bar[0]").Resource, 1020 &states.ResourceInstanceObjectSrc{ 1021 Status: states.ObjectTainted, 1022 AttrsJSON: []byte(`{"id":"bar"}`), 1023 }, 1024 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1025 ) 1026 root.SetResourceInstanceDeposed( 1027 mustResourceInstanceAddr("aws_instance.bar[0]").Resource, 1028 states.NewDeposedKey(), 1029 &states.ResourceInstanceObjectSrc{ 1030 Status: states.ObjectTainted, 1031 AttrsJSON: []byte(`{"id":"foo"}`), 1032 }, 1033 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1034 ) 1035 root.SetResourceInstanceCurrent( 1036 mustResourceInstanceAddr("aws_instance.bar[1]").Resource, 1037 &states.ResourceInstanceObjectSrc{ 1038 Status: states.ObjectTainted, 1039 AttrsJSON: []byte(`{"id":"bar"}`), 1040 }, 1041 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1042 ) 1043 root.SetResourceInstanceDeposed( 1044 mustResourceInstanceAddr("aws_instance.bar[1]").Resource, 1045 states.NewDeposedKey(), 1046 &states.ResourceInstanceObjectSrc{ 1047 Status: states.ObjectTainted, 1048 AttrsJSON: []byte(`{"id":"bar"}`), 1049 }, 1050 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1051 ) 1052 1053 ctx := testContext2(t, &ContextOpts{ 1054 Config: m, 1055 Providers: map[addrs.Provider]providers.Factory{ 1056 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1057 }, 1058 State: state, 1059 }) 1060 1061 if p, diags := ctx.Plan(); diags.HasErrors() { 1062 t.Fatalf("diags: %s", diags.Err()) 1063 } else { 1064 t.Logf(legacyDiffComparisonString(p.Changes)) 1065 } 1066 1067 state, diags := ctx.Apply() 1068 if diags.HasErrors() { 1069 t.Fatalf("diags: %s", diags.Err()) 1070 } 1071 1072 checkStateString(t, state, ` 1073aws_instance.bar.0: 1074 ID = foo 1075 provider = provider["registry.terraform.io/hashicorp/aws"] 1076 foo = bar 1077 type = aws_instance 1078aws_instance.bar.1: 1079 ID = foo 1080 provider = provider["registry.terraform.io/hashicorp/aws"] 1081 foo = bar 1082 type = aws_instance 1083 `) 1084} 1085 1086// Test that when we have a deposed instance but a good primary, we still 1087// destroy the deposed instance. 1088func TestContext2Apply_createBeforeDestroy_deposedOnly(t *testing.T) { 1089 m := testModule(t, "apply-cbd-deposed-only") 1090 p := testProvider("aws") 1091 p.PlanResourceChangeFn = testDiffFn 1092 p.ApplyResourceChangeFn = testApplyFn 1093 1094 state := states.NewState() 1095 root := state.EnsureModule(addrs.RootModuleInstance) 1096 root.SetResourceInstanceCurrent( 1097 mustResourceInstanceAddr("aws_instance.bar").Resource, 1098 &states.ResourceInstanceObjectSrc{ 1099 Status: states.ObjectReady, 1100 AttrsJSON: []byte(`{"id":"bar"}`), 1101 }, 1102 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1103 ) 1104 root.SetResourceInstanceDeposed( 1105 mustResourceInstanceAddr("aws_instance.bar").Resource, 1106 states.NewDeposedKey(), 1107 &states.ResourceInstanceObjectSrc{ 1108 Status: states.ObjectTainted, 1109 AttrsJSON: []byte(`{"id":"foo"}`), 1110 }, 1111 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1112 ) 1113 1114 ctx := testContext2(t, &ContextOpts{ 1115 Config: m, 1116 Providers: map[addrs.Provider]providers.Factory{ 1117 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1118 }, 1119 State: state, 1120 }) 1121 1122 if p, diags := ctx.Plan(); diags.HasErrors() { 1123 t.Fatalf("diags: %s", diags.Err()) 1124 } else { 1125 t.Logf(legacyDiffComparisonString(p.Changes)) 1126 } 1127 1128 state, diags := ctx.Apply() 1129 if diags.HasErrors() { 1130 t.Fatalf("diags: %s", diags.Err()) 1131 } 1132 1133 checkStateString(t, state, ` 1134aws_instance.bar: 1135 ID = bar 1136 provider = provider["registry.terraform.io/hashicorp/aws"] 1137 type = aws_instance 1138 `) 1139} 1140 1141func TestContext2Apply_destroyComputed(t *testing.T) { 1142 m := testModule(t, "apply-destroy-computed") 1143 p := testProvider("aws") 1144 p.PlanResourceChangeFn = testDiffFn 1145 state := states.NewState() 1146 root := state.EnsureModule(addrs.RootModuleInstance) 1147 root.SetResourceInstanceCurrent( 1148 mustResourceInstanceAddr("aws_instance.foo").Resource, 1149 &states.ResourceInstanceObjectSrc{ 1150 Status: states.ObjectReady, 1151 AttrsJSON: []byte(`{"id":"foo", "output": "value"}`), 1152 }, 1153 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1154 ) 1155 ctx := testContext2(t, &ContextOpts{ 1156 Config: m, 1157 Providers: map[addrs.Provider]providers.Factory{ 1158 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1159 }, 1160 State: state, 1161 PlanMode: plans.DestroyMode, 1162 }) 1163 1164 if p, diags := ctx.Plan(); diags.HasErrors() { 1165 logDiagnostics(t, diags) 1166 t.Fatal("plan failed") 1167 } else { 1168 t.Logf("plan:\n\n%s", legacyDiffComparisonString(p.Changes)) 1169 } 1170 1171 if _, diags := ctx.Apply(); diags.HasErrors() { 1172 logDiagnostics(t, diags) 1173 t.Fatal("apply failed") 1174 } 1175} 1176 1177// Test that the destroy operation uses depends_on as a source of ordering. 1178func TestContext2Apply_destroyDependsOn(t *testing.T) { 1179 // It is possible for this to be racy, so we loop a number of times 1180 // just to check. 1181 for i := 0; i < 10; i++ { 1182 testContext2Apply_destroyDependsOn(t) 1183 } 1184} 1185 1186func testContext2Apply_destroyDependsOn(t *testing.T) { 1187 m := testModule(t, "apply-destroy-depends-on") 1188 p := testProvider("aws") 1189 p.PlanResourceChangeFn = testDiffFn 1190 1191 state := states.NewState() 1192 root := state.EnsureModule(addrs.RootModuleInstance) 1193 root.SetResourceInstanceCurrent( 1194 mustResourceInstanceAddr("aws_instance.bar").Resource, 1195 &states.ResourceInstanceObjectSrc{ 1196 Status: states.ObjectReady, 1197 AttrsJSON: []byte(`{"id":"bar"}`), 1198 }, 1199 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1200 ) 1201 root.SetResourceInstanceCurrent( 1202 mustResourceInstanceAddr("aws_instance.foo").Resource, 1203 &states.ResourceInstanceObjectSrc{ 1204 Status: states.ObjectReady, 1205 AttrsJSON: []byte(`{"id":"foo"}`), 1206 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.bar")}, 1207 }, 1208 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1209 ) 1210 1211 // Record the order we see Apply 1212 var actual []string 1213 var actualLock sync.Mutex 1214 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1215 actualLock.Lock() 1216 defer actualLock.Unlock() 1217 id := req.PriorState.GetAttr("id").AsString() 1218 actual = append(actual, id) 1219 1220 return testApplyFn(req) 1221 } 1222 1223 ctx := testContext2(t, &ContextOpts{ 1224 Config: m, 1225 Providers: map[addrs.Provider]providers.Factory{ 1226 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1227 }, 1228 State: state, 1229 PlanMode: plans.DestroyMode, 1230 Parallelism: 1, // To check ordering 1231 }) 1232 1233 if _, diags := ctx.Plan(); diags.HasErrors() { 1234 t.Fatalf("plan errors: %s", diags.Err()) 1235 } 1236 1237 if _, diags := ctx.Apply(); diags.HasErrors() { 1238 t.Fatalf("apply errors: %s", diags.Err()) 1239 } 1240 1241 expected := []string{"foo", "bar"} 1242 if !reflect.DeepEqual(actual, expected) { 1243 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1244 } 1245} 1246 1247// Test that destroy ordering is correct with dependencies only 1248// in the state. 1249func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) { 1250 newState := states.NewState() 1251 root := newState.EnsureModule(addrs.RootModuleInstance) 1252 root.SetResourceInstanceCurrent( 1253 addrs.Resource{ 1254 Mode: addrs.ManagedResourceMode, 1255 Type: "aws_instance", 1256 Name: "foo", 1257 }.Instance(addrs.NoKey), 1258 &states.ResourceInstanceObjectSrc{ 1259 Status: states.ObjectReady, 1260 AttrsJSON: []byte(`{"id":"foo"}`), 1261 Dependencies: []addrs.ConfigResource{}, 1262 }, 1263 addrs.AbsProviderConfig{ 1264 Provider: addrs.NewDefaultProvider("aws"), 1265 Module: addrs.RootModule, 1266 }, 1267 ) 1268 root.SetResourceInstanceCurrent( 1269 addrs.Resource{ 1270 Mode: addrs.ManagedResourceMode, 1271 Type: "aws_instance", 1272 Name: "bar", 1273 }.Instance(addrs.NoKey), 1274 &states.ResourceInstanceObjectSrc{ 1275 Status: states.ObjectReady, 1276 AttrsJSON: []byte(`{"id":"bar"}`), 1277 Dependencies: []addrs.ConfigResource{ 1278 addrs.ConfigResource{ 1279 Resource: addrs.Resource{ 1280 Mode: addrs.ManagedResourceMode, 1281 Type: "aws_instance", 1282 Name: "foo", 1283 }, 1284 Module: root.Addr.Module(), 1285 }, 1286 }, 1287 }, 1288 addrs.AbsProviderConfig{ 1289 Provider: addrs.NewDefaultProvider("aws"), 1290 Module: addrs.RootModule, 1291 }, 1292 ) 1293 1294 // It is possible for this to be racy, so we loop a number of times 1295 // just to check. 1296 for i := 0; i < 10; i++ { 1297 t.Run("new", func(t *testing.T) { 1298 testContext2Apply_destroyDependsOnStateOnly(t, newState) 1299 }) 1300 } 1301} 1302 1303func testContext2Apply_destroyDependsOnStateOnly(t *testing.T, state *states.State) { 1304 state = state.DeepCopy() 1305 m := testModule(t, "empty") 1306 p := testProvider("aws") 1307 p.PlanResourceChangeFn = testDiffFn 1308 // Record the order we see Apply 1309 var actual []string 1310 var actualLock sync.Mutex 1311 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1312 actualLock.Lock() 1313 defer actualLock.Unlock() 1314 id := req.PriorState.GetAttr("id").AsString() 1315 actual = append(actual, id) 1316 return testApplyFn(req) 1317 } 1318 1319 ctx := testContext2(t, &ContextOpts{ 1320 Config: m, 1321 Providers: map[addrs.Provider]providers.Factory{ 1322 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1323 }, 1324 State: state, 1325 PlanMode: plans.DestroyMode, 1326 Parallelism: 1, // To check ordering 1327 }) 1328 1329 if _, diags := ctx.Plan(); diags.HasErrors() { 1330 t.Fatalf("plan errors: %s", diags.Err()) 1331 } 1332 1333 if _, diags := ctx.Apply(); diags.HasErrors() { 1334 t.Fatalf("apply errors: %s", diags.Err()) 1335 } 1336 1337 expected := []string{"bar", "foo"} 1338 if !reflect.DeepEqual(actual, expected) { 1339 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1340 } 1341} 1342 1343// Test that destroy ordering is correct with dependencies only 1344// in the state within a module (GH-11749) 1345func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) { 1346 newState := states.NewState() 1347 child := newState.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 1348 child.SetResourceInstanceCurrent( 1349 addrs.Resource{ 1350 Mode: addrs.ManagedResourceMode, 1351 Type: "aws_instance", 1352 Name: "foo", 1353 }.Instance(addrs.NoKey), 1354 &states.ResourceInstanceObjectSrc{ 1355 Status: states.ObjectReady, 1356 AttrsJSON: []byte(`{"id":"foo"}`), 1357 Dependencies: []addrs.ConfigResource{}, 1358 }, 1359 addrs.AbsProviderConfig{ 1360 Provider: addrs.NewDefaultProvider("aws"), 1361 Module: addrs.RootModule, 1362 }, 1363 ) 1364 child.SetResourceInstanceCurrent( 1365 addrs.Resource{ 1366 Mode: addrs.ManagedResourceMode, 1367 Type: "aws_instance", 1368 Name: "bar", 1369 }.Instance(addrs.NoKey), 1370 &states.ResourceInstanceObjectSrc{ 1371 Status: states.ObjectReady, 1372 AttrsJSON: []byte(`{"id":"bar"}`), 1373 Dependencies: []addrs.ConfigResource{ 1374 addrs.ConfigResource{ 1375 Resource: addrs.Resource{ 1376 Mode: addrs.ManagedResourceMode, 1377 Type: "aws_instance", 1378 Name: "foo", 1379 }, 1380 Module: child.Addr.Module(), 1381 }, 1382 }, 1383 }, 1384 addrs.AbsProviderConfig{ 1385 Provider: addrs.NewDefaultProvider("aws"), 1386 Module: addrs.RootModule, 1387 }, 1388 ) 1389 1390 // It is possible for this to be racy, so we loop a number of times 1391 // just to check. 1392 for i := 0; i < 10; i++ { 1393 t.Run("new", func(t *testing.T) { 1394 testContext2Apply_destroyDependsOnStateOnlyModule(t, newState) 1395 }) 1396 } 1397} 1398 1399func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T, state *states.State) { 1400 state = state.DeepCopy() 1401 m := testModule(t, "empty") 1402 p := testProvider("aws") 1403 p.PlanResourceChangeFn = testDiffFn 1404 1405 // Record the order we see Apply 1406 var actual []string 1407 var actualLock sync.Mutex 1408 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1409 actualLock.Lock() 1410 defer actualLock.Unlock() 1411 id := req.PriorState.GetAttr("id").AsString() 1412 actual = append(actual, id) 1413 return testApplyFn(req) 1414 } 1415 1416 ctx := testContext2(t, &ContextOpts{ 1417 Config: m, 1418 Providers: map[addrs.Provider]providers.Factory{ 1419 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1420 }, 1421 State: state, 1422 PlanMode: plans.DestroyMode, 1423 Parallelism: 1, // To check ordering 1424 }) 1425 1426 if _, diags := ctx.Plan(); diags.HasErrors() { 1427 t.Fatalf("plan errors: %s", diags.Err()) 1428 } 1429 1430 if _, diags := ctx.Apply(); diags.HasErrors() { 1431 t.Fatalf("apply errors: %s", diags.Err()) 1432 } 1433 1434 expected := []string{"bar", "foo"} 1435 if !reflect.DeepEqual(actual, expected) { 1436 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1437 } 1438} 1439 1440func TestContext2Apply_dataBasic(t *testing.T) { 1441 m := testModule(t, "apply-data-basic") 1442 p := testProvider("null") 1443 p.PlanResourceChangeFn = testDiffFn 1444 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 1445 State: cty.ObjectVal(map[string]cty.Value{ 1446 "id": cty.StringVal("yo"), 1447 "foo": cty.NullVal(cty.String), 1448 }), 1449 } 1450 1451 ctx := testContext2(t, &ContextOpts{ 1452 Config: m, 1453 Providers: map[addrs.Provider]providers.Factory{ 1454 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 1455 }, 1456 }) 1457 1458 if p, diags := ctx.Plan(); diags.HasErrors() { 1459 t.Fatalf("diags: %s", diags.Err()) 1460 } else { 1461 t.Logf(legacyDiffComparisonString(p.Changes)) 1462 } 1463 1464 state, diags := ctx.Apply() 1465 assertNoErrors(t, diags) 1466 1467 actual := strings.TrimSpace(state.String()) 1468 expected := strings.TrimSpace(testTerraformApplyDataBasicStr) 1469 if actual != expected { 1470 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1471 } 1472} 1473 1474func TestContext2Apply_destroyData(t *testing.T) { 1475 m := testModule(t, "apply-destroy-data-resource") 1476 p := testProvider("null") 1477 p.PlanResourceChangeFn = testDiffFn 1478 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 1479 return providers.ReadDataSourceResponse{ 1480 State: req.Config, 1481 } 1482 } 1483 1484 state := states.NewState() 1485 root := state.EnsureModule(addrs.RootModuleInstance) 1486 root.SetResourceInstanceCurrent( 1487 mustResourceInstanceAddr("data.null_data_source.testing").Resource, 1488 &states.ResourceInstanceObjectSrc{ 1489 Status: states.ObjectReady, 1490 AttrsJSON: []byte(`{"id":"-"}`), 1491 }, 1492 mustProviderConfig(`provider["registry.terraform.io/hashicorp/null"]`), 1493 ) 1494 1495 hook := &testHook{} 1496 ctx := testContext2(t, &ContextOpts{ 1497 Config: m, 1498 Providers: map[addrs.Provider]providers.Factory{ 1499 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 1500 }, 1501 State: state, 1502 PlanMode: plans.DestroyMode, 1503 Hooks: []Hook{hook}, 1504 }) 1505 1506 if p, diags := ctx.Plan(); diags.HasErrors() { 1507 t.Fatalf("diags: %s", diags.Err()) 1508 } else { 1509 t.Logf(legacyDiffComparisonString(p.Changes)) 1510 } 1511 1512 newState, diags := ctx.Apply() 1513 if diags.HasErrors() { 1514 t.Fatalf("diags: %s", diags.Err()) 1515 } 1516 1517 if got := len(newState.Modules); got != 1 { 1518 t.Fatalf("state has %d modules after destroy; want 1", got) 1519 } 1520 1521 if got := len(newState.RootModule().Resources); got != 0 { 1522 t.Fatalf("state has %d resources after destroy; want 0", got) 1523 } 1524 1525 wantHookCalls := []*testHookCall{ 1526 {"PreDiff", "data.null_data_source.testing"}, 1527 {"PostDiff", "data.null_data_source.testing"}, 1528 {"PreDiff", "data.null_data_source.testing"}, 1529 {"PostDiff", "data.null_data_source.testing"}, 1530 {"PostStateUpdate", ""}, 1531 } 1532 if !reflect.DeepEqual(hook.Calls, wantHookCalls) { 1533 t.Errorf("wrong hook calls\ngot: %swant: %s", spew.Sdump(hook.Calls), spew.Sdump(wantHookCalls)) 1534 } 1535} 1536 1537// https://github.com/hashicorp/terraform/pull/5096 1538func TestContext2Apply_destroySkipsCBD(t *testing.T) { 1539 // Config contains CBD resource depending on non-CBD resource, which triggers 1540 // a cycle if they are both replaced, but should _not_ trigger a cycle when 1541 // just doing a `terraform destroy`. 1542 m := testModule(t, "apply-destroy-cbd") 1543 p := testProvider("aws") 1544 p.PlanResourceChangeFn = testDiffFn 1545 state := states.NewState() 1546 root := state.EnsureModule(addrs.RootModuleInstance) 1547 root.SetResourceInstanceCurrent( 1548 mustResourceInstanceAddr("aws_instance.foo").Resource, 1549 &states.ResourceInstanceObjectSrc{ 1550 Status: states.ObjectReady, 1551 AttrsJSON: []byte(`{"id":"foo"}`), 1552 }, 1553 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1554 ) 1555 root.SetResourceInstanceCurrent( 1556 mustResourceInstanceAddr("aws_instance.bar").Resource, 1557 &states.ResourceInstanceObjectSrc{ 1558 Status: states.ObjectReady, 1559 AttrsJSON: []byte(`{"id":"foo"}`), 1560 }, 1561 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1562 ) 1563 1564 ctx := testContext2(t, &ContextOpts{ 1565 Config: m, 1566 Providers: map[addrs.Provider]providers.Factory{ 1567 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1568 }, 1569 State: state, 1570 PlanMode: plans.DestroyMode, 1571 }) 1572 1573 if p, diags := ctx.Plan(); diags.HasErrors() { 1574 t.Fatalf("diags: %s", diags.Err()) 1575 } else { 1576 t.Logf(legacyDiffComparisonString(p.Changes)) 1577 } 1578 1579 if _, diags := ctx.Apply(); diags.HasErrors() { 1580 t.Fatalf("apply errors: %s", diags.Err()) 1581 } 1582} 1583 1584func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) { 1585 m := testModule(t, "apply-destroy-mod-var-provider-config") 1586 p := testProvider("aws") 1587 p.PlanResourceChangeFn = testDiffFn 1588 state := states.NewState() 1589 root := state.EnsureModule(addrs.RootModuleInstance) 1590 root.SetResourceInstanceCurrent( 1591 mustResourceInstanceAddr("aws_instance.foo").Resource, 1592 &states.ResourceInstanceObjectSrc{ 1593 Status: states.ObjectReady, 1594 AttrsJSON: []byte(`{"id":"foo"}`), 1595 }, 1596 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1597 ) 1598 ctx := testContext2(t, &ContextOpts{ 1599 Config: m, 1600 Providers: map[addrs.Provider]providers.Factory{ 1601 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1602 }, 1603 State: state, 1604 PlanMode: plans.DestroyMode, 1605 }) 1606 1607 if _, diags := ctx.Plan(); diags.HasErrors() { 1608 t.Fatalf("plan errors: %s", diags.Err()) 1609 } 1610 1611 _, diags := ctx.Apply() 1612 if diags.HasErrors() { 1613 t.Fatalf("diags: %s", diags.Err()) 1614 } 1615} 1616 1617func TestContext2Apply_destroyCrossProviders(t *testing.T) { 1618 m := testModule(t, "apply-destroy-cross-providers") 1619 1620 p_aws := testProvider("aws") 1621 p_aws.ApplyResourceChangeFn = testApplyFn 1622 p_aws.PlanResourceChangeFn = testDiffFn 1623 p_aws.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1624 ResourceTypes: map[string]*configschema.Block{ 1625 "aws_instance": { 1626 Attributes: map[string]*configschema.Attribute{ 1627 "id": { 1628 Type: cty.String, 1629 Computed: true, 1630 }, 1631 }, 1632 }, 1633 "aws_vpc": { 1634 Attributes: map[string]*configschema.Attribute{ 1635 "id": { 1636 Type: cty.String, 1637 Computed: true, 1638 }, 1639 "value": { 1640 Type: cty.String, 1641 Optional: true, 1642 }, 1643 }, 1644 }, 1645 }, 1646 }) 1647 1648 providers := map[addrs.Provider]providers.Factory{ 1649 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p_aws), 1650 } 1651 1652 ctx := getContextForApply_destroyCrossProviders(t, m, providers) 1653 1654 if _, diags := ctx.Plan(); diags.HasErrors() { 1655 logDiagnostics(t, diags) 1656 t.Fatal("plan failed") 1657 } 1658 1659 if _, diags := ctx.Apply(); diags.HasErrors() { 1660 logDiagnostics(t, diags) 1661 t.Fatal("apply failed") 1662 } 1663} 1664 1665func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, providerFactories map[addrs.Provider]providers.Factory) *Context { 1666 state := states.NewState() 1667 root := state.EnsureModule(addrs.RootModuleInstance) 1668 root.SetResourceInstanceCurrent( 1669 mustResourceInstanceAddr("aws_instance.shared").Resource, 1670 &states.ResourceInstanceObjectSrc{ 1671 Status: states.ObjectReady, 1672 AttrsJSON: []byte(`{"id":"test"}`), 1673 }, 1674 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1675 ) 1676 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 1677 child.SetResourceInstanceCurrent( 1678 mustResourceInstanceAddr("aws_vpc.bar").Resource, 1679 &states.ResourceInstanceObjectSrc{ 1680 Status: states.ObjectReady, 1681 AttrsJSON: []byte(`{"id": "vpc-aaabbb12", "value":"test"}`), 1682 }, 1683 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1684 ) 1685 1686 ctx := testContext2(t, &ContextOpts{ 1687 Config: m, 1688 Providers: providerFactories, 1689 State: state, 1690 PlanMode: plans.DestroyMode, 1691 }) 1692 1693 return ctx 1694} 1695 1696func TestContext2Apply_minimal(t *testing.T) { 1697 m := testModule(t, "apply-minimal") 1698 p := testProvider("aws") 1699 p.PlanResourceChangeFn = testDiffFn 1700 p.ApplyResourceChangeFn = testApplyFn 1701 ctx := testContext2(t, &ContextOpts{ 1702 Config: m, 1703 Providers: map[addrs.Provider]providers.Factory{ 1704 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1705 }, 1706 }) 1707 1708 if _, diags := ctx.Plan(); diags.HasErrors() { 1709 t.Fatalf("plan errors: %s", diags.Err()) 1710 } 1711 1712 state, diags := ctx.Apply() 1713 if diags.HasErrors() { 1714 t.Fatalf("diags: %s", diags.Err()) 1715 } 1716 1717 actual := strings.TrimSpace(state.String()) 1718 expected := strings.TrimSpace(testTerraformApplyMinimalStr) 1719 if actual != expected { 1720 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1721 } 1722} 1723 1724func TestContext2Apply_cancel(t *testing.T) { 1725 stopped := false 1726 1727 m := testModule(t, "apply-cancel") 1728 p := testProvider("aws") 1729 ctx := testContext2(t, &ContextOpts{ 1730 Config: m, 1731 Providers: map[addrs.Provider]providers.Factory{ 1732 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1733 }, 1734 }) 1735 1736 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1737 if !stopped { 1738 stopped = true 1739 go ctx.Stop() 1740 1741 for { 1742 if ctx.sh.Stopped() { 1743 break 1744 } 1745 time.Sleep(10 * time.Millisecond) 1746 } 1747 } 1748 return testApplyFn(req) 1749 } 1750 p.PlanResourceChangeFn = testDiffFn 1751 1752 if _, diags := ctx.Plan(); diags.HasErrors() { 1753 t.Fatalf("plan errors: %s", diags.Err()) 1754 } 1755 1756 // Start the Apply in a goroutine 1757 var applyDiags tfdiags.Diagnostics 1758 stateCh := make(chan *states.State) 1759 go func() { 1760 state, diags := ctx.Apply() 1761 applyDiags = diags 1762 1763 stateCh <- state 1764 }() 1765 1766 state := <-stateCh 1767 // only expecting an early exit error 1768 if !applyDiags.HasErrors() { 1769 t.Fatal("expected early exit error") 1770 } 1771 1772 for _, d := range applyDiags { 1773 desc := d.Description() 1774 if desc.Summary != "execution halted" { 1775 t.Fatalf("unexpected error: %v", applyDiags.Err()) 1776 } 1777 } 1778 1779 actual := strings.TrimSpace(state.String()) 1780 expected := strings.TrimSpace(testTerraformApplyCancelStr) 1781 if actual != expected { 1782 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1783 } 1784 1785 if !p.StopCalled { 1786 t.Fatal("stop should be called") 1787 } 1788} 1789 1790func TestContext2Apply_cancelBlock(t *testing.T) { 1791 m := testModule(t, "apply-cancel-block") 1792 p := testProvider("aws") 1793 ctx := testContext2(t, &ContextOpts{ 1794 Config: m, 1795 Providers: map[addrs.Provider]providers.Factory{ 1796 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1797 }, 1798 }) 1799 1800 applyCh := make(chan struct{}) 1801 p.PlanResourceChangeFn = testDiffFn 1802 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 1803 close(applyCh) 1804 1805 for !ctx.sh.Stopped() { 1806 // Wait for stop to be called. We call Gosched here so that 1807 // the other goroutines can always be scheduled to set Stopped. 1808 runtime.Gosched() 1809 } 1810 1811 // Sleep 1812 time.Sleep(100 * time.Millisecond) 1813 return testApplyFn(req) 1814 } 1815 1816 if _, diags := ctx.Plan(); diags.HasErrors() { 1817 t.Fatalf("plan errors: %s", diags.Err()) 1818 } 1819 1820 // Start the Apply in a goroutine 1821 var applyDiags tfdiags.Diagnostics 1822 stateCh := make(chan *states.State) 1823 go func() { 1824 state, diags := ctx.Apply() 1825 applyDiags = diags 1826 1827 stateCh <- state 1828 }() 1829 1830 stopDone := make(chan struct{}) 1831 go func() { 1832 defer close(stopDone) 1833 <-applyCh 1834 ctx.Stop() 1835 }() 1836 1837 // Make sure that stop blocks 1838 select { 1839 case <-stopDone: 1840 t.Fatal("stop should block") 1841 case <-time.After(10 * time.Millisecond): 1842 } 1843 1844 // Wait for stop 1845 select { 1846 case <-stopDone: 1847 case <-time.After(500 * time.Millisecond): 1848 t.Fatal("stop should be done") 1849 } 1850 1851 // Wait for apply to complete 1852 state := <-stateCh 1853 // only expecting an early exit error 1854 if !applyDiags.HasErrors() { 1855 t.Fatal("expected early exit error") 1856 } 1857 1858 for _, d := range applyDiags { 1859 desc := d.Description() 1860 if desc.Summary != "execution halted" { 1861 t.Fatalf("unexpected error: %v", applyDiags.Err()) 1862 } 1863 } 1864 1865 checkStateString(t, state, ` 1866aws_instance.foo: 1867 ID = foo 1868 provider = provider["registry.terraform.io/hashicorp/aws"] 1869 num = 2 1870 type = aws_instance 1871 `) 1872} 1873 1874func TestContext2Apply_cancelProvisioner(t *testing.T) { 1875 m := testModule(t, "apply-cancel-provisioner") 1876 p := testProvider("aws") 1877 p.PlanResourceChangeFn = testDiffFn 1878 p.ApplyResourceChangeFn = testApplyFn 1879 1880 pr := testProvisioner() 1881 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 1882 Provisioner: &configschema.Block{ 1883 Attributes: map[string]*configschema.Attribute{ 1884 "foo": { 1885 Type: cty.String, 1886 Optional: true, 1887 }, 1888 }, 1889 }, 1890 } 1891 1892 ctx := testContext2(t, &ContextOpts{ 1893 Config: m, 1894 Providers: map[addrs.Provider]providers.Factory{ 1895 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1896 }, 1897 Provisioners: map[string]provisioners.Factory{ 1898 "shell": testProvisionerFuncFixed(pr), 1899 }, 1900 }) 1901 1902 prStopped := make(chan struct{}) 1903 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 1904 // Start the stop process 1905 go ctx.Stop() 1906 1907 <-prStopped 1908 return 1909 } 1910 pr.StopFn = func() error { 1911 close(prStopped) 1912 return nil 1913 } 1914 1915 if _, diags := ctx.Plan(); diags.HasErrors() { 1916 t.Fatalf("plan errors: %s", diags.Err()) 1917 } 1918 1919 // Start the Apply in a goroutine 1920 var applyDiags tfdiags.Diagnostics 1921 stateCh := make(chan *states.State) 1922 go func() { 1923 state, diags := ctx.Apply() 1924 applyDiags = diags 1925 1926 stateCh <- state 1927 }() 1928 1929 // Wait for completion 1930 state := <-stateCh 1931 1932 // we are expecting only an early exit error 1933 if !applyDiags.HasErrors() { 1934 t.Fatal("expected early exit error") 1935 } 1936 1937 for _, d := range applyDiags { 1938 desc := d.Description() 1939 if desc.Summary != "execution halted" { 1940 t.Fatalf("unexpected error: %v", applyDiags.Err()) 1941 } 1942 } 1943 1944 checkStateString(t, state, ` 1945aws_instance.foo: (tainted) 1946 ID = foo 1947 provider = provider["registry.terraform.io/hashicorp/aws"] 1948 num = 2 1949 type = aws_instance 1950 `) 1951 1952 if !pr.StopCalled { 1953 t.Fatal("stop should be called") 1954 } 1955} 1956 1957func TestContext2Apply_compute(t *testing.T) { 1958 m := testModule(t, "apply-compute") 1959 p := testProvider("aws") 1960 p.PlanResourceChangeFn = testDiffFn 1961 p.ApplyResourceChangeFn = testApplyFn 1962 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1963 ResourceTypes: map[string]*configschema.Block{ 1964 "aws_instance": { 1965 Attributes: map[string]*configschema.Attribute{ 1966 "num": { 1967 Type: cty.Number, 1968 Optional: true, 1969 }, 1970 "compute": { 1971 Type: cty.String, 1972 Optional: true, 1973 }, 1974 "compute_value": { 1975 Type: cty.String, 1976 Optional: true, 1977 }, 1978 "foo": { 1979 Type: cty.String, 1980 Optional: true, 1981 }, 1982 "id": { 1983 Type: cty.String, 1984 Computed: true, 1985 }, 1986 "type": { 1987 Type: cty.String, 1988 Computed: true, 1989 }, 1990 "value": { // Populated from compute_value because compute = "value" in the config fixture 1991 Type: cty.String, 1992 Computed: true, 1993 }, 1994 }, 1995 }, 1996 }, 1997 }) 1998 1999 ctx := testContext2(t, &ContextOpts{ 2000 Config: m, 2001 Providers: map[addrs.Provider]providers.Factory{ 2002 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2003 }, 2004 }) 2005 2006 ctx.variables = InputValues{ 2007 "value": &InputValue{ 2008 Value: cty.NumberIntVal(1), 2009 SourceType: ValueFromCaller, 2010 }, 2011 } 2012 2013 if _, diags := ctx.Plan(); diags.HasErrors() { 2014 t.Fatalf("plan errors: %s", diags.Err()) 2015 } 2016 2017 state, diags := ctx.Apply() 2018 if diags.HasErrors() { 2019 t.Fatalf("unexpected errors: %s", diags.Err()) 2020 } 2021 2022 actual := strings.TrimSpace(state.String()) 2023 expected := strings.TrimSpace(testTerraformApplyComputeStr) 2024 if actual != expected { 2025 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2026 } 2027} 2028 2029func TestContext2Apply_countDecrease(t *testing.T) { 2030 m := testModule(t, "apply-count-dec") 2031 p := testProvider("aws") 2032 p.PlanResourceChangeFn = testDiffFn 2033 p.ApplyResourceChangeFn = testApplyFn 2034 state := states.NewState() 2035 root := state.EnsureModule(addrs.RootModuleInstance) 2036 root.SetResourceInstanceCurrent( 2037 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2038 &states.ResourceInstanceObjectSrc{ 2039 Status: states.ObjectReady, 2040 AttrsJSON: []byte(`{"id":"bar","foo": "foo","type": "aws_instance"}`), 2041 }, 2042 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2043 ) 2044 root.SetResourceInstanceCurrent( 2045 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 2046 &states.ResourceInstanceObjectSrc{ 2047 Status: states.ObjectReady, 2048 AttrsJSON: []byte(`{"id":"bar","foo": "foo","type": "aws_instance"}`), 2049 }, 2050 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2051 ) 2052 root.SetResourceInstanceCurrent( 2053 mustResourceInstanceAddr("aws_instance.foo[2]").Resource, 2054 &states.ResourceInstanceObjectSrc{ 2055 Status: states.ObjectReady, 2056 AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`), 2057 }, 2058 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2059 ) 2060 2061 ctx := testContext2(t, &ContextOpts{ 2062 Config: m, 2063 Providers: map[addrs.Provider]providers.Factory{ 2064 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2065 }, 2066 State: state, 2067 }) 2068 2069 if _, diags := ctx.Plan(); diags.HasErrors() { 2070 logDiagnostics(t, diags) 2071 t.Fatal("plan failed") 2072 } 2073 2074 s, diags := ctx.Apply() 2075 assertNoErrors(t, diags) 2076 2077 actual := strings.TrimSpace(s.String()) 2078 expected := strings.TrimSpace(testTerraformApplyCountDecStr) 2079 if actual != expected { 2080 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2081 } 2082} 2083 2084func TestContext2Apply_countDecreaseToOneX(t *testing.T) { 2085 m := testModule(t, "apply-count-dec-one") 2086 p := testProvider("aws") 2087 p.PlanResourceChangeFn = testDiffFn 2088 state := states.NewState() 2089 root := state.EnsureModule(addrs.RootModuleInstance) 2090 root.SetResourceInstanceCurrent( 2091 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2092 &states.ResourceInstanceObjectSrc{ 2093 Status: states.ObjectReady, 2094 AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`), 2095 }, 2096 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2097 ) 2098 root.SetResourceInstanceCurrent( 2099 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 2100 &states.ResourceInstanceObjectSrc{ 2101 Status: states.ObjectReady, 2102 AttrsJSON: []byte(`{"id":"bar"}`), 2103 }, 2104 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2105 ) 2106 root.SetResourceInstanceCurrent( 2107 mustResourceInstanceAddr("aws_instance.foo[2]").Resource, 2108 &states.ResourceInstanceObjectSrc{ 2109 Status: states.ObjectReady, 2110 AttrsJSON: []byte(`{"id":"bar"}`), 2111 }, 2112 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2113 ) 2114 2115 ctx := testContext2(t, &ContextOpts{ 2116 Config: m, 2117 Providers: map[addrs.Provider]providers.Factory{ 2118 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2119 }, 2120 State: state, 2121 }) 2122 2123 if _, diags := ctx.Plan(); diags.HasErrors() { 2124 t.Fatalf("plan errors: %s", diags.Err()) 2125 } 2126 2127 s, diags := ctx.Apply() 2128 if diags.HasErrors() { 2129 t.Fatalf("diags: %s", diags.Err()) 2130 } 2131 2132 actual := strings.TrimSpace(s.String()) 2133 expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr) 2134 if actual != expected { 2135 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2136 } 2137} 2138 2139// https://github.com/PeoplePerHour/terraform/pull/11 2140// 2141// This tests a case where both a "resource" and "resource.0" are in 2142// the state file, which apparently is a reasonable backwards compatibility 2143// concern found in the above 3rd party repo. 2144func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) { 2145 m := testModule(t, "apply-count-dec-one") 2146 p := testProvider("aws") 2147 p.PlanResourceChangeFn = testDiffFn 2148 state := states.NewState() 2149 root := state.EnsureModule(addrs.RootModuleInstance) 2150 root.SetResourceInstanceCurrent( 2151 mustResourceInstanceAddr("aws_instance.foo").Resource, 2152 &states.ResourceInstanceObjectSrc{ 2153 Status: states.ObjectReady, 2154 AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`), 2155 }, 2156 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2157 ) 2158 root.SetResourceInstanceCurrent( 2159 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2160 &states.ResourceInstanceObjectSrc{ 2161 Status: states.ObjectReady, 2162 AttrsJSON: []byte(`{"id":"baz", "type": "aws_instance"}`), 2163 }, 2164 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2165 ) 2166 2167 ctx := testContext2(t, &ContextOpts{ 2168 Config: m, 2169 Providers: map[addrs.Provider]providers.Factory{ 2170 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2171 }, 2172 State: state, 2173 }) 2174 2175 if p, diags := ctx.Plan(); diags.HasErrors() { 2176 t.Fatalf("diags: %s", diags.Err()) 2177 } else { 2178 got := strings.TrimSpace(legacyPlanComparisonString(ctx.State(), p.Changes)) 2179 want := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedPlanStr) 2180 if got != want { 2181 t.Fatalf("wrong plan result\ngot:\n%s\nwant:\n%s", got, want) 2182 } 2183 } 2184 2185 s, diags := ctx.Apply() 2186 if diags.HasErrors() { 2187 t.Fatalf("diags: %s", diags.Err()) 2188 } 2189 2190 actual := strings.TrimSpace(s.String()) 2191 expected := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedStr) 2192 if actual != expected { 2193 t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2194 } 2195} 2196 2197func TestContext2Apply_countTainted(t *testing.T) { 2198 m := testModule(t, "apply-count-tainted") 2199 p := testProvider("aws") 2200 p.PlanResourceChangeFn = testDiffFn 2201 p.ApplyResourceChangeFn = testApplyFn 2202 state := states.NewState() 2203 root := state.EnsureModule(addrs.RootModuleInstance) 2204 root.SetResourceInstanceCurrent( 2205 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 2206 &states.ResourceInstanceObjectSrc{ 2207 Status: states.ObjectTainted, 2208 AttrsJSON: []byte(`{"id":"bar", "type": "aws_instance", "foo": "foo"}`), 2209 }, 2210 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2211 ) 2212 ctx := testContext2(t, &ContextOpts{ 2213 Config: m, 2214 Providers: map[addrs.Provider]providers.Factory{ 2215 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2216 }, 2217 State: state, 2218 }) 2219 2220 { 2221 plan, diags := ctx.Plan() 2222 assertNoErrors(t, diags) 2223 got := strings.TrimSpace(legacyDiffComparisonString(plan.Changes)) 2224 want := strings.TrimSpace(` 2225DESTROY/CREATE: aws_instance.foo[0] 2226 foo: "foo" => "foo" 2227 id: "bar" => "<computed>" 2228 type: "aws_instance" => "<computed>" 2229CREATE: aws_instance.foo[1] 2230 foo: "" => "foo" 2231 id: "" => "<computed>" 2232 type: "" => "<computed>" 2233`) 2234 if got != want { 2235 t.Fatalf("wrong plan\n\ngot:\n%s\n\nwant:\n%s", got, want) 2236 } 2237 } 2238 2239 s, diags := ctx.Apply() 2240 assertNoErrors(t, diags) 2241 2242 got := strings.TrimSpace(s.String()) 2243 want := strings.TrimSpace(` 2244aws_instance.foo.0: 2245 ID = foo 2246 provider = provider["registry.terraform.io/hashicorp/aws"] 2247 foo = foo 2248 type = aws_instance 2249aws_instance.foo.1: 2250 ID = foo 2251 provider = provider["registry.terraform.io/hashicorp/aws"] 2252 foo = foo 2253 type = aws_instance 2254`) 2255 if got != want { 2256 t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", got, want) 2257 } 2258} 2259 2260func TestContext2Apply_countVariable(t *testing.T) { 2261 m := testModule(t, "apply-count-variable") 2262 p := testProvider("aws") 2263 p.PlanResourceChangeFn = testDiffFn 2264 p.ApplyResourceChangeFn = testApplyFn 2265 ctx := testContext2(t, &ContextOpts{ 2266 Config: m, 2267 Providers: map[addrs.Provider]providers.Factory{ 2268 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2269 }, 2270 }) 2271 2272 if _, diags := ctx.Plan(); diags.HasErrors() { 2273 t.Fatalf("plan errors: %s", diags.Err()) 2274 } 2275 2276 state, diags := ctx.Apply() 2277 if diags.HasErrors() { 2278 t.Fatalf("diags: %s", diags.Err()) 2279 } 2280 2281 actual := strings.TrimSpace(state.String()) 2282 expected := strings.TrimSpace(testTerraformApplyCountVariableStr) 2283 if actual != expected { 2284 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2285 } 2286} 2287 2288func TestContext2Apply_countVariableRef(t *testing.T) { 2289 m := testModule(t, "apply-count-variable-ref") 2290 p := testProvider("aws") 2291 p.PlanResourceChangeFn = testDiffFn 2292 p.ApplyResourceChangeFn = testApplyFn 2293 ctx := testContext2(t, &ContextOpts{ 2294 Config: m, 2295 Providers: map[addrs.Provider]providers.Factory{ 2296 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2297 }, 2298 }) 2299 2300 if _, diags := ctx.Plan(); diags.HasErrors() { 2301 t.Fatalf("plan errors: %s", diags.Err()) 2302 } 2303 2304 state, diags := ctx.Apply() 2305 if diags.HasErrors() { 2306 t.Fatalf("diags: %s", diags.Err()) 2307 } 2308 2309 actual := strings.TrimSpace(state.String()) 2310 expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr) 2311 if actual != expected { 2312 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2313 } 2314} 2315 2316func TestContext2Apply_provisionerInterpCount(t *testing.T) { 2317 // This test ensures that a provisioner can interpolate a resource count 2318 // even though the provisioner expression is evaluated during the plan 2319 // walk. https://github.com/hashicorp/terraform/issues/16840 2320 2321 m, snap := testModuleWithSnapshot(t, "apply-provisioner-interp-count") 2322 2323 p := testProvider("aws") 2324 p.PlanResourceChangeFn = testDiffFn 2325 2326 pr := testProvisioner() 2327 2328 Providers := map[addrs.Provider]providers.Factory{ 2329 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2330 } 2331 2332 provisioners := map[string]provisioners.Factory{ 2333 "local-exec": testProvisionerFuncFixed(pr), 2334 } 2335 ctx := testContext2(t, &ContextOpts{ 2336 Config: m, 2337 Providers: Providers, 2338 Provisioners: provisioners, 2339 }) 2340 2341 plan, diags := ctx.Plan() 2342 if diags.HasErrors() { 2343 t.Fatalf("plan failed unexpectedly: %s", diags.Err()) 2344 } 2345 2346 // We'll marshal and unmarshal the plan here, to ensure that we have 2347 // a clean new context as would be created if we separately ran 2348 // terraform plan -out=tfplan && terraform apply tfplan 2349 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 2350 if err != nil { 2351 t.Fatal(err) 2352 } 2353 ctxOpts.Providers = Providers 2354 ctxOpts.Provisioners = provisioners 2355 ctx, diags = NewContext(ctxOpts) 2356 if diags.HasErrors() { 2357 t.Fatalf("failed to create context for plan: %s", diags.Err()) 2358 } 2359 2360 // Applying the plan should now succeed 2361 _, diags = ctx.Apply() 2362 if diags.HasErrors() { 2363 t.Fatalf("apply failed unexpectedly: %s", diags.Err()) 2364 } 2365 2366 // Verify apply was invoked 2367 if !pr.ProvisionResourceCalled { 2368 t.Fatalf("provisioner was not called") 2369 } 2370} 2371 2372func TestContext2Apply_foreachVariable(t *testing.T) { 2373 m := testModule(t, "plan-for-each-unknown-value") 2374 p := testProvider("aws") 2375 p.PlanResourceChangeFn = testDiffFn 2376 p.ApplyResourceChangeFn = testApplyFn 2377 ctx := testContext2(t, &ContextOpts{ 2378 Config: m, 2379 Providers: map[addrs.Provider]providers.Factory{ 2380 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2381 }, 2382 Variables: InputValues{ 2383 "foo": &InputValue{ 2384 Value: cty.StringVal("hello"), 2385 }, 2386 }, 2387 }) 2388 2389 if _, diags := ctx.Plan(); diags.HasErrors() { 2390 t.Fatalf("plan errors: %s", diags.Err()) 2391 } 2392 2393 state, diags := ctx.Apply() 2394 if diags.HasErrors() { 2395 t.Fatalf("diags: %s", diags.Err()) 2396 } 2397 2398 actual := strings.TrimSpace(state.String()) 2399 expected := strings.TrimSpace(testTerraformApplyForEachVariableStr) 2400 if actual != expected { 2401 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2402 } 2403} 2404 2405func TestContext2Apply_moduleBasic(t *testing.T) { 2406 m := testModule(t, "apply-module") 2407 p := testProvider("aws") 2408 p.PlanResourceChangeFn = testDiffFn 2409 p.ApplyResourceChangeFn = testApplyFn 2410 ctx := testContext2(t, &ContextOpts{ 2411 Config: m, 2412 Providers: map[addrs.Provider]providers.Factory{ 2413 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2414 }, 2415 }) 2416 2417 if _, diags := ctx.Plan(); diags.HasErrors() { 2418 t.Fatalf("plan errors: %s", diags.Err()) 2419 } 2420 2421 state, diags := ctx.Apply() 2422 if diags.HasErrors() { 2423 t.Fatalf("diags: %s", diags.Err()) 2424 } 2425 2426 actual := strings.TrimSpace(state.String()) 2427 expected := strings.TrimSpace(testTerraformApplyModuleStr) 2428 if actual != expected { 2429 t.Fatalf("bad, expected:\n%s\n\nactual:\n%s", expected, actual) 2430 } 2431} 2432 2433func TestContext2Apply_moduleDestroyOrder(t *testing.T) { 2434 m := testModule(t, "apply-module-destroy-order") 2435 p := testProvider("aws") 2436 p.PlanResourceChangeFn = testDiffFn 2437 2438 // Create a custom apply function to track the order they were destroyed 2439 var order []string 2440 var orderLock sync.Mutex 2441 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 2442 id := req.PriorState.GetAttr("id").AsString() 2443 2444 if id == "b" { 2445 // Pause briefly to make any race conditions more visible, since 2446 // missing edges here can cause undeterministic ordering. 2447 time.Sleep(100 * time.Millisecond) 2448 } 2449 2450 orderLock.Lock() 2451 defer orderLock.Unlock() 2452 2453 order = append(order, id) 2454 resp.NewState = req.PlannedState 2455 return resp 2456 } 2457 2458 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 2459 ResourceTypes: map[string]*configschema.Block{ 2460 "aws_instance": { 2461 Attributes: map[string]*configschema.Attribute{ 2462 "id": {Type: cty.String, Required: true}, 2463 "blah": {Type: cty.String, Optional: true}, 2464 "value": {Type: cty.String, Optional: true}, 2465 }, 2466 }, 2467 }, 2468 }) 2469 2470 state := states.NewState() 2471 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 2472 child.SetResourceInstanceCurrent( 2473 mustResourceInstanceAddr("aws_instance.a").Resource, 2474 &states.ResourceInstanceObjectSrc{ 2475 Status: states.ObjectReady, 2476 AttrsJSON: []byte(`{"id":"a"}`), 2477 }, 2478 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2479 ) 2480 root := state.EnsureModule(addrs.RootModuleInstance) 2481 root.SetResourceInstanceCurrent( 2482 mustResourceInstanceAddr("aws_instance.b").Resource, 2483 &states.ResourceInstanceObjectSrc{ 2484 Status: states.ObjectReady, 2485 AttrsJSON: []byte(`{"id":"b"}`), 2486 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.aws_instance.a")}, 2487 }, 2488 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2489 ) 2490 2491 ctx := testContext2(t, &ContextOpts{ 2492 Config: m, 2493 Providers: map[addrs.Provider]providers.Factory{ 2494 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2495 }, 2496 State: state, 2497 PlanMode: plans.DestroyMode, 2498 }) 2499 2500 if _, diags := ctx.Plan(); diags.HasErrors() { 2501 t.Fatalf("plan errors: %s", diags.Err()) 2502 } 2503 2504 state, diags := ctx.Apply() 2505 if diags.HasErrors() { 2506 t.Fatalf("diags: %s", diags.Err()) 2507 } 2508 2509 expected := []string{"b", "a"} 2510 if !reflect.DeepEqual(order, expected) { 2511 t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) 2512 } 2513 2514 { 2515 actual := strings.TrimSpace(state.String()) 2516 expected := strings.TrimSpace(testTerraformApplyModuleDestroyOrderStr) 2517 if actual != expected { 2518 t.Errorf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2519 } 2520 } 2521} 2522 2523func TestContext2Apply_moduleInheritAlias(t *testing.T) { 2524 m := testModule(t, "apply-module-provider-inherit-alias") 2525 p := testProvider("aws") 2526 p.PlanResourceChangeFn = testDiffFn 2527 p.ApplyResourceChangeFn = testApplyFn 2528 2529 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2530 val := req.Config.GetAttr("value") 2531 if val.IsNull() { 2532 return 2533 } 2534 2535 root := req.Config.GetAttr("root") 2536 if !root.IsNull() { 2537 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("child should not get root")) 2538 } 2539 2540 return 2541 } 2542 2543 ctx := testContext2(t, &ContextOpts{ 2544 Config: m, 2545 Providers: map[addrs.Provider]providers.Factory{ 2546 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2547 }, 2548 }) 2549 2550 if _, diags := ctx.Plan(); diags.HasErrors() { 2551 t.Fatalf("plan errors: %s", diags.Err()) 2552 } 2553 2554 state, diags := ctx.Apply() 2555 if diags.HasErrors() { 2556 t.Fatalf("diags: %s", diags.Err()) 2557 } 2558 2559 checkStateString(t, state, ` 2560<no state> 2561module.child: 2562 aws_instance.foo: 2563 ID = foo 2564 provider = provider["registry.terraform.io/hashicorp/aws"].eu 2565 type = aws_instance 2566 `) 2567} 2568 2569func TestContext2Apply_orphanResource(t *testing.T) { 2570 // This is a two-step test: 2571 // 1. Apply a configuration with resources that have count set. 2572 // This should place the empty resource object in the state to record 2573 // that each exists, and record any instances. 2574 // 2. Apply an empty configuration against the same state, which should 2575 // then clean up both the instances and the containing resource objects. 2576 p := testProvider("test") 2577 p.PlanResourceChangeFn = testDiffFn 2578 p.ApplyResourceChangeFn = testApplyFn 2579 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 2580 ResourceTypes: map[string]*configschema.Block{ 2581 "test_thing": { 2582 Attributes: map[string]*configschema.Attribute{ 2583 "id": {Type: cty.String, Computed: true}, 2584 "foo": {Type: cty.String, Optional: true}, 2585 }, 2586 }, 2587 }, 2588 }) 2589 2590 // Step 1: create the resources and instances 2591 m := testModule(t, "apply-orphan-resource") 2592 ctx := testContext2(t, &ContextOpts{ 2593 Config: m, 2594 Providers: map[addrs.Provider]providers.Factory{ 2595 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 2596 }, 2597 }) 2598 _, diags := ctx.Plan() 2599 assertNoErrors(t, diags) 2600 state, diags := ctx.Apply() 2601 assertNoErrors(t, diags) 2602 2603 // At this point both resources should be recorded in the state, along 2604 // with the single instance associated with test_thing.one. 2605 want := states.BuildState(func(s *states.SyncState) { 2606 providerAddr := addrs.AbsProviderConfig{ 2607 Provider: addrs.NewDefaultProvider("test"), 2608 Module: addrs.RootModule, 2609 } 2610 oneAddr := addrs.Resource{ 2611 Mode: addrs.ManagedResourceMode, 2612 Type: "test_thing", 2613 Name: "one", 2614 }.Absolute(addrs.RootModuleInstance) 2615 s.SetResourceProvider(oneAddr, providerAddr) 2616 s.SetResourceInstanceCurrent(oneAddr.Instance(addrs.IntKey(0)), &states.ResourceInstanceObjectSrc{ 2617 Status: states.ObjectReady, 2618 AttrsJSON: []byte(`{"id":"foo"}`), 2619 }, providerAddr) 2620 }) 2621 2622 if state.String() != want.String() { 2623 t.Fatalf("wrong state after step 1\n%s", cmp.Diff(want, state)) 2624 } 2625 2626 // Step 2: update with an empty config, to destroy everything 2627 m = testModule(t, "empty") 2628 ctx = testContext2(t, &ContextOpts{ 2629 Config: m, 2630 State: state, 2631 Providers: map[addrs.Provider]providers.Factory{ 2632 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 2633 }, 2634 }) 2635 _, diags = ctx.Plan() 2636 assertNoErrors(t, diags) 2637 state, diags = ctx.Apply() 2638 assertNoErrors(t, diags) 2639 2640 // The state should now be _totally_ empty, with just an empty root module 2641 // (since that always exists) and no resources at all. 2642 want = states.NewState() 2643 if !cmp.Equal(state, want) { 2644 t.Fatalf("wrong state after step 2\ngot: %swant: %s", spew.Sdump(state), spew.Sdump(want)) 2645 } 2646 2647} 2648 2649func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) { 2650 m := testModule(t, "apply-module-provider-inherit-alias-orphan") 2651 p := testProvider("aws") 2652 p.PlanResourceChangeFn = testDiffFn 2653 2654 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2655 val := req.Config.GetAttr("value") 2656 if val.IsNull() { 2657 return 2658 } 2659 2660 root := req.Config.GetAttr("root") 2661 if !root.IsNull() { 2662 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("child should not get root")) 2663 } 2664 2665 return 2666 } 2667 2668 // Create a state with an orphan module 2669 state := states.NewState() 2670 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 2671 child.SetResourceInstanceCurrent( 2672 mustResourceInstanceAddr("aws_instance.bar").Resource, 2673 &states.ResourceInstanceObjectSrc{ 2674 Status: states.ObjectReady, 2675 AttrsJSON: []byte(`{"id":"bar"}`), 2676 }, 2677 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2678 ) 2679 2680 ctx := testContext2(t, &ContextOpts{ 2681 Config: m, 2682 State: state, 2683 Providers: map[addrs.Provider]providers.Factory{ 2684 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2685 }, 2686 }) 2687 2688 if _, diags := ctx.Plan(); diags.HasErrors() { 2689 t.Fatalf("plan errors: %s", diags.Err()) 2690 } 2691 2692 state, diags := ctx.Apply() 2693 if diags.HasErrors() { 2694 t.Fatalf("diags: %s", diags.Err()) 2695 } 2696 2697 if !p.ConfigureProviderCalled { 2698 t.Fatal("must call configure") 2699 } 2700 2701 checkStateString(t, state, "<no state>") 2702} 2703 2704func TestContext2Apply_moduleOrphanProvider(t *testing.T) { 2705 m := testModule(t, "apply-module-orphan-provider-inherit") 2706 p := testProvider("aws") 2707 p.PlanResourceChangeFn = testDiffFn 2708 2709 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2710 val := req.Config.GetAttr("value") 2711 if val.IsNull() { 2712 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 2713 } 2714 2715 return 2716 } 2717 2718 // Create a state with an orphan module 2719 state := states.NewState() 2720 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 2721 child.SetResourceInstanceCurrent( 2722 mustResourceInstanceAddr("aws_instance.bar").Resource, 2723 &states.ResourceInstanceObjectSrc{ 2724 Status: states.ObjectReady, 2725 AttrsJSON: []byte(`{"id":"bar"}`), 2726 }, 2727 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2728 ) 2729 2730 ctx := testContext2(t, &ContextOpts{ 2731 Config: m, 2732 State: state, 2733 Providers: map[addrs.Provider]providers.Factory{ 2734 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2735 }, 2736 }) 2737 2738 if _, diags := ctx.Plan(); diags.HasErrors() { 2739 t.Fatalf("plan errors: %s", diags.Err()) 2740 } 2741 2742 if _, diags := ctx.Apply(); diags.HasErrors() { 2743 t.Fatalf("apply errors: %s", diags.Err()) 2744 } 2745} 2746 2747func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) { 2748 m := testModule(t, "apply-module-orphan-provider-inherit") 2749 p := testProvider("aws") 2750 p.PlanResourceChangeFn = testDiffFn 2751 2752 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2753 val := req.Config.GetAttr("value") 2754 if val.IsNull() { 2755 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 2756 } 2757 2758 return 2759 } 2760 2761 // Create a state with an orphan module that is nested (grandchild) 2762 state := states.NewState() 2763 child := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey).Child("child", addrs.NoKey)) 2764 child.SetResourceInstanceCurrent( 2765 mustResourceInstanceAddr("aws_instance.bar").Resource, 2766 &states.ResourceInstanceObjectSrc{ 2767 Status: states.ObjectReady, 2768 AttrsJSON: []byte(`{"id":"bar"}`), 2769 }, 2770 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2771 ) 2772 2773 ctx := testContext2(t, &ContextOpts{ 2774 Config: m, 2775 State: state, 2776 Providers: map[addrs.Provider]providers.Factory{ 2777 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2778 }, 2779 }) 2780 2781 if _, diags := ctx.Plan(); diags.HasErrors() { 2782 t.Fatalf("plan errors: %s", diags.Err()) 2783 } 2784 2785 if _, diags := ctx.Apply(); diags.HasErrors() { 2786 t.Fatalf("apply errors: %s", diags.Err()) 2787 } 2788} 2789 2790func TestContext2Apply_moduleGrandchildProvider(t *testing.T) { 2791 m := testModule(t, "apply-module-grandchild-provider-inherit") 2792 p := testProvider("aws") 2793 p.PlanResourceChangeFn = testDiffFn 2794 2795 var callLock sync.Mutex 2796 called := false 2797 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 2798 val := req.Config.GetAttr("value") 2799 if val.IsNull() { 2800 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 2801 } 2802 2803 callLock.Lock() 2804 called = true 2805 callLock.Unlock() 2806 2807 return 2808 } 2809 2810 ctx := testContext2(t, &ContextOpts{ 2811 Config: m, 2812 Providers: map[addrs.Provider]providers.Factory{ 2813 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2814 }, 2815 }) 2816 2817 if _, diags := ctx.Plan(); diags.HasErrors() { 2818 t.Fatalf("plan errors: %s", diags.Err()) 2819 } 2820 2821 if _, diags := ctx.Apply(); diags.HasErrors() { 2822 t.Fatalf("apply errors: %s", diags.Err()) 2823 } 2824 2825 callLock.Lock() 2826 defer callLock.Unlock() 2827 if called != true { 2828 t.Fatalf("err: configure never called") 2829 } 2830} 2831 2832// This tests an issue where all the providers in a module but not 2833// in the root weren't being added to the root properly. In this test 2834// case: aws is explicitly added to root, but "test" should be added to. 2835// With the bug, it wasn't. 2836func TestContext2Apply_moduleOnlyProvider(t *testing.T) { 2837 m := testModule(t, "apply-module-only-provider") 2838 p := testProvider("aws") 2839 p.PlanResourceChangeFn = testDiffFn 2840 p.ApplyResourceChangeFn = testApplyFn 2841 pTest := testProvider("test") 2842 pTest.ApplyResourceChangeFn = testApplyFn 2843 pTest.PlanResourceChangeFn = testDiffFn 2844 2845 ctx := testContext2(t, &ContextOpts{ 2846 Config: m, 2847 Providers: map[addrs.Provider]providers.Factory{ 2848 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2849 addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest), 2850 }, 2851 }) 2852 2853 if _, diags := ctx.Plan(); diags.HasErrors() { 2854 t.Fatalf("plan errors: %s", diags.Err()) 2855 } 2856 2857 state, diags := ctx.Apply() 2858 if diags.HasErrors() { 2859 t.Fatalf("diags: %s", diags.Err()) 2860 } 2861 2862 actual := strings.TrimSpace(state.String()) 2863 expected := strings.TrimSpace(testTerraformApplyModuleOnlyProviderStr) 2864 if actual != expected { 2865 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2866 } 2867} 2868 2869func TestContext2Apply_moduleProviderAlias(t *testing.T) { 2870 m := testModule(t, "apply-module-provider-alias") 2871 p := testProvider("aws") 2872 p.PlanResourceChangeFn = testDiffFn 2873 p.ApplyResourceChangeFn = testApplyFn 2874 ctx := testContext2(t, &ContextOpts{ 2875 Config: m, 2876 Providers: map[addrs.Provider]providers.Factory{ 2877 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2878 }, 2879 }) 2880 2881 if _, diags := ctx.Plan(); diags.HasErrors() { 2882 t.Fatalf("plan errors: %s", diags.Err()) 2883 } 2884 2885 state, diags := ctx.Apply() 2886 if diags.HasErrors() { 2887 t.Fatalf("diags: %s", diags.Err()) 2888 } 2889 2890 actual := strings.TrimSpace(state.String()) 2891 expected := strings.TrimSpace(testTerraformApplyModuleProviderAliasStr) 2892 if actual != expected { 2893 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2894 } 2895} 2896 2897func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) { 2898 m := testModule(t, "apply-module-provider-alias") 2899 p := testProvider("aws") 2900 p.PlanResourceChangeFn = testDiffFn 2901 ctx := testContext2(t, &ContextOpts{ 2902 Config: m, 2903 Providers: map[addrs.Provider]providers.Factory{ 2904 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2905 }, 2906 Targets: []addrs.Targetable{ 2907 addrs.ConfigResource{ 2908 Module: addrs.RootModule, 2909 Resource: addrs.Resource{ 2910 Mode: addrs.ManagedResourceMode, 2911 Type: "nonexistent", 2912 Name: "thing", 2913 }, 2914 }, 2915 }, 2916 }) 2917 2918 if _, diags := ctx.Plan(); diags.HasErrors() { 2919 t.Fatalf("plan errors: %s", diags.Err()) 2920 } 2921 2922 state, diags := ctx.Apply() 2923 if diags.HasErrors() { 2924 t.Fatalf("diags: %s", diags.Err()) 2925 } 2926 2927 actual := strings.TrimSpace(state.String()) 2928 expected := strings.TrimSpace(` 2929<no state> 2930 `) 2931 if actual != expected { 2932 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2933 } 2934} 2935 2936func TestContext2Apply_moduleProviderCloseNested(t *testing.T) { 2937 m := testModule(t, "apply-module-provider-close-nested") 2938 p := testProvider("aws") 2939 p.PlanResourceChangeFn = testDiffFn 2940 state := states.NewState() 2941 root := state.EnsureModule(addrs.RootModuleInstance) 2942 root.SetResourceInstanceCurrent( 2943 mustResourceInstanceAddr("aws_instance.foo").Resource, 2944 &states.ResourceInstanceObjectSrc{ 2945 Status: states.ObjectReady, 2946 AttrsJSON: []byte(`{"id":"bar"}`), 2947 }, 2948 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2949 ) 2950 2951 ctx := testContext2(t, &ContextOpts{ 2952 Config: m, 2953 Providers: map[addrs.Provider]providers.Factory{ 2954 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2955 }, 2956 State: state, 2957 PlanMode: plans.DestroyMode, 2958 }) 2959 2960 if _, diags := ctx.Plan(); diags.HasErrors() { 2961 t.Fatalf("plan errors: %s", diags.Err()) 2962 } 2963 2964 if _, diags := ctx.Apply(); diags.HasErrors() { 2965 t.Fatalf("apply errors: %s", diags.Err()) 2966 } 2967} 2968 2969// Tests that variables used as module vars that reference data that 2970// already exists in the state and requires no diff works properly. This 2971// fixes an issue faced where module variables were pruned because they were 2972// accessing "non-existent" resources (they existed, just not in the graph 2973// cause they weren't in the diff). 2974func TestContext2Apply_moduleVarRefExisting(t *testing.T) { 2975 m := testModule(t, "apply-ref-existing") 2976 p := testProvider("aws") 2977 p.PlanResourceChangeFn = testDiffFn 2978 p.ApplyResourceChangeFn = testApplyFn 2979 state := states.NewState() 2980 root := state.EnsureModule(addrs.RootModuleInstance) 2981 root.SetResourceInstanceCurrent( 2982 mustResourceInstanceAddr("aws_instance.foo").Resource, 2983 &states.ResourceInstanceObjectSrc{ 2984 Status: states.ObjectReady, 2985 AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`), 2986 }, 2987 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 2988 ) 2989 2990 ctx := testContext2(t, &ContextOpts{ 2991 Config: m, 2992 Providers: map[addrs.Provider]providers.Factory{ 2993 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 2994 }, 2995 State: state, 2996 }) 2997 2998 if _, diags := ctx.Plan(); diags.HasErrors() { 2999 t.Fatalf("plan errors: %s", diags.Err()) 3000 } 3001 3002 state, diags := ctx.Apply() 3003 if diags.HasErrors() { 3004 t.Fatalf("diags: %s", diags.Err()) 3005 } 3006 3007 actual := strings.TrimSpace(state.String()) 3008 expected := strings.TrimSpace(testTerraformApplyModuleVarRefExistingStr) 3009 if actual != expected { 3010 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3011 } 3012} 3013 3014func TestContext2Apply_moduleVarResourceCount(t *testing.T) { 3015 m := testModule(t, "apply-module-var-resource-count") 3016 p := testProvider("aws") 3017 p.PlanResourceChangeFn = testDiffFn 3018 ctx := testContext2(t, &ContextOpts{ 3019 Config: m, 3020 Providers: map[addrs.Provider]providers.Factory{ 3021 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3022 }, 3023 Variables: InputValues{ 3024 "num": &InputValue{ 3025 Value: cty.NumberIntVal(2), 3026 SourceType: ValueFromCaller, 3027 }, 3028 }, 3029 PlanMode: plans.DestroyMode, 3030 }) 3031 3032 if _, diags := ctx.Plan(); diags.HasErrors() { 3033 t.Fatalf("plan errors: %s", diags.Err()) 3034 } 3035 3036 if _, diags := ctx.Apply(); diags.HasErrors() { 3037 t.Fatalf("apply errors: %s", diags.Err()) 3038 } 3039 3040 ctx = testContext2(t, &ContextOpts{ 3041 Config: m, 3042 Providers: map[addrs.Provider]providers.Factory{ 3043 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3044 }, 3045 Variables: InputValues{ 3046 "num": &InputValue{ 3047 Value: cty.NumberIntVal(5), 3048 SourceType: ValueFromCaller, 3049 }, 3050 }, 3051 }) 3052 3053 if _, diags := ctx.Plan(); diags.HasErrors() { 3054 t.Fatalf("plan errors: %s", diags.Err()) 3055 } 3056 3057 if _, diags := ctx.Apply(); diags.HasErrors() { 3058 t.Fatalf("apply errors: %s", diags.Err()) 3059 } 3060} 3061 3062// GH-819 3063func TestContext2Apply_moduleBool(t *testing.T) { 3064 m := testModule(t, "apply-module-bool") 3065 p := testProvider("aws") 3066 p.PlanResourceChangeFn = testDiffFn 3067 p.ApplyResourceChangeFn = testApplyFn 3068 ctx := testContext2(t, &ContextOpts{ 3069 Config: m, 3070 Providers: map[addrs.Provider]providers.Factory{ 3071 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3072 }, 3073 }) 3074 3075 if _, diags := ctx.Plan(); diags.HasErrors() { 3076 t.Fatalf("plan errors: %s", diags.Err()) 3077 } 3078 3079 state, diags := ctx.Apply() 3080 if diags.HasErrors() { 3081 t.Fatalf("diags: %s", diags.Err()) 3082 } 3083 3084 actual := strings.TrimSpace(state.String()) 3085 expected := strings.TrimSpace(testTerraformApplyModuleBoolStr) 3086 if actual != expected { 3087 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3088 } 3089} 3090 3091// Tests that a module can be targeted and everything is properly created. 3092// This adds to the plan test to also just verify that apply works. 3093func TestContext2Apply_moduleTarget(t *testing.T) { 3094 m := testModule(t, "plan-targeted-cross-module") 3095 p := testProvider("aws") 3096 p.PlanResourceChangeFn = testDiffFn 3097 p.ApplyResourceChangeFn = testApplyFn 3098 ctx := testContext2(t, &ContextOpts{ 3099 Config: m, 3100 Providers: map[addrs.Provider]providers.Factory{ 3101 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3102 }, 3103 Targets: []addrs.Targetable{ 3104 addrs.RootModuleInstance.Child("B", addrs.NoKey), 3105 }, 3106 }) 3107 3108 if _, diags := ctx.Plan(); diags.HasErrors() { 3109 t.Fatalf("plan errors: %s", diags.Err()) 3110 } 3111 3112 state, diags := ctx.Apply() 3113 if diags.HasErrors() { 3114 t.Fatalf("diags: %s", diags.Err()) 3115 } 3116 3117 checkStateString(t, state, ` 3118<no state> 3119module.A: 3120 aws_instance.foo: 3121 ID = foo 3122 provider = provider["registry.terraform.io/hashicorp/aws"] 3123 foo = bar 3124 type = aws_instance 3125 3126 Outputs: 3127 3128 value = foo 3129module.B: 3130 aws_instance.bar: 3131 ID = foo 3132 provider = provider["registry.terraform.io/hashicorp/aws"] 3133 foo = foo 3134 type = aws_instance 3135 3136 Dependencies: 3137 module.A.aws_instance.foo 3138 `) 3139} 3140 3141func TestContext2Apply_multiProvider(t *testing.T) { 3142 m := testModule(t, "apply-multi-provider") 3143 p := testProvider("aws") 3144 p.PlanResourceChangeFn = testDiffFn 3145 p.ApplyResourceChangeFn = testApplyFn 3146 3147 pDO := testProvider("do") 3148 pDO.ApplyResourceChangeFn = testApplyFn 3149 pDO.PlanResourceChangeFn = testDiffFn 3150 3151 ctx := testContext2(t, &ContextOpts{ 3152 Config: m, 3153 Providers: map[addrs.Provider]providers.Factory{ 3154 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3155 addrs.NewDefaultProvider("do"): testProviderFuncFixed(pDO), 3156 }, 3157 }) 3158 3159 if _, diags := ctx.Plan(); diags.HasErrors() { 3160 t.Fatalf("plan errors: %s", diags.Err()) 3161 } 3162 3163 state, diags := ctx.Apply() 3164 if diags.HasErrors() { 3165 t.Fatalf("diags: %s", diags.Err()) 3166 } 3167 3168 mod := state.RootModule() 3169 if len(mod.Resources) < 2 { 3170 t.Fatalf("bad: %#v", mod.Resources) 3171 } 3172 3173 actual := strings.TrimSpace(state.String()) 3174 expected := strings.TrimSpace(testTerraformApplyMultiProviderStr) 3175 if actual != expected { 3176 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3177 } 3178} 3179 3180func TestContext2Apply_multiProviderDestroy(t *testing.T) { 3181 m := testModule(t, "apply-multi-provider-destroy") 3182 p := testProvider("aws") 3183 p.PlanResourceChangeFn = testDiffFn 3184 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3185 Provider: &configschema.Block{ 3186 Attributes: map[string]*configschema.Attribute{ 3187 "addr": {Type: cty.String, Optional: true}, 3188 }, 3189 }, 3190 ResourceTypes: map[string]*configschema.Block{ 3191 "aws_instance": { 3192 Attributes: map[string]*configschema.Attribute{ 3193 "id": {Type: cty.String, Computed: true}, 3194 "foo": {Type: cty.String, Optional: true}, 3195 }, 3196 }, 3197 }, 3198 }) 3199 3200 p2 := testProvider("vault") 3201 p2.ApplyResourceChangeFn = testApplyFn 3202 p2.PlanResourceChangeFn = testDiffFn 3203 p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3204 ResourceTypes: map[string]*configschema.Block{ 3205 "vault_instance": { 3206 Attributes: map[string]*configschema.Attribute{ 3207 "id": {Type: cty.String, Computed: true}, 3208 }, 3209 }, 3210 }, 3211 }) 3212 3213 var state *states.State 3214 3215 // First, create the instances 3216 { 3217 ctx := testContext2(t, &ContextOpts{ 3218 Config: m, 3219 Providers: map[addrs.Provider]providers.Factory{ 3220 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3221 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3222 }, 3223 }) 3224 3225 if _, diags := ctx.Plan(); diags.HasErrors() { 3226 t.Fatalf("errors during create plan: %s", diags.Err()) 3227 } 3228 3229 s, diags := ctx.Apply() 3230 if diags.HasErrors() { 3231 t.Fatalf("errors during create apply: %s", diags.Err()) 3232 } 3233 3234 state = s 3235 } 3236 3237 // Destroy them 3238 { 3239 // Verify that aws_instance.bar is destroyed first 3240 var checked bool 3241 var called int32 3242 var lock sync.Mutex 3243 applyFn := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 3244 lock.Lock() 3245 defer lock.Unlock() 3246 3247 if req.TypeName == "aws_instance" { 3248 checked = true 3249 3250 // Sleep to allow parallel execution 3251 time.Sleep(50 * time.Millisecond) 3252 3253 // Verify that called is 0 (dep not called) 3254 if atomic.LoadInt32(&called) != 0 { 3255 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called")) 3256 return resp 3257 } 3258 } 3259 3260 atomic.AddInt32(&called, 1) 3261 return testApplyFn(req) 3262 } 3263 3264 // Set the apply functions 3265 p.ApplyResourceChangeFn = applyFn 3266 p2.ApplyResourceChangeFn = applyFn 3267 3268 ctx := testContext2(t, &ContextOpts{ 3269 PlanMode: plans.DestroyMode, 3270 State: state, 3271 Config: m, 3272 Providers: map[addrs.Provider]providers.Factory{ 3273 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3274 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3275 }, 3276 }) 3277 3278 if _, diags := ctx.Plan(); diags.HasErrors() { 3279 t.Fatalf("errors during destroy plan: %s", diags.Err()) 3280 } 3281 3282 s, diags := ctx.Apply() 3283 if diags.HasErrors() { 3284 t.Fatalf("errors during destroy apply: %s", diags.Err()) 3285 } 3286 3287 if !checked { 3288 t.Fatal("should be checked") 3289 } 3290 3291 state = s 3292 } 3293 3294 checkStateString(t, state, `<no state>`) 3295} 3296 3297// This is like the multiProviderDestroy test except it tests that 3298// dependent resources within a child module that inherit provider 3299// configuration are still destroyed first. 3300func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { 3301 m := testModule(t, "apply-multi-provider-destroy-child") 3302 p := testProvider("aws") 3303 p.PlanResourceChangeFn = testDiffFn 3304 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3305 Provider: &configschema.Block{ 3306 Attributes: map[string]*configschema.Attribute{ 3307 "value": {Type: cty.String, Optional: true}, 3308 }, 3309 }, 3310 ResourceTypes: map[string]*configschema.Block{ 3311 "aws_instance": { 3312 Attributes: map[string]*configschema.Attribute{ 3313 "id": {Type: cty.String, Computed: true}, 3314 "foo": {Type: cty.String, Optional: true}, 3315 }, 3316 }, 3317 }, 3318 }) 3319 3320 p2 := testProvider("vault") 3321 p2.ApplyResourceChangeFn = testApplyFn 3322 p2.PlanResourceChangeFn = testDiffFn 3323 p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3324 Provider: &configschema.Block{}, 3325 ResourceTypes: map[string]*configschema.Block{ 3326 "vault_instance": { 3327 Attributes: map[string]*configschema.Attribute{ 3328 "id": {Type: cty.String, Computed: true}, 3329 }, 3330 }, 3331 }, 3332 }) 3333 3334 var state *states.State 3335 3336 // First, create the instances 3337 { 3338 ctx := testContext2(t, &ContextOpts{ 3339 Config: m, 3340 Providers: map[addrs.Provider]providers.Factory{ 3341 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3342 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3343 }, 3344 }) 3345 3346 if _, diags := ctx.Plan(); diags.HasErrors() { 3347 t.Fatalf("diags: %s", diags.Err()) 3348 } 3349 3350 s, diags := ctx.Apply() 3351 if diags.HasErrors() { 3352 t.Fatalf("diags: %s", diags.Err()) 3353 } 3354 3355 state = s 3356 } 3357 3358 // Destroy them 3359 { 3360 // Verify that aws_instance.bar is destroyed first 3361 var checked bool 3362 var called int32 3363 var lock sync.Mutex 3364 applyFn := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 3365 lock.Lock() 3366 defer lock.Unlock() 3367 3368 if req.TypeName == "aws_instance" { 3369 checked = true 3370 3371 // Sleep to allow parallel execution 3372 time.Sleep(50 * time.Millisecond) 3373 3374 // Verify that called is 0 (dep not called) 3375 if atomic.LoadInt32(&called) != 0 { 3376 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called")) 3377 return resp 3378 } 3379 } 3380 3381 atomic.AddInt32(&called, 1) 3382 return testApplyFn(req) 3383 } 3384 3385 // Set the apply functions 3386 p.ApplyResourceChangeFn = applyFn 3387 p2.ApplyResourceChangeFn = applyFn 3388 3389 ctx := testContext2(t, &ContextOpts{ 3390 PlanMode: plans.DestroyMode, 3391 State: state, 3392 Config: m, 3393 Providers: map[addrs.Provider]providers.Factory{ 3394 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3395 addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), 3396 }, 3397 }) 3398 3399 if _, diags := ctx.Plan(); diags.HasErrors() { 3400 t.Fatalf("diags: %s", diags.Err()) 3401 } 3402 3403 s, diags := ctx.Apply() 3404 if diags.HasErrors() { 3405 t.Fatalf("diags: %s", diags.Err()) 3406 } 3407 3408 if !checked { 3409 t.Fatal("should be checked") 3410 } 3411 3412 state = s 3413 } 3414 3415 checkStateString(t, state, ` 3416<no state> 3417`) 3418} 3419 3420func TestContext2Apply_multiVar(t *testing.T) { 3421 m := testModule(t, "apply-multi-var") 3422 p := testProvider("aws") 3423 p.PlanResourceChangeFn = testDiffFn 3424 3425 // First, apply with a count of 3 3426 ctx := testContext2(t, &ContextOpts{ 3427 Config: m, 3428 Providers: map[addrs.Provider]providers.Factory{ 3429 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3430 }, 3431 Variables: InputValues{ 3432 "num": &InputValue{ 3433 Value: cty.NumberIntVal(3), 3434 SourceType: ValueFromCaller, 3435 }, 3436 }, 3437 }) 3438 3439 if _, diags := ctx.Plan(); diags.HasErrors() { 3440 t.Fatalf("plan errors: %s", diags.Err()) 3441 } 3442 3443 state, diags := ctx.Apply() 3444 if diags.HasErrors() { 3445 t.Fatalf("diags: %s", diags.Err()) 3446 } 3447 3448 actual := state.RootModule().OutputValues["output"] 3449 expected := cty.StringVal("bar0,bar1,bar2") 3450 if actual == nil || actual.Value != expected { 3451 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3452 } 3453 3454 t.Logf("Initial state: %s", state.String()) 3455 3456 // Apply again, reduce the count to 1 3457 { 3458 ctx := testContext2(t, &ContextOpts{ 3459 Config: m, 3460 State: state, 3461 Providers: map[addrs.Provider]providers.Factory{ 3462 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3463 }, 3464 Variables: InputValues{ 3465 "num": &InputValue{ 3466 Value: cty.NumberIntVal(1), 3467 SourceType: ValueFromCaller, 3468 }, 3469 }, 3470 }) 3471 3472 if _, diags := ctx.Plan(); diags.HasErrors() { 3473 t.Fatalf("diags: %s", diags.Err()) 3474 } 3475 3476 state, diags := ctx.Apply() 3477 if diags.HasErrors() { 3478 t.Fatalf("diags: %s", diags.Err()) 3479 } 3480 3481 t.Logf("End state: %s", state.String()) 3482 3483 actual := state.RootModule().OutputValues["output"] 3484 if actual == nil { 3485 t.Fatal("missing output") 3486 } 3487 3488 expected := cty.StringVal("bar0") 3489 if actual.Value != expected { 3490 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3491 } 3492 } 3493} 3494 3495// This is a holistic test of multi-var (aka "splat variable") handling 3496// across several different Terraform subsystems. This is here because 3497// historically there were quirky differences in handling across different 3498// parts of Terraform and so here we want to assert the expected behavior and 3499// ensure that it remains consistent in future. 3500func TestContext2Apply_multiVarComprehensive(t *testing.T) { 3501 m := testModule(t, "apply-multi-var-comprehensive") 3502 p := testProvider("test") 3503 3504 configs := map[string]cty.Value{} 3505 var configsLock sync.Mutex 3506 3507 p.ApplyResourceChangeFn = testApplyFn 3508 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 3509 proposed := req.ProposedNewState 3510 configsLock.Lock() 3511 defer configsLock.Unlock() 3512 key := proposed.GetAttr("key").AsString() 3513 // This test was originally written using the legacy p.PlanResourceChangeFn interface, 3514 // and so the assertions below expect an old-style ResourceConfig, which 3515 // we'll construct via our shim for now to avoid rewriting all of the 3516 // assertions. 3517 configs[key] = req.ProposedNewState 3518 3519 retVals := make(map[string]cty.Value) 3520 for it := proposed.ElementIterator(); it.Next(); { 3521 idxVal, val := it.Element() 3522 idx := idxVal.AsString() 3523 3524 switch idx { 3525 case "id": 3526 retVals[idx] = cty.UnknownVal(cty.String) 3527 case "name": 3528 retVals[idx] = cty.StringVal(key) 3529 default: 3530 retVals[idx] = val 3531 } 3532 } 3533 3534 return providers.PlanResourceChangeResponse{ 3535 PlannedState: cty.ObjectVal(retVals), 3536 } 3537 } 3538 3539 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3540 ResourceTypes: map[string]*configschema.Block{ 3541 "test_thing": { 3542 Attributes: map[string]*configschema.Attribute{ 3543 "key": {Type: cty.String, Required: true}, 3544 3545 "source_id": {Type: cty.String, Optional: true}, 3546 "source_name": {Type: cty.String, Optional: true}, 3547 "first_source_id": {Type: cty.String, Optional: true}, 3548 "first_source_name": {Type: cty.String, Optional: true}, 3549 "source_ids": {Type: cty.List(cty.String), Optional: true}, 3550 "source_names": {Type: cty.List(cty.String), Optional: true}, 3551 "source_ids_from_func": {Type: cty.List(cty.String), Optional: true}, 3552 "source_names_from_func": {Type: cty.List(cty.String), Optional: true}, 3553 "source_ids_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true}, 3554 "source_names_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true}, 3555 3556 "id": {Type: cty.String, Computed: true}, 3557 "name": {Type: cty.String, Computed: true}, 3558 }, 3559 }, 3560 }, 3561 }) 3562 3563 // First, apply with a count of 3 3564 ctx := testContext2(t, &ContextOpts{ 3565 Config: m, 3566 Providers: map[addrs.Provider]providers.Factory{ 3567 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 3568 }, 3569 Variables: InputValues{ 3570 "num": &InputValue{ 3571 Value: cty.NumberIntVal(3), 3572 SourceType: ValueFromCaller, 3573 }, 3574 }, 3575 }) 3576 3577 if _, diags := ctx.Plan(); diags.HasErrors() { 3578 logDiagnostics(t, diags) 3579 t.Fatalf("errors during plan") 3580 } 3581 3582 checkConfig := func(key string, want cty.Value) { 3583 configsLock.Lock() 3584 defer configsLock.Unlock() 3585 3586 got, ok := configs[key] 3587 if !ok { 3588 t.Errorf("no config recorded for %s; expected a configuration", key) 3589 return 3590 } 3591 3592 t.Run("config for "+key, func(t *testing.T) { 3593 for _, problem := range deep.Equal(got, want) { 3594 t.Errorf(problem) 3595 } 3596 }) 3597 } 3598 3599 checkConfig("multi_count_var.0", cty.ObjectVal(map[string]cty.Value{ 3600 "source_id": cty.UnknownVal(cty.String), 3601 "source_name": cty.StringVal("source.0"), 3602 })) 3603 checkConfig("multi_count_var.2", cty.ObjectVal(map[string]cty.Value{ 3604 "source_id": cty.UnknownVal(cty.String), 3605 "source_name": cty.StringVal("source.2"), 3606 })) 3607 checkConfig("multi_count_derived.0", cty.ObjectVal(map[string]cty.Value{ 3608 "source_id": cty.UnknownVal(cty.String), 3609 "source_name": cty.StringVal("source.0"), 3610 })) 3611 checkConfig("multi_count_derived.2", cty.ObjectVal(map[string]cty.Value{ 3612 "source_id": cty.UnknownVal(cty.String), 3613 "source_name": cty.StringVal("source.2"), 3614 })) 3615 checkConfig("whole_splat", cty.ObjectVal(map[string]cty.Value{ 3616 "source_ids": cty.ListVal([]cty.Value{ 3617 cty.UnknownVal(cty.String), 3618 cty.UnknownVal(cty.String), 3619 cty.UnknownVal(cty.String), 3620 }), 3621 "source_names": cty.ListVal([]cty.Value{ 3622 cty.StringVal("source.0"), 3623 cty.StringVal("source.1"), 3624 cty.StringVal("source.2"), 3625 }), 3626 "source_ids_from_func": cty.UnknownVal(cty.String), 3627 "source_names_from_func": cty.ListVal([]cty.Value{ 3628 cty.StringVal("source.0"), 3629 cty.StringVal("source.1"), 3630 cty.StringVal("source.2"), 3631 }), 3632 "source_ids_wrapped": cty.ListVal([]cty.Value{ 3633 cty.ListVal([]cty.Value{ 3634 cty.UnknownVal(cty.String), 3635 cty.UnknownVal(cty.String), 3636 cty.UnknownVal(cty.String), 3637 }), 3638 }), 3639 "source_names_wrapped": cty.ListVal([]cty.Value{ 3640 cty.ListVal([]cty.Value{ 3641 cty.StringVal("source.0"), 3642 cty.StringVal("source.1"), 3643 cty.StringVal("source.2"), 3644 }), 3645 }), 3646 "first_source_id": cty.UnknownVal(cty.String), 3647 "first_source_name": cty.StringVal("source.0"), 3648 })) 3649 checkConfig("child.whole_splat", cty.ObjectVal(map[string]cty.Value{ 3650 "source_ids": cty.ListVal([]cty.Value{ 3651 cty.UnknownVal(cty.String), 3652 cty.UnknownVal(cty.String), 3653 cty.UnknownVal(cty.String), 3654 }), 3655 "source_names": cty.ListVal([]cty.Value{ 3656 cty.StringVal("source.0"), 3657 cty.StringVal("source.1"), 3658 cty.StringVal("source.2"), 3659 }), 3660 "source_ids_wrapped": cty.ListVal([]cty.Value{ 3661 cty.ListVal([]cty.Value{ 3662 cty.UnknownVal(cty.String), 3663 cty.UnknownVal(cty.String), 3664 cty.UnknownVal(cty.String), 3665 }), 3666 }), 3667 "source_names_wrapped": cty.ListVal([]cty.Value{ 3668 cty.ListVal([]cty.Value{ 3669 cty.StringVal("source.0"), 3670 cty.StringVal("source.1"), 3671 cty.StringVal("source.2"), 3672 }), 3673 }), 3674 })) 3675 3676 t.Run("apply", func(t *testing.T) { 3677 state, diags := ctx.Apply() 3678 if diags.HasErrors() { 3679 t.Fatalf("error during apply: %s", diags.Err()) 3680 } 3681 3682 want := map[string]interface{}{ 3683 "source_ids": []interface{}{"foo", "foo", "foo"}, 3684 "source_names": []interface{}{ 3685 "source.0", 3686 "source.1", 3687 "source.2", 3688 }, 3689 } 3690 got := map[string]interface{}{} 3691 for k, s := range state.RootModule().OutputValues { 3692 got[k] = hcl2shim.ConfigValueFromHCL2(s.Value) 3693 } 3694 if !reflect.DeepEqual(got, want) { 3695 t.Errorf( 3696 "wrong outputs\ngot: %s\nwant: %s", 3697 spew.Sdump(got), spew.Sdump(want), 3698 ) 3699 } 3700 }) 3701} 3702 3703// Test that multi-var (splat) access is ordered by count, not by 3704// value. 3705func TestContext2Apply_multiVarOrder(t *testing.T) { 3706 m := testModule(t, "apply-multi-var-order") 3707 p := testProvider("aws") 3708 p.PlanResourceChangeFn = testDiffFn 3709 3710 // First, apply with a count of 3 3711 ctx := testContext2(t, &ContextOpts{ 3712 Config: m, 3713 Providers: map[addrs.Provider]providers.Factory{ 3714 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3715 }, 3716 }) 3717 3718 if _, diags := ctx.Plan(); diags.HasErrors() { 3719 t.Fatalf("plan errors: %s", diags.Err()) 3720 } 3721 3722 state, diags := ctx.Apply() 3723 if diags.HasErrors() { 3724 t.Fatalf("diags: %s", diags.Err()) 3725 } 3726 3727 t.Logf("State: %s", state.String()) 3728 3729 actual := state.RootModule().OutputValues["should-be-11"] 3730 expected := cty.StringVal("index-11") 3731 if actual == nil || actual.Value != expected { 3732 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3733 } 3734} 3735 3736// Test that multi-var (splat) access is ordered by count, not by 3737// value, through interpolations. 3738func TestContext2Apply_multiVarOrderInterp(t *testing.T) { 3739 m := testModule(t, "apply-multi-var-order-interp") 3740 p := testProvider("aws") 3741 p.PlanResourceChangeFn = testDiffFn 3742 3743 // First, apply with a count of 3 3744 ctx := testContext2(t, &ContextOpts{ 3745 Config: m, 3746 Providers: map[addrs.Provider]providers.Factory{ 3747 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3748 }, 3749 }) 3750 3751 if _, diags := ctx.Plan(); diags.HasErrors() { 3752 t.Fatalf("plan errors: %s", diags.Err()) 3753 } 3754 3755 state, diags := ctx.Apply() 3756 if diags.HasErrors() { 3757 t.Fatalf("diags: %s", diags.Err()) 3758 } 3759 3760 t.Logf("State: %s", state.String()) 3761 3762 actual := state.RootModule().OutputValues["should-be-11"] 3763 expected := cty.StringVal("baz-index-11") 3764 if actual == nil || actual.Value != expected { 3765 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3766 } 3767} 3768 3769// Based on GH-10440 where a graph edge wasn't properly being created 3770// between a modified resource and a count instance being destroyed. 3771func TestContext2Apply_multiVarCountDec(t *testing.T) { 3772 var s *states.State 3773 3774 // First create resources. Nothing sneaky here. 3775 { 3776 m := testModule(t, "apply-multi-var-count-dec") 3777 p := testProvider("aws") 3778 p.PlanResourceChangeFn = testDiffFn 3779 p.ApplyResourceChangeFn = testApplyFn 3780 ctx := testContext2(t, &ContextOpts{ 3781 Config: m, 3782 Providers: map[addrs.Provider]providers.Factory{ 3783 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3784 }, 3785 Variables: InputValues{ 3786 "num": &InputValue{ 3787 Value: cty.NumberIntVal(2), 3788 SourceType: ValueFromCaller, 3789 }, 3790 }, 3791 }) 3792 3793 log.Print("\n========\nStep 1 Plan\n========") 3794 if _, diags := ctx.Plan(); diags.HasErrors() { 3795 t.Fatalf("diags: %s", diags.Err()) 3796 } 3797 3798 log.Print("\n========\nStep 1 Apply\n========") 3799 state, diags := ctx.Apply() 3800 if diags.HasErrors() { 3801 t.Fatalf("diags: %s", diags.Err()) 3802 } 3803 3804 t.Logf("Step 1 state:\n%s", state) 3805 3806 s = state 3807 } 3808 3809 // Decrease the count by 1 and verify that everything happens in the 3810 // right order. 3811 m := testModule(t, "apply-multi-var-count-dec") 3812 p := testProvider("aws") 3813 p.PlanResourceChangeFn = testDiffFn 3814 3815 // Verify that aws_instance.bar is modified first and nothing 3816 // else happens at the same time. 3817 { 3818 var checked bool 3819 var called int32 3820 var lock sync.Mutex 3821 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 3822 lock.Lock() 3823 defer lock.Unlock() 3824 3825 if !req.PlannedState.IsNull() { 3826 s := req.PlannedState.AsValueMap() 3827 if ami, ok := s["ami"]; ok && !ami.IsNull() && ami.AsString() == "special" { 3828 checked = true 3829 3830 // Sleep to allow parallel execution 3831 time.Sleep(50 * time.Millisecond) 3832 3833 // Verify that called is 0 (dep not called) 3834 if atomic.LoadInt32(&called) != 1 { 3835 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called")) 3836 return 3837 } 3838 } 3839 } 3840 atomic.AddInt32(&called, 1) 3841 return testApplyFn(req) 3842 } 3843 3844 ctx := testContext2(t, &ContextOpts{ 3845 State: s, 3846 Config: m, 3847 Providers: map[addrs.Provider]providers.Factory{ 3848 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3849 }, 3850 Variables: InputValues{ 3851 "num": &InputValue{ 3852 Value: cty.NumberIntVal(1), 3853 SourceType: ValueFromCaller, 3854 }, 3855 }, 3856 }) 3857 3858 log.Print("\n========\nStep 2 Plan\n========") 3859 plan, diags := ctx.Plan() 3860 if diags.HasErrors() { 3861 t.Fatalf("plan errors: %s", diags.Err()) 3862 } 3863 3864 t.Logf("Step 2 plan:\n%s", legacyDiffComparisonString(plan.Changes)) 3865 3866 log.Print("\n========\nStep 2 Apply\n========") 3867 _, diags = ctx.Apply() 3868 if diags.HasErrors() { 3869 t.Fatalf("apply errors: %s", diags.Err()) 3870 } 3871 3872 if !checked { 3873 t.Error("apply never called") 3874 } 3875 } 3876} 3877 3878// Test that we can resolve a multi-var (splat) for the first resource 3879// created in a non-root module, which happens when the module state doesn't 3880// exist yet. 3881// https://github.com/hashicorp/terraform/issues/14438 3882func TestContext2Apply_multiVarMissingState(t *testing.T) { 3883 m := testModule(t, "apply-multi-var-missing-state") 3884 p := testProvider("test") 3885 p.PlanResourceChangeFn = testDiffFn 3886 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 3887 ResourceTypes: map[string]*configschema.Block{ 3888 "test_thing": { 3889 Attributes: map[string]*configschema.Attribute{ 3890 "a_ids": {Type: cty.String, Optional: true}, 3891 "id": {Type: cty.String, Computed: true}, 3892 }, 3893 }, 3894 }, 3895 }) 3896 3897 // First, apply with a count of 3 3898 ctx := testContext2(t, &ContextOpts{ 3899 Config: m, 3900 Providers: map[addrs.Provider]providers.Factory{ 3901 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 3902 }, 3903 }) 3904 3905 if _, diags := ctx.Plan(); diags.HasErrors() { 3906 t.Fatalf("plan failed: %s", diags.Err()) 3907 } 3908 3909 // Before the relevant bug was fixed, Tdiagsaform would panic during apply. 3910 if _, diags := ctx.Apply(); diags.HasErrors() { 3911 t.Fatalf("apply failed: %s", diags.Err()) 3912 } 3913 3914 // If we get here with no errors or panics then our test was successful. 3915} 3916 3917func TestContext2Apply_outputOrphan(t *testing.T) { 3918 m := testModule(t, "apply-output-orphan") 3919 p := testProvider("aws") 3920 p.PlanResourceChangeFn = testDiffFn 3921 3922 state := states.NewState() 3923 root := state.EnsureModule(addrs.RootModuleInstance) 3924 root.SetOutputValue("foo", cty.StringVal("bar"), false) 3925 root.SetOutputValue("bar", cty.StringVal("baz"), false) 3926 3927 ctx := testContext2(t, &ContextOpts{ 3928 Config: m, 3929 Providers: map[addrs.Provider]providers.Factory{ 3930 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3931 }, 3932 State: state, 3933 }) 3934 3935 if _, diags := ctx.Plan(); diags.HasErrors() { 3936 t.Fatalf("plan errors: %s", diags.Err()) 3937 } 3938 3939 state, diags := ctx.Apply() 3940 if diags.HasErrors() { 3941 t.Fatalf("diags: %s", diags.Err()) 3942 } 3943 3944 actual := strings.TrimSpace(state.String()) 3945 expected := strings.TrimSpace(testTerraformApplyOutputOrphanStr) 3946 if actual != expected { 3947 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3948 } 3949} 3950 3951func TestContext2Apply_outputOrphanModule(t *testing.T) { 3952 m := testModule(t, "apply-output-orphan-module") 3953 p := testProvider("aws") 3954 p.PlanResourceChangeFn = testDiffFn 3955 3956 state := states.NewState() 3957 3958 ctx := testContext2(t, &ContextOpts{ 3959 Config: m, 3960 Providers: map[addrs.Provider]providers.Factory{ 3961 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3962 }, 3963 State: state, 3964 }) 3965 3966 if _, diags := ctx.Plan(); diags.HasErrors() { 3967 t.Fatalf("plan errors: %s", diags.Err()) 3968 } 3969 3970 s, diags := ctx.Apply() 3971 if diags.HasErrors() { 3972 t.Fatalf("diags: %s", diags.Err()) 3973 } 3974 3975 actual := strings.TrimSpace(s.String()) 3976 expected := strings.TrimSpace(testTerraformApplyOutputOrphanModuleStr) 3977 if actual != expected { 3978 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 3979 } 3980 3981 // now apply with no module in the config, which should remove the 3982 // remaining output 3983 ctx = testContext2(t, &ContextOpts{ 3984 Config: configs.NewEmptyConfig(), 3985 Providers: map[addrs.Provider]providers.Factory{ 3986 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 3987 }, 3988 State: state.DeepCopy(), 3989 }) 3990 3991 if _, diags := ctx.Plan(); diags.HasErrors() { 3992 t.Fatalf("plan errors: %s", diags.Err()) 3993 } 3994 3995 state, diags = ctx.Apply() 3996 if diags.HasErrors() { 3997 t.Fatalf("diags: %s", diags.Err()) 3998 } 3999 4000 if !state.Empty() { 4001 t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(state)) 4002 } 4003} 4004 4005func TestContext2Apply_providerComputedVar(t *testing.T) { 4006 m := testModule(t, "apply-provider-computed") 4007 p := testProvider("aws") 4008 p.PlanResourceChangeFn = testDiffFn 4009 4010 pTest := testProvider("test") 4011 pTest.ApplyResourceChangeFn = testApplyFn 4012 pTest.PlanResourceChangeFn = testDiffFn 4013 4014 ctx := testContext2(t, &ContextOpts{ 4015 Config: m, 4016 Providers: map[addrs.Provider]providers.Factory{ 4017 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4018 addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest), 4019 }, 4020 }) 4021 4022 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 4023 val := req.Config.GetAttr("value") 4024 if val.IsNull() { 4025 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 4026 return 4027 } 4028 return 4029 } 4030 4031 if _, diags := ctx.Plan(); diags.HasErrors() { 4032 t.Fatalf("plan errors: %s", diags.Err()) 4033 } 4034 4035 if _, diags := ctx.Apply(); diags.HasErrors() { 4036 t.Fatalf("apply errors: %s", diags.Err()) 4037 } 4038} 4039 4040func TestContext2Apply_providerConfigureDisabled(t *testing.T) { 4041 m := testModule(t, "apply-provider-configure-disabled") 4042 p := testProvider("aws") 4043 p.PlanResourceChangeFn = testDiffFn 4044 4045 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 4046 val := req.Config.GetAttr("value") 4047 if val.IsNull() { 4048 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) 4049 } 4050 4051 return 4052 } 4053 4054 ctx := testContext2(t, &ContextOpts{ 4055 Config: m, 4056 Providers: map[addrs.Provider]providers.Factory{ 4057 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4058 }, 4059 }) 4060 4061 if _, diags := ctx.Plan(); diags.HasErrors() { 4062 t.Fatalf("plan errors: %s", diags.Err()) 4063 } 4064 4065 if _, diags := ctx.Apply(); diags.HasErrors() { 4066 t.Fatalf("apply errors: %s", diags.Err()) 4067 } 4068 4069 if !p.ConfigureProviderCalled { 4070 t.Fatal("configure never called") 4071 } 4072} 4073 4074func TestContext2Apply_provisionerModule(t *testing.T) { 4075 m := testModule(t, "apply-provisioner-module") 4076 4077 p := testProvider("aws") 4078 p.PlanResourceChangeFn = testDiffFn 4079 p.ApplyResourceChangeFn = testApplyFn 4080 4081 pr := testProvisioner() 4082 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 4083 Provisioner: &configschema.Block{ 4084 Attributes: map[string]*configschema.Attribute{ 4085 "foo": {Type: cty.String, Optional: true}, 4086 }, 4087 }, 4088 } 4089 4090 ctx := testContext2(t, &ContextOpts{ 4091 Config: m, 4092 Providers: map[addrs.Provider]providers.Factory{ 4093 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4094 }, 4095 Provisioners: map[string]provisioners.Factory{ 4096 "shell": testProvisionerFuncFixed(pr), 4097 }, 4098 }) 4099 4100 if _, diags := ctx.Plan(); diags.HasErrors() { 4101 t.Fatalf("plan errors: %s", diags.Err()) 4102 } 4103 4104 state, diags := ctx.Apply() 4105 if diags.HasErrors() { 4106 t.Fatalf("diags: %s", diags.Err()) 4107 } 4108 4109 actual := strings.TrimSpace(state.String()) 4110 expected := strings.TrimSpace(testTerraformApplyProvisionerModuleStr) 4111 if actual != expected { 4112 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4113 } 4114 4115 // Verify apply was invoked 4116 if !pr.ProvisionResourceCalled { 4117 t.Fatalf("provisioner not invoked") 4118 } 4119} 4120 4121func TestContext2Apply_Provisioner_compute(t *testing.T) { 4122 m := testModule(t, "apply-provisioner-compute") 4123 p := testProvider("aws") 4124 pr := testProvisioner() 4125 p.PlanResourceChangeFn = testDiffFn 4126 p.ApplyResourceChangeFn = testApplyFn 4127 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4128 4129 val := req.Config.GetAttr("command").AsString() 4130 if val != "computed_value" { 4131 t.Fatalf("bad value for foo: %q", val) 4132 } 4133 req.UIOutput.Output(fmt.Sprintf("Executing: %q", val)) 4134 4135 return 4136 } 4137 h := new(MockHook) 4138 ctx := testContext2(t, &ContextOpts{ 4139 Config: m, 4140 Hooks: []Hook{h}, 4141 Providers: map[addrs.Provider]providers.Factory{ 4142 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4143 }, 4144 Provisioners: map[string]provisioners.Factory{ 4145 "shell": testProvisionerFuncFixed(pr), 4146 }, 4147 Variables: InputValues{ 4148 "value": &InputValue{ 4149 Value: cty.NumberIntVal(1), 4150 SourceType: ValueFromCaller, 4151 }, 4152 }, 4153 }) 4154 4155 if _, diags := ctx.Plan(); diags.HasErrors() { 4156 t.Fatalf("plan errors: %s", diags.Err()) 4157 } 4158 4159 state, diags := ctx.Apply() 4160 if diags.HasErrors() { 4161 t.Fatalf("diags: %s", diags.Err()) 4162 } 4163 4164 actual := strings.TrimSpace(state.String()) 4165 expected := strings.TrimSpace(testTerraformApplyProvisionerStr) 4166 if actual != expected { 4167 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4168 } 4169 4170 // Verify apply was invoked 4171 if !pr.ProvisionResourceCalled { 4172 t.Fatalf("provisioner not invoked") 4173 } 4174 4175 // Verify output was rendered 4176 if !h.ProvisionOutputCalled { 4177 t.Fatalf("ProvisionOutput hook not called") 4178 } 4179 if got, want := h.ProvisionOutputMessage, `Executing: "computed_value"`; got != want { 4180 t.Errorf("expected output to be %q, but was %q", want, got) 4181 } 4182} 4183 4184func TestContext2Apply_provisionerCreateFail(t *testing.T) { 4185 m := testModule(t, "apply-provisioner-fail-create") 4186 p := testProvider("aws") 4187 pr := testProvisioner() 4188 p.PlanResourceChangeFn = testDiffFn 4189 4190 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 4191 resp := testApplyFn(req) 4192 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 4193 4194 return resp 4195 } 4196 4197 ctx := testContext2(t, &ContextOpts{ 4198 Config: m, 4199 Providers: map[addrs.Provider]providers.Factory{ 4200 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4201 }, 4202 Provisioners: map[string]provisioners.Factory{ 4203 "shell": testProvisionerFuncFixed(pr), 4204 }, 4205 }) 4206 4207 if _, diags := ctx.Plan(); diags.HasErrors() { 4208 t.Fatalf("plan errors: %s", diags.Err()) 4209 } 4210 4211 state, diags := ctx.Apply() 4212 if diags == nil { 4213 t.Fatal("should error") 4214 } 4215 4216 got := strings.TrimSpace(state.String()) 4217 want := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr) 4218 if got != want { 4219 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want) 4220 } 4221} 4222 4223func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) { 4224 m := testModule(t, "apply-provisioner-fail-create") 4225 p := testProvider("aws") 4226 pr := testProvisioner() 4227 p.PlanResourceChangeFn = testDiffFn 4228 4229 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4230 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 4231 return 4232 } 4233 4234 ctx := testContext2(t, &ContextOpts{ 4235 Config: m, 4236 Providers: map[addrs.Provider]providers.Factory{ 4237 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4238 }, 4239 Provisioners: map[string]provisioners.Factory{ 4240 "shell": testProvisionerFuncFixed(pr), 4241 }, 4242 }) 4243 4244 if _, diags := ctx.Plan(); diags.HasErrors() { 4245 t.Fatalf("plan errors: %s", diags.Err()) 4246 } 4247 4248 state, diags := ctx.Apply() 4249 if diags == nil { 4250 t.Fatal("should error") 4251 } 4252 4253 actual := strings.TrimSpace(state.String()) 4254 expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr) 4255 if actual != expected { 4256 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4257 } 4258} 4259 4260func TestContext2Apply_provisionerFail(t *testing.T) { 4261 m := testModule(t, "apply-provisioner-fail") 4262 p := testProvider("aws") 4263 p.PlanResourceChangeFn = testDiffFn 4264 p.ApplyResourceChangeFn = testApplyFn 4265 pr := testProvisioner() 4266 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4267 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION")) 4268 return 4269 } 4270 4271 ctx := testContext2(t, &ContextOpts{ 4272 Config: m, 4273 Providers: map[addrs.Provider]providers.Factory{ 4274 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4275 }, 4276 Provisioners: map[string]provisioners.Factory{ 4277 "shell": testProvisionerFuncFixed(pr), 4278 }, 4279 }) 4280 4281 if _, diags := ctx.Plan(); diags.HasErrors() { 4282 t.Fatalf("plan errors: %s", diags.Err()) 4283 } 4284 4285 state, diags := ctx.Apply() 4286 if diags == nil { 4287 t.Fatal("should error") 4288 } 4289 4290 actual := strings.TrimSpace(state.String()) 4291 expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr) 4292 if actual != expected { 4293 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4294 } 4295} 4296 4297func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) { 4298 m := testModule(t, "apply-provisioner-fail-create-before") 4299 p := testProvider("aws") 4300 pr := testProvisioner() 4301 p.PlanResourceChangeFn = testDiffFn 4302 p.ApplyResourceChangeFn = testApplyFn 4303 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4304 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION")) 4305 return 4306 } 4307 4308 state := states.NewState() 4309 root := state.EnsureModule(addrs.RootModuleInstance) 4310 root.SetResourceInstanceCurrent( 4311 mustResourceInstanceAddr("aws_instance.bar").Resource, 4312 &states.ResourceInstanceObjectSrc{ 4313 Status: states.ObjectReady, 4314 AttrsJSON: []byte(`{"id":"bar","require_new":"abc"}`), 4315 }, 4316 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4317 ) 4318 4319 ctx := testContext2(t, &ContextOpts{ 4320 Config: m, 4321 Providers: map[addrs.Provider]providers.Factory{ 4322 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4323 }, 4324 Provisioners: map[string]provisioners.Factory{ 4325 "shell": testProvisionerFuncFixed(pr), 4326 }, 4327 State: state, 4328 }) 4329 4330 if _, diags := ctx.Plan(); diags.HasErrors() { 4331 t.Fatalf("plan errors: %s", diags.Err()) 4332 } 4333 4334 state, diags := ctx.Apply() 4335 if diags == nil { 4336 t.Fatal("should error") 4337 } 4338 4339 actual := strings.TrimSpace(state.String()) 4340 expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr) 4341 if actual != expected { 4342 t.Fatalf("expected:\n%s\n:got\n%s", expected, actual) 4343 } 4344} 4345 4346func TestContext2Apply_error_createBeforeDestroy(t *testing.T) { 4347 m := testModule(t, "apply-error-create-before") 4348 p := testProvider("aws") 4349 4350 state := states.NewState() 4351 root := state.EnsureModule(addrs.RootModuleInstance) 4352 root.SetResourceInstanceCurrent( 4353 mustResourceInstanceAddr("aws_instance.bar").Resource, 4354 &states.ResourceInstanceObjectSrc{ 4355 Status: states.ObjectReady, 4356 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc","type":"aws_instance"}`), 4357 }, 4358 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4359 ) 4360 4361 ctx := testContext2(t, &ContextOpts{ 4362 Config: m, 4363 Providers: map[addrs.Provider]providers.Factory{ 4364 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4365 }, 4366 State: state, 4367 }) 4368 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4369 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("placeholder error from ApplyFn")) 4370 return 4371 } 4372 p.PlanResourceChangeFn = testDiffFn 4373 4374 if _, diags := ctx.Plan(); diags.HasErrors() { 4375 t.Fatalf("plan errors: %s", diags.Err()) 4376 } 4377 4378 state, diags := ctx.Apply() 4379 if diags == nil { 4380 t.Fatal("should have error") 4381 } 4382 if got, want := diags.Err().Error(), "placeholder error from ApplyFn"; got != want { 4383 // We're looking for our artificial error from ApplyFn above, whose 4384 // message is literally "placeholder error from ApplyFn". 4385 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 4386 } 4387 4388 actual := strings.TrimSpace(state.String()) 4389 expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr) 4390 if actual != expected { 4391 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected) 4392 } 4393} 4394 4395func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) { 4396 m := testModule(t, "apply-error-create-before") 4397 p := testProvider("aws") 4398 4399 state := states.NewState() 4400 root := state.EnsureModule(addrs.RootModuleInstance) 4401 root.SetResourceInstanceCurrent( 4402 mustResourceInstanceAddr("aws_instance.bar").Resource, 4403 &states.ResourceInstanceObjectSrc{ 4404 Status: states.ObjectReady, 4405 AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`), 4406 }, 4407 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4408 ) 4409 4410 ctx := testContext2(t, &ContextOpts{ 4411 Config: m, 4412 Providers: map[addrs.Provider]providers.Factory{ 4413 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4414 }, 4415 State: state, 4416 }) 4417 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4418 // Fail the destroy! 4419 if req.PlannedState.IsNull() { 4420 resp.NewState = req.PriorState 4421 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 4422 return 4423 } 4424 4425 return testApplyFn(req) 4426 } 4427 p.PlanResourceChangeFn = testDiffFn 4428 4429 if _, diags := ctx.Plan(); diags.HasErrors() { 4430 t.Fatalf("plan errors: %s", diags.Err()) 4431 } 4432 4433 state, diags := ctx.Apply() 4434 if diags == nil { 4435 t.Fatal("should have error") 4436 } 4437 4438 actual := strings.TrimSpace(state.String()) 4439 expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr) 4440 if actual != expected { 4441 t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected) 4442 } 4443} 4444 4445func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) { 4446 m := testModule(t, "apply-multi-depose-create-before-destroy") 4447 p := testProvider("aws") 4448 ps := map[addrs.Provider]providers.Factory{addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p)} 4449 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 4450 ResourceTypes: map[string]*configschema.Block{ 4451 "aws_instance": { 4452 Attributes: map[string]*configschema.Attribute{ 4453 "require_new": {Type: cty.String, Optional: true}, 4454 "id": {Type: cty.String, Computed: true}, 4455 }, 4456 }, 4457 }, 4458 }) 4459 4460 state := states.NewState() 4461 root := state.EnsureModule(addrs.RootModuleInstance) 4462 root.SetResourceInstanceCurrent( 4463 mustResourceInstanceAddr("aws_instance.web").Resource, 4464 &states.ResourceInstanceObjectSrc{ 4465 Status: states.ObjectReady, 4466 AttrsJSON: []byte(`{"id":"foo"}`), 4467 }, 4468 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4469 ) 4470 4471 p.PlanResourceChangeFn = testDiffFn 4472 4473 ctx := testContext2(t, &ContextOpts{ 4474 Config: m, 4475 Providers: ps, 4476 State: state, 4477 Variables: InputValues{ 4478 "require_new": &InputValue{ 4479 Value: cty.StringVal("yes"), 4480 }, 4481 }, 4482 }) 4483 createdInstanceId := "bar" 4484 // Create works 4485 createFunc := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4486 s := req.PlannedState.AsValueMap() 4487 s["id"] = cty.StringVal(createdInstanceId) 4488 resp.NewState = cty.ObjectVal(s) 4489 return 4490 } 4491 4492 // Destroy starts broken 4493 destroyFunc := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4494 resp.NewState = req.PriorState 4495 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("destroy failed")) 4496 return 4497 } 4498 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4499 if req.PlannedState.IsNull() { 4500 return destroyFunc(req) 4501 } else { 4502 return createFunc(req) 4503 } 4504 } 4505 4506 if _, diags := ctx.Plan(); diags.HasErrors() { 4507 t.Fatalf("plan errors: %s", diags.Err()) 4508 } 4509 4510 // Destroy is broken, so even though CBD successfully replaces the instance, 4511 // we'll have to save the Deposed instance to destroy later 4512 state, diags := ctx.Apply() 4513 if diags == nil { 4514 t.Fatal("should have error") 4515 } 4516 4517 checkStateString(t, state, ` 4518aws_instance.web: (1 deposed) 4519 ID = bar 4520 provider = provider["registry.terraform.io/hashicorp/aws"] 4521 require_new = yes 4522 Deposed ID 1 = foo 4523 `) 4524 4525 createdInstanceId = "baz" 4526 ctx = testContext2(t, &ContextOpts{ 4527 Config: m, 4528 Providers: ps, 4529 State: state, 4530 Variables: InputValues{ 4531 "require_new": &InputValue{ 4532 Value: cty.StringVal("baz"), 4533 }, 4534 }, 4535 }) 4536 4537 if _, diags := ctx.Plan(); diags.HasErrors() { 4538 t.Fatalf("plan errors: %s", diags.Err()) 4539 } 4540 4541 // We're replacing the primary instance once again. Destroy is _still_ 4542 // broken, so the Deposed list gets longer 4543 state, diags = ctx.Apply() 4544 if diags == nil { 4545 t.Fatal("should have error") 4546 } 4547 4548 // For this one we can't rely on checkStateString because its result is 4549 // not deterministic when multiple deposed objects are present. Instead, 4550 // we will probe the state object directly. 4551 { 4552 is := state.RootModule().Resources["aws_instance.web"].Instances[addrs.NoKey] 4553 if is.Current == nil { 4554 t.Fatalf("no current object for aws_instance web; should have one") 4555 } 4556 if !bytes.Contains(is.Current.AttrsJSON, []byte("baz")) { 4557 t.Fatalf("incorrect current object attrs %s; want id=baz", is.Current.AttrsJSON) 4558 } 4559 if got, want := len(is.Deposed), 2; got != want { 4560 t.Fatalf("wrong number of deposed instances %d; want %d", got, want) 4561 } 4562 var foos, bars int 4563 for _, obj := range is.Deposed { 4564 if bytes.Contains(obj.AttrsJSON, []byte("foo")) { 4565 foos++ 4566 } 4567 if bytes.Contains(obj.AttrsJSON, []byte("bar")) { 4568 bars++ 4569 } 4570 } 4571 if got, want := foos, 1; got != want { 4572 t.Fatalf("wrong number of deposed instances with id=foo %d; want %d", got, want) 4573 } 4574 if got, want := bars, 1; got != want { 4575 t.Fatalf("wrong number of deposed instances with id=bar %d; want %d", got, want) 4576 } 4577 } 4578 4579 // Destroy partially fixed! 4580 destroyFunc = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4581 s := req.PriorState.AsValueMap() 4582 id := s["id"].AsString() 4583 if id == "foo" || id == "baz" { 4584 resp.NewState = cty.NullVal(req.PriorState.Type()) 4585 } else { 4586 resp.NewState = req.PriorState 4587 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("destroy partially failed")) 4588 } 4589 return 4590 } 4591 4592 createdInstanceId = "qux" 4593 ctx = testContext2(t, &ContextOpts{ 4594 Config: m, 4595 Providers: ps, 4596 State: state, 4597 Variables: InputValues{ 4598 "require_new": &InputValue{ 4599 Value: cty.StringVal("qux"), 4600 }, 4601 }, 4602 }) 4603 if _, diags := ctx.Plan(); diags.HasErrors() { 4604 t.Fatalf("plan errors: %s", diags.Err()) 4605 } 4606 4607 state, diags = ctx.Apply() 4608 // Expect error because 1/2 of Deposed destroys failed 4609 if diags == nil { 4610 t.Fatal("should have error") 4611 } 4612 4613 // foo and baz are now gone, bar sticks around 4614 checkStateString(t, state, ` 4615aws_instance.web: (1 deposed) 4616 ID = qux 4617 provider = provider["registry.terraform.io/hashicorp/aws"] 4618 require_new = qux 4619 Deposed ID 1 = bar 4620 `) 4621 4622 // Destroy working fully! 4623 destroyFunc = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 4624 resp.NewState = cty.NullVal(req.PriorState.Type()) 4625 return 4626 } 4627 4628 createdInstanceId = "quux" 4629 ctx = testContext2(t, &ContextOpts{ 4630 Config: m, 4631 Providers: ps, 4632 State: state, 4633 Variables: InputValues{ 4634 "require_new": &InputValue{ 4635 Value: cty.StringVal("quux"), 4636 }, 4637 }, 4638 }) 4639 if _, diags := ctx.Plan(); diags.HasErrors() { 4640 t.Fatalf("plan errors: %s", diags.Err()) 4641 } 4642 state, diags = ctx.Apply() 4643 if diags.HasErrors() { 4644 t.Fatal("should not have error:", diags.Err()) 4645 } 4646 4647 // And finally the state is clean 4648 checkStateString(t, state, ` 4649aws_instance.web: 4650 ID = quux 4651 provider = provider["registry.terraform.io/hashicorp/aws"] 4652 require_new = quux 4653 `) 4654} 4655 4656// Verify that a normal provisioner with on_failure "continue" set won't 4657// taint the resource and continues executing. 4658func TestContext2Apply_provisionerFailContinue(t *testing.T) { 4659 m := testModule(t, "apply-provisioner-fail-continue") 4660 p := testProvider("aws") 4661 pr := testProvisioner() 4662 p.PlanResourceChangeFn = testDiffFn 4663 p.ApplyResourceChangeFn = testApplyFn 4664 4665 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4666 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4667 return 4668 } 4669 4670 ctx := testContext2(t, &ContextOpts{ 4671 Config: m, 4672 Providers: map[addrs.Provider]providers.Factory{ 4673 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4674 }, 4675 Provisioners: map[string]provisioners.Factory{ 4676 "shell": testProvisionerFuncFixed(pr), 4677 }, 4678 }) 4679 4680 if _, diags := ctx.Plan(); diags.HasErrors() { 4681 t.Fatalf("plan errors: %s", diags.Err()) 4682 } 4683 4684 state, diags := ctx.Apply() 4685 if diags.HasErrors() { 4686 t.Fatalf("diags: %s", diags.Err()) 4687 } 4688 4689 checkStateString(t, state, ` 4690aws_instance.foo: 4691 ID = foo 4692 provider = provider["registry.terraform.io/hashicorp/aws"] 4693 foo = bar 4694 type = aws_instance 4695 `) 4696 4697 // Verify apply was invoked 4698 if !pr.ProvisionResourceCalled { 4699 t.Fatalf("provisioner not invoked") 4700 } 4701} 4702 4703// Verify that a normal provisioner with on_failure "continue" records 4704// the error with the hook. 4705func TestContext2Apply_provisionerFailContinueHook(t *testing.T) { 4706 h := new(MockHook) 4707 m := testModule(t, "apply-provisioner-fail-continue") 4708 p := testProvider("aws") 4709 pr := testProvisioner() 4710 p.PlanResourceChangeFn = testDiffFn 4711 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4712 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4713 return 4714 } 4715 4716 ctx := testContext2(t, &ContextOpts{ 4717 Config: m, 4718 Hooks: []Hook{h}, 4719 Providers: map[addrs.Provider]providers.Factory{ 4720 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4721 }, 4722 Provisioners: map[string]provisioners.Factory{ 4723 "shell": testProvisionerFuncFixed(pr), 4724 }, 4725 }) 4726 4727 if _, diags := ctx.Plan(); diags.HasErrors() { 4728 t.Fatalf("plan errors: %s", diags.Err()) 4729 } 4730 4731 if _, diags := ctx.Apply(); diags.HasErrors() { 4732 t.Fatalf("apply errors: %s", diags.Err()) 4733 } 4734 4735 if !h.PostProvisionInstanceStepCalled { 4736 t.Fatal("PostProvisionInstanceStep not called") 4737 } 4738 if h.PostProvisionInstanceStepErrorArg == nil { 4739 t.Fatal("should have error") 4740 } 4741} 4742 4743func TestContext2Apply_provisionerDestroy(t *testing.T) { 4744 m := testModule(t, "apply-provisioner-destroy") 4745 p := testProvider("aws") 4746 pr := testProvisioner() 4747 p.PlanResourceChangeFn = testDiffFn 4748 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4749 val := req.Config.GetAttr("command").AsString() 4750 if val != "destroy a bar" { 4751 t.Fatalf("bad value for foo: %q", val) 4752 } 4753 4754 return 4755 } 4756 4757 state := states.NewState() 4758 root := state.RootModule() 4759 root.SetResourceInstanceCurrent( 4760 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4761 &states.ResourceInstanceObjectSrc{ 4762 Status: states.ObjectReady, 4763 AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), 4764 }, 4765 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4766 ) 4767 4768 ctx := testContext2(t, &ContextOpts{ 4769 Config: m, 4770 State: state, 4771 PlanMode: plans.DestroyMode, 4772 Providers: map[addrs.Provider]providers.Factory{ 4773 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4774 }, 4775 Provisioners: map[string]provisioners.Factory{ 4776 "shell": testProvisionerFuncFixed(pr), 4777 }, 4778 }) 4779 4780 if _, diags := ctx.Plan(); diags.HasErrors() { 4781 t.Fatalf("plan errors: %s", diags.Err()) 4782 } 4783 4784 state, diags := ctx.Apply() 4785 if diags.HasErrors() { 4786 t.Fatalf("diags: %s", diags.Err()) 4787 } 4788 4789 checkStateString(t, state, `<no state>`) 4790 4791 // Verify apply was invoked 4792 if !pr.ProvisionResourceCalled { 4793 t.Fatalf("provisioner not invoked") 4794 } 4795} 4796 4797// Verify that on destroy provisioner failure, nothing happens to the instance 4798func TestContext2Apply_provisionerDestroyFail(t *testing.T) { 4799 m := testModule(t, "apply-provisioner-destroy") 4800 p := testProvider("aws") 4801 pr := testProvisioner() 4802 p.PlanResourceChangeFn = testDiffFn 4803 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4804 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4805 return 4806 } 4807 4808 state := states.NewState() 4809 root := state.RootModule() 4810 root.SetResourceInstanceCurrent( 4811 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4812 &states.ResourceInstanceObjectSrc{ 4813 Status: states.ObjectReady, 4814 AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), 4815 }, 4816 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4817 ) 4818 4819 ctx := testContext2(t, &ContextOpts{ 4820 Config: m, 4821 State: state, 4822 PlanMode: plans.DestroyMode, 4823 Providers: map[addrs.Provider]providers.Factory{ 4824 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4825 }, 4826 Provisioners: map[string]provisioners.Factory{ 4827 "shell": testProvisionerFuncFixed(pr), 4828 }, 4829 }) 4830 4831 if _, diags := ctx.Plan(); diags.HasErrors() { 4832 t.Fatalf("plan errors: %s", diags.Err()) 4833 } 4834 4835 state, diags := ctx.Apply() 4836 if diags == nil { 4837 t.Fatal("should error") 4838 } 4839 4840 checkStateString(t, state, ` 4841aws_instance.foo["a"]: 4842 ID = bar 4843 provider = provider["registry.terraform.io/hashicorp/aws"] 4844 foo = bar 4845 `) 4846 4847 // Verify apply was invoked 4848 if !pr.ProvisionResourceCalled { 4849 t.Fatalf("provisioner not invoked") 4850 } 4851} 4852 4853// Verify that on destroy provisioner failure with "continue" that 4854// we continue to the next provisioner. 4855func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) { 4856 m := testModule(t, "apply-provisioner-destroy-continue") 4857 p := testProvider("aws") 4858 pr := testProvisioner() 4859 p.PlanResourceChangeFn = testDiffFn 4860 4861 var l sync.Mutex 4862 var calls []string 4863 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4864 val := req.Config.GetAttr("command") 4865 if val.IsNull() { 4866 t.Fatalf("bad value for foo: %#v", val) 4867 } 4868 4869 l.Lock() 4870 defer l.Unlock() 4871 calls = append(calls, val.AsString()) 4872 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4873 return 4874 } 4875 4876 state := states.NewState() 4877 root := state.RootModule() 4878 root.SetResourceInstanceCurrent( 4879 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 4880 &states.ResourceInstanceObjectSrc{ 4881 Status: states.ObjectReady, 4882 AttrsJSON: []byte(`{"id":"bar"}`), 4883 }, 4884 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4885 ) 4886 4887 ctx := testContext2(t, &ContextOpts{ 4888 Config: m, 4889 State: state, 4890 PlanMode: plans.DestroyMode, 4891 Providers: map[addrs.Provider]providers.Factory{ 4892 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4893 }, 4894 Provisioners: map[string]provisioners.Factory{ 4895 "shell": testProvisionerFuncFixed(pr), 4896 }, 4897 }) 4898 4899 if _, diags := ctx.Plan(); diags.HasErrors() { 4900 t.Fatalf("plan errors: %s", diags.Err()) 4901 } 4902 4903 state, diags := ctx.Apply() 4904 if diags.HasErrors() { 4905 t.Fatalf("diags: %s", diags.Err()) 4906 } 4907 4908 checkStateString(t, state, `<no state>`) 4909 4910 // Verify apply was invoked 4911 if !pr.ProvisionResourceCalled { 4912 t.Fatalf("provisioner not invoked") 4913 } 4914 4915 expected := []string{"one", "two"} 4916 if !reflect.DeepEqual(calls, expected) { 4917 t.Fatalf("wrong commands\ngot: %#v\nwant: %#v", calls, expected) 4918 } 4919} 4920 4921// Verify that on destroy provisioner failure with "continue" that 4922// we continue to the next provisioner. But if the next provisioner defines 4923// to fail, then we fail after running it. 4924func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) { 4925 m := testModule(t, "apply-provisioner-destroy-fail") 4926 p := testProvider("aws") 4927 pr := testProvisioner() 4928 p.PlanResourceChangeFn = testDiffFn 4929 4930 var l sync.Mutex 4931 var calls []string 4932 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 4933 val := req.Config.GetAttr("command") 4934 if val.IsNull() { 4935 t.Fatalf("bad value for foo: %#v", val) 4936 } 4937 4938 l.Lock() 4939 defer l.Unlock() 4940 calls = append(calls, val.AsString()) 4941 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) 4942 return 4943 } 4944 4945 state := states.NewState() 4946 root := state.EnsureModule(addrs.RootModuleInstance) 4947 root.SetResourceInstanceCurrent( 4948 mustResourceInstanceAddr("aws_instance.foo").Resource, 4949 &states.ResourceInstanceObjectSrc{ 4950 Status: states.ObjectReady, 4951 AttrsJSON: []byte(`{"id":"bar"}`), 4952 }, 4953 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 4954 ) 4955 4956 ctx := testContext2(t, &ContextOpts{ 4957 Config: m, 4958 State: state, 4959 PlanMode: plans.DestroyMode, 4960 Providers: map[addrs.Provider]providers.Factory{ 4961 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 4962 }, 4963 Provisioners: map[string]provisioners.Factory{ 4964 "shell": testProvisionerFuncFixed(pr), 4965 }, 4966 }) 4967 4968 if _, diags := ctx.Plan(); diags.HasErrors() { 4969 t.Fatalf("plan errors: %s", diags.Err()) 4970 } 4971 4972 state, diags := ctx.Apply() 4973 if diags == nil { 4974 t.Fatal("apply succeeded; wanted error from second provisioner") 4975 } 4976 4977 checkStateString(t, state, ` 4978aws_instance.foo: 4979 ID = bar 4980 provider = provider["registry.terraform.io/hashicorp/aws"] 4981 `) 4982 4983 // Verify apply was invoked 4984 if !pr.ProvisionResourceCalled { 4985 t.Fatalf("provisioner not invoked") 4986 } 4987 4988 expected := []string{"one", "two"} 4989 if !reflect.DeepEqual(calls, expected) { 4990 t.Fatalf("bad: %#v", calls) 4991 } 4992} 4993 4994// Verify destroy provisioners are not run for tainted instances. 4995func TestContext2Apply_provisionerDestroyTainted(t *testing.T) { 4996 m := testModule(t, "apply-provisioner-destroy") 4997 p := testProvider("aws") 4998 pr := testProvisioner() 4999 p.PlanResourceChangeFn = testDiffFn 5000 p.ApplyResourceChangeFn = testApplyFn 5001 5002 destroyCalled := false 5003 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5004 expected := "create a b" 5005 val := req.Config.GetAttr("command") 5006 if val.AsString() != expected { 5007 t.Fatalf("bad value for command: %#v", val) 5008 } 5009 5010 return 5011 } 5012 5013 state := states.NewState() 5014 root := state.RootModule() 5015 root.SetResourceInstanceCurrent( 5016 mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource, 5017 &states.ResourceInstanceObjectSrc{ 5018 Status: states.ObjectTainted, 5019 AttrsJSON: []byte(`{"id":"bar"}`), 5020 }, 5021 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5022 ) 5023 5024 ctx := testContext2(t, &ContextOpts{ 5025 Config: m, 5026 State: state, 5027 Providers: map[addrs.Provider]providers.Factory{ 5028 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5029 }, 5030 Provisioners: map[string]provisioners.Factory{ 5031 "shell": testProvisionerFuncFixed(pr), 5032 }, 5033 Variables: InputValues{ 5034 "input": &InputValue{ 5035 Value: cty.MapVal(map[string]cty.Value{ 5036 "a": cty.StringVal("b"), 5037 }), 5038 SourceType: ValueFromInput, 5039 }, 5040 }, 5041 }) 5042 5043 if _, diags := ctx.Plan(); diags.HasErrors() { 5044 t.Fatalf("plan errors: %s", diags.Err()) 5045 } 5046 5047 state, diags := ctx.Apply() 5048 if diags.HasErrors() { 5049 t.Fatalf("diags: %s", diags.Err()) 5050 } 5051 5052 checkStateString(t, state, ` 5053aws_instance.foo["a"]: 5054 ID = foo 5055 provider = provider["registry.terraform.io/hashicorp/aws"] 5056 foo = bar 5057 type = aws_instance 5058 `) 5059 5060 // Verify apply was invoked 5061 if !pr.ProvisionResourceCalled { 5062 t.Fatalf("provisioner not invoked") 5063 } 5064 5065 if destroyCalled { 5066 t.Fatal("destroy should not be called") 5067 } 5068} 5069 5070func TestContext2Apply_provisionerResourceRef(t *testing.T) { 5071 m := testModule(t, "apply-provisioner-resource-ref") 5072 p := testProvider("aws") 5073 p.PlanResourceChangeFn = testDiffFn 5074 p.ApplyResourceChangeFn = testApplyFn 5075 5076 pr := testProvisioner() 5077 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5078 val := req.Config.GetAttr("command") 5079 if val.AsString() != "2" { 5080 t.Fatalf("bad value for command: %#v", val) 5081 } 5082 5083 return 5084 } 5085 5086 ctx := testContext2(t, &ContextOpts{ 5087 Config: m, 5088 Providers: map[addrs.Provider]providers.Factory{ 5089 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5090 }, 5091 Provisioners: map[string]provisioners.Factory{ 5092 "shell": testProvisionerFuncFixed(pr), 5093 }, 5094 }) 5095 5096 if _, diags := ctx.Plan(); diags.HasErrors() { 5097 t.Fatalf("plan errors: %s", diags.Err()) 5098 } 5099 5100 state, diags := ctx.Apply() 5101 if diags.HasErrors() { 5102 t.Fatalf("diags: %s", diags.Err()) 5103 } 5104 5105 actual := strings.TrimSpace(state.String()) 5106 expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr) 5107 if actual != expected { 5108 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5109 } 5110 5111 // Verify apply was invoked 5112 if !pr.ProvisionResourceCalled { 5113 t.Fatalf("provisioner not invoked") 5114 } 5115} 5116 5117func TestContext2Apply_provisionerSelfRef(t *testing.T) { 5118 m := testModule(t, "apply-provisioner-self-ref") 5119 p := testProvider("aws") 5120 pr := testProvisioner() 5121 p.PlanResourceChangeFn = testDiffFn 5122 p.ApplyResourceChangeFn = testApplyFn 5123 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5124 val := req.Config.GetAttr("command") 5125 if val.AsString() != "bar" { 5126 t.Fatalf("bad value for command: %#v", val) 5127 } 5128 5129 return 5130 } 5131 5132 ctx := testContext2(t, &ContextOpts{ 5133 Config: m, 5134 Providers: map[addrs.Provider]providers.Factory{ 5135 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5136 }, 5137 Provisioners: map[string]provisioners.Factory{ 5138 "shell": testProvisionerFuncFixed(pr), 5139 }, 5140 }) 5141 5142 if _, diags := ctx.Plan(); diags.HasErrors() { 5143 t.Fatalf("plan errors: %s", diags.Err()) 5144 } 5145 5146 state, diags := ctx.Apply() 5147 if diags.HasErrors() { 5148 t.Fatalf("diags: %s", diags.Err()) 5149 } 5150 5151 actual := strings.TrimSpace(state.String()) 5152 expected := strings.TrimSpace(testTerraformApplyProvisionerSelfRefStr) 5153 if actual != expected { 5154 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5155 } 5156 5157 // Verify apply was invoked 5158 if !pr.ProvisionResourceCalled { 5159 t.Fatalf("provisioner not invoked") 5160 } 5161} 5162 5163func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { 5164 var lock sync.Mutex 5165 commands := make([]string, 0, 5) 5166 5167 m := testModule(t, "apply-provisioner-multi-self-ref") 5168 p := testProvider("aws") 5169 pr := testProvisioner() 5170 p.PlanResourceChangeFn = testDiffFn 5171 p.ApplyResourceChangeFn = testApplyFn 5172 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5173 lock.Lock() 5174 defer lock.Unlock() 5175 5176 val := req.Config.GetAttr("command") 5177 if val.IsNull() { 5178 t.Fatalf("bad value for command: %#v", val) 5179 } 5180 5181 commands = append(commands, val.AsString()) 5182 return 5183 } 5184 5185 ctx := testContext2(t, &ContextOpts{ 5186 Config: m, 5187 Providers: map[addrs.Provider]providers.Factory{ 5188 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5189 }, 5190 Provisioners: map[string]provisioners.Factory{ 5191 "shell": testProvisionerFuncFixed(pr), 5192 }, 5193 }) 5194 5195 if _, diags := ctx.Plan(); diags.HasErrors() { 5196 t.Fatalf("plan errors: %s", diags.Err()) 5197 } 5198 5199 state, diags := ctx.Apply() 5200 if diags.HasErrors() { 5201 t.Fatalf("diags: %s", diags.Err()) 5202 } 5203 5204 actual := strings.TrimSpace(state.String()) 5205 expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefStr) 5206 if actual != expected { 5207 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5208 } 5209 5210 // Verify apply was invoked 5211 if !pr.ProvisionResourceCalled { 5212 t.Fatalf("provisioner not invoked") 5213 } 5214 5215 // Verify our result 5216 sort.Strings(commands) 5217 expectedCommands := []string{"number 0", "number 1", "number 2"} 5218 if !reflect.DeepEqual(commands, expectedCommands) { 5219 t.Fatalf("bad: %#v", commands) 5220 } 5221} 5222 5223func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) { 5224 var lock sync.Mutex 5225 order := make([]string, 0, 5) 5226 5227 m := testModule(t, "apply-provisioner-multi-self-ref-single") 5228 p := testProvider("aws") 5229 pr := testProvisioner() 5230 p.PlanResourceChangeFn = testDiffFn 5231 p.ApplyResourceChangeFn = testApplyFn 5232 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5233 lock.Lock() 5234 defer lock.Unlock() 5235 5236 val := req.Config.GetAttr("order") 5237 if val.IsNull() { 5238 t.Fatalf("no val for order") 5239 } 5240 5241 order = append(order, val.AsString()) 5242 return 5243 } 5244 5245 ctx := testContext2(t, &ContextOpts{ 5246 Config: m, 5247 Providers: map[addrs.Provider]providers.Factory{ 5248 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5249 }, 5250 Provisioners: map[string]provisioners.Factory{ 5251 "shell": testProvisionerFuncFixed(pr), 5252 }, 5253 }) 5254 5255 if _, diags := ctx.Plan(); diags.HasErrors() { 5256 t.Fatalf("plan errors: %s", diags.Err()) 5257 } 5258 5259 state, diags := ctx.Apply() 5260 if diags.HasErrors() { 5261 t.Fatalf("diags: %s", diags.Err()) 5262 } 5263 5264 actual := strings.TrimSpace(state.String()) 5265 expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefSingleStr) 5266 if actual != expected { 5267 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5268 } 5269 5270 // Verify apply was invoked 5271 if !pr.ProvisionResourceCalled { 5272 t.Fatalf("provisioner not invoked") 5273 } 5274 5275 // Verify our result 5276 sort.Strings(order) 5277 expectedOrder := []string{"0", "1", "2"} 5278 if !reflect.DeepEqual(order, expectedOrder) { 5279 t.Fatalf("bad: %#v", order) 5280 } 5281} 5282 5283func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) { 5284 m := testModule(t, "apply-provisioner-explicit-self-ref") 5285 p := testProvider("aws") 5286 pr := testProvisioner() 5287 p.PlanResourceChangeFn = testDiffFn 5288 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5289 val := req.Config.GetAttr("command") 5290 if val.IsNull() || val.AsString() != "bar" { 5291 t.Fatalf("bad value for command: %#v", val) 5292 } 5293 5294 return 5295 } 5296 5297 var state *states.State 5298 { 5299 ctx := testContext2(t, &ContextOpts{ 5300 Config: m, 5301 Providers: map[addrs.Provider]providers.Factory{ 5302 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5303 }, 5304 Provisioners: map[string]provisioners.Factory{ 5305 "shell": testProvisionerFuncFixed(pr), 5306 }, 5307 }) 5308 5309 _, diags := ctx.Plan() 5310 if diags.HasErrors() { 5311 t.Fatalf("diags: %s", diags.Err()) 5312 } 5313 5314 state, diags = ctx.Apply() 5315 if diags.HasErrors() { 5316 t.Fatalf("diags: %s", diags.Err()) 5317 } 5318 5319 // Verify apply was invoked 5320 if !pr.ProvisionResourceCalled { 5321 t.Fatalf("provisioner not invoked") 5322 } 5323 } 5324 5325 { 5326 ctx := testContext2(t, &ContextOpts{ 5327 Config: m, 5328 PlanMode: plans.DestroyMode, 5329 State: state, 5330 Providers: map[addrs.Provider]providers.Factory{ 5331 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5332 }, 5333 Provisioners: map[string]provisioners.Factory{ 5334 "shell": testProvisionerFuncFixed(pr), 5335 }, 5336 }) 5337 5338 _, diags := ctx.Plan() 5339 if diags.HasErrors() { 5340 t.Fatalf("diags: %s", diags.Err()) 5341 } 5342 5343 state, diags = ctx.Apply() 5344 if diags.HasErrors() { 5345 t.Fatalf("diags: %s", diags.Err()) 5346 } 5347 5348 checkStateString(t, state, `<no state>`) 5349 } 5350} 5351 5352func TestContext2Apply_provisionerForEachSelfRef(t *testing.T) { 5353 m := testModule(t, "apply-provisioner-for-each-self") 5354 p := testProvider("aws") 5355 pr := testProvisioner() 5356 p.PlanResourceChangeFn = testDiffFn 5357 5358 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 5359 val := req.Config.GetAttr("command") 5360 if val.IsNull() { 5361 t.Fatalf("bad value for command: %#v", val) 5362 } 5363 5364 return resp 5365 } 5366 5367 ctx := testContext2(t, &ContextOpts{ 5368 Config: m, 5369 Providers: map[addrs.Provider]providers.Factory{ 5370 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5371 }, 5372 Provisioners: map[string]provisioners.Factory{ 5373 "shell": testProvisionerFuncFixed(pr), 5374 }, 5375 }) 5376 5377 if _, diags := ctx.Plan(); diags.HasErrors() { 5378 t.Fatalf("plan errors: %s", diags.Err()) 5379 } 5380 5381 _, diags := ctx.Apply() 5382 if diags.HasErrors() { 5383 t.Fatalf("diags: %s", diags.Err()) 5384 } 5385} 5386 5387// Provisioner should NOT run on a diff, only create 5388func TestContext2Apply_Provisioner_Diff(t *testing.T) { 5389 m := testModule(t, "apply-provisioner-diff") 5390 p := testProvider("aws") 5391 pr := testProvisioner() 5392 p.PlanResourceChangeFn = testDiffFn 5393 p.ApplyResourceChangeFn = testApplyFn 5394 ctx := testContext2(t, &ContextOpts{ 5395 Config: m, 5396 Providers: map[addrs.Provider]providers.Factory{ 5397 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5398 }, 5399 Provisioners: map[string]provisioners.Factory{ 5400 "shell": testProvisionerFuncFixed(pr), 5401 }, 5402 }) 5403 5404 if _, diags := ctx.Plan(); diags.HasErrors() { 5405 logDiagnostics(t, diags) 5406 t.Fatal("plan failed") 5407 } 5408 5409 state, diags := ctx.Apply() 5410 if diags.HasErrors() { 5411 logDiagnostics(t, diags) 5412 t.Fatal("apply failed") 5413 } 5414 5415 actual := strings.TrimSpace(state.String()) 5416 expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr) 5417 if actual != expected { 5418 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5419 } 5420 5421 // Verify apply was invoked 5422 if !pr.ProvisionResourceCalled { 5423 t.Fatalf("provisioner was not called on first apply") 5424 } 5425 pr.ProvisionResourceCalled = false 5426 5427 // Change the state to force a diff 5428 mod := state.RootModule() 5429 obj := mod.Resources["aws_instance.bar"].Instances[addrs.NoKey].Current 5430 var attrs map[string]interface{} 5431 err := json.Unmarshal(obj.AttrsJSON, &attrs) 5432 if err != nil { 5433 t.Fatal(err) 5434 } 5435 attrs["foo"] = "baz" 5436 obj.AttrsJSON, err = json.Marshal(attrs) 5437 if err != nil { 5438 t.Fatal(err) 5439 } 5440 5441 // Re-create context with state 5442 ctx = testContext2(t, &ContextOpts{ 5443 Config: m, 5444 Providers: map[addrs.Provider]providers.Factory{ 5445 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5446 }, 5447 Provisioners: map[string]provisioners.Factory{ 5448 "shell": testProvisionerFuncFixed(pr), 5449 }, 5450 State: state, 5451 }) 5452 5453 if _, diags := ctx.Plan(); diags.HasErrors() { 5454 logDiagnostics(t, diags) 5455 t.Fatal("plan failed") 5456 } 5457 5458 state2, diags := ctx.Apply() 5459 if diags.HasErrors() { 5460 logDiagnostics(t, diags) 5461 t.Fatal("apply failed") 5462 } 5463 5464 actual = strings.TrimSpace(state2.String()) 5465 if actual != expected { 5466 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5467 } 5468 5469 // Verify apply was NOT invoked 5470 if pr.ProvisionResourceCalled { 5471 t.Fatalf("provisioner was called on second apply; should not have been") 5472 } 5473} 5474 5475func TestContext2Apply_outputDiffVars(t *testing.T) { 5476 m := testModule(t, "apply-good") 5477 p := testProvider("aws") 5478 5479 state := states.NewState() 5480 root := state.EnsureModule(addrs.RootModuleInstance) 5481 root.SetResourceInstanceCurrent( 5482 mustResourceInstanceAddr("aws_instance.baz").Resource, 5483 &states.ResourceInstanceObjectSrc{ 5484 Status: states.ObjectReady, 5485 AttrsJSON: []byte(`{"id":"bar"}`), 5486 }, 5487 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5488 ) 5489 5490 ctx := testContext2(t, &ContextOpts{ 5491 Config: m, 5492 Providers: map[addrs.Provider]providers.Factory{ 5493 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5494 }, 5495 State: state, 5496 }) 5497 5498 p.PlanResourceChangeFn = testDiffFn 5499 //func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 5500 // d := &InstanceDiff{ 5501 // Attributes: map[string]*ResourceAttrDiff{}, 5502 // } 5503 // if new, ok := rc.Get("value"); ok { 5504 // d.Attributes["value"] = &ResourceAttrDiff{ 5505 // New: new.(string), 5506 // } 5507 // } 5508 // if new, ok := rc.Get("foo"); ok { 5509 // d.Attributes["foo"] = &ResourceAttrDiff{ 5510 // New: new.(string), 5511 // } 5512 // } else if rc.IsComputed("foo") { 5513 // d.Attributes["foo"] = &ResourceAttrDiff{ 5514 // NewComputed: true, 5515 // Type: DiffAttrOutput, // This doesn't actually really do anything anymore, but this test originally set it. 5516 // } 5517 // } 5518 // if new, ok := rc.Get("num"); ok { 5519 // d.Attributes["num"] = &ResourceAttrDiff{ 5520 // New: fmt.Sprintf("%#v", new), 5521 // } 5522 // } 5523 // return d, nil 5524 //} 5525 5526 if _, diags := ctx.Plan(); diags.HasErrors() { 5527 logDiagnostics(t, diags) 5528 t.Fatal("plan failed") 5529 } 5530 if _, diags := ctx.Apply(); diags.HasErrors() { 5531 logDiagnostics(t, diags) 5532 t.Fatal("apply failed") 5533 } 5534} 5535 5536func TestContext2Apply_destroyX(t *testing.T) { 5537 m := testModule(t, "apply-destroy") 5538 h := new(HookRecordApplyOrder) 5539 p := testProvider("aws") 5540 p.PlanResourceChangeFn = testDiffFn 5541 ctx := testContext2(t, &ContextOpts{ 5542 Config: m, 5543 Hooks: []Hook{h}, 5544 Providers: map[addrs.Provider]providers.Factory{ 5545 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5546 }, 5547 }) 5548 5549 // First plan and apply a create operation 5550 if _, diags := ctx.Plan(); diags.HasErrors() { 5551 t.Fatalf("plan errors: %s", diags.Err()) 5552 } 5553 5554 state, diags := ctx.Apply() 5555 if diags.HasErrors() { 5556 t.Fatalf("diags: %s", diags.Err()) 5557 } 5558 5559 // Next, plan and apply a destroy operation 5560 h.Active = true 5561 ctx = testContext2(t, &ContextOpts{ 5562 PlanMode: plans.DestroyMode, 5563 State: state, 5564 Config: m, 5565 Hooks: []Hook{h}, 5566 Providers: map[addrs.Provider]providers.Factory{ 5567 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5568 }, 5569 }) 5570 5571 if _, diags := ctx.Plan(); diags.HasErrors() { 5572 t.Fatalf("plan errors: %s", diags.Err()) 5573 } 5574 5575 state, diags = ctx.Apply() 5576 if diags.HasErrors() { 5577 t.Fatalf("diags: %s", diags.Err()) 5578 } 5579 5580 // Test that things were destroyed 5581 actual := strings.TrimSpace(state.String()) 5582 expected := strings.TrimSpace(testTerraformApplyDestroyStr) 5583 if actual != expected { 5584 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5585 } 5586 5587 // Test that things were destroyed _in the right order_ 5588 expected2 := []string{"aws_instance.bar", "aws_instance.foo"} 5589 actual2 := h.IDs 5590 if !reflect.DeepEqual(actual2, expected2) { 5591 t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) 5592 } 5593} 5594 5595func TestContext2Apply_destroyOrder(t *testing.T) { 5596 m := testModule(t, "apply-destroy") 5597 h := new(HookRecordApplyOrder) 5598 p := testProvider("aws") 5599 p.PlanResourceChangeFn = testDiffFn 5600 ctx := testContext2(t, &ContextOpts{ 5601 Config: m, 5602 Hooks: []Hook{h}, 5603 Providers: map[addrs.Provider]providers.Factory{ 5604 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5605 }, 5606 }) 5607 5608 // First plan and apply a create operation 5609 if _, diags := ctx.Plan(); diags.HasErrors() { 5610 t.Fatalf("plan errors: %s", diags.Err()) 5611 } 5612 5613 state, diags := ctx.Apply() 5614 if diags.HasErrors() { 5615 t.Fatalf("diags: %s", diags.Err()) 5616 } 5617 5618 t.Logf("State 1: %s", state) 5619 5620 // Next, plan and apply a destroy 5621 h.Active = true 5622 ctx = testContext2(t, &ContextOpts{ 5623 PlanMode: plans.DestroyMode, 5624 State: state, 5625 Config: m, 5626 Hooks: []Hook{h}, 5627 Providers: map[addrs.Provider]providers.Factory{ 5628 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5629 }, 5630 }) 5631 5632 if _, diags := ctx.Plan(); diags.HasErrors() { 5633 t.Fatalf("plan errors: %s", diags.Err()) 5634 } 5635 5636 state, diags = ctx.Apply() 5637 if diags.HasErrors() { 5638 t.Fatalf("diags: %s", diags.Err()) 5639 } 5640 5641 // Test that things were destroyed 5642 actual := strings.TrimSpace(state.String()) 5643 expected := strings.TrimSpace(testTerraformApplyDestroyStr) 5644 if actual != expected { 5645 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5646 } 5647 5648 // Test that things were destroyed _in the right order_ 5649 expected2 := []string{"aws_instance.bar", "aws_instance.foo"} 5650 actual2 := h.IDs 5651 if !reflect.DeepEqual(actual2, expected2) { 5652 t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) 5653 } 5654} 5655 5656// https://github.com/hashicorp/terraform/issues/2767 5657func TestContext2Apply_destroyModulePrefix(t *testing.T) { 5658 m := testModule(t, "apply-destroy-module-resource-prefix") 5659 h := new(MockHook) 5660 p := testProvider("aws") 5661 p.PlanResourceChangeFn = testDiffFn 5662 ctx := testContext2(t, &ContextOpts{ 5663 Config: m, 5664 Hooks: []Hook{h}, 5665 Providers: map[addrs.Provider]providers.Factory{ 5666 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5667 }, 5668 }) 5669 5670 // First plan and apply a create operation 5671 if _, diags := ctx.Plan(); diags.HasErrors() { 5672 t.Fatalf("plan errors: %s", diags.Err()) 5673 } 5674 5675 state, diags := ctx.Apply() 5676 if diags.HasErrors() { 5677 t.Fatalf("diags: %s", diags.Err()) 5678 } 5679 5680 // Verify that we got the apply info correct 5681 if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" { 5682 t.Fatalf("bad: %s", v) 5683 } 5684 5685 // Next, plan and apply a destroy operation and reset the hook 5686 h = new(MockHook) 5687 ctx = testContext2(t, &ContextOpts{ 5688 PlanMode: plans.DestroyMode, 5689 State: state, 5690 Config: m, 5691 Hooks: []Hook{h}, 5692 Providers: map[addrs.Provider]providers.Factory{ 5693 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5694 }, 5695 }) 5696 5697 if _, diags := ctx.Plan(); diags.HasErrors() { 5698 t.Fatalf("plan errors: %s", diags.Err()) 5699 } 5700 5701 _, diags = ctx.Apply() 5702 if diags.HasErrors() { 5703 t.Fatalf("diags: %s", diags.Err()) 5704 } 5705 5706 // Test that things were destroyed 5707 if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" { 5708 t.Fatalf("bad: %s", v) 5709 } 5710} 5711 5712func TestContext2Apply_destroyNestedModule(t *testing.T) { 5713 m := testModule(t, "apply-destroy-nested-module") 5714 p := testProvider("aws") 5715 p.PlanResourceChangeFn = testDiffFn 5716 5717 state := states.NewState() 5718 root := state.EnsureModule(addrs.RootModuleInstance) 5719 root.SetResourceInstanceCurrent( 5720 mustResourceInstanceAddr("aws_instance.bar").Resource, 5721 &states.ResourceInstanceObjectSrc{ 5722 Status: states.ObjectReady, 5723 AttrsJSON: []byte(`{"id":"bar"}`), 5724 }, 5725 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5726 ) 5727 5728 ctx := testContext2(t, &ContextOpts{ 5729 Config: m, 5730 Providers: map[addrs.Provider]providers.Factory{ 5731 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5732 }, 5733 State: state, 5734 }) 5735 5736 // First plan and apply a create operation 5737 if _, diags := ctx.Plan(); diags.HasErrors() { 5738 t.Fatalf("plan errors: %s", diags.Err()) 5739 } 5740 5741 s, diags := ctx.Apply() 5742 if diags.HasErrors() { 5743 t.Fatalf("diags: %s", diags.Err()) 5744 } 5745 5746 // Test that things were destroyed 5747 actual := strings.TrimSpace(s.String()) 5748 if actual != "<no state>" { 5749 t.Fatalf("expected no state, got: %s", actual) 5750 } 5751} 5752 5753func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) { 5754 m := testModule(t, "apply-destroy-deeply-nested-module") 5755 p := testProvider("aws") 5756 p.PlanResourceChangeFn = testDiffFn 5757 5758 state := states.NewState() 5759 root := state.EnsureModule(addrs.RootModuleInstance) 5760 root.SetResourceInstanceCurrent( 5761 mustResourceInstanceAddr("aws_instance.bar").Resource, 5762 &states.ResourceInstanceObjectSrc{ 5763 Status: states.ObjectReady, 5764 AttrsJSON: []byte(`{"id":"bar"}`), 5765 }, 5766 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 5767 ) 5768 5769 ctx := testContext2(t, &ContextOpts{ 5770 Config: m, 5771 Providers: map[addrs.Provider]providers.Factory{ 5772 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5773 }, 5774 State: state, 5775 }) 5776 5777 // First plan and apply a create operation 5778 if _, diags := ctx.Plan(); diags.HasErrors() { 5779 t.Fatalf("plan errors: %s", diags.Err()) 5780 } 5781 5782 s, diags := ctx.Apply() 5783 if diags.HasErrors() { 5784 t.Fatalf("diags: %s", diags.Err()) 5785 } 5786 5787 // Test that things were destroyed 5788 if !s.Empty() { 5789 t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(s)) 5790 } 5791} 5792 5793// https://github.com/hashicorp/terraform/issues/5440 5794func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) { 5795 m, snap := testModuleWithSnapshot(t, "apply-destroy-module-with-attrs") 5796 p := testProvider("aws") 5797 p.PlanResourceChangeFn = testDiffFn 5798 5799 var state *states.State 5800 { 5801 ctx := testContext2(t, &ContextOpts{ 5802 Config: m, 5803 Providers: map[addrs.Provider]providers.Factory{ 5804 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5805 }, 5806 }) 5807 5808 // First plan and apply a create operation 5809 if p, diags := ctx.Plan(); diags.HasErrors() { 5810 t.Fatalf("plan diags: %s", diags.Err()) 5811 } else { 5812 t.Logf("Step 1 plan: %s", legacyDiffComparisonString(p.Changes)) 5813 } 5814 5815 var diags tfdiags.Diagnostics 5816 state, diags = ctx.Apply() 5817 if diags.HasErrors() { 5818 t.Fatalf("apply errs: %s", diags.Err()) 5819 } 5820 5821 t.Logf("Step 1 state: %s", state) 5822 } 5823 5824 h := new(HookRecordApplyOrder) 5825 h.Active = true 5826 5827 { 5828 ctx := testContext2(t, &ContextOpts{ 5829 PlanMode: plans.DestroyMode, 5830 Config: m, 5831 State: state, 5832 Hooks: []Hook{h}, 5833 Providers: map[addrs.Provider]providers.Factory{ 5834 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5835 }, 5836 }) 5837 5838 // First plan and apply a create operation 5839 plan, diags := ctx.Plan() 5840 if diags.HasErrors() { 5841 t.Fatalf("destroy plan err: %s", diags.Err()) 5842 } 5843 5844 t.Logf("Step 2 plan: %s", legacyDiffComparisonString(plan.Changes)) 5845 5846 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 5847 if err != nil { 5848 t.Fatalf("failed to round-trip through planfile: %s", err) 5849 } 5850 5851 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 5852 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5853 } 5854 5855 ctx, diags = NewContext(ctxOpts) 5856 if diags.HasErrors() { 5857 t.Fatalf("err: %s", diags.Err()) 5858 } 5859 5860 state, diags = ctx.Apply() 5861 if diags.HasErrors() { 5862 t.Fatalf("destroy apply err: %s", diags.Err()) 5863 } 5864 5865 t.Logf("Step 2 state: %s", state) 5866 } 5867 5868 //Test that things were destroyed 5869 if state.HasResources() { 5870 t.Fatal("expected empty state, got:", state) 5871 } 5872} 5873 5874func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { 5875 m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count") 5876 p := testProvider("aws") 5877 p.PlanResourceChangeFn = testDiffFn 5878 5879 var state *states.State 5880 var diags tfdiags.Diagnostics 5881 { 5882 ctx := testContext2(t, &ContextOpts{ 5883 Config: m, 5884 Providers: map[addrs.Provider]providers.Factory{ 5885 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5886 }, 5887 }) 5888 5889 // First plan and apply a create operation 5890 if _, diags := ctx.Plan(); diags.HasErrors() { 5891 t.Fatalf("plan err: %s", diags.Err()) 5892 } 5893 5894 state, diags = ctx.Apply() 5895 if diags.HasErrors() { 5896 t.Fatalf("apply err: %s", diags.Err()) 5897 } 5898 } 5899 5900 h := new(HookRecordApplyOrder) 5901 h.Active = true 5902 5903 { 5904 ctx := testContext2(t, &ContextOpts{ 5905 PlanMode: plans.DestroyMode, 5906 Config: m, 5907 State: state, 5908 Hooks: []Hook{h}, 5909 Providers: map[addrs.Provider]providers.Factory{ 5910 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5911 }, 5912 }) 5913 5914 // First plan and apply a create operation 5915 plan, diags := ctx.Plan() 5916 if diags.HasErrors() { 5917 t.Fatalf("destroy plan err: %s", diags.Err()) 5918 } 5919 5920 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 5921 if err != nil { 5922 t.Fatalf("failed to round-trip through planfile: %s", err) 5923 } 5924 5925 ctxOpts.Providers = 5926 map[addrs.Provider]providers.Factory{ 5927 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5928 } 5929 5930 ctx, diags = NewContext(ctxOpts) 5931 if diags.HasErrors() { 5932 t.Fatalf("err: %s", diags.Err()) 5933 } 5934 5935 state, diags = ctx.Apply() 5936 if diags.HasErrors() { 5937 t.Fatalf("destroy apply err: %s", diags.Err()) 5938 } 5939 } 5940 5941 //Test that things were destroyed 5942 actual := strings.TrimSpace(state.String()) 5943 expected := strings.TrimSpace(` 5944<no state>`) 5945 if actual != expected { 5946 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 5947 } 5948} 5949 5950func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) { 5951 m := testModule(t, "apply-destroy-mod-var-and-count") 5952 p := testProvider("aws") 5953 p.PlanResourceChangeFn = testDiffFn 5954 5955 var state *states.State 5956 var diags tfdiags.Diagnostics 5957 { 5958 ctx := testContext2(t, &ContextOpts{ 5959 Config: m, 5960 Providers: map[addrs.Provider]providers.Factory{ 5961 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5962 }, 5963 }) 5964 5965 // First plan and apply a create operation 5966 if _, diags := ctx.Plan(); diags.HasErrors() { 5967 t.Fatalf("plan err: %s", diags.Err()) 5968 } 5969 5970 state, diags = ctx.Apply() 5971 if diags.HasErrors() { 5972 t.Fatalf("apply err: %s", diags.Err()) 5973 } 5974 } 5975 5976 { 5977 ctx := testContext2(t, &ContextOpts{ 5978 PlanMode: plans.DestroyMode, 5979 Config: m, 5980 State: state, 5981 Providers: map[addrs.Provider]providers.Factory{ 5982 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 5983 }, 5984 Targets: []addrs.Targetable{ 5985 addrs.RootModuleInstance.Child("child", addrs.NoKey), 5986 }, 5987 }) 5988 5989 _, diags := ctx.Plan() 5990 if diags.HasErrors() { 5991 t.Fatalf("plan err: %s", diags) 5992 } 5993 if len(diags) != 1 { 5994 // Should have one warning that -target is in effect. 5995 t.Fatalf("got %d diagnostics in plan; want 1", len(diags)) 5996 } 5997 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 5998 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 5999 } 6000 if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want { 6001 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 6002 } 6003 6004 // Destroy, targeting the module explicitly 6005 state, diags = ctx.Apply() 6006 if diags.HasErrors() { 6007 t.Fatalf("destroy apply err: %s", diags) 6008 } 6009 if len(diags) != 1 { 6010 t.Fatalf("got %d diagnostics; want 1", len(diags)) 6011 } 6012 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 6013 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 6014 } 6015 if got, want := diags[0].Description().Summary, "Applied changes may be incomplete"; got != want { 6016 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 6017 } 6018 } 6019 6020 //Test that things were destroyed 6021 actual := strings.TrimSpace(state.String()) 6022 expected := strings.TrimSpace(`<no state>`) 6023 if actual != expected { 6024 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6025 } 6026} 6027 6028func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { 6029 m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count-nested") 6030 p := testProvider("aws") 6031 p.PlanResourceChangeFn = testDiffFn 6032 6033 var state *states.State 6034 var diags tfdiags.Diagnostics 6035 { 6036 ctx := testContext2(t, &ContextOpts{ 6037 Config: m, 6038 Providers: map[addrs.Provider]providers.Factory{ 6039 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6040 }, 6041 }) 6042 6043 // First plan and apply a create operation 6044 if _, diags := ctx.Plan(); diags.HasErrors() { 6045 t.Fatalf("plan err: %s", diags.Err()) 6046 } 6047 6048 state, diags = ctx.Apply() 6049 if diags.HasErrors() { 6050 t.Fatalf("apply err: %s", diags.Err()) 6051 } 6052 } 6053 6054 h := new(HookRecordApplyOrder) 6055 h.Active = true 6056 6057 { 6058 ctx := testContext2(t, &ContextOpts{ 6059 PlanMode: plans.DestroyMode, 6060 Config: m, 6061 State: state, 6062 Hooks: []Hook{h}, 6063 Providers: map[addrs.Provider]providers.Factory{ 6064 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6065 }, 6066 }) 6067 6068 // First plan and apply a create operation 6069 plan, diags := ctx.Plan() 6070 if diags.HasErrors() { 6071 t.Fatalf("destroy plan err: %s", diags.Err()) 6072 } 6073 6074 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 6075 if err != nil { 6076 t.Fatalf("failed to round-trip through planfile: %s", err) 6077 } 6078 6079 ctxOpts.Providers = 6080 map[addrs.Provider]providers.Factory{ 6081 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6082 } 6083 6084 ctx, diags = NewContext(ctxOpts) 6085 if diags.HasErrors() { 6086 t.Fatalf("err: %s", diags.Err()) 6087 } 6088 6089 state, diags = ctx.Apply() 6090 if diags.HasErrors() { 6091 t.Fatalf("destroy apply err: %s", diags.Err()) 6092 } 6093 } 6094 6095 //Test that things were destroyed 6096 actual := strings.TrimSpace(state.String()) 6097 expected := strings.TrimSpace(` 6098<no state>`) 6099 if actual != expected { 6100 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6101 } 6102} 6103 6104func TestContext2Apply_destroyOutputs(t *testing.T) { 6105 m := testModule(t, "apply-destroy-outputs") 6106 p := testProvider("test") 6107 p.PlanResourceChangeFn = testDiffFn 6108 6109 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 6110 // add the required id 6111 m := req.Config.AsValueMap() 6112 m["id"] = cty.StringVal("foo") 6113 6114 return providers.ReadDataSourceResponse{ 6115 State: cty.ObjectVal(m), 6116 } 6117 } 6118 6119 ctx := testContext2(t, &ContextOpts{ 6120 Config: m, 6121 Providers: map[addrs.Provider]providers.Factory{ 6122 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6123 }, 6124 }) 6125 6126 // First plan and apply a create operation 6127 if _, diags := ctx.Plan(); diags.HasErrors() { 6128 t.Fatalf("plan errors: %s", diags.Err()) 6129 } 6130 6131 state, diags := ctx.Apply() 6132 6133 if diags.HasErrors() { 6134 t.Fatalf("diags: %s", diags.Err()) 6135 } 6136 6137 // Next, plan and apply a destroy operation 6138 ctx = testContext2(t, &ContextOpts{ 6139 PlanMode: plans.DestroyMode, 6140 State: state, 6141 Config: m, 6142 Providers: map[addrs.Provider]providers.Factory{ 6143 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6144 }, 6145 }) 6146 6147 if _, diags := ctx.Plan(); diags.HasErrors() { 6148 t.Fatalf("plan errors: %s", diags.Err()) 6149 } 6150 6151 state, diags = ctx.Apply() 6152 if diags.HasErrors() { 6153 t.Fatalf("diags: %s", diags.Err()) 6154 } 6155 6156 mod := state.RootModule() 6157 if len(mod.Resources) > 0 { 6158 t.Fatalf("expected no resources, got: %#v", mod) 6159 } 6160 6161 // destroying again should produce no errors 6162 ctx = testContext2(t, &ContextOpts{ 6163 PlanMode: plans.DestroyMode, 6164 State: state, 6165 Config: m, 6166 Providers: map[addrs.Provider]providers.Factory{ 6167 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6168 }, 6169 }) 6170 if _, diags := ctx.Plan(); diags.HasErrors() { 6171 t.Fatal(diags.Err()) 6172 } 6173 6174 if _, diags := ctx.Apply(); diags.HasErrors() { 6175 t.Fatal(diags.Err()) 6176 } 6177} 6178 6179func TestContext2Apply_destroyOrphan(t *testing.T) { 6180 m := testModule(t, "apply-error") 6181 p := testProvider("aws") 6182 state := states.NewState() 6183 root := state.EnsureModule(addrs.RootModuleInstance) 6184 root.SetResourceInstanceCurrent( 6185 mustResourceInstanceAddr("aws_instance.baz").Resource, 6186 &states.ResourceInstanceObjectSrc{ 6187 Status: states.ObjectReady, 6188 AttrsJSON: []byte(`{"id":"bar"}`), 6189 }, 6190 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6191 ) 6192 ctx := testContext2(t, &ContextOpts{ 6193 Config: m, 6194 Providers: map[addrs.Provider]providers.Factory{ 6195 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6196 }, 6197 State: state, 6198 }) 6199 6200 p.PlanResourceChangeFn = testDiffFn 6201 6202 if _, diags := ctx.Plan(); diags.HasErrors() { 6203 t.Fatalf("plan errors: %s", diags.Err()) 6204 } 6205 6206 s, diags := ctx.Apply() 6207 if diags.HasErrors() { 6208 t.Fatalf("diags: %s", diags.Err()) 6209 } 6210 6211 mod := s.RootModule() 6212 if _, ok := mod.Resources["aws_instance.baz"]; ok { 6213 t.Fatalf("bad: %#v", mod.Resources) 6214 } 6215} 6216 6217func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) { 6218 m := testModule(t, "apply-destroy-provisioner") 6219 p := testProvider("aws") 6220 pr := testProvisioner() 6221 p.PlanResourceChangeFn = testDiffFn 6222 6223 state := states.NewState() 6224 root := state.EnsureModule(addrs.RootModuleInstance) 6225 root.SetResourceInstanceCurrent( 6226 mustResourceInstanceAddr("aws_instance.foo").Resource, 6227 &states.ResourceInstanceObjectSrc{ 6228 Status: states.ObjectReady, 6229 AttrsJSON: []byte(`{"id":"bar"}`), 6230 }, 6231 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6232 ) 6233 6234 ctx := testContext2(t, &ContextOpts{ 6235 Config: m, 6236 Providers: map[addrs.Provider]providers.Factory{ 6237 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6238 }, 6239 Provisioners: map[string]provisioners.Factory{ 6240 "shell": testProvisionerFuncFixed(pr), 6241 }, 6242 State: state, 6243 PlanMode: plans.DestroyMode, 6244 }) 6245 6246 if _, diags := ctx.Plan(); diags.HasErrors() { 6247 t.Fatalf("plan errors: %s", diags.Err()) 6248 } 6249 6250 s, diags := ctx.Apply() 6251 if diags.HasErrors() { 6252 t.Fatalf("diags: %s", diags.Err()) 6253 } 6254 6255 if pr.ProvisionResourceCalled { 6256 t.Fatal("provisioner should not be called") 6257 } 6258 6259 actual := strings.TrimSpace(s.String()) 6260 expected := strings.TrimSpace("<no state>") 6261 if actual != expected { 6262 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6263 } 6264} 6265 6266func TestContext2Apply_error(t *testing.T) { 6267 errored := false 6268 6269 m := testModule(t, "apply-error") 6270 p := testProvider("aws") 6271 ctx := testContext2(t, &ContextOpts{ 6272 Config: m, 6273 Providers: map[addrs.Provider]providers.Factory{ 6274 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6275 }, 6276 }) 6277 6278 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 6279 if errored { 6280 resp.NewState = req.PlannedState 6281 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 6282 return 6283 } 6284 errored = true 6285 6286 return testApplyFn(req) 6287 } 6288 p.PlanResourceChangeFn = testDiffFn 6289 6290 if _, diags := ctx.Plan(); diags.HasErrors() { 6291 t.Fatalf("plan errors: %s", diags.Err()) 6292 } 6293 6294 state, diags := ctx.Apply() 6295 if diags == nil { 6296 t.Fatal("should have error") 6297 } 6298 6299 actual := strings.TrimSpace(state.String()) 6300 expected := strings.TrimSpace(testTerraformApplyErrorStr) 6301 if actual != expected { 6302 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 6303 } 6304} 6305 6306func TestContext2Apply_errorDestroy(t *testing.T) { 6307 m := testModule(t, "empty") 6308 p := testProvider("test") 6309 6310 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 6311 ResourceTypes: map[string]*configschema.Block{ 6312 "test_thing": { 6313 Attributes: map[string]*configschema.Attribute{ 6314 "id": {Type: cty.String, Optional: true}, 6315 }, 6316 }, 6317 }, 6318 }) 6319 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 6320 // Should actually be called for this test, because Terraform Core 6321 // constructs the plan for a destroy operation itself. 6322 return providers.PlanResourceChangeResponse{ 6323 PlannedState: req.ProposedNewState, 6324 } 6325 } 6326 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6327 // The apply (in this case, a destroy) always fails, so we can verify 6328 // that the object stays in the state after a destroy fails even though 6329 // we aren't returning a new state object here. 6330 return providers.ApplyResourceChangeResponse{ 6331 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("failed")), 6332 } 6333 } 6334 6335 ctx := testContext2(t, &ContextOpts{ 6336 Config: m, 6337 State: states.BuildState(func(ss *states.SyncState) { 6338 ss.SetResourceInstanceCurrent( 6339 addrs.Resource{ 6340 Mode: addrs.ManagedResourceMode, 6341 Type: "test_thing", 6342 Name: "foo", 6343 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 6344 &states.ResourceInstanceObjectSrc{ 6345 Status: states.ObjectReady, 6346 AttrsJSON: []byte(`{"id":"baz"}`), 6347 }, 6348 addrs.AbsProviderConfig{ 6349 Provider: addrs.NewDefaultProvider("test"), 6350 Module: addrs.RootModule, 6351 }, 6352 ) 6353 }), 6354 Providers: map[addrs.Provider]providers.Factory{ 6355 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 6356 }, 6357 }) 6358 6359 if _, diags := ctx.Plan(); diags.HasErrors() { 6360 t.Fatalf("plan errors: %s", diags.Err()) 6361 } 6362 6363 state, diags := ctx.Apply() 6364 if diags == nil { 6365 t.Fatal("should have error") 6366 } 6367 6368 actual := strings.TrimSpace(state.String()) 6369 expected := strings.TrimSpace(` 6370test_thing.foo: 6371 ID = baz 6372 provider = provider["registry.terraform.io/hashicorp/test"] 6373`) // test_thing.foo is still here, even though provider returned no new state along with its error 6374 if actual != expected { 6375 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 6376 } 6377} 6378 6379func TestContext2Apply_errorCreateInvalidNew(t *testing.T) { 6380 m := testModule(t, "apply-error") 6381 6382 p := testProvider("aws") 6383 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 6384 ResourceTypes: map[string]*configschema.Block{ 6385 "aws_instance": { 6386 Attributes: map[string]*configschema.Attribute{ 6387 "value": {Type: cty.String, Optional: true}, 6388 "foo": {Type: cty.String, Optional: true}, 6389 }, 6390 }, 6391 }, 6392 }) 6393 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 6394 return providers.PlanResourceChangeResponse{ 6395 PlannedState: req.ProposedNewState, 6396 } 6397 } 6398 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6399 // We're intentionally returning an inconsistent new state here 6400 // because we want to test that Terraform ignores the inconsistency 6401 // when accompanied by another error. 6402 return providers.ApplyResourceChangeResponse{ 6403 NewState: cty.ObjectVal(map[string]cty.Value{ 6404 "value": cty.StringVal("wrong wrong wrong wrong"), 6405 "foo": cty.StringVal("absolutely brimming over with wrongability"), 6406 }), 6407 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")), 6408 } 6409 } 6410 6411 ctx := testContext2(t, &ContextOpts{ 6412 Config: m, 6413 Providers: map[addrs.Provider]providers.Factory{ 6414 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6415 }, 6416 }) 6417 6418 if _, diags := ctx.Plan(); diags.HasErrors() { 6419 t.Fatalf("plan errors: %s", diags.Err()) 6420 } 6421 6422 state, diags := ctx.Apply() 6423 if diags == nil { 6424 t.Fatal("should have error") 6425 } 6426 if got, want := len(diags), 1; got != want { 6427 // There should be no additional diagnostics generated by Terraform's own eval logic, 6428 // because the provider's own error supersedes them. 6429 t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err()) 6430 } 6431 if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) { 6432 t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err()) 6433 } 6434 if got, want := len(state.RootModule().Resources), 2; got != want { 6435 t.Errorf("%d resources in state before prune; should have %d\n%s", got, want, spew.Sdump(state)) 6436 } 6437 state.PruneResourceHusks() // aws_instance.bar with no instances gets left behind when we bail out, but that's okay 6438 if got, want := len(state.RootModule().Resources), 1; got != want { 6439 t.Errorf("%d resources in state after prune; should have only one (aws_instance.foo, tainted)\n%s", got, spew.Sdump(state)) 6440 } 6441} 6442 6443func TestContext2Apply_errorUpdateNullNew(t *testing.T) { 6444 m := testModule(t, "apply-error") 6445 6446 p := testProvider("aws") 6447 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 6448 ResourceTypes: map[string]*configschema.Block{ 6449 "aws_instance": { 6450 Attributes: map[string]*configschema.Attribute{ 6451 "value": {Type: cty.String, Optional: true}, 6452 "foo": {Type: cty.String, Optional: true}, 6453 }, 6454 }, 6455 }, 6456 }) 6457 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 6458 return providers.PlanResourceChangeResponse{ 6459 PlannedState: req.ProposedNewState, 6460 } 6461 } 6462 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6463 // We're intentionally returning no NewState here because we want to 6464 // test that Terraform retains the prior state, rather than treating 6465 // the returned null as "no state" (object deleted). 6466 return providers.ApplyResourceChangeResponse{ 6467 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")), 6468 } 6469 } 6470 6471 ctx := testContext2(t, &ContextOpts{ 6472 Config: m, 6473 State: states.BuildState(func(ss *states.SyncState) { 6474 ss.SetResourceInstanceCurrent( 6475 addrs.Resource{ 6476 Mode: addrs.ManagedResourceMode, 6477 Type: "aws_instance", 6478 Name: "foo", 6479 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 6480 &states.ResourceInstanceObjectSrc{ 6481 Status: states.ObjectReady, 6482 AttrsJSON: []byte(`{"value":"old"}`), 6483 }, 6484 addrs.AbsProviderConfig{ 6485 Provider: addrs.NewDefaultProvider("aws"), 6486 Module: addrs.RootModule, 6487 }, 6488 ) 6489 }), 6490 Providers: map[addrs.Provider]providers.Factory{ 6491 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6492 }, 6493 }) 6494 6495 if _, diags := ctx.Plan(); diags.HasErrors() { 6496 t.Fatalf("plan errors: %s", diags.Err()) 6497 } 6498 6499 state, diags := ctx.Apply() 6500 if diags == nil { 6501 t.Fatal("should have error") 6502 } 6503 if got, want := len(diags), 1; got != want { 6504 // There should be no additional diagnostics generated by Terraform's own eval logic, 6505 // because the provider's own error supersedes them. 6506 t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err()) 6507 } 6508 if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) { 6509 t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err()) 6510 } 6511 state.PruneResourceHusks() 6512 if got, want := len(state.RootModule().Resources), 1; got != want { 6513 t.Fatalf("%d resources in state; should have only one (aws_instance.foo, unmodified)\n%s", got, spew.Sdump(state)) 6514 } 6515 6516 is := state.ResourceInstance(addrs.Resource{ 6517 Mode: addrs.ManagedResourceMode, 6518 Type: "aws_instance", 6519 Name: "foo", 6520 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 6521 if is == nil { 6522 t.Fatalf("aws_instance.foo is not in the state after apply") 6523 } 6524 if got, want := is.Current.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) { 6525 t.Fatalf("incorrect attributes for aws_instance.foo\ngot: %s\nwant: JSON containing %s\n\n%s", got, want, spew.Sdump(is)) 6526 } 6527} 6528 6529func TestContext2Apply_errorPartial(t *testing.T) { 6530 errored := false 6531 6532 m := testModule(t, "apply-error") 6533 p := testProvider("aws") 6534 6535 state := states.NewState() 6536 root := state.EnsureModule(addrs.RootModuleInstance) 6537 root.SetResourceInstanceCurrent( 6538 mustResourceInstanceAddr("aws_instance.bar").Resource, 6539 &states.ResourceInstanceObjectSrc{ 6540 Status: states.ObjectReady, 6541 AttrsJSON: []byte(`{"id":"bar","type":"aws_instance"}`), 6542 }, 6543 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6544 ) 6545 6546 ctx := testContext2(t, &ContextOpts{ 6547 Config: m, 6548 Providers: map[addrs.Provider]providers.Factory{ 6549 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6550 }, 6551 State: state, 6552 }) 6553 6554 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 6555 if errored { 6556 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error")) 6557 return 6558 } 6559 errored = true 6560 6561 return testApplyFn(req) 6562 } 6563 p.PlanResourceChangeFn = testDiffFn 6564 6565 if _, diags := ctx.Plan(); diags.HasErrors() { 6566 t.Fatalf("plan errors: %s", diags.Err()) 6567 } 6568 6569 s, diags := ctx.Apply() 6570 if diags == nil { 6571 t.Fatal("should have error") 6572 } 6573 6574 mod := s.RootModule() 6575 if len(mod.Resources) != 2 { 6576 t.Fatalf("bad: %#v", mod.Resources) 6577 } 6578 6579 actual := strings.TrimSpace(s.String()) 6580 expected := strings.TrimSpace(testTerraformApplyErrorPartialStr) 6581 if actual != expected { 6582 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 6583 } 6584} 6585 6586func TestContext2Apply_hook(t *testing.T) { 6587 m := testModule(t, "apply-good") 6588 h := new(MockHook) 6589 p := testProvider("aws") 6590 p.PlanResourceChangeFn = testDiffFn 6591 ctx := testContext2(t, &ContextOpts{ 6592 Config: m, 6593 Hooks: []Hook{h}, 6594 Providers: map[addrs.Provider]providers.Factory{ 6595 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6596 }, 6597 }) 6598 6599 if _, diags := ctx.Plan(); diags.HasErrors() { 6600 t.Fatalf("plan errors: %s", diags.Err()) 6601 } 6602 6603 if _, diags := ctx.Apply(); diags.HasErrors() { 6604 t.Fatalf("apply errors: %s", diags.Err()) 6605 } 6606 6607 if !h.PreApplyCalled { 6608 t.Fatal("should be called") 6609 } 6610 if !h.PostApplyCalled { 6611 t.Fatal("should be called") 6612 } 6613 if !h.PostStateUpdateCalled { 6614 t.Fatalf("should call post state update") 6615 } 6616} 6617 6618func TestContext2Apply_hookOrphan(t *testing.T) { 6619 m := testModule(t, "apply-blank") 6620 h := new(MockHook) 6621 p := testProvider("aws") 6622 p.PlanResourceChangeFn = testDiffFn 6623 6624 state := states.NewState() 6625 root := state.EnsureModule(addrs.RootModuleInstance) 6626 root.SetResourceInstanceCurrent( 6627 mustResourceInstanceAddr("aws_instance.bar").Resource, 6628 &states.ResourceInstanceObjectSrc{ 6629 Status: states.ObjectReady, 6630 AttrsJSON: []byte(`{"id":"bar"}`), 6631 }, 6632 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6633 ) 6634 6635 ctx := testContext2(t, &ContextOpts{ 6636 Config: m, 6637 State: state, 6638 Hooks: []Hook{h}, 6639 Providers: map[addrs.Provider]providers.Factory{ 6640 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6641 }, 6642 }) 6643 6644 if _, diags := ctx.Plan(); diags.HasErrors() { 6645 t.Fatalf("plan errors: %s", diags.Err()) 6646 } 6647 6648 if _, diags := ctx.Apply(); diags.HasErrors() { 6649 t.Fatalf("apply errors: %s", diags.Err()) 6650 } 6651 6652 if !h.PreApplyCalled { 6653 t.Fatal("should be called") 6654 } 6655 if !h.PostApplyCalled { 6656 t.Fatal("should be called") 6657 } 6658 if !h.PostStateUpdateCalled { 6659 t.Fatalf("should call post state update") 6660 } 6661} 6662 6663func TestContext2Apply_idAttr(t *testing.T) { 6664 m := testModule(t, "apply-idattr") 6665 p := testProvider("aws") 6666 ctx := testContext2(t, &ContextOpts{ 6667 Config: m, 6668 Providers: map[addrs.Provider]providers.Factory{ 6669 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6670 }, 6671 }) 6672 6673 p.PlanResourceChangeFn = testDiffFn 6674 p.ApplyResourceChangeFn = testApplyFn 6675 6676 if _, diags := ctx.Plan(); diags.HasErrors() { 6677 t.Fatalf("plan errors: %s", diags.Err()) 6678 } 6679 6680 state, diags := ctx.Apply() 6681 if diags.HasErrors() { 6682 t.Fatalf("apply errors: %s", diags.Err()) 6683 } 6684 6685 mod := state.RootModule() 6686 rs, ok := mod.Resources["aws_instance.foo"] 6687 if !ok { 6688 t.Fatal("not in state") 6689 } 6690 var attrs map[string]interface{} 6691 err := json.Unmarshal(rs.Instances[addrs.NoKey].Current.AttrsJSON, &attrs) 6692 if err != nil { 6693 t.Fatal(err) 6694 } 6695 if got, want := attrs["id"], "foo"; got != want { 6696 t.Fatalf("wrong id\ngot: %#v\nwant: %#v", got, want) 6697 } 6698} 6699 6700func TestContext2Apply_outputBasic(t *testing.T) { 6701 m := testModule(t, "apply-output") 6702 p := testProvider("aws") 6703 p.PlanResourceChangeFn = testDiffFn 6704 p.ApplyResourceChangeFn = testApplyFn 6705 ctx := testContext2(t, &ContextOpts{ 6706 Config: m, 6707 Providers: map[addrs.Provider]providers.Factory{ 6708 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6709 }, 6710 }) 6711 6712 if _, diags := ctx.Plan(); diags.HasErrors() { 6713 t.Fatalf("plan errors: %s", diags.Err()) 6714 } 6715 6716 state, diags := ctx.Apply() 6717 if diags.HasErrors() { 6718 t.Fatalf("diags: %s", diags.Err()) 6719 } 6720 6721 actual := strings.TrimSpace(state.String()) 6722 expected := strings.TrimSpace(testTerraformApplyOutputStr) 6723 if actual != expected { 6724 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6725 } 6726} 6727 6728func TestContext2Apply_outputAdd(t *testing.T) { 6729 m1 := testModule(t, "apply-output-add-before") 6730 p1 := testProvider("aws") 6731 p1.ApplyResourceChangeFn = testApplyFn 6732 p1.PlanResourceChangeFn = testDiffFn 6733 ctx1 := testContext2(t, &ContextOpts{ 6734 Config: m1, 6735 Providers: map[addrs.Provider]providers.Factory{ 6736 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p1), 6737 }, 6738 }) 6739 6740 if _, diags := ctx1.Plan(); diags.HasErrors() { 6741 t.Fatalf("diags: %s", diags.Err()) 6742 } 6743 6744 state1, diags := ctx1.Apply() 6745 if diags.HasErrors() { 6746 t.Fatalf("diags: %s", diags.Err()) 6747 } 6748 6749 m2 := testModule(t, "apply-output-add-after") 6750 p2 := testProvider("aws") 6751 p2.ApplyResourceChangeFn = testApplyFn 6752 p2.PlanResourceChangeFn = testDiffFn 6753 ctx2 := testContext2(t, &ContextOpts{ 6754 Config: m2, 6755 Providers: map[addrs.Provider]providers.Factory{ 6756 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p2), 6757 }, 6758 6759 State: state1, 6760 }) 6761 6762 if _, diags := ctx2.Plan(); diags.HasErrors() { 6763 t.Fatalf("diags: %s", diags.Err()) 6764 } 6765 6766 state2, diags := ctx2.Apply() 6767 if diags.HasErrors() { 6768 t.Fatalf("diags: %s", diags.Err()) 6769 } 6770 6771 actual := strings.TrimSpace(state2.String()) 6772 expected := strings.TrimSpace(testTerraformApplyOutputAddStr) 6773 if actual != expected { 6774 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6775 } 6776} 6777 6778func TestContext2Apply_outputList(t *testing.T) { 6779 m := testModule(t, "apply-output-list") 6780 p := testProvider("aws") 6781 p.PlanResourceChangeFn = testDiffFn 6782 p.ApplyResourceChangeFn = testApplyFn 6783 ctx := testContext2(t, &ContextOpts{ 6784 Config: m, 6785 Providers: map[addrs.Provider]providers.Factory{ 6786 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6787 }, 6788 }) 6789 6790 if _, diags := ctx.Plan(); diags.HasErrors() { 6791 t.Fatalf("plan errors: %s", diags.Err()) 6792 } 6793 6794 state, diags := ctx.Apply() 6795 if diags.HasErrors() { 6796 t.Fatalf("diags: %s", diags.Err()) 6797 } 6798 6799 actual := strings.TrimSpace(state.String()) 6800 expected := strings.TrimSpace(testTerraformApplyOutputListStr) 6801 if actual != expected { 6802 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6803 } 6804} 6805 6806func TestContext2Apply_outputMulti(t *testing.T) { 6807 m := testModule(t, "apply-output-multi") 6808 p := testProvider("aws") 6809 p.PlanResourceChangeFn = testDiffFn 6810 p.ApplyResourceChangeFn = testApplyFn 6811 ctx := testContext2(t, &ContextOpts{ 6812 Config: m, 6813 Providers: map[addrs.Provider]providers.Factory{ 6814 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6815 }, 6816 }) 6817 6818 if _, diags := ctx.Plan(); diags.HasErrors() { 6819 t.Fatalf("plan errors: %s", diags.Err()) 6820 } 6821 6822 state, diags := ctx.Apply() 6823 if diags.HasErrors() { 6824 t.Fatalf("diags: %s", diags.Err()) 6825 } 6826 6827 actual := strings.TrimSpace(state.String()) 6828 expected := strings.TrimSpace(testTerraformApplyOutputMultiStr) 6829 if actual != expected { 6830 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6831 } 6832} 6833 6834func TestContext2Apply_outputMultiIndex(t *testing.T) { 6835 m := testModule(t, "apply-output-multi-index") 6836 p := testProvider("aws") 6837 p.PlanResourceChangeFn = testDiffFn 6838 p.ApplyResourceChangeFn = testApplyFn 6839 ctx := testContext2(t, &ContextOpts{ 6840 Config: m, 6841 Providers: map[addrs.Provider]providers.Factory{ 6842 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6843 }, 6844 }) 6845 6846 if _, diags := ctx.Plan(); diags.HasErrors() { 6847 t.Fatalf("plan errors: %s", diags.Err()) 6848 } 6849 6850 state, diags := ctx.Apply() 6851 if diags.HasErrors() { 6852 t.Fatalf("diags: %s", diags.Err()) 6853 } 6854 6855 actual := strings.TrimSpace(state.String()) 6856 expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr) 6857 if actual != expected { 6858 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6859 } 6860} 6861 6862func TestContext2Apply_taintX(t *testing.T) { 6863 m := testModule(t, "apply-taint") 6864 p := testProvider("aws") 6865 // destroyCount tests against regression of 6866 // https://github.com/hashicorp/terraform/issues/1056 6867 var destroyCount = int32(0) 6868 var once sync.Once 6869 simulateProviderDelay := func() { 6870 time.Sleep(10 * time.Millisecond) 6871 } 6872 6873 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 6874 once.Do(simulateProviderDelay) 6875 if req.PlannedState.IsNull() { 6876 atomic.AddInt32(&destroyCount, 1) 6877 } 6878 return testApplyFn(req) 6879 } 6880 p.PlanResourceChangeFn = testDiffFn 6881 6882 state := states.NewState() 6883 root := state.EnsureModule(addrs.RootModuleInstance) 6884 root.SetResourceInstanceCurrent( 6885 mustResourceInstanceAddr("aws_instance.bar").Resource, 6886 &states.ResourceInstanceObjectSrc{ 6887 Status: states.ObjectTainted, 6888 AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`), 6889 }, 6890 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6891 ) 6892 6893 ctx := testContext2(t, &ContextOpts{ 6894 Config: m, 6895 Providers: map[addrs.Provider]providers.Factory{ 6896 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6897 }, 6898 State: state, 6899 }) 6900 6901 if p, diags := ctx.Plan(); diags.HasErrors() { 6902 t.Fatalf("diags: %s", diags.Err()) 6903 } else { 6904 t.Logf("plan: %s", legacyDiffComparisonString(p.Changes)) 6905 } 6906 6907 s, diags := ctx.Apply() 6908 if diags.HasErrors() { 6909 t.Fatalf("diags: %s", diags.Err()) 6910 } 6911 6912 actual := strings.TrimSpace(s.String()) 6913 expected := strings.TrimSpace(testTerraformApplyTaintStr) 6914 if actual != expected { 6915 t.Fatalf("bad:\n%s", actual) 6916 } 6917 6918 if destroyCount != 1 { 6919 t.Fatalf("Expected 1 destroy, got %d", destroyCount) 6920 } 6921} 6922 6923func TestContext2Apply_taintDep(t *testing.T) { 6924 m := testModule(t, "apply-taint-dep") 6925 p := testProvider("aws") 6926 p.PlanResourceChangeFn = testDiffFn 6927 p.ApplyResourceChangeFn = testApplyFn 6928 6929 state := states.NewState() 6930 root := state.EnsureModule(addrs.RootModuleInstance) 6931 root.SetResourceInstanceCurrent( 6932 mustResourceInstanceAddr("aws_instance.foo").Resource, 6933 &states.ResourceInstanceObjectSrc{ 6934 Status: states.ObjectTainted, 6935 AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`), 6936 }, 6937 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6938 ) 6939 root.SetResourceInstanceCurrent( 6940 mustResourceInstanceAddr("aws_instance.bar").Resource, 6941 &states.ResourceInstanceObjectSrc{ 6942 Status: states.ObjectReady, 6943 AttrsJSON: []byte(`{"id":"bar","num": "2", "type": "aws_instance", "foo": "baz"}`), 6944 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")}, 6945 }, 6946 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6947 ) 6948 6949 ctx := testContext2(t, &ContextOpts{ 6950 Config: m, 6951 Providers: map[addrs.Provider]providers.Factory{ 6952 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 6953 }, 6954 State: state, 6955 }) 6956 6957 if p, diags := ctx.Plan(); diags.HasErrors() { 6958 t.Fatalf("diags: %s", diags.Err()) 6959 } else { 6960 t.Logf("plan: %s", legacyDiffComparisonString(p.Changes)) 6961 } 6962 6963 s, diags := ctx.Apply() 6964 if diags.HasErrors() { 6965 t.Fatalf("diags: %s", diags.Err()) 6966 } 6967 6968 actual := strings.TrimSpace(s.String()) 6969 expected := strings.TrimSpace(testTerraformApplyTaintDepStr) 6970 if actual != expected { 6971 t.Fatalf("bad:\n%s", actual) 6972 } 6973} 6974 6975func TestContext2Apply_taintDepRequiresNew(t *testing.T) { 6976 m := testModule(t, "apply-taint-dep-requires-new") 6977 p := testProvider("aws") 6978 p.PlanResourceChangeFn = testDiffFn 6979 p.ApplyResourceChangeFn = testApplyFn 6980 6981 state := states.NewState() 6982 root := state.EnsureModule(addrs.RootModuleInstance) 6983 root.SetResourceInstanceCurrent( 6984 mustResourceInstanceAddr("aws_instance.foo").Resource, 6985 &states.ResourceInstanceObjectSrc{ 6986 Status: states.ObjectTainted, 6987 AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`), 6988 }, 6989 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6990 ) 6991 root.SetResourceInstanceCurrent( 6992 mustResourceInstanceAddr("aws_instance.bar").Resource, 6993 &states.ResourceInstanceObjectSrc{ 6994 Status: states.ObjectReady, 6995 AttrsJSON: []byte(`{"id":"bar","num": "2", "type": "aws_instance", "foo": "baz"}`), 6996 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")}, 6997 }, 6998 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 6999 ) 7000 7001 ctx := testContext2(t, &ContextOpts{ 7002 Config: m, 7003 Providers: map[addrs.Provider]providers.Factory{ 7004 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7005 }, 7006 State: state, 7007 }) 7008 7009 if p, diags := ctx.Plan(); diags.HasErrors() { 7010 t.Fatalf("diags: %s", diags.Err()) 7011 } else { 7012 t.Logf("plan: %s", legacyDiffComparisonString(p.Changes)) 7013 } 7014 7015 s, diags := ctx.Apply() 7016 if diags.HasErrors() { 7017 t.Fatalf("diags: %s", diags.Err()) 7018 } 7019 7020 actual := strings.TrimSpace(s.String()) 7021 expected := strings.TrimSpace(testTerraformApplyTaintDepRequireNewStr) 7022 if actual != expected { 7023 t.Fatalf("bad:\n%s", actual) 7024 } 7025} 7026 7027func TestContext2Apply_targeted(t *testing.T) { 7028 m := testModule(t, "apply-targeted") 7029 p := testProvider("aws") 7030 p.PlanResourceChangeFn = testDiffFn 7031 p.ApplyResourceChangeFn = testApplyFn 7032 ctx := testContext2(t, &ContextOpts{ 7033 Config: m, 7034 Providers: map[addrs.Provider]providers.Factory{ 7035 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7036 }, 7037 Targets: []addrs.Targetable{ 7038 addrs.RootModuleInstance.Resource( 7039 addrs.ManagedResourceMode, "aws_instance", "foo", 7040 ), 7041 }, 7042 }) 7043 7044 if _, diags := ctx.Plan(); diags.HasErrors() { 7045 t.Fatalf("plan errors: %s", diags.Err()) 7046 } 7047 7048 state, diags := ctx.Apply() 7049 if diags.HasErrors() { 7050 t.Fatalf("diags: %s", diags.Err()) 7051 } 7052 7053 mod := state.RootModule() 7054 if len(mod.Resources) != 1 { 7055 t.Fatalf("expected 1 resource, got: %#v", mod.Resources) 7056 } 7057 7058 checkStateString(t, state, ` 7059aws_instance.foo: 7060 ID = foo 7061 provider = provider["registry.terraform.io/hashicorp/aws"] 7062 num = 2 7063 type = aws_instance 7064 `) 7065} 7066 7067func TestContext2Apply_targetedCount(t *testing.T) { 7068 m := testModule(t, "apply-targeted-count") 7069 p := testProvider("aws") 7070 p.PlanResourceChangeFn = testDiffFn 7071 p.ApplyResourceChangeFn = testApplyFn 7072 ctx := testContext2(t, &ContextOpts{ 7073 Config: m, 7074 Providers: map[addrs.Provider]providers.Factory{ 7075 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7076 }, 7077 Targets: []addrs.Targetable{ 7078 addrs.RootModuleInstance.Resource( 7079 addrs.ManagedResourceMode, "aws_instance", "foo", 7080 ), 7081 }, 7082 }) 7083 7084 if _, diags := ctx.Plan(); diags.HasErrors() { 7085 t.Fatalf("plan errors: %s", diags.Err()) 7086 } 7087 7088 state, diags := ctx.Apply() 7089 if diags.HasErrors() { 7090 t.Fatalf("diags: %s", diags.Err()) 7091 } 7092 7093 checkStateString(t, state, ` 7094aws_instance.foo.0: 7095 ID = foo 7096 provider = provider["registry.terraform.io/hashicorp/aws"] 7097 type = aws_instance 7098aws_instance.foo.1: 7099 ID = foo 7100 provider = provider["registry.terraform.io/hashicorp/aws"] 7101 type = aws_instance 7102aws_instance.foo.2: 7103 ID = foo 7104 provider = provider["registry.terraform.io/hashicorp/aws"] 7105 type = aws_instance 7106 `) 7107} 7108 7109func TestContext2Apply_targetedCountIndex(t *testing.T) { 7110 m := testModule(t, "apply-targeted-count") 7111 p := testProvider("aws") 7112 p.PlanResourceChangeFn = testDiffFn 7113 p.ApplyResourceChangeFn = testApplyFn 7114 ctx := testContext2(t, &ContextOpts{ 7115 Config: m, 7116 Providers: map[addrs.Provider]providers.Factory{ 7117 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7118 }, 7119 Targets: []addrs.Targetable{ 7120 addrs.RootModuleInstance.ResourceInstance( 7121 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1), 7122 ), 7123 }, 7124 }) 7125 7126 if _, diags := ctx.Plan(); diags.HasErrors() { 7127 t.Fatalf("plan errors: %s", diags.Err()) 7128 } 7129 7130 state, diags := ctx.Apply() 7131 if diags.HasErrors() { 7132 t.Fatalf("diags: %s", diags.Err()) 7133 } 7134 7135 checkStateString(t, state, ` 7136aws_instance.foo.1: 7137 ID = foo 7138 provider = provider["registry.terraform.io/hashicorp/aws"] 7139 type = aws_instance 7140 `) 7141} 7142 7143func TestContext2Apply_targetedDestroy(t *testing.T) { 7144 m := testModule(t, "destroy-targeted") 7145 p := testProvider("aws") 7146 p.PlanResourceChangeFn = testDiffFn 7147 7148 state := states.NewState() 7149 root := state.EnsureModule(addrs.RootModuleInstance) 7150 root.SetResourceInstanceCurrent( 7151 mustResourceInstanceAddr("aws_instance.a").Resource, 7152 &states.ResourceInstanceObjectSrc{ 7153 Status: states.ObjectReady, 7154 AttrsJSON: []byte(`{"id":"bar"}`), 7155 }, 7156 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7157 ) 7158 root.SetOutputValue("out", cty.StringVal("bar"), false) 7159 7160 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7161 child.SetResourceInstanceCurrent( 7162 mustResourceInstanceAddr("aws_instance.b").Resource, 7163 &states.ResourceInstanceObjectSrc{ 7164 Status: states.ObjectReady, 7165 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7166 }, 7167 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7168 ) 7169 7170 ctx := testContext2(t, &ContextOpts{ 7171 Config: m, 7172 Providers: map[addrs.Provider]providers.Factory{ 7173 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7174 }, 7175 State: state, 7176 Targets: []addrs.Targetable{ 7177 addrs.RootModuleInstance.Resource( 7178 addrs.ManagedResourceMode, "aws_instance", "a", 7179 ), 7180 }, 7181 PlanMode: plans.DestroyMode, 7182 }) 7183 7184 if diags := ctx.Validate(); diags.HasErrors() { 7185 t.Fatalf("validate errors: %s", diags.Err()) 7186 } 7187 7188 if _, diags := ctx.Plan(); diags.HasErrors() { 7189 t.Fatalf("plan errors: %s", diags.Err()) 7190 } 7191 7192 state, diags := ctx.Apply() 7193 if diags.HasErrors() { 7194 t.Fatalf("diags: %s", diags.Err()) 7195 } 7196 7197 mod := state.RootModule() 7198 if len(mod.Resources) != 0 { 7199 t.Fatalf("expected 0 resources, got: %#v", mod.Resources) 7200 } 7201 7202 // the root output should not get removed; only the targeted resource. 7203 // 7204 // Note: earlier versions of this test expected 0 outputs, but it turns out 7205 // that was because Validate - not apply or destroy - removed the output 7206 // (which depends on the targeted resource) from state. That version of this 7207 // test did not match actual terraform behavior: the output remains in 7208 // state. 7209 // 7210 // TODO: Future refactoring may enable us to remove the output from state in 7211 // this case, and that would be Just Fine - this test can be modified to 7212 // expect 0 outputs. 7213 if len(mod.OutputValues) != 1 { 7214 t.Fatalf("expected 1 outputs, got: %#v", mod.OutputValues) 7215 } 7216 7217 // the module instance should remain 7218 mod = state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7219 if len(mod.Resources) != 1 { 7220 t.Fatalf("expected 1 resources, got: %#v", mod.Resources) 7221 } 7222} 7223 7224func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) { 7225 m := testModule(t, "apply-destroy-targeted-count") 7226 p := testProvider("aws") 7227 p.PlanResourceChangeFn = testDiffFn 7228 7229 state := states.NewState() 7230 root := state.EnsureModule(addrs.RootModuleInstance) 7231 root.SetResourceInstanceCurrent( 7232 mustResourceInstanceAddr("aws_instance.foo").Resource, 7233 &states.ResourceInstanceObjectSrc{ 7234 Status: states.ObjectReady, 7235 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7236 }, 7237 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7238 ) 7239 root.SetResourceInstanceCurrent( 7240 mustResourceInstanceAddr("aws_instance.bar").Resource, 7241 &states.ResourceInstanceObjectSrc{ 7242 Status: states.ObjectReady, 7243 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7244 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")}, 7245 }, 7246 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7247 ) 7248 7249 ctx := testContext2(t, &ContextOpts{ 7250 Config: m, 7251 Providers: map[addrs.Provider]providers.Factory{ 7252 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7253 }, 7254 State: state, 7255 Targets: []addrs.Targetable{ 7256 addrs.RootModuleInstance.Resource( 7257 addrs.ManagedResourceMode, "aws_instance", "foo", 7258 ), 7259 }, 7260 PlanMode: plans.DestroyMode, 7261 }) 7262 7263 if _, diags := ctx.Plan(); diags.HasErrors() { 7264 t.Fatalf("plan errors: %s", diags.Err()) 7265 } 7266 7267 state, diags := ctx.Apply() 7268 if diags.HasErrors() { 7269 t.Fatalf("diags: %s", diags.Err()) 7270 } 7271 7272 checkStateString(t, state, `<no state>`) 7273} 7274 7275// https://github.com/hashicorp/terraform/issues/4462 7276func TestContext2Apply_targetedDestroyModule(t *testing.T) { 7277 m := testModule(t, "apply-targeted-module") 7278 p := testProvider("aws") 7279 p.PlanResourceChangeFn = testDiffFn 7280 7281 state := states.NewState() 7282 root := state.EnsureModule(addrs.RootModuleInstance) 7283 root.SetResourceInstanceCurrent( 7284 mustResourceInstanceAddr("aws_instance.foo").Resource, 7285 &states.ResourceInstanceObjectSrc{ 7286 Status: states.ObjectReady, 7287 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7288 }, 7289 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7290 ) 7291 root.SetResourceInstanceCurrent( 7292 mustResourceInstanceAddr("aws_instance.bar").Resource, 7293 &states.ResourceInstanceObjectSrc{ 7294 Status: states.ObjectReady, 7295 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7296 }, 7297 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7298 ) 7299 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7300 child.SetResourceInstanceCurrent( 7301 mustResourceInstanceAddr("aws_instance.foo").Resource, 7302 &states.ResourceInstanceObjectSrc{ 7303 Status: states.ObjectReady, 7304 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7305 }, 7306 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7307 ) 7308 child.SetResourceInstanceCurrent( 7309 mustResourceInstanceAddr("aws_instance.bar").Resource, 7310 &states.ResourceInstanceObjectSrc{ 7311 Status: states.ObjectReady, 7312 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7313 }, 7314 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7315 ) 7316 7317 ctx := testContext2(t, &ContextOpts{ 7318 Config: m, 7319 Providers: map[addrs.Provider]providers.Factory{ 7320 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7321 }, 7322 State: state, 7323 Targets: []addrs.Targetable{ 7324 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 7325 addrs.ManagedResourceMode, "aws_instance", "foo", 7326 ), 7327 }, 7328 PlanMode: plans.DestroyMode, 7329 }) 7330 7331 if _, diags := ctx.Plan(); diags.HasErrors() { 7332 t.Fatalf("plan errors: %s", diags.Err()) 7333 } 7334 7335 state, diags := ctx.Apply() 7336 if diags.HasErrors() { 7337 t.Fatalf("diags: %s", diags.Err()) 7338 } 7339 7340 checkStateString(t, state, ` 7341aws_instance.bar: 7342 ID = i-abc123 7343 provider = provider["registry.terraform.io/hashicorp/aws"] 7344aws_instance.foo: 7345 ID = i-bcd345 7346 provider = provider["registry.terraform.io/hashicorp/aws"] 7347 7348module.child: 7349 aws_instance.bar: 7350 ID = i-abc123 7351 provider = provider["registry.terraform.io/hashicorp/aws"] 7352 `) 7353} 7354 7355func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) { 7356 m := testModule(t, "apply-targeted-count") 7357 p := testProvider("aws") 7358 p.PlanResourceChangeFn = testDiffFn 7359 7360 foo := &states.ResourceInstanceObjectSrc{ 7361 Status: states.ObjectReady, 7362 AttrsJSON: []byte(`{"id":"i-bcd345"}`), 7363 } 7364 bar := &states.ResourceInstanceObjectSrc{ 7365 Status: states.ObjectReady, 7366 AttrsJSON: []byte(`{"id":"i-abc123"}`), 7367 } 7368 7369 state := states.NewState() 7370 root := state.EnsureModule(addrs.RootModuleInstance) 7371 root.SetResourceInstanceCurrent( 7372 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 7373 foo, 7374 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7375 ) 7376 root.SetResourceInstanceCurrent( 7377 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 7378 foo, 7379 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7380 ) 7381 root.SetResourceInstanceCurrent( 7382 mustResourceInstanceAddr("aws_instance.foo[2]").Resource, 7383 foo, 7384 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7385 ) 7386 root.SetResourceInstanceCurrent( 7387 mustResourceInstanceAddr("aws_instance.bar[0]").Resource, 7388 bar, 7389 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7390 ) 7391 root.SetResourceInstanceCurrent( 7392 mustResourceInstanceAddr("aws_instance.bar[1]").Resource, 7393 bar, 7394 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7395 ) 7396 root.SetResourceInstanceCurrent( 7397 mustResourceInstanceAddr("aws_instance.bar[2]").Resource, 7398 bar, 7399 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7400 ) 7401 7402 ctx := testContext2(t, &ContextOpts{ 7403 Config: m, 7404 Providers: map[addrs.Provider]providers.Factory{ 7405 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7406 }, 7407 State: state, 7408 Targets: []addrs.Targetable{ 7409 addrs.RootModuleInstance.ResourceInstance( 7410 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(2), 7411 ), 7412 addrs.RootModuleInstance.ResourceInstance( 7413 addrs.ManagedResourceMode, "aws_instance", "bar", addrs.IntKey(1), 7414 ), 7415 }, 7416 PlanMode: plans.DestroyMode, 7417 }) 7418 7419 if _, diags := ctx.Plan(); diags.HasErrors() { 7420 t.Fatalf("plan errors: %s", diags.Err()) 7421 } 7422 7423 state, diags := ctx.Apply() 7424 if diags.HasErrors() { 7425 t.Fatalf("diags: %s", diags.Err()) 7426 } 7427 7428 checkStateString(t, state, ` 7429aws_instance.bar.0: 7430 ID = i-abc123 7431 provider = provider["registry.terraform.io/hashicorp/aws"] 7432aws_instance.bar.2: 7433 ID = i-abc123 7434 provider = provider["registry.terraform.io/hashicorp/aws"] 7435aws_instance.foo.0: 7436 ID = i-bcd345 7437 provider = provider["registry.terraform.io/hashicorp/aws"] 7438aws_instance.foo.1: 7439 ID = i-bcd345 7440 provider = provider["registry.terraform.io/hashicorp/aws"] 7441 `) 7442} 7443 7444func TestContext2Apply_targetedModule(t *testing.T) { 7445 m := testModule(t, "apply-targeted-module") 7446 p := testProvider("aws") 7447 p.PlanResourceChangeFn = testDiffFn 7448 p.ApplyResourceChangeFn = testApplyFn 7449 ctx := testContext2(t, &ContextOpts{ 7450 Config: m, 7451 Providers: map[addrs.Provider]providers.Factory{ 7452 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7453 }, 7454 Targets: []addrs.Targetable{ 7455 addrs.RootModuleInstance.Child("child", addrs.NoKey), 7456 }, 7457 }) 7458 7459 if _, diags := ctx.Plan(); diags.HasErrors() { 7460 t.Fatalf("plan errors: %s", diags.Err()) 7461 } 7462 7463 state, diags := ctx.Apply() 7464 if diags.HasErrors() { 7465 t.Fatalf("diags: %s", diags.Err()) 7466 } 7467 7468 mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7469 if mod == nil { 7470 t.Fatalf("no child module found in the state!\n\n%#v", state) 7471 } 7472 if len(mod.Resources) != 2 { 7473 t.Fatalf("expected 2 resources, got: %#v", mod.Resources) 7474 } 7475 7476 checkStateString(t, state, ` 7477<no state> 7478module.child: 7479 aws_instance.bar: 7480 ID = foo 7481 provider = provider["registry.terraform.io/hashicorp/aws"] 7482 num = 2 7483 type = aws_instance 7484 aws_instance.foo: 7485 ID = foo 7486 provider = provider["registry.terraform.io/hashicorp/aws"] 7487 num = 2 7488 type = aws_instance 7489 `) 7490} 7491 7492// GH-1858 7493func TestContext2Apply_targetedModuleDep(t *testing.T) { 7494 m := testModule(t, "apply-targeted-module-dep") 7495 p := testProvider("aws") 7496 p.PlanResourceChangeFn = testDiffFn 7497 p.ApplyResourceChangeFn = testApplyFn 7498 ctx := testContext2(t, &ContextOpts{ 7499 Config: m, 7500 Providers: map[addrs.Provider]providers.Factory{ 7501 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7502 }, 7503 Targets: []addrs.Targetable{ 7504 addrs.RootModuleInstance.Resource( 7505 addrs.ManagedResourceMode, "aws_instance", "foo", 7506 ), 7507 }, 7508 }) 7509 7510 if p, diags := ctx.Plan(); diags.HasErrors() { 7511 t.Fatalf("diags: %s", diags.Err()) 7512 } else { 7513 t.Logf("Diff: %s", legacyDiffComparisonString(p.Changes)) 7514 } 7515 7516 state, diags := ctx.Apply() 7517 if diags.HasErrors() { 7518 t.Fatalf("diags: %s", diags.Err()) 7519 } 7520 7521 checkStateString(t, state, ` 7522aws_instance.foo: 7523 ID = foo 7524 provider = provider["registry.terraform.io/hashicorp/aws"] 7525 foo = foo 7526 type = aws_instance 7527 7528 Dependencies: 7529 module.child.aws_instance.mod 7530 7531module.child: 7532 aws_instance.mod: 7533 ID = foo 7534 provider = provider["registry.terraform.io/hashicorp/aws"] 7535 type = aws_instance 7536 7537 Outputs: 7538 7539 output = foo 7540 `) 7541} 7542 7543// GH-10911 untargeted outputs should not be in the graph, and therefore 7544// not execute. 7545func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) { 7546 m := testModule(t, "apply-targeted-module-unrelated-outputs") 7547 p := testProvider("aws") 7548 p.PlanResourceChangeFn = testDiffFn 7549 p.ApplyResourceChangeFn = testApplyFn 7550 7551 state := states.NewState() 7552 _ = state.EnsureModule(addrs.RootModuleInstance.Child("child2", addrs.NoKey)) 7553 7554 ctx := testContext2(t, &ContextOpts{ 7555 Config: m, 7556 Providers: map[addrs.Provider]providers.Factory{ 7557 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7558 }, 7559 Targets: []addrs.Targetable{ 7560 addrs.RootModuleInstance.Child("child2", addrs.NoKey), 7561 }, 7562 State: state, 7563 }) 7564 7565 if _, diags := ctx.Plan(); diags.HasErrors() { 7566 t.Fatalf("plan errors: %s", diags.Err()) 7567 } 7568 7569 s, diags := ctx.Apply() 7570 if diags.HasErrors() { 7571 t.Fatalf("diags: %s", diags.Err()) 7572 } 7573 7574 // - module.child1's instance_id output is dropped because we don't preserve 7575 // non-root module outputs between runs (they can be recalculated from config) 7576 // - module.child2's instance_id is updated because its dependency is updated 7577 // - child2_id is updated because if its transitive dependency via module.child2 7578 checkStateString(t, s, ` 7579<no state> 7580Outputs: 7581 7582child2_id = foo 7583 7584module.child2: 7585 aws_instance.foo: 7586 ID = foo 7587 provider = provider["registry.terraform.io/hashicorp/aws"] 7588 type = aws_instance 7589 7590 Outputs: 7591 7592 instance_id = foo 7593`) 7594} 7595 7596func TestContext2Apply_targetedModuleResource(t *testing.T) { 7597 m := testModule(t, "apply-targeted-module-resource") 7598 p := testProvider("aws") 7599 p.PlanResourceChangeFn = testDiffFn 7600 p.ApplyResourceChangeFn = testApplyFn 7601 ctx := testContext2(t, &ContextOpts{ 7602 Config: m, 7603 Providers: map[addrs.Provider]providers.Factory{ 7604 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7605 }, 7606 Targets: []addrs.Targetable{ 7607 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 7608 addrs.ManagedResourceMode, "aws_instance", "foo", 7609 ), 7610 }, 7611 }) 7612 7613 if _, diags := ctx.Plan(); diags.HasErrors() { 7614 t.Fatalf("plan errors: %s", diags.Err()) 7615 } 7616 7617 state, diags := ctx.Apply() 7618 if diags.HasErrors() { 7619 t.Fatalf("diags: %s", diags.Err()) 7620 } 7621 7622 mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 7623 if mod == nil || len(mod.Resources) != 1 { 7624 t.Fatalf("expected 1 resource, got: %#v", mod) 7625 } 7626 7627 checkStateString(t, state, ` 7628<no state> 7629module.child: 7630 aws_instance.foo: 7631 ID = foo 7632 provider = provider["registry.terraform.io/hashicorp/aws"] 7633 num = 2 7634 type = aws_instance 7635 `) 7636} 7637 7638func TestContext2Apply_targetedResourceOrphanModule(t *testing.T) { 7639 m := testModule(t, "apply-targeted-resource-orphan-module") 7640 p := testProvider("aws") 7641 p.PlanResourceChangeFn = testDiffFn 7642 7643 state := states.NewState() 7644 child := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey)) 7645 child.SetResourceInstanceCurrent( 7646 mustResourceInstanceAddr("aws_instance.bar").Resource, 7647 &states.ResourceInstanceObjectSrc{ 7648 Status: states.ObjectReady, 7649 AttrsJSON: []byte(`{"type":"aws_instance"}`), 7650 }, 7651 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 7652 ) 7653 7654 ctx := testContext2(t, &ContextOpts{ 7655 Config: m, 7656 Providers: map[addrs.Provider]providers.Factory{ 7657 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7658 }, 7659 State: state, 7660 Targets: []addrs.Targetable{ 7661 addrs.RootModuleInstance.Resource( 7662 addrs.ManagedResourceMode, "aws_instance", "foo", 7663 ), 7664 }, 7665 }) 7666 7667 if _, diags := ctx.Plan(); diags.HasErrors() { 7668 t.Fatalf("plan errors: %s", diags.Err()) 7669 } 7670 7671 if _, diags := ctx.Apply(); diags.HasErrors() { 7672 t.Fatalf("apply errors: %s", diags.Err()) 7673 } 7674} 7675 7676func TestContext2Apply_unknownAttribute(t *testing.T) { 7677 m := testModule(t, "apply-unknown") 7678 p := testProvider("aws") 7679 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 7680 resp = testDiffFn(req) 7681 planned := resp.PlannedState.AsValueMap() 7682 planned["unknown"] = cty.UnknownVal(cty.String) 7683 resp.PlannedState = cty.ObjectVal(planned) 7684 return resp 7685 } 7686 p.ApplyResourceChangeFn = testApplyFn 7687 7688 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 7689 ResourceTypes: map[string]*configschema.Block{ 7690 "aws_instance": { 7691 Attributes: map[string]*configschema.Attribute{ 7692 "id": {Type: cty.String, Computed: true}, 7693 "num": {Type: cty.Number, Optional: true}, 7694 "unknown": {Type: cty.String, Computed: true}, 7695 "type": {Type: cty.String, Computed: true}, 7696 }, 7697 }, 7698 }, 7699 }) 7700 7701 ctx := testContext2(t, &ContextOpts{ 7702 Config: m, 7703 Providers: map[addrs.Provider]providers.Factory{ 7704 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7705 }, 7706 }) 7707 7708 if _, diags := ctx.Plan(); diags.HasErrors() { 7709 t.Fatalf("plan errors: %s", diags.Err()) 7710 } 7711 7712 state, diags := ctx.Apply() 7713 if !diags.HasErrors() { 7714 t.Error("should error, because attribute 'unknown' is still unknown after apply") 7715 } 7716 7717 actual := strings.TrimSpace(state.String()) 7718 expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr) 7719 if actual != expected { 7720 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7721 } 7722} 7723 7724func TestContext2Apply_unknownAttributeInterpolate(t *testing.T) { 7725 m := testModule(t, "apply-unknown-interpolate") 7726 p := testProvider("aws") 7727 p.PlanResourceChangeFn = testDiffFn 7728 ctx := testContext2(t, &ContextOpts{ 7729 Config: m, 7730 Providers: map[addrs.Provider]providers.Factory{ 7731 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7732 }, 7733 }) 7734 7735 if _, diags := ctx.Plan(); diags == nil { 7736 t.Fatal("should error") 7737 } 7738} 7739 7740func TestContext2Apply_vars(t *testing.T) { 7741 fixture := contextFixtureApplyVars(t) 7742 opts := fixture.ContextOpts() 7743 opts.Variables = InputValues{ 7744 "foo": &InputValue{ 7745 Value: cty.StringVal("us-east-1"), 7746 SourceType: ValueFromCaller, 7747 }, 7748 "test_list": &InputValue{ 7749 Value: cty.ListVal([]cty.Value{ 7750 cty.StringVal("Hello"), 7751 cty.StringVal("World"), 7752 }), 7753 SourceType: ValueFromCaller, 7754 }, 7755 "test_map": &InputValue{ 7756 Value: cty.MapVal(map[string]cty.Value{ 7757 "Hello": cty.StringVal("World"), 7758 "Foo": cty.StringVal("Bar"), 7759 "Baz": cty.StringVal("Foo"), 7760 }), 7761 SourceType: ValueFromCaller, 7762 }, 7763 "amis": &InputValue{ 7764 Value: cty.MapVal(map[string]cty.Value{ 7765 "us-east-1": cty.StringVal("override"), 7766 }), 7767 SourceType: ValueFromCaller, 7768 }, 7769 } 7770 ctx := testContext2(t, opts) 7771 7772 diags := ctx.Validate() 7773 if len(diags) != 0 { 7774 t.Fatalf("bad: %s", diags.ErrWithWarnings()) 7775 } 7776 7777 if _, diags := ctx.Plan(); diags.HasErrors() { 7778 t.Fatalf("err: %s", diags.Err()) 7779 } 7780 7781 state, diags := ctx.Apply() 7782 if diags.HasErrors() { 7783 t.Fatalf("err: %s", diags.Err()) 7784 } 7785 7786 got := strings.TrimSpace(state.String()) 7787 want := strings.TrimSpace(testTerraformApplyVarsStr) 7788 if got != want { 7789 t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want) 7790 } 7791} 7792 7793func TestContext2Apply_varsEnv(t *testing.T) { 7794 fixture := contextFixtureApplyVarsEnv(t) 7795 opts := fixture.ContextOpts() 7796 opts.Variables = InputValues{ 7797 "string": &InputValue{ 7798 Value: cty.StringVal("baz"), 7799 SourceType: ValueFromEnvVar, 7800 }, 7801 "list": &InputValue{ 7802 Value: cty.ListVal([]cty.Value{ 7803 cty.StringVal("Hello"), 7804 cty.StringVal("World"), 7805 }), 7806 SourceType: ValueFromEnvVar, 7807 }, 7808 "map": &InputValue{ 7809 Value: cty.MapVal(map[string]cty.Value{ 7810 "Hello": cty.StringVal("World"), 7811 "Foo": cty.StringVal("Bar"), 7812 "Baz": cty.StringVal("Foo"), 7813 }), 7814 SourceType: ValueFromEnvVar, 7815 }, 7816 } 7817 ctx := testContext2(t, opts) 7818 7819 diags := ctx.Validate() 7820 if len(diags) != 0 { 7821 t.Fatalf("bad: %s", diags.ErrWithWarnings()) 7822 } 7823 7824 if _, diags := ctx.Plan(); diags.HasErrors() { 7825 t.Fatalf("err: %s", diags.Err()) 7826 } 7827 7828 state, diags := ctx.Apply() 7829 if diags.HasErrors() { 7830 t.Fatalf("err: %s", diags.Err()) 7831 } 7832 7833 actual := strings.TrimSpace(state.String()) 7834 expected := strings.TrimSpace(testTerraformApplyVarsEnvStr) 7835 if actual != expected { 7836 t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7837 } 7838} 7839 7840func TestContext2Apply_createBefore_depends(t *testing.T) { 7841 m := testModule(t, "apply-depends-create-before") 7842 h := new(HookRecordApplyOrder) 7843 p := testProvider("aws") 7844 p.PlanResourceChangeFn = testDiffFn 7845 p.ApplyResourceChangeFn = testApplyFn 7846 state := states.NewState() 7847 root := state.EnsureModule(addrs.RootModuleInstance) 7848 root.SetResourceInstanceCurrent( 7849 addrs.Resource{ 7850 Mode: addrs.ManagedResourceMode, 7851 Type: "aws_instance", 7852 Name: "web", 7853 }.Instance(addrs.NoKey), 7854 &states.ResourceInstanceObjectSrc{ 7855 Status: states.ObjectReady, 7856 AttrsJSON: []byte(`{"id":"bar","require_new":"ami-old"}`), 7857 }, 7858 addrs.AbsProviderConfig{ 7859 Provider: addrs.NewDefaultProvider("aws"), 7860 Module: addrs.RootModule, 7861 }, 7862 ) 7863 7864 root.SetResourceInstanceCurrent( 7865 addrs.Resource{ 7866 Mode: addrs.ManagedResourceMode, 7867 Type: "aws_instance", 7868 Name: "lb", 7869 }.Instance(addrs.NoKey), 7870 &states.ResourceInstanceObjectSrc{ 7871 Status: states.ObjectReady, 7872 AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`), 7873 Dependencies: []addrs.ConfigResource{ 7874 addrs.ConfigResource{ 7875 Resource: addrs.Resource{ 7876 Mode: addrs.ManagedResourceMode, 7877 Type: "aws_instance", 7878 Name: "web", 7879 }, 7880 Module: addrs.RootModule, 7881 }, 7882 }, 7883 }, 7884 addrs.AbsProviderConfig{ 7885 Provider: addrs.NewDefaultProvider("aws"), 7886 Module: addrs.RootModule, 7887 }, 7888 ) 7889 7890 ctx := testContext2(t, &ContextOpts{ 7891 Config: m, 7892 Hooks: []Hook{h}, 7893 Providers: map[addrs.Provider]providers.Factory{ 7894 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 7895 }, 7896 State: state, 7897 }) 7898 7899 if p, diags := ctx.Plan(); diags.HasErrors() { 7900 logDiagnostics(t, diags) 7901 t.Fatal("plan failed") 7902 } else { 7903 t.Logf("plan:\n%s", legacyDiffComparisonString(p.Changes)) 7904 } 7905 7906 h.Active = true 7907 state, diags := ctx.Apply() 7908 if diags.HasErrors() { 7909 logDiagnostics(t, diags) 7910 t.Fatal("apply failed") 7911 } 7912 7913 mod := state.RootModule() 7914 if len(mod.Resources) < 2 { 7915 t.Logf("state after apply:\n%s", state.String()) 7916 t.Fatalf("only %d resources in root module; want at least 2", len(mod.Resources)) 7917 } 7918 7919 got := strings.TrimSpace(state.String()) 7920 want := strings.TrimSpace(testTerraformApplyDependsCreateBeforeStr) 7921 if got != want { 7922 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", got, want) 7923 } 7924 7925 // Test that things were managed _in the right order_ 7926 order := h.States 7927 7928 diffs := h.Diffs 7929 if !order[0].IsNull() || diffs[0].Action == plans.Delete { 7930 t.Fatalf("should create new instance first: %#v", order) 7931 } 7932 7933 if order[1].GetAttr("id").AsString() != "baz" { 7934 t.Fatalf("update must happen after create: %#v", order[1]) 7935 } 7936 7937 if order[2].GetAttr("id").AsString() != "bar" || diffs[2].Action != plans.Delete { 7938 t.Fatalf("destroy must happen after update: %#v", order[2]) 7939 } 7940} 7941 7942func TestContext2Apply_singleDestroy(t *testing.T) { 7943 m := testModule(t, "apply-depends-create-before") 7944 h := new(HookRecordApplyOrder) 7945 p := testProvider("aws") 7946 invokeCount := 0 7947 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 7948 invokeCount++ 7949 switch invokeCount { 7950 case 1: 7951 if req.PlannedState.IsNull() { 7952 t.Fatalf("should not destroy") 7953 } 7954 if id := req.PlannedState.GetAttr("id"); id.IsKnown() { 7955 t.Fatalf("should not have ID") 7956 } 7957 case 2: 7958 if req.PlannedState.IsNull() { 7959 t.Fatalf("should not destroy") 7960 } 7961 if id := req.PlannedState.GetAttr("id"); id.AsString() != "baz" { 7962 t.Fatalf("should have id") 7963 } 7964 case 3: 7965 if !req.PlannedState.IsNull() { 7966 t.Fatalf("should destroy") 7967 } 7968 default: 7969 t.Fatalf("bad invoke count %d", invokeCount) 7970 } 7971 return testApplyFn(req) 7972 } 7973 7974 p.PlanResourceChangeFn = testDiffFn 7975 state := states.NewState() 7976 root := state.EnsureModule(addrs.RootModuleInstance) 7977 root.SetResourceInstanceCurrent( 7978 addrs.Resource{ 7979 Mode: addrs.ManagedResourceMode, 7980 Type: "aws_instance", 7981 Name: "web", 7982 }.Instance(addrs.NoKey), 7983 &states.ResourceInstanceObjectSrc{ 7984 Status: states.ObjectReady, 7985 AttrsJSON: []byte(`{"id":"bar","require_new":"ami-old"}`), 7986 }, 7987 addrs.AbsProviderConfig{ 7988 Provider: addrs.NewDefaultProvider("aws"), 7989 Module: addrs.RootModule, 7990 }, 7991 ) 7992 7993 root.SetResourceInstanceCurrent( 7994 addrs.Resource{ 7995 Mode: addrs.ManagedResourceMode, 7996 Type: "aws_instance", 7997 Name: "lb", 7998 }.Instance(addrs.NoKey), 7999 &states.ResourceInstanceObjectSrc{ 8000 Status: states.ObjectReady, 8001 AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`), 8002 Dependencies: []addrs.ConfigResource{ 8003 addrs.ConfigResource{ 8004 Resource: addrs.Resource{ 8005 Mode: addrs.ManagedResourceMode, 8006 Type: "aws_instance", 8007 Name: "web", 8008 }, 8009 Module: addrs.RootModule, 8010 }, 8011 }, 8012 }, 8013 addrs.AbsProviderConfig{ 8014 Provider: addrs.NewDefaultProvider("aws"), 8015 Module: addrs.RootModule, 8016 }, 8017 ) 8018 8019 ctx := testContext2(t, &ContextOpts{ 8020 Config: m, 8021 Hooks: []Hook{h}, 8022 Providers: map[addrs.Provider]providers.Factory{ 8023 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8024 }, 8025 State: state, 8026 }) 8027 8028 if _, diags := ctx.Plan(); diags.HasErrors() { 8029 t.Fatalf("plan errors: %s", diags.Err()) 8030 } 8031 8032 h.Active = true 8033 _, diags := ctx.Apply() 8034 if diags.HasErrors() { 8035 t.Fatalf("diags: %s", diags.Err()) 8036 } 8037 8038 if invokeCount != 3 { 8039 t.Fatalf("bad: %d", invokeCount) 8040 } 8041} 8042 8043// GH-7824 8044func TestContext2Apply_issue7824(t *testing.T) { 8045 p := testProvider("template") 8046 p.PlanResourceChangeFn = testDiffFn 8047 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 8048 ResourceTypes: map[string]*configschema.Block{ 8049 "template_file": { 8050 Attributes: map[string]*configschema.Attribute{ 8051 "template": {Type: cty.String, Optional: true}, 8052 "__template_requires_new": {Type: cty.Bool, Optional: true}, 8053 }, 8054 }, 8055 }, 8056 }) 8057 8058 m, snap := testModuleWithSnapshot(t, "issue-7824") 8059 8060 // Apply cleanly step 0 8061 ctx := testContext2(t, &ContextOpts{ 8062 Config: m, 8063 Providers: map[addrs.Provider]providers.Factory{ 8064 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 8065 }, 8066 }) 8067 8068 plan, diags := ctx.Plan() 8069 if diags.HasErrors() { 8070 t.Fatalf("err: %s", diags.Err()) 8071 } 8072 8073 // Write / Read plan to simulate running it through a Plan file 8074 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 8075 if err != nil { 8076 t.Fatalf("failed to round-trip through planfile: %s", err) 8077 } 8078 8079 ctxOpts.Providers = 8080 map[addrs.Provider]providers.Factory{ 8081 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 8082 } 8083 8084 ctx, diags = NewContext(ctxOpts) 8085 if diags.HasErrors() { 8086 t.Fatalf("err: %s", diags.Err()) 8087 } 8088 8089 _, diags = ctx.Apply() 8090 if diags.HasErrors() { 8091 t.Fatalf("err: %s", diags.Err()) 8092 } 8093} 8094 8095// This deals with the situation where a splat expression is used referring 8096// to another resource whose count is non-constant. 8097func TestContext2Apply_issue5254(t *testing.T) { 8098 // Create a provider. We use "template" here just to match the repro 8099 // we got from the issue itself. 8100 p := testProvider("template") 8101 p.PlanResourceChangeFn = testDiffFn 8102 p.ApplyResourceChangeFn = testApplyFn 8103 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 8104 ResourceTypes: map[string]*configschema.Block{ 8105 "template_file": { 8106 Attributes: map[string]*configschema.Attribute{ 8107 "template": {Type: cty.String, Optional: true}, 8108 "__template_requires_new": {Type: cty.Bool, Optional: true}, 8109 "id": {Type: cty.String, Computed: true}, 8110 "type": {Type: cty.String, Computed: true}, 8111 }, 8112 }, 8113 }, 8114 }) 8115 8116 // Apply cleanly step 0 8117 ctx := testContext2(t, &ContextOpts{ 8118 Config: testModule(t, "issue-5254/step-0"), 8119 Providers: map[addrs.Provider]providers.Factory{ 8120 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 8121 }, 8122 }) 8123 8124 _, diags := ctx.Plan() 8125 if diags.HasErrors() { 8126 t.Fatalf("err: %s", diags.Err()) 8127 } 8128 8129 state, diags := ctx.Apply() 8130 if diags.HasErrors() { 8131 t.Fatalf("err: %s", diags.Err()) 8132 } 8133 8134 m, snap := testModuleWithSnapshot(t, "issue-5254/step-1") 8135 8136 // Application success. Now make the modification and store a plan 8137 ctx = testContext2(t, &ContextOpts{ 8138 Config: m, 8139 State: state, 8140 Providers: map[addrs.Provider]providers.Factory{ 8141 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 8142 }, 8143 }) 8144 8145 plan, diags := ctx.Plan() 8146 if diags.HasErrors() { 8147 t.Fatalf("err: %s", diags.Err()) 8148 } 8149 8150 // Write / Read plan to simulate running it through a Plan file 8151 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 8152 if err != nil { 8153 t.Fatalf("failed to round-trip through planfile: %s", err) 8154 } 8155 8156 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 8157 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 8158 } 8159 8160 ctx, diags = NewContext(ctxOpts) 8161 if diags.HasErrors() { 8162 t.Fatalf("err: %s", diags.Err()) 8163 } 8164 8165 state, diags = ctx.Apply() 8166 if diags.HasErrors() { 8167 t.Fatalf("err: %s", diags.Err()) 8168 } 8169 8170 actual := strings.TrimSpace(state.String()) 8171 expected := strings.TrimSpace(` 8172template_file.child: 8173 ID = foo 8174 provider = provider["registry.terraform.io/hashicorp/template"] 8175 __template_requires_new = true 8176 template = Hi 8177 type = template_file 8178 8179 Dependencies: 8180 template_file.parent 8181template_file.parent.0: 8182 ID = foo 8183 provider = provider["registry.terraform.io/hashicorp/template"] 8184 template = Hi 8185 type = template_file 8186`) 8187 if actual != expected { 8188 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected) 8189 } 8190} 8191 8192func TestContext2Apply_targetedWithTaintedInState(t *testing.T) { 8193 p := testProvider("aws") 8194 p.PlanResourceChangeFn = testDiffFn 8195 p.ApplyResourceChangeFn = testApplyFn 8196 m, snap := testModuleWithSnapshot(t, "apply-tainted-targets") 8197 8198 state := states.NewState() 8199 root := state.EnsureModule(addrs.RootModuleInstance) 8200 root.SetResourceInstanceCurrent( 8201 mustResourceInstanceAddr("aws_instance.ifailedprovisioners").Resource, 8202 &states.ResourceInstanceObjectSrc{ 8203 Status: states.ObjectTainted, 8204 AttrsJSON: []byte(`{"id":"ifailedprovisioners"}`), 8205 }, 8206 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8207 ) 8208 8209 ctx := testContext2(t, &ContextOpts{ 8210 Config: m, 8211 Providers: map[addrs.Provider]providers.Factory{ 8212 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8213 }, 8214 Targets: []addrs.Targetable{ 8215 addrs.RootModuleInstance.Resource( 8216 addrs.ManagedResourceMode, "aws_instance", "iambeingadded", 8217 ), 8218 }, 8219 State: state, 8220 }) 8221 8222 plan, diags := ctx.Plan() 8223 if diags.HasErrors() { 8224 t.Fatalf("err: %s", diags.Err()) 8225 } 8226 8227 // Write / Read plan to simulate running it through a Plan file 8228 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 8229 if err != nil { 8230 t.Fatalf("failed to round-trip through planfile: %s", err) 8231 } 8232 8233 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 8234 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8235 } 8236 8237 ctx, diags = NewContext(ctxOpts) 8238 if diags.HasErrors() { 8239 t.Fatalf("err: %s", diags.Err()) 8240 } 8241 8242 s, diags := ctx.Apply() 8243 if diags.HasErrors() { 8244 t.Fatalf("err: %s", diags.Err()) 8245 } 8246 8247 actual := strings.TrimSpace(s.String()) 8248 expected := strings.TrimSpace(` 8249aws_instance.iambeingadded: 8250 ID = foo 8251 provider = provider["registry.terraform.io/hashicorp/aws"] 8252 type = aws_instance 8253aws_instance.ifailedprovisioners: (tainted) 8254 ID = ifailedprovisioners 8255 provider = provider["registry.terraform.io/hashicorp/aws"] 8256 `) 8257 if actual != expected { 8258 t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual) 8259 } 8260} 8261 8262// Higher level test exposing the bug this covers in 8263// TestResource_ignoreChangesRequired 8264func TestContext2Apply_ignoreChangesCreate(t *testing.T) { 8265 m := testModule(t, "apply-ignore-changes-create") 8266 p := testProvider("aws") 8267 p.PlanResourceChangeFn = testDiffFn 8268 p.ApplyResourceChangeFn = testApplyFn 8269 8270 instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block 8271 instanceSchema.Attributes["required_field"] = &configschema.Attribute{ 8272 Type: cty.String, 8273 Required: true, 8274 } 8275 8276 ctx := testContext2(t, &ContextOpts{ 8277 Config: m, 8278 Providers: map[addrs.Provider]providers.Factory{ 8279 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8280 }, 8281 }) 8282 8283 if p, diags := ctx.Plan(); diags.HasErrors() { 8284 t.Fatalf("diags: %s", diags.Err()) 8285 } else { 8286 t.Logf(legacyDiffComparisonString(p.Changes)) 8287 } 8288 8289 state, diags := ctx.Apply() 8290 if diags.HasErrors() { 8291 t.Fatalf("diags: %s", diags.Err()) 8292 } 8293 8294 mod := state.RootModule() 8295 if len(mod.Resources) != 1 { 8296 t.Fatalf("bad: %s", state) 8297 } 8298 8299 actual := strings.TrimSpace(state.String()) 8300 // Expect no changes from original state 8301 expected := strings.TrimSpace(` 8302aws_instance.foo: 8303 ID = foo 8304 provider = provider["registry.terraform.io/hashicorp/aws"] 8305 required_field = set 8306 type = aws_instance 8307`) 8308 if actual != expected { 8309 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 8310 } 8311} 8312 8313func TestContext2Apply_ignoreChangesWithDep(t *testing.T) { 8314 m := testModule(t, "apply-ignore-changes-dep") 8315 p := testProvider("aws") 8316 8317 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 8318 resp.PlannedState = req.ProposedNewState 8319 8320 switch req.TypeName { 8321 case "aws_instance": 8322 resp.RequiresReplace = append(resp.RequiresReplace, cty.Path{cty.GetAttrStep{Name: "ami"}}) 8323 case "aws_eip": 8324 return testDiffFn(req) 8325 default: 8326 t.Fatalf("Unexpected type: %s", req.TypeName) 8327 } 8328 return 8329 } 8330 8331 state := states.NewState() 8332 root := state.EnsureModule(addrs.RootModuleInstance) 8333 root.SetResourceInstanceCurrent( 8334 mustResourceInstanceAddr("aws_instance.foo[0]").Resource, 8335 &states.ResourceInstanceObjectSrc{ 8336 Status: states.ObjectReady, 8337 AttrsJSON: []byte(`{"id":"i-abc123","ami":"ami-abcd1234"}`), 8338 }, 8339 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8340 ) 8341 root.SetResourceInstanceCurrent( 8342 mustResourceInstanceAddr("aws_instance.foo[1]").Resource, 8343 &states.ResourceInstanceObjectSrc{ 8344 Status: states.ObjectReady, 8345 AttrsJSON: []byte(`{"id":"i-bcd234","ami":"i-bcd234"}`), 8346 }, 8347 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8348 ) 8349 root.SetResourceInstanceCurrent( 8350 mustResourceInstanceAddr("aws_eip.foo[0]").Resource, 8351 &states.ResourceInstanceObjectSrc{ 8352 Status: states.ObjectReady, 8353 AttrsJSON: []byte(`{"id":"eip-abc123","instance":"i-abc123"}`), 8354 Dependencies: []addrs.ConfigResource{ 8355 addrs.ConfigResource{ 8356 Resource: addrs.Resource{ 8357 Mode: addrs.ManagedResourceMode, 8358 Type: "aws_instance", 8359 Name: "foo", 8360 }, 8361 Module: addrs.RootModule, 8362 }, 8363 }, 8364 }, 8365 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8366 ) 8367 root.SetResourceInstanceCurrent( 8368 mustResourceInstanceAddr("aws_eip.foo[1]").Resource, 8369 &states.ResourceInstanceObjectSrc{ 8370 Status: states.ObjectReady, 8371 AttrsJSON: []byte(`{"id":"eip-bcd234","instance":"i-bcd234"}`), 8372 Dependencies: []addrs.ConfigResource{ 8373 addrs.ConfigResource{ 8374 Resource: addrs.Resource{ 8375 Mode: addrs.ManagedResourceMode, 8376 Type: "aws_instance", 8377 Name: "foo", 8378 }, 8379 Module: addrs.RootModule, 8380 }, 8381 }, 8382 }, 8383 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8384 ) 8385 8386 ctx := testContext2(t, &ContextOpts{ 8387 Config: m, 8388 Providers: map[addrs.Provider]providers.Factory{ 8389 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8390 }, 8391 State: state.DeepCopy(), 8392 }) 8393 8394 _, diags := ctx.Plan() 8395 assertNoErrors(t, diags) 8396 8397 s, diags := ctx.Apply() 8398 assertNoErrors(t, diags) 8399 8400 actual := strings.TrimSpace(s.String()) 8401 expected := strings.TrimSpace(state.String()) 8402 if actual != expected { 8403 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 8404 } 8405} 8406 8407func TestContext2Apply_ignoreChangesAll(t *testing.T) { 8408 m := testModule(t, "apply-ignore-changes-all") 8409 p := testProvider("aws") 8410 p.PlanResourceChangeFn = testDiffFn 8411 p.ApplyResourceChangeFn = testApplyFn 8412 8413 instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block 8414 instanceSchema.Attributes["required_field"] = &configschema.Attribute{ 8415 Type: cty.String, 8416 Required: true, 8417 } 8418 8419 ctx := testContext2(t, &ContextOpts{ 8420 Config: m, 8421 Providers: map[addrs.Provider]providers.Factory{ 8422 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8423 }, 8424 }) 8425 8426 if p, diags := ctx.Plan(); diags.HasErrors() { 8427 logDiagnostics(t, diags) 8428 t.Fatal("plan failed") 8429 } else { 8430 t.Logf(legacyDiffComparisonString(p.Changes)) 8431 } 8432 8433 state, diags := ctx.Apply() 8434 assertNoErrors(t, diags) 8435 8436 mod := state.RootModule() 8437 if len(mod.Resources) != 1 { 8438 t.Fatalf("bad: %s", state) 8439 } 8440 8441 actual := strings.TrimSpace(state.String()) 8442 // Expect no changes from original state 8443 expected := strings.TrimSpace(` 8444aws_instance.foo: 8445 ID = foo 8446 provider = provider["registry.terraform.io/hashicorp/aws"] 8447 required_field = set 8448 type = aws_instance 8449`) 8450 if actual != expected { 8451 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 8452 } 8453} 8454 8455// https://github.com/hashicorp/terraform/issues/7378 8456func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testing.T) { 8457 m, snap := testModuleWithSnapshot(t, "apply-destroy-nested-module-with-attrs") 8458 p := testProvider("null") 8459 p.PlanResourceChangeFn = testDiffFn 8460 8461 var state *states.State 8462 var diags tfdiags.Diagnostics 8463 { 8464 ctx := testContext2(t, &ContextOpts{ 8465 Config: m, 8466 Providers: map[addrs.Provider]providers.Factory{ 8467 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8468 }, 8469 }) 8470 8471 // First plan and apply a create operation 8472 if _, diags := ctx.Plan(); diags.HasErrors() { 8473 t.Fatalf("plan err: %s", diags.Err()) 8474 } 8475 8476 state, diags = ctx.Apply() 8477 if diags.HasErrors() { 8478 t.Fatalf("apply err: %s", diags.Err()) 8479 } 8480 } 8481 8482 { 8483 ctx := testContext2(t, &ContextOpts{ 8484 PlanMode: plans.DestroyMode, 8485 Config: m, 8486 State: state, 8487 Providers: map[addrs.Provider]providers.Factory{ 8488 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8489 }, 8490 }) 8491 8492 plan, diags := ctx.Plan() 8493 if diags.HasErrors() { 8494 t.Fatalf("destroy plan err: %s", diags.Err()) 8495 } 8496 8497 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 8498 if err != nil { 8499 t.Fatalf("failed to round-trip through planfile: %s", err) 8500 } 8501 8502 ctxOpts.Providers = map[addrs.Provider]providers.Factory{ 8503 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8504 } 8505 8506 ctx, diags = NewContext(ctxOpts) 8507 if diags.HasErrors() { 8508 t.Fatalf("err: %s", diags.Err()) 8509 } 8510 8511 state, diags = ctx.Apply() 8512 if diags.HasErrors() { 8513 t.Fatalf("destroy apply err: %s", diags.Err()) 8514 } 8515 } 8516 8517 if !state.Empty() { 8518 t.Fatalf("state after apply: %s\nwant empty state", spew.Sdump(state)) 8519 } 8520} 8521 8522// If a data source explicitly depends on another resource, it's because we need 8523// that resource to be applied first. 8524func TestContext2Apply_dataDependsOn(t *testing.T) { 8525 p := testProvider("null") 8526 m := testModuleInline(t, map[string]string{ 8527 "main.tf": ` 8528resource "null_instance" "write" { 8529 foo = "attribute" 8530} 8531 8532data "null_data_source" "read" { 8533 count = 1 8534 depends_on = ["null_instance.write"] 8535} 8536 8537resource "null_instance" "depends" { 8538 foo = data.null_data_source.read[0].foo 8539} 8540`}) 8541 8542 ctx := testContext2(t, &ContextOpts{ 8543 Config: m, 8544 Providers: map[addrs.Provider]providers.Factory{ 8545 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8546 }, 8547 }) 8548 8549 // the "provisioner" here writes to this variable, because the intent is to 8550 // create a dependency which can't be viewed through the graph, and depends 8551 // solely on the configuration providing "depends_on" 8552 provisionerOutput := "" 8553 8554 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 8555 // the side effect of the resource being applied 8556 provisionerOutput = "APPLIED" 8557 return testApplyFn(req) 8558 } 8559 8560 p.PlanResourceChangeFn = testDiffFn 8561 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 8562 return providers.ReadDataSourceResponse{ 8563 State: cty.ObjectVal(map[string]cty.Value{ 8564 "id": cty.StringVal("boop"), 8565 "foo": cty.StringVal(provisionerOutput), 8566 }), 8567 } 8568 } 8569 8570 _, diags := ctx.Plan() 8571 assertNoErrors(t, diags) 8572 8573 state, diags := ctx.Apply() 8574 assertNoErrors(t, diags) 8575 8576 root := state.Module(addrs.RootModuleInstance) 8577 is := root.ResourceInstance(addrs.Resource{ 8578 Mode: addrs.DataResourceMode, 8579 Type: "null_data_source", 8580 Name: "read", 8581 }.Instance(addrs.IntKey(0))) 8582 if is == nil { 8583 t.Fatal("data resource instance is not present in state; should be") 8584 } 8585 var attrs map[string]interface{} 8586 err := json.Unmarshal(is.Current.AttrsJSON, &attrs) 8587 if err != nil { 8588 t.Fatal(err) 8589 } 8590 actual := attrs["foo"] 8591 expected := "APPLIED" 8592 if actual != expected { 8593 t.Fatalf("bad:\n%s", strings.TrimSpace(state.String())) 8594 } 8595 8596 // run another plan to make sure the data source doesn't show as a change 8597 plan, diags := ctx.Plan() 8598 assertNoErrors(t, diags) 8599 8600 for _, c := range plan.Changes.Resources { 8601 if c.Action != plans.NoOp { 8602 t.Fatalf("unexpected change for %s", c.Addr) 8603 } 8604 } 8605 8606 // now we cause a change in the first resource, which should trigger a plan 8607 // in the data source, and the resource that depends on the data source 8608 // must plan a change as well. 8609 m = testModuleInline(t, map[string]string{ 8610 "main.tf": ` 8611resource "null_instance" "write" { 8612 foo = "new" 8613} 8614 8615data "null_data_source" "read" { 8616 depends_on = ["null_instance.write"] 8617} 8618 8619resource "null_instance" "depends" { 8620 foo = data.null_data_source.read.foo 8621} 8622`}) 8623 8624 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 8625 // the side effect of the resource being applied 8626 provisionerOutput = "APPLIED_AGAIN" 8627 return testApplyFn(req) 8628 } 8629 8630 ctx = testContext2(t, &ContextOpts{ 8631 Config: m, 8632 State: state, 8633 Providers: map[addrs.Provider]providers.Factory{ 8634 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 8635 }, 8636 }) 8637 8638 plan, diags = ctx.Plan() 8639 assertNoErrors(t, diags) 8640 8641 expectedChanges := map[string]plans.Action{ 8642 "null_instance.write": plans.Update, 8643 "data.null_data_source.read": plans.Read, 8644 "null_instance.depends": plans.Update, 8645 } 8646 8647 for _, c := range plan.Changes.Resources { 8648 if c.Action != expectedChanges[c.Addr.String()] { 8649 t.Errorf("unexpected %s for %s", c.Action, c.Addr) 8650 } 8651 } 8652} 8653 8654func TestContext2Apply_terraformWorkspace(t *testing.T) { 8655 m := testModule(t, "apply-terraform-workspace") 8656 p := testProvider("aws") 8657 p.PlanResourceChangeFn = testDiffFn 8658 8659 ctx := testContext2(t, &ContextOpts{ 8660 Meta: &ContextMeta{Env: "foo"}, 8661 Config: m, 8662 Providers: map[addrs.Provider]providers.Factory{ 8663 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8664 }, 8665 }) 8666 8667 if _, diags := ctx.Plan(); diags.HasErrors() { 8668 t.Fatalf("plan errors: %s", diags.Err()) 8669 } 8670 8671 state, diags := ctx.Apply() 8672 if diags.HasErrors() { 8673 t.Fatalf("diags: %s", diags.Err()) 8674 } 8675 8676 actual := state.RootModule().OutputValues["output"] 8677 expected := cty.StringVal("foo") 8678 if actual == nil || actual.Value != expected { 8679 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 8680 } 8681} 8682 8683// verify that multiple config references only create a single depends_on entry 8684func TestContext2Apply_multiRef(t *testing.T) { 8685 m := testModule(t, "apply-multi-ref") 8686 p := testProvider("aws") 8687 p.PlanResourceChangeFn = testDiffFn 8688 ctx := testContext2(t, &ContextOpts{ 8689 Config: m, 8690 Providers: map[addrs.Provider]providers.Factory{ 8691 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8692 }, 8693 }) 8694 8695 if _, diags := ctx.Plan(); diags.HasErrors() { 8696 t.Fatalf("err: %s", diags.Err()) 8697 } 8698 8699 state, diags := ctx.Apply() 8700 if diags.HasErrors() { 8701 t.Fatalf("err: %s", diags.Err()) 8702 } 8703 8704 deps := state.Modules[""].Resources["aws_instance.other"].Instances[addrs.NoKey].Current.Dependencies 8705 if len(deps) != 1 || deps[0].String() != "aws_instance.create" { 8706 t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps) 8707 } 8708} 8709 8710func TestContext2Apply_targetedModuleRecursive(t *testing.T) { 8711 m := testModule(t, "apply-targeted-module-recursive") 8712 p := testProvider("aws") 8713 p.PlanResourceChangeFn = testDiffFn 8714 p.ApplyResourceChangeFn = testApplyFn 8715 ctx := testContext2(t, &ContextOpts{ 8716 Config: m, 8717 Providers: map[addrs.Provider]providers.Factory{ 8718 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8719 }, 8720 Targets: []addrs.Targetable{ 8721 addrs.RootModuleInstance.Child("child", addrs.NoKey), 8722 }, 8723 }) 8724 8725 if _, diags := ctx.Plan(); diags.HasErrors() { 8726 t.Fatalf("err: %s", diags.Err()) 8727 } 8728 8729 state, diags := ctx.Apply() 8730 if diags.HasErrors() { 8731 t.Fatalf("err: %s", diags.Err()) 8732 } 8733 8734 mod := state.Module( 8735 addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("subchild", addrs.NoKey), 8736 ) 8737 if mod == nil { 8738 t.Fatalf("no subchild module found in the state!\n\n%#v", state) 8739 } 8740 if len(mod.Resources) != 1 { 8741 t.Fatalf("expected 1 resources, got: %#v", mod.Resources) 8742 } 8743 8744 checkStateString(t, state, ` 8745<no state> 8746module.child.subchild: 8747 aws_instance.foo: 8748 ID = foo 8749 provider = provider["registry.terraform.io/hashicorp/aws"] 8750 num = 2 8751 type = aws_instance 8752 `) 8753} 8754 8755func TestContext2Apply_localVal(t *testing.T) { 8756 m := testModule(t, "apply-local-val") 8757 ctx := testContext2(t, &ContextOpts{ 8758 Config: m, 8759 Providers: map[addrs.Provider]providers.Factory{}, 8760 }) 8761 8762 if _, diags := ctx.Plan(); diags.HasErrors() { 8763 t.Fatalf("error during plan: %s", diags.Err()) 8764 } 8765 8766 state, diags := ctx.Apply() 8767 if diags.HasErrors() { 8768 t.Fatalf("error during apply: %s", diags.Err()) 8769 } 8770 8771 got := strings.TrimSpace(state.String()) 8772 want := strings.TrimSpace(` 8773<no state> 8774Outputs: 8775 8776result_1 = hello 8777result_3 = hello world 8778`) 8779 if got != want { 8780 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 8781 } 8782} 8783 8784func TestContext2Apply_destroyWithLocals(t *testing.T) { 8785 m := testModule(t, "apply-destroy-with-locals") 8786 p := testProvider("aws") 8787 p.PlanResourceChangeFn = testDiffFn 8788 8789 state := states.NewState() 8790 root := state.EnsureModule(addrs.RootModuleInstance) 8791 root.SetResourceInstanceCurrent( 8792 mustResourceInstanceAddr("aws_instance.foo").Resource, 8793 &states.ResourceInstanceObjectSrc{ 8794 Status: states.ObjectReady, 8795 AttrsJSON: []byte(`{"id":"foo"}`), 8796 }, 8797 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8798 ) 8799 root.SetOutputValue("name", cty.StringVal("test-bar"), false) 8800 8801 ctx := testContext2(t, &ContextOpts{ 8802 Config: m, 8803 Providers: map[addrs.Provider]providers.Factory{ 8804 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8805 }, 8806 State: state, 8807 PlanMode: plans.DestroyMode, 8808 }) 8809 8810 if _, diags := ctx.Plan(); diags.HasErrors() { 8811 t.Fatalf("err: %s", diags.Err()) 8812 } 8813 8814 s, diags := ctx.Apply() 8815 if diags.HasErrors() { 8816 t.Fatalf("error during apply: %s", diags.Err()) 8817 } 8818 8819 got := strings.TrimSpace(s.String()) 8820 want := strings.TrimSpace(`<no state>`) 8821 if got != want { 8822 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 8823 } 8824} 8825 8826func TestContext2Apply_providerWithLocals(t *testing.T) { 8827 m := testModule(t, "provider-with-locals") 8828 p := testProvider("aws") 8829 8830 providerRegion := "" 8831 // this should not be overridden during destroy 8832 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 8833 val := req.Config.GetAttr("region") 8834 if !val.IsNull() { 8835 providerRegion = val.AsString() 8836 } 8837 8838 return 8839 } 8840 8841 p.PlanResourceChangeFn = testDiffFn 8842 ctx := testContext2(t, &ContextOpts{ 8843 Config: m, 8844 Providers: map[addrs.Provider]providers.Factory{ 8845 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8846 }, 8847 }) 8848 8849 if _, diags := ctx.Plan(); diags.HasErrors() { 8850 t.Fatalf("err: %s", diags.Err()) 8851 } 8852 8853 state, diags := ctx.Apply() 8854 if diags.HasErrors() { 8855 t.Fatalf("err: %s", diags.Err()) 8856 } 8857 8858 ctx = testContext2(t, &ContextOpts{ 8859 Config: m, 8860 Providers: map[addrs.Provider]providers.Factory{ 8861 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8862 }, 8863 State: state, 8864 PlanMode: plans.DestroyMode, 8865 }) 8866 8867 if _, diags := ctx.Plan(); diags.HasErrors() { 8868 t.Fatalf("err: %s", diags.Err()) 8869 } 8870 8871 state, diags = ctx.Apply() 8872 if diags.HasErrors() { 8873 t.Fatalf("err: %s", diags.Err()) 8874 } 8875 8876 if state.HasResources() { 8877 t.Fatal("expected no state, got:", state) 8878 } 8879 8880 if providerRegion != "bar" { 8881 t.Fatalf("expected region %q, got: %q", "bar", providerRegion) 8882 } 8883} 8884 8885func TestContext2Apply_destroyWithProviders(t *testing.T) { 8886 m := testModule(t, "destroy-module-with-provider") 8887 p := testProvider("aws") 8888 p.PlanResourceChangeFn = testDiffFn 8889 8890 state := states.NewState() 8891 removed := state.EnsureModule(addrs.RootModuleInstance.Child("mod", addrs.NoKey).Child("removed", addrs.NoKey)) 8892 removed.SetResourceInstanceCurrent( 8893 mustResourceInstanceAddr("aws_instance.child").Resource, 8894 &states.ResourceInstanceObjectSrc{ 8895 Status: states.ObjectReady, 8896 AttrsJSON: []byte(`{"id":"bar"}`), 8897 }, 8898 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].baz`), 8899 ) 8900 8901 ctx := testContext2(t, &ContextOpts{ 8902 Config: m, 8903 Providers: map[addrs.Provider]providers.Factory{ 8904 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8905 }, 8906 State: state, 8907 PlanMode: plans.DestroyMode, 8908 }) 8909 8910 // test that we can't destroy if the provider is missing 8911 if _, diags := ctx.Plan(); diags == nil { 8912 t.Fatal("expected plan error, provider.aws.baz doesn't exist") 8913 } 8914 8915 // correct the state 8916 state.Modules["module.mod.module.removed"].Resources["aws_instance.child"].ProviderConfig = mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].bar`) 8917 8918 ctx = testContext2(t, &ContextOpts{ 8919 Config: m, 8920 Providers: map[addrs.Provider]providers.Factory{ 8921 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 8922 }, 8923 State: state, 8924 PlanMode: plans.DestroyMode, 8925 }) 8926 8927 if _, diags := ctx.Plan(); diags.HasErrors() { 8928 t.Fatal(diags.Err()) 8929 } 8930 state, diags := ctx.Apply() 8931 if diags.HasErrors() { 8932 t.Fatalf("error during apply: %s", diags.Err()) 8933 } 8934 8935 got := strings.TrimSpace(state.String()) 8936 8937 want := strings.TrimSpace("<no state>") 8938 if got != want { 8939 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 8940 } 8941} 8942 8943func TestContext2Apply_providersFromState(t *testing.T) { 8944 m := configs.NewEmptyConfig() 8945 p := testProvider("aws") 8946 p.PlanResourceChangeFn = testDiffFn 8947 8948 implicitProviderState := states.NewState() 8949 impRoot := implicitProviderState.EnsureModule(addrs.RootModuleInstance) 8950 impRoot.SetResourceInstanceCurrent( 8951 mustResourceInstanceAddr("aws_instance.a").Resource, 8952 &states.ResourceInstanceObjectSrc{ 8953 Status: states.ObjectReady, 8954 AttrsJSON: []byte(`{"id":"bar"}`), 8955 }, 8956 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 8957 ) 8958 8959 aliasedProviderState := states.NewState() 8960 aliasRoot := aliasedProviderState.EnsureModule(addrs.RootModuleInstance) 8961 aliasRoot.SetResourceInstanceCurrent( 8962 mustResourceInstanceAddr("aws_instance.a").Resource, 8963 &states.ResourceInstanceObjectSrc{ 8964 Status: states.ObjectReady, 8965 AttrsJSON: []byte(`{"id":"bar"}`), 8966 }, 8967 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].bar`), 8968 ) 8969 8970 moduleProviderState := states.NewState() 8971 moduleProviderRoot := moduleProviderState.EnsureModule(addrs.RootModuleInstance) 8972 moduleProviderRoot.SetResourceInstanceCurrent( 8973 mustResourceInstanceAddr("aws_instance.a").Resource, 8974 &states.ResourceInstanceObjectSrc{ 8975 Status: states.ObjectReady, 8976 AttrsJSON: []byte(`{"id":"bar"}`), 8977 }, 8978 mustProviderConfig(`module.child.provider["registry.terraform.io/hashicorp/aws"]`), 8979 ) 8980 8981 for _, tc := range []struct { 8982 name string 8983 state *states.State 8984 output string 8985 err bool 8986 }{ 8987 { 8988 name: "add implicit provider", 8989 state: implicitProviderState, 8990 err: false, 8991 output: "<no state>", 8992 }, 8993 8994 // an aliased provider must be in the config to remove a resource 8995 { 8996 name: "add aliased provider", 8997 state: aliasedProviderState, 8998 err: true, 8999 }, 9000 9001 // a provider in a module implies some sort of config, so this isn't 9002 // allowed even without an alias 9003 { 9004 name: "add unaliased module provider", 9005 state: moduleProviderState, 9006 err: true, 9007 }, 9008 } { 9009 t.Run(tc.name, func(t *testing.T) { 9010 ctx := testContext2(t, &ContextOpts{ 9011 Config: m, 9012 Providers: map[addrs.Provider]providers.Factory{ 9013 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 9014 }, 9015 State: tc.state, 9016 }) 9017 9018 _, diags := ctx.Plan() 9019 if tc.err { 9020 if diags == nil { 9021 t.Fatal("expected error") 9022 } else { 9023 return 9024 } 9025 } 9026 if !tc.err && diags.HasErrors() { 9027 t.Fatal(diags.Err()) 9028 } 9029 9030 state, diags := ctx.Apply() 9031 if diags.HasErrors() { 9032 t.Fatalf("diags: %s", diags.Err()) 9033 } 9034 9035 checkStateString(t, state, "<no state>") 9036 9037 }) 9038 } 9039} 9040 9041func TestContext2Apply_plannedInterpolatedCount(t *testing.T) { 9042 m, snap := testModuleWithSnapshot(t, "apply-interpolated-count") 9043 9044 p := testProvider("aws") 9045 p.PlanResourceChangeFn = testDiffFn 9046 9047 Providers := map[addrs.Provider]providers.Factory{ 9048 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 9049 } 9050 9051 state := states.NewState() 9052 root := state.EnsureModule(addrs.RootModuleInstance) 9053 root.SetResourceInstanceCurrent( 9054 mustResourceInstanceAddr("aws_instance.test").Resource, 9055 &states.ResourceInstanceObjectSrc{ 9056 Status: states.ObjectReady, 9057 AttrsJSON: []byte(`{"id":"foo"}`), 9058 }, 9059 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 9060 ) 9061 9062 ctx := testContext2(t, &ContextOpts{ 9063 Config: m, 9064 Providers: Providers, 9065 State: state, 9066 }) 9067 9068 plan, diags := ctx.Plan() 9069 if diags.HasErrors() { 9070 t.Fatalf("plan failed: %s", diags.Err()) 9071 } 9072 9073 // We'll marshal and unmarshal the plan here, to ensure that we have 9074 // a clean new context as would be created if we separately ran 9075 // terraform plan -out=tfplan && terraform apply tfplan 9076 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 9077 if err != nil { 9078 t.Fatalf("failed to round-trip through planfile: %s", err) 9079 } 9080 9081 ctxOpts.Providers = Providers 9082 ctx, diags = NewContext(ctxOpts) 9083 if diags.HasErrors() { 9084 t.Fatalf("err: %s", diags.Err()) 9085 } 9086 9087 // Applying the plan should now succeed 9088 _, diags = ctx.Apply() 9089 if diags.HasErrors() { 9090 t.Fatalf("apply failed: %s", diags.Err()) 9091 } 9092} 9093 9094func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) { 9095 m, snap := testModuleWithSnapshot(t, "plan-destroy-interpolated-count") 9096 9097 p := testProvider("aws") 9098 p.PlanResourceChangeFn = testDiffFn 9099 providers := map[addrs.Provider]providers.Factory{ 9100 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 9101 } 9102 9103 state := states.NewState() 9104 root := state.EnsureModule(addrs.RootModuleInstance) 9105 root.SetResourceInstanceCurrent( 9106 mustResourceInstanceAddr("aws_instance.a[0]").Resource, 9107 &states.ResourceInstanceObjectSrc{ 9108 Status: states.ObjectReady, 9109 AttrsJSON: []byte(`{"id":"foo"}`), 9110 }, 9111 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 9112 ) 9113 root.SetResourceInstanceCurrent( 9114 mustResourceInstanceAddr("aws_instance.a[1]").Resource, 9115 &states.ResourceInstanceObjectSrc{ 9116 Status: states.ObjectReady, 9117 AttrsJSON: []byte(`{"id":"foo"}`), 9118 }, 9119 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 9120 ) 9121 root.SetOutputValue("out", cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("foo")}), false) 9122 9123 ctx := testContext2(t, &ContextOpts{ 9124 Config: m, 9125 Providers: providers, 9126 State: state, 9127 PlanMode: plans.DestroyMode, 9128 }) 9129 9130 plan, diags := ctx.Plan() 9131 if diags.HasErrors() { 9132 t.Fatalf("plan failed: %s", diags.Err()) 9133 } 9134 9135 // We'll marshal and unmarshal the plan here, to ensure that we have 9136 // a clean new context as would be created if we separately ran 9137 // terraform plan -out=tfplan && terraform apply tfplan 9138 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 9139 if err != nil { 9140 t.Fatalf("failed to round-trip through planfile: %s", err) 9141 } 9142 9143 ctxOpts.Providers = providers 9144 ctx, diags = NewContext(ctxOpts) 9145 if diags.HasErrors() { 9146 t.Fatalf("err: %s", diags.Err()) 9147 } 9148 9149 // Applying the plan should now succeed 9150 state, diags = ctx.Apply() 9151 if diags.HasErrors() { 9152 t.Fatalf("apply failed: %s", diags.Err()) 9153 } 9154 if !state.Empty() { 9155 t.Fatalf("state not empty: %s\n", state) 9156 } 9157} 9158 9159func TestContext2Apply_scaleInMultivarRef(t *testing.T) { 9160 m := testModule(t, "apply-resource-scale-in") 9161 9162 p := testProvider("aws") 9163 p.PlanResourceChangeFn = testDiffFn 9164 9165 Providers := map[addrs.Provider]providers.Factory{ 9166 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 9167 } 9168 9169 state := states.NewState() 9170 root := state.EnsureModule(addrs.RootModuleInstance) 9171 root.SetResourceInstanceCurrent( 9172 mustResourceInstanceAddr("aws_instance.one").Resource, 9173 &states.ResourceInstanceObjectSrc{ 9174 Status: states.ObjectReady, 9175 AttrsJSON: []byte(`{"id":"foo"}`), 9176 }, 9177 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 9178 ) 9179 root.SetResourceInstanceCurrent( 9180 mustResourceInstanceAddr("aws_instance.two").Resource, 9181 &states.ResourceInstanceObjectSrc{ 9182 Status: states.ObjectReady, 9183 AttrsJSON: []byte(`{"id":"foo"}`), 9184 }, 9185 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 9186 ) 9187 9188 ctx := testContext2(t, &ContextOpts{ 9189 Config: m, 9190 Providers: Providers, 9191 State: state, 9192 Variables: InputValues{ 9193 "instance_count": { 9194 Value: cty.NumberIntVal(0), 9195 SourceType: ValueFromCaller, 9196 }, 9197 }, 9198 }) 9199 9200 _, diags := ctx.Plan() 9201 assertNoErrors(t, diags) 9202 9203 // Applying the plan should now succeed 9204 _, diags = ctx.Apply() 9205 assertNoErrors(t, diags) 9206} 9207 9208func TestContext2Apply_inconsistentWithPlan(t *testing.T) { 9209 m := testModule(t, "apply-inconsistent-with-plan") 9210 p := testProvider("test") 9211 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9212 ResourceTypes: map[string]*configschema.Block{ 9213 "test": { 9214 Attributes: map[string]*configschema.Attribute{ 9215 "id": {Type: cty.String, Computed: true}, 9216 }, 9217 }, 9218 }, 9219 }) 9220 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 9221 return providers.PlanResourceChangeResponse{ 9222 PlannedState: cty.ObjectVal(map[string]cty.Value{ 9223 "id": cty.StringVal("before"), 9224 }), 9225 } 9226 } 9227 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 9228 return providers.ApplyResourceChangeResponse{ 9229 NewState: cty.ObjectVal(map[string]cty.Value{ 9230 // This is intentionally incorrect: because id was fixed at "before" 9231 // during plan, it must not change during apply. 9232 "id": cty.StringVal("after"), 9233 }), 9234 } 9235 } 9236 ctx := testContext2(t, &ContextOpts{ 9237 Config: m, 9238 Providers: map[addrs.Provider]providers.Factory{ 9239 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9240 }, 9241 }) 9242 9243 if _, diags := ctx.Plan(); diags.HasErrors() { 9244 t.Fatalf("plan errors: %s", diags.Err()) 9245 } 9246 9247 _, diags := ctx.Apply() 9248 if !diags.HasErrors() { 9249 t.Fatalf("apply succeeded; want error") 9250 } 9251 if got, want := diags.Err().Error(), "Provider produced inconsistent result after apply"; !strings.Contains(got, want) { 9252 t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want) 9253 } 9254} 9255 9256// Issue 19908 was about retaining an existing object in the state when an 9257// update to it fails and the provider does not return a partially-updated 9258// value for it. Previously we were incorrectly removing it from the state 9259// in that case, but instead it should be retained so the update can be 9260// retried. 9261func TestContext2Apply_issue19908(t *testing.T) { 9262 m := testModule(t, "apply-issue19908") 9263 p := testProvider("test") 9264 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9265 ResourceTypes: map[string]*configschema.Block{ 9266 "test": { 9267 Attributes: map[string]*configschema.Attribute{ 9268 "baz": {Type: cty.String, Required: true}, 9269 }, 9270 }, 9271 }, 9272 }) 9273 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 9274 return providers.PlanResourceChangeResponse{ 9275 PlannedState: req.ProposedNewState, 9276 } 9277 } 9278 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 9279 var diags tfdiags.Diagnostics 9280 diags = diags.Append(fmt.Errorf("update failed")) 9281 return providers.ApplyResourceChangeResponse{ 9282 Diagnostics: diags, 9283 } 9284 } 9285 ctx := testContext2(t, &ContextOpts{ 9286 Config: m, 9287 State: states.BuildState(func(s *states.SyncState) { 9288 s.SetResourceInstanceCurrent( 9289 addrs.Resource{ 9290 Mode: addrs.ManagedResourceMode, 9291 Type: "test", 9292 Name: "foo", 9293 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 9294 &states.ResourceInstanceObjectSrc{ 9295 AttrsJSON: []byte(`{"baz":"old"}`), 9296 Status: states.ObjectReady, 9297 }, 9298 addrs.AbsProviderConfig{ 9299 Provider: addrs.NewDefaultProvider("test"), 9300 Module: addrs.RootModule, 9301 }, 9302 ) 9303 }), 9304 Providers: map[addrs.Provider]providers.Factory{ 9305 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9306 }, 9307 }) 9308 9309 if _, diags := ctx.Plan(); diags.HasErrors() { 9310 t.Fatalf("plan errors: %s", diags.Err()) 9311 } 9312 9313 state, diags := ctx.Apply() 9314 if !diags.HasErrors() { 9315 t.Fatalf("apply succeeded; want error") 9316 } 9317 if got, want := diags.Err().Error(), "update failed"; !strings.Contains(got, want) { 9318 t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want) 9319 } 9320 9321 mod := state.RootModule() 9322 rs := mod.Resources["test.foo"] 9323 if rs == nil { 9324 t.Fatalf("test.foo not in state after apply, but should be") 9325 } 9326 is := rs.Instances[addrs.NoKey] 9327 if is == nil { 9328 t.Fatalf("test.foo not in state after apply, but should be") 9329 } 9330 obj := is.Current 9331 if obj == nil { 9332 t.Fatalf("test.foo has no current object in state after apply, but should do") 9333 } 9334 9335 if got, want := obj.Status, states.ObjectReady; got != want { 9336 t.Errorf("test.foo has wrong status %s after apply; want %s", got, want) 9337 } 9338 if got, want := obj.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) { 9339 t.Errorf("test.foo attributes JSON doesn't contain %s after apply\ngot: %s", want, got) 9340 } 9341} 9342 9343func TestContext2Apply_invalidIndexRef(t *testing.T) { 9344 p := testProvider("test") 9345 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9346 ResourceTypes: map[string]*configschema.Block{ 9347 "test_instance": { 9348 Attributes: map[string]*configschema.Attribute{ 9349 "value": {Type: cty.String, Optional: true, Computed: true}, 9350 }, 9351 }, 9352 }, 9353 }) 9354 p.PlanResourceChangeFn = testDiffFn 9355 9356 m := testModule(t, "apply-invalid-index") 9357 c := testContext2(t, &ContextOpts{ 9358 Config: m, 9359 Providers: map[addrs.Provider]providers.Factory{ 9360 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9361 }, 9362 }) 9363 diags := c.Validate() 9364 if diags.HasErrors() { 9365 t.Fatalf("unexpected validation failure: %s", diags.Err()) 9366 } 9367 9368 wantErr := `The given key does not identify an element in this collection value` 9369 _, diags = c.Plan() 9370 9371 if !diags.HasErrors() { 9372 t.Fatalf("plan succeeded; want error") 9373 } 9374 gotErr := diags.Err().Error() 9375 9376 if !strings.Contains(gotErr, wantErr) { 9377 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErr, wantErr) 9378 } 9379} 9380 9381func TestContext2Apply_moduleReplaceCycle(t *testing.T) { 9382 for _, mode := range []string{"normal", "cbd"} { 9383 var m *configs.Config 9384 9385 switch mode { 9386 case "normal": 9387 m = testModule(t, "apply-module-replace-cycle") 9388 case "cbd": 9389 m = testModule(t, "apply-module-replace-cycle-cbd") 9390 } 9391 9392 p := testProvider("aws") 9393 p.PlanResourceChangeFn = testDiffFn 9394 9395 instanceSchema := &configschema.Block{ 9396 Attributes: map[string]*configschema.Attribute{ 9397 "id": {Type: cty.String, Computed: true}, 9398 "require_new": {Type: cty.String, Optional: true}, 9399 }, 9400 } 9401 9402 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9403 ResourceTypes: map[string]*configschema.Block{ 9404 "aws_instance": instanceSchema, 9405 }, 9406 }) 9407 9408 state := states.NewState() 9409 modA := state.EnsureModule(addrs.RootModuleInstance.Child("a", addrs.NoKey)) 9410 modA.SetResourceInstanceCurrent( 9411 addrs.Resource{ 9412 Mode: addrs.ManagedResourceMode, 9413 Type: "aws_instance", 9414 Name: "a", 9415 }.Instance(addrs.NoKey), 9416 &states.ResourceInstanceObjectSrc{ 9417 Status: states.ObjectReady, 9418 AttrsJSON: []byte(`{"id":"a","require_new":"old"}`), 9419 CreateBeforeDestroy: mode == "cbd", 9420 }, 9421 addrs.AbsProviderConfig{ 9422 Provider: addrs.NewDefaultProvider("aws"), 9423 Module: addrs.RootModule, 9424 }, 9425 ) 9426 9427 modB := state.EnsureModule(addrs.RootModuleInstance.Child("b", addrs.NoKey)) 9428 modB.SetResourceInstanceCurrent( 9429 addrs.Resource{ 9430 Mode: addrs.ManagedResourceMode, 9431 Type: "aws_instance", 9432 Name: "b", 9433 }.Instance(addrs.IntKey(0)), 9434 &states.ResourceInstanceObjectSrc{ 9435 Status: states.ObjectReady, 9436 AttrsJSON: []byte(`{"id":"b","require_new":"old"}`), 9437 }, 9438 addrs.AbsProviderConfig{ 9439 Provider: addrs.NewDefaultProvider("aws"), 9440 Module: addrs.RootModule, 9441 }, 9442 ) 9443 9444 aBefore, _ := plans.NewDynamicValue( 9445 cty.ObjectVal(map[string]cty.Value{ 9446 "id": cty.StringVal("a"), 9447 "require_new": cty.StringVal("old"), 9448 }), instanceSchema.ImpliedType()) 9449 aAfter, _ := plans.NewDynamicValue( 9450 cty.ObjectVal(map[string]cty.Value{ 9451 "id": cty.UnknownVal(cty.String), 9452 "require_new": cty.StringVal("new"), 9453 }), instanceSchema.ImpliedType()) 9454 bBefore, _ := plans.NewDynamicValue( 9455 cty.ObjectVal(map[string]cty.Value{ 9456 "id": cty.StringVal("b"), 9457 "require_new": cty.StringVal("old"), 9458 }), instanceSchema.ImpliedType()) 9459 bAfter, _ := plans.NewDynamicValue( 9460 cty.ObjectVal(map[string]cty.Value{ 9461 "id": cty.UnknownVal(cty.String), 9462 "require_new": cty.UnknownVal(cty.String), 9463 }), instanceSchema.ImpliedType()) 9464 9465 var aAction plans.Action 9466 switch mode { 9467 case "normal": 9468 aAction = plans.DeleteThenCreate 9469 case "cbd": 9470 aAction = plans.CreateThenDelete 9471 } 9472 9473 changes := &plans.Changes{ 9474 Resources: []*plans.ResourceInstanceChangeSrc{ 9475 { 9476 Addr: addrs.Resource{ 9477 Mode: addrs.ManagedResourceMode, 9478 Type: "aws_instance", 9479 Name: "a", 9480 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("a", addrs.NoKey)), 9481 ProviderAddr: addrs.AbsProviderConfig{ 9482 Provider: addrs.NewDefaultProvider("aws"), 9483 Module: addrs.RootModule, 9484 }, 9485 ChangeSrc: plans.ChangeSrc{ 9486 Action: aAction, 9487 Before: aBefore, 9488 After: aAfter, 9489 }, 9490 }, 9491 { 9492 Addr: addrs.Resource{ 9493 Mode: addrs.ManagedResourceMode, 9494 Type: "aws_instance", 9495 Name: "b", 9496 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("b", addrs.NoKey)), 9497 ProviderAddr: addrs.AbsProviderConfig{ 9498 Provider: addrs.NewDefaultProvider("aws"), 9499 Module: addrs.RootModule, 9500 }, 9501 ChangeSrc: plans.ChangeSrc{ 9502 Action: plans.DeleteThenCreate, 9503 Before: bBefore, 9504 After: bAfter, 9505 }, 9506 }, 9507 }, 9508 } 9509 9510 ctx := testContext2(t, &ContextOpts{ 9511 Config: m, 9512 Providers: map[addrs.Provider]providers.Factory{ 9513 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 9514 }, 9515 State: state, 9516 Changes: changes, 9517 }) 9518 9519 t.Run(mode, func(t *testing.T) { 9520 _, diags := ctx.Apply() 9521 if diags.HasErrors() { 9522 t.Fatal(diags.Err()) 9523 } 9524 }) 9525 } 9526} 9527 9528func TestContext2Apply_destroyDataCycle(t *testing.T) { 9529 m, snap := testModuleWithSnapshot(t, "apply-destroy-data-cycle") 9530 p := testProvider("null") 9531 p.PlanResourceChangeFn = testDiffFn 9532 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 9533 return providers.ReadDataSourceResponse{ 9534 State: cty.ObjectVal(map[string]cty.Value{ 9535 "id": cty.StringVal("new"), 9536 "foo": cty.NullVal(cty.String), 9537 }), 9538 } 9539 } 9540 9541 tp := testProvider("test") 9542 tp.PlanResourceChangeFn = testDiffFn 9543 9544 state := states.NewState() 9545 root := state.EnsureModule(addrs.RootModuleInstance) 9546 root.SetResourceInstanceCurrent( 9547 addrs.Resource{ 9548 Mode: addrs.ManagedResourceMode, 9549 Type: "null_resource", 9550 Name: "a", 9551 }.Instance(addrs.IntKey(0)), 9552 &states.ResourceInstanceObjectSrc{ 9553 Status: states.ObjectReady, 9554 AttrsJSON: []byte(`{"id":"a"}`), 9555 }, 9556 addrs.AbsProviderConfig{ 9557 Provider: addrs.NewDefaultProvider("null"), 9558 Module: addrs.RootModule, 9559 }, 9560 ) 9561 root.SetResourceInstanceCurrent( 9562 addrs.Resource{ 9563 Mode: addrs.ManagedResourceMode, 9564 Type: "test_resource", 9565 Name: "a", 9566 }.Instance(addrs.IntKey(0)), 9567 &states.ResourceInstanceObjectSrc{ 9568 Status: states.ObjectReady, 9569 AttrsJSON: []byte(`{"id":"a"}`), 9570 Dependencies: []addrs.ConfigResource{ 9571 addrs.ConfigResource{ 9572 Resource: addrs.Resource{ 9573 Mode: addrs.DataResourceMode, 9574 Type: "null_data_source", 9575 Name: "d", 9576 }, 9577 Module: addrs.RootModule, 9578 }, 9579 }, 9580 }, 9581 addrs.AbsProviderConfig{ 9582 Provider: addrs.NewDefaultProvider("test"), 9583 Module: addrs.RootModule, 9584 }, 9585 ) 9586 root.SetResourceInstanceCurrent( 9587 addrs.Resource{ 9588 Mode: addrs.DataResourceMode, 9589 Type: "null_data_source", 9590 Name: "d", 9591 }.Instance(addrs.NoKey), 9592 &states.ResourceInstanceObjectSrc{ 9593 Status: states.ObjectReady, 9594 AttrsJSON: []byte(`{"id":"old"}`), 9595 }, 9596 addrs.AbsProviderConfig{ 9597 Provider: addrs.NewDefaultProvider("null"), 9598 Module: addrs.RootModule, 9599 }, 9600 ) 9601 9602 Providers := map[addrs.Provider]providers.Factory{ 9603 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 9604 addrs.NewDefaultProvider("test"): testProviderFuncFixed(tp), 9605 } 9606 9607 ctx := testContext2(t, &ContextOpts{ 9608 Config: m, 9609 Providers: Providers, 9610 State: state, 9611 PlanMode: plans.DestroyMode, 9612 }) 9613 9614 plan, diags := ctx.Plan() 9615 diags.HasErrors() 9616 if diags.HasErrors() { 9617 t.Fatalf("diags: %s", diags.Err()) 9618 } 9619 9620 // We'll marshal and unmarshal the plan here, to ensure that we have 9621 // a clean new context as would be created if we separately ran 9622 // terraform plan -out=tfplan && terraform apply tfplan 9623 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 9624 if err != nil { 9625 t.Fatal(err) 9626 } 9627 ctxOpts.Providers = Providers 9628 ctx, diags = NewContext(ctxOpts) 9629 if diags.HasErrors() { 9630 t.Fatalf("failed to create context for plan: %s", diags.Err()) 9631 } 9632 9633 tp.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 9634 foo := req.Config.GetAttr("foo") 9635 if !foo.IsKnown() { 9636 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown config value foo")) 9637 return resp 9638 } 9639 9640 if foo.AsString() != "new" { 9641 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("wrong config value: %q", foo.AsString())) 9642 } 9643 return resp 9644 } 9645 9646 _, diags = ctx.Apply() 9647 if diags.HasErrors() { 9648 t.Fatalf("diags: %s", diags.Err()) 9649 } 9650} 9651 9652func TestContext2Apply_taintedDestroyFailure(t *testing.T) { 9653 m := testModule(t, "apply-destroy-tainted") 9654 p := testProvider("test") 9655 p.PlanResourceChangeFn = testDiffFn 9656 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 9657 // All destroys fail. 9658 if req.PlannedState.IsNull() { 9659 resp.Diagnostics = resp.Diagnostics.Append(errors.New("failure")) 9660 return 9661 } 9662 9663 // c will also fail to create, meaning the existing tainted instance 9664 // becomes deposed, ans is then promoted back to current. 9665 // only C has a foo attribute 9666 planned := req.PlannedState.AsValueMap() 9667 foo, ok := planned["foo"] 9668 if ok && !foo.IsNull() && foo.AsString() == "c" { 9669 resp.Diagnostics = resp.Diagnostics.Append(errors.New("failure")) 9670 return 9671 } 9672 9673 return testApplyFn(req) 9674 } 9675 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 9676 ResourceTypes: map[string]*configschema.Block{ 9677 "test_instance": { 9678 Attributes: map[string]*configschema.Attribute{ 9679 "id": { 9680 Type: cty.String, 9681 Computed: true, 9682 }, 9683 "foo": { 9684 Type: cty.String, 9685 Optional: true, 9686 }, 9687 }, 9688 }, 9689 }, 9690 }) 9691 9692 state := states.NewState() 9693 root := state.EnsureModule(addrs.RootModuleInstance) 9694 root.SetResourceInstanceCurrent( 9695 addrs.Resource{ 9696 Mode: addrs.ManagedResourceMode, 9697 Type: "test_instance", 9698 Name: "a", 9699 }.Instance(addrs.NoKey), 9700 &states.ResourceInstanceObjectSrc{ 9701 Status: states.ObjectTainted, 9702 AttrsJSON: []byte(`{"id":"a","foo":"a"}`), 9703 }, 9704 addrs.AbsProviderConfig{ 9705 Provider: addrs.NewDefaultProvider("test"), 9706 Module: addrs.RootModule, 9707 }, 9708 ) 9709 root.SetResourceInstanceCurrent( 9710 addrs.Resource{ 9711 Mode: addrs.ManagedResourceMode, 9712 Type: "test_instance", 9713 Name: "b", 9714 }.Instance(addrs.NoKey), 9715 &states.ResourceInstanceObjectSrc{ 9716 Status: states.ObjectTainted, 9717 AttrsJSON: []byte(`{"id":"b","foo":"b"}`), 9718 }, 9719 addrs.AbsProviderConfig{ 9720 Provider: addrs.NewDefaultProvider("test"), 9721 Module: addrs.RootModule, 9722 }, 9723 ) 9724 root.SetResourceInstanceCurrent( 9725 addrs.Resource{ 9726 Mode: addrs.ManagedResourceMode, 9727 Type: "test_instance", 9728 Name: "c", 9729 }.Instance(addrs.NoKey), 9730 &states.ResourceInstanceObjectSrc{ 9731 Status: states.ObjectTainted, 9732 AttrsJSON: []byte(`{"id":"c","foo":"old"}`), 9733 }, 9734 addrs.AbsProviderConfig{ 9735 Provider: addrs.NewDefaultProvider("test"), 9736 Module: addrs.RootModule, 9737 }, 9738 ) 9739 9740 Providers := map[addrs.Provider]providers.Factory{ 9741 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9742 } 9743 9744 ctx := testContext2(t, &ContextOpts{ 9745 Config: m, 9746 Providers: Providers, 9747 State: state, 9748 Hooks: []Hook{&testHook{}}, 9749 }) 9750 9751 _, diags := ctx.Plan() 9752 diags.HasErrors() 9753 if diags.HasErrors() { 9754 t.Fatalf("diags: %s", diags.Err()) 9755 } 9756 9757 state, diags = ctx.Apply() 9758 if !diags.HasErrors() { 9759 t.Fatal("expected error") 9760 } 9761 9762 root = state.Module(addrs.RootModuleInstance) 9763 9764 // the instance that failed to destroy should remain tainted 9765 a := root.ResourceInstance(addrs.Resource{ 9766 Mode: addrs.ManagedResourceMode, 9767 Type: "test_instance", 9768 Name: "a", 9769 }.Instance(addrs.NoKey)) 9770 9771 if a.Current.Status != states.ObjectTainted { 9772 t.Fatal("test_instance.a should be tainted") 9773 } 9774 9775 // b is create_before_destroy, and the destroy failed, so there should be 1 9776 // deposed instance. 9777 b := root.ResourceInstance(addrs.Resource{ 9778 Mode: addrs.ManagedResourceMode, 9779 Type: "test_instance", 9780 Name: "b", 9781 }.Instance(addrs.NoKey)) 9782 9783 if b.Current.Status != states.ObjectReady { 9784 t.Fatal("test_instance.b should be Ready") 9785 } 9786 9787 if len(b.Deposed) != 1 { 9788 t.Fatal("test_instance.b failed to keep deposed instance") 9789 } 9790 9791 // the desposed c instance should be promoted back to Current, and remain 9792 // tainted 9793 c := root.ResourceInstance(addrs.Resource{ 9794 Mode: addrs.ManagedResourceMode, 9795 Type: "test_instance", 9796 Name: "c", 9797 }.Instance(addrs.NoKey)) 9798 9799 if c.Current == nil { 9800 t.Fatal("test_instance.c has no current instance, but it should") 9801 } 9802 9803 if c.Current.Status != states.ObjectTainted { 9804 t.Fatal("test_instance.c should be tainted") 9805 } 9806 9807 if len(c.Deposed) != 0 { 9808 t.Fatal("test_instance.c should have no deposed instances") 9809 } 9810 9811 if string(c.Current.AttrsJSON) != `{"foo":"old","id":"c"}` { 9812 t.Fatalf("unexpected attrs for c: %q\n", c.Current.AttrsJSON) 9813 } 9814} 9815 9816func TestContext2Apply_plannedConnectionRefs(t *testing.T) { 9817 m := testModule(t, "apply-plan-connection-refs") 9818 p := testProvider("test") 9819 p.PlanResourceChangeFn = testDiffFn 9820 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 9821 s := req.PlannedState.AsValueMap() 9822 // delay "a" slightly, so if the reference edge is missing the "b" 9823 // provisioner will see an unknown value. 9824 if s["foo"].AsString() == "a" { 9825 time.Sleep(500 * time.Millisecond) 9826 } 9827 9828 s["id"] = cty.StringVal("ID") 9829 if ty, ok := s["type"]; ok && !ty.IsKnown() { 9830 s["type"] = cty.StringVal(req.TypeName) 9831 } 9832 resp.NewState = cty.ObjectVal(s) 9833 return resp 9834 } 9835 9836 provisionerFactory := func() (provisioners.Interface, error) { 9837 pr := testProvisioner() 9838 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 9839 host := req.Connection.GetAttr("host") 9840 if host.IsNull() || !host.IsKnown() { 9841 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("invalid host value: %#v", host)) 9842 } 9843 9844 return resp 9845 } 9846 return pr, nil 9847 } 9848 9849 Providers := map[addrs.Provider]providers.Factory{ 9850 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9851 } 9852 9853 provisioners := map[string]provisioners.Factory{ 9854 "shell": provisionerFactory, 9855 } 9856 9857 hook := &testHook{} 9858 ctx := testContext2(t, &ContextOpts{ 9859 Config: m, 9860 Providers: Providers, 9861 Provisioners: provisioners, 9862 Hooks: []Hook{hook}, 9863 }) 9864 9865 _, diags := ctx.Plan() 9866 diags.HasErrors() 9867 if diags.HasErrors() { 9868 t.Fatalf("diags: %s", diags.Err()) 9869 } 9870 9871 _, diags = ctx.Apply() 9872 if diags.HasErrors() { 9873 t.Fatalf("diags: %s", diags.Err()) 9874 } 9875} 9876 9877func TestContext2Apply_cbdCycle(t *testing.T) { 9878 m, snap := testModuleWithSnapshot(t, "apply-cbd-cycle") 9879 p := testProvider("test") 9880 p.PlanResourceChangeFn = testDiffFn 9881 9882 state := states.NewState() 9883 root := state.EnsureModule(addrs.RootModuleInstance) 9884 root.SetResourceInstanceCurrent( 9885 addrs.Resource{ 9886 Mode: addrs.ManagedResourceMode, 9887 Type: "test_instance", 9888 Name: "a", 9889 }.Instance(addrs.NoKey), 9890 &states.ResourceInstanceObjectSrc{ 9891 Status: states.ObjectReady, 9892 AttrsJSON: []byte(`{"id":"a","require_new":"old","foo":"b"}`), 9893 Dependencies: []addrs.ConfigResource{ 9894 addrs.ConfigResource{ 9895 Resource: addrs.Resource{ 9896 Mode: addrs.ManagedResourceMode, 9897 Type: "test_instance", 9898 Name: "b", 9899 }, 9900 Module: addrs.RootModule, 9901 }, 9902 addrs.ConfigResource{ 9903 Resource: addrs.Resource{ 9904 Mode: addrs.ManagedResourceMode, 9905 Type: "test_instance", 9906 Name: "c", 9907 }, 9908 Module: addrs.RootModule, 9909 }, 9910 }, 9911 }, 9912 addrs.AbsProviderConfig{ 9913 Provider: addrs.NewDefaultProvider("test"), 9914 Module: addrs.RootModule, 9915 }, 9916 ) 9917 root.SetResourceInstanceCurrent( 9918 addrs.Resource{ 9919 Mode: addrs.ManagedResourceMode, 9920 Type: "test_instance", 9921 Name: "b", 9922 }.Instance(addrs.NoKey), 9923 &states.ResourceInstanceObjectSrc{ 9924 Status: states.ObjectReady, 9925 AttrsJSON: []byte(`{"id":"b","require_new":"old","foo":"c"}`), 9926 Dependencies: []addrs.ConfigResource{ 9927 addrs.ConfigResource{ 9928 Resource: addrs.Resource{ 9929 Mode: addrs.ManagedResourceMode, 9930 Type: "test_instance", 9931 Name: "c", 9932 }, 9933 Module: addrs.RootModule, 9934 }, 9935 }, 9936 }, 9937 addrs.AbsProviderConfig{ 9938 Provider: addrs.NewDefaultProvider("test"), 9939 Module: addrs.RootModule, 9940 }, 9941 ) 9942 root.SetResourceInstanceCurrent( 9943 addrs.Resource{ 9944 Mode: addrs.ManagedResourceMode, 9945 Type: "test_instance", 9946 Name: "c", 9947 }.Instance(addrs.NoKey), 9948 &states.ResourceInstanceObjectSrc{ 9949 Status: states.ObjectReady, 9950 AttrsJSON: []byte(`{"id":"c","require_new":"old"}`), 9951 }, 9952 addrs.AbsProviderConfig{ 9953 Provider: addrs.NewDefaultProvider("test"), 9954 Module: addrs.RootModule, 9955 }, 9956 ) 9957 9958 Providers := map[addrs.Provider]providers.Factory{ 9959 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 9960 } 9961 9962 hook := &testHook{} 9963 ctx := testContext2(t, &ContextOpts{ 9964 Config: m, 9965 Providers: Providers, 9966 State: state, 9967 Hooks: []Hook{hook}, 9968 }) 9969 9970 plan, diags := ctx.Plan() 9971 diags.HasErrors() 9972 if diags.HasErrors() { 9973 t.Fatalf("diags: %s", diags.Err()) 9974 } 9975 9976 // We'll marshal and unmarshal the plan here, to ensure that we have 9977 // a clean new context as would be created if we separately ran 9978 // terraform plan -out=tfplan && terraform apply tfplan 9979 ctxOpts, err := contextOptsForPlanViaFile(snap, plan) 9980 if err != nil { 9981 t.Fatal(err) 9982 } 9983 ctxOpts.Providers = Providers 9984 ctx, diags = NewContext(ctxOpts) 9985 if diags.HasErrors() { 9986 t.Fatalf("failed to create context for plan: %s", diags.Err()) 9987 } 9988 9989 _, diags = ctx.Apply() 9990 if diags.HasErrors() { 9991 t.Fatalf("diags: %s", diags.Err()) 9992 } 9993} 9994 9995func TestContext2Apply_ProviderMeta_apply_set(t *testing.T) { 9996 m := testModule(t, "provider-meta-set") 9997 p := testProvider("test") 9998 p.PlanResourceChangeFn = testDiffFn 9999 schema := p.ProviderSchema() 10000 schema.ProviderMeta = &configschema.Block{ 10001 Attributes: map[string]*configschema.Attribute{ 10002 "baz": { 10003 Type: cty.String, 10004 Required: true, 10005 }, 10006 }, 10007 } 10008 10009 var pmMu sync.Mutex 10010 arcPMs := map[string]cty.Value{} 10011 10012 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 10013 pmMu.Lock() 10014 defer pmMu.Unlock() 10015 arcPMs[req.TypeName] = req.ProviderMeta 10016 10017 s := req.PlannedState.AsValueMap() 10018 s["id"] = cty.StringVal("ID") 10019 if ty, ok := s["type"]; ok && !ty.IsKnown() { 10020 s["type"] = cty.StringVal(req.TypeName) 10021 } 10022 return providers.ApplyResourceChangeResponse{ 10023 NewState: cty.ObjectVal(s), 10024 } 10025 } 10026 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10027 ctx := testContext2(t, &ContextOpts{ 10028 Config: m, 10029 Providers: map[addrs.Provider]providers.Factory{ 10030 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10031 }, 10032 }) 10033 10034 _, diags := ctx.Plan() 10035 assertNoErrors(t, diags) 10036 10037 _, diags = ctx.Apply() 10038 assertNoErrors(t, diags) 10039 10040 if !p.ApplyResourceChangeCalled { 10041 t.Fatalf("ApplyResourceChange not called") 10042 } 10043 10044 expectations := map[string]cty.Value{} 10045 10046 if pm, ok := arcPMs["test_resource"]; !ok { 10047 t.Fatalf("sub-module ApplyResourceChange not called") 10048 } else if pm.IsNull() { 10049 t.Fatalf("null ProviderMeta in sub-module ApplyResourceChange") 10050 } else { 10051 expectations["quux-submodule"] = pm 10052 } 10053 10054 if pm, ok := arcPMs["test_instance"]; !ok { 10055 t.Fatalf("root module ApplyResourceChange not called") 10056 } else if pm.IsNull() { 10057 t.Fatalf("null ProviderMeta in root module ApplyResourceChange") 10058 } else { 10059 expectations["quux"] = pm 10060 } 10061 10062 type metaStruct struct { 10063 Baz string `cty:"baz"` 10064 } 10065 10066 for expected, v := range expectations { 10067 var meta metaStruct 10068 err := gocty.FromCtyValue(v, &meta) 10069 if err != nil { 10070 t.Fatalf("Error parsing cty value: %s", err) 10071 } 10072 if meta.Baz != expected { 10073 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10074 } 10075 } 10076} 10077 10078func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) { 10079 m := testModule(t, "provider-meta-unset") 10080 p := testProvider("test") 10081 p.PlanResourceChangeFn = testDiffFn 10082 schema := p.ProviderSchema() 10083 schema.ProviderMeta = &configschema.Block{ 10084 Attributes: map[string]*configschema.Attribute{ 10085 "baz": { 10086 Type: cty.String, 10087 Required: true, 10088 }, 10089 }, 10090 } 10091 var pmMu sync.Mutex 10092 arcPMs := map[string]cty.Value{} 10093 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 10094 pmMu.Lock() 10095 defer pmMu.Unlock() 10096 arcPMs[req.TypeName] = req.ProviderMeta 10097 10098 s := req.PlannedState.AsValueMap() 10099 s["id"] = cty.StringVal("ID") 10100 if ty, ok := s["type"]; ok && !ty.IsKnown() { 10101 s["type"] = cty.StringVal(req.TypeName) 10102 } 10103 return providers.ApplyResourceChangeResponse{ 10104 NewState: cty.ObjectVal(s), 10105 } 10106 } 10107 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10108 ctx := testContext2(t, &ContextOpts{ 10109 Config: m, 10110 Providers: map[addrs.Provider]providers.Factory{ 10111 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10112 }, 10113 }) 10114 10115 _, diags := ctx.Plan() 10116 assertNoErrors(t, diags) 10117 10118 _, diags = ctx.Apply() 10119 assertNoErrors(t, diags) 10120 10121 if !p.ApplyResourceChangeCalled { 10122 t.Fatalf("ApplyResourceChange not called") 10123 } 10124 10125 if pm, ok := arcPMs["test_resource"]; !ok { 10126 t.Fatalf("sub-module ApplyResourceChange not called") 10127 } else if !pm.IsNull() { 10128 t.Fatalf("non-null ProviderMeta in sub-module ApplyResourceChange: %+v", pm) 10129 } 10130 10131 if pm, ok := arcPMs["test_instance"]; !ok { 10132 t.Fatalf("root module ApplyResourceChange not called") 10133 } else if !pm.IsNull() { 10134 t.Fatalf("non-null ProviderMeta in root module ApplyResourceChange: %+v", pm) 10135 } 10136} 10137 10138func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) { 10139 m := testModule(t, "provider-meta-set") 10140 p := testProvider("test") 10141 schema := p.ProviderSchema() 10142 schema.ProviderMeta = &configschema.Block{ 10143 Attributes: map[string]*configschema.Attribute{ 10144 "baz": { 10145 Type: cty.String, 10146 Required: true, 10147 }, 10148 }, 10149 } 10150 prcPMs := map[string]cty.Value{} 10151 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 10152 prcPMs[req.TypeName] = req.ProviderMeta 10153 return providers.PlanResourceChangeResponse{ 10154 PlannedState: req.ProposedNewState, 10155 } 10156 } 10157 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10158 ctx := testContext2(t, &ContextOpts{ 10159 Config: m, 10160 Providers: map[addrs.Provider]providers.Factory{ 10161 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10162 }, 10163 }) 10164 10165 _, diags := ctx.Plan() 10166 assertNoErrors(t, diags) 10167 10168 if !p.PlanResourceChangeCalled { 10169 t.Fatalf("PlanResourceChange not called") 10170 } 10171 10172 expectations := map[string]cty.Value{} 10173 10174 if pm, ok := prcPMs["test_resource"]; !ok { 10175 t.Fatalf("sub-module PlanResourceChange not called") 10176 } else if pm.IsNull() { 10177 t.Fatalf("null ProviderMeta in sub-module PlanResourceChange") 10178 } else { 10179 expectations["quux-submodule"] = pm 10180 } 10181 10182 if pm, ok := prcPMs["test_instance"]; !ok { 10183 t.Fatalf("root module PlanResourceChange not called") 10184 } else if pm.IsNull() { 10185 t.Fatalf("null ProviderMeta in root module PlanResourceChange") 10186 } else { 10187 expectations["quux"] = pm 10188 } 10189 10190 type metaStruct struct { 10191 Baz string `cty:"baz"` 10192 } 10193 10194 for expected, v := range expectations { 10195 var meta metaStruct 10196 err := gocty.FromCtyValue(v, &meta) 10197 if err != nil { 10198 t.Fatalf("Error parsing cty value: %s", err) 10199 } 10200 if meta.Baz != expected { 10201 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10202 } 10203 } 10204} 10205 10206func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) { 10207 m := testModule(t, "provider-meta-unset") 10208 p := testProvider("test") 10209 schema := p.ProviderSchema() 10210 schema.ProviderMeta = &configschema.Block{ 10211 Attributes: map[string]*configschema.Attribute{ 10212 "baz": { 10213 Type: cty.String, 10214 Required: true, 10215 }, 10216 }, 10217 } 10218 prcPMs := map[string]cty.Value{} 10219 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 10220 prcPMs[req.TypeName] = req.ProviderMeta 10221 return providers.PlanResourceChangeResponse{ 10222 PlannedState: req.ProposedNewState, 10223 } 10224 } 10225 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10226 ctx := testContext2(t, &ContextOpts{ 10227 Config: m, 10228 Providers: map[addrs.Provider]providers.Factory{ 10229 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10230 }, 10231 }) 10232 10233 _, diags := ctx.Plan() 10234 assertNoErrors(t, diags) 10235 10236 if !p.PlanResourceChangeCalled { 10237 t.Fatalf("PlanResourceChange not called") 10238 } 10239 10240 if pm, ok := prcPMs["test_resource"]; !ok { 10241 t.Fatalf("sub-module PlanResourceChange not called") 10242 } else if !pm.IsNull() { 10243 t.Fatalf("non-null ProviderMeta in sub-module PlanResourceChange: %+v", pm) 10244 } 10245 10246 if pm, ok := prcPMs["test_instance"]; !ok { 10247 t.Fatalf("root module PlanResourceChange not called") 10248 } else if !pm.IsNull() { 10249 t.Fatalf("non-null ProviderMeta in root module PlanResourceChange: %+v", pm) 10250 } 10251} 10252 10253func TestContext2Apply_ProviderMeta_plan_setNoSchema(t *testing.T) { 10254 m := testModule(t, "provider-meta-set") 10255 p := testProvider("test") 10256 p.PlanResourceChangeFn = testDiffFn 10257 ctx := testContext2(t, &ContextOpts{ 10258 Config: m, 10259 Providers: map[addrs.Provider]providers.Factory{ 10260 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10261 }, 10262 }) 10263 10264 _, diags := ctx.Plan() 10265 if !diags.HasErrors() { 10266 t.Fatalf("plan supposed to error, has no errors") 10267 } 10268 10269 var rootErr, subErr bool 10270 errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks" 10271 for _, diag := range diags { 10272 if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" { 10273 t.Errorf("Unexpected error: %+v", diag.Description()) 10274 } 10275 switch diag.Description().Detail { 10276 case fmt.Sprintf(errorSummary, "instance"): 10277 rootErr = true 10278 case fmt.Sprintf(errorSummary, "resource"): 10279 subErr = true 10280 default: 10281 t.Errorf("Unexpected error: %s", diag.Description()) 10282 } 10283 } 10284 if !rootErr { 10285 t.Errorf("Expected unsupported provider_meta block error for root module, none received") 10286 } 10287 if !subErr { 10288 t.Errorf("Expected unsupported provider_meta block error for sub-module, none received") 10289 } 10290} 10291 10292func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) { 10293 m := testModule(t, "provider-meta-set") 10294 p := testProvider("test") 10295 p.PlanResourceChangeFn = testDiffFn 10296 schema := p.ProviderSchema() 10297 schema.ProviderMeta = &configschema.Block{ 10298 Attributes: map[string]*configschema.Attribute{ 10299 "quux": { 10300 Type: cty.String, 10301 Required: true, 10302 }, 10303 }, 10304 } 10305 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10306 ctx := testContext2(t, &ContextOpts{ 10307 Config: m, 10308 Providers: map[addrs.Provider]providers.Factory{ 10309 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10310 }, 10311 }) 10312 10313 _, diags := ctx.Plan() 10314 if !diags.HasErrors() { 10315 t.Fatalf("plan supposed to error, has no errors") 10316 } 10317 10318 var reqErr, invalidErr bool 10319 for _, diag := range diags { 10320 switch diag.Description().Summary { 10321 case "Missing required argument": 10322 if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` { 10323 reqErr = true 10324 } else { 10325 t.Errorf("Unexpected error %+v", diag.Description()) 10326 } 10327 case "Unsupported argument": 10328 if diag.Description().Detail == `An argument named "baz" is not expected here.` { 10329 invalidErr = true 10330 } else { 10331 t.Errorf("Unexpected error %+v", diag.Description()) 10332 } 10333 default: 10334 t.Errorf("Unexpected error %+v", diag.Description()) 10335 } 10336 } 10337 if !reqErr { 10338 t.Errorf("Expected missing required argument error, none received") 10339 } 10340 if !invalidErr { 10341 t.Errorf("Expected unsupported argument error, none received") 10342 } 10343} 10344 10345func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) { 10346 m := testModule(t, "provider-meta-set") 10347 p := testProvider("test") 10348 p.PlanResourceChangeFn = testDiffFn 10349 schema := p.ProviderSchema() 10350 schema.ProviderMeta = &configschema.Block{ 10351 Attributes: map[string]*configschema.Attribute{ 10352 "baz": { 10353 Type: cty.String, 10354 Required: true, 10355 }, 10356 }, 10357 } 10358 rrcPMs := map[string]cty.Value{} 10359 p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { 10360 rrcPMs[req.TypeName] = req.ProviderMeta 10361 newState, err := p.GetProviderSchemaResponse.ResourceTypes[req.TypeName].Block.CoerceValue(req.PriorState) 10362 if err != nil { 10363 panic(err) 10364 } 10365 resp.NewState = newState 10366 return resp 10367 } 10368 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10369 ctx := testContext2(t, &ContextOpts{ 10370 Config: m, 10371 Providers: map[addrs.Provider]providers.Factory{ 10372 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10373 }, 10374 }) 10375 10376 _, diags := ctx.Plan() 10377 assertNoErrors(t, diags) 10378 10379 _, diags = ctx.Apply() 10380 assertNoErrors(t, diags) 10381 10382 _, diags = ctx.Refresh() 10383 assertNoErrors(t, diags) 10384 10385 if !p.ReadResourceCalled { 10386 t.Fatalf("ReadResource not called") 10387 } 10388 10389 expectations := map[string]cty.Value{} 10390 10391 if pm, ok := rrcPMs["test_resource"]; !ok { 10392 t.Fatalf("sub-module ReadResource not called") 10393 } else if pm.IsNull() { 10394 t.Fatalf("null ProviderMeta in sub-module ReadResource") 10395 } else { 10396 expectations["quux-submodule"] = pm 10397 } 10398 10399 if pm, ok := rrcPMs["test_instance"]; !ok { 10400 t.Fatalf("root module ReadResource not called") 10401 } else if pm.IsNull() { 10402 t.Fatalf("null ProviderMeta in root module ReadResource") 10403 } else { 10404 expectations["quux"] = pm 10405 } 10406 10407 type metaStruct struct { 10408 Baz string `cty:"baz"` 10409 } 10410 10411 for expected, v := range expectations { 10412 var meta metaStruct 10413 err := gocty.FromCtyValue(v, &meta) 10414 if err != nil { 10415 t.Fatalf("Error parsing cty value: %s", err) 10416 } 10417 if meta.Baz != expected { 10418 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10419 } 10420 } 10421} 10422 10423func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) { 10424 m := testModule(t, "provider-meta-set") 10425 p := testProvider("test") 10426 p.PlanResourceChangeFn = testDiffFn 10427 10428 // we need a schema for plan/apply so they don't error 10429 schema := p.ProviderSchema() 10430 schema.ProviderMeta = &configschema.Block{ 10431 Attributes: map[string]*configschema.Attribute{ 10432 "baz": { 10433 Type: cty.String, 10434 Required: true, 10435 }, 10436 }, 10437 } 10438 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10439 ctx := testContext2(t, &ContextOpts{ 10440 Config: m, 10441 Providers: map[addrs.Provider]providers.Factory{ 10442 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10443 }, 10444 }) 10445 10446 _, diags := ctx.Plan() 10447 assertNoErrors(t, diags) 10448 10449 _, diags = ctx.Apply() 10450 assertNoErrors(t, diags) 10451 10452 // drop the schema before refresh, to test that it errors 10453 schema.ProviderMeta = nil 10454 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10455 ctx = testContext2(t, &ContextOpts{ 10456 Config: m, 10457 Providers: map[addrs.Provider]providers.Factory{ 10458 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10459 }, 10460 State: ctx.State(), 10461 }) 10462 10463 _, diags = ctx.Refresh() 10464 if !diags.HasErrors() { 10465 t.Fatalf("refresh supposed to error, has no errors") 10466 } 10467 10468 var rootErr, subErr bool 10469 errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks" 10470 for _, diag := range diags { 10471 if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" { 10472 t.Errorf("Unexpected error: %+v", diag.Description()) 10473 } 10474 switch diag.Description().Detail { 10475 case fmt.Sprintf(errorSummary, "instance"): 10476 rootErr = true 10477 case fmt.Sprintf(errorSummary, "resource"): 10478 subErr = true 10479 default: 10480 t.Errorf("Unexpected error: %s", diag.Description()) 10481 } 10482 } 10483 if !rootErr { 10484 t.Errorf("Expected unsupported provider_meta block error for root module, none received") 10485 } 10486 if !subErr { 10487 t.Errorf("Expected unsupported provider_meta block error for sub-module, none received") 10488 } 10489} 10490 10491func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) { 10492 m := testModule(t, "provider-meta-set") 10493 p := testProvider("test") 10494 p.PlanResourceChangeFn = testDiffFn 10495 10496 // we need a matching schema for plan/apply so they don't error 10497 schema := p.ProviderSchema() 10498 schema.ProviderMeta = &configschema.Block{ 10499 Attributes: map[string]*configschema.Attribute{ 10500 "baz": { 10501 Type: cty.String, 10502 Required: true, 10503 }, 10504 }, 10505 } 10506 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10507 ctx := testContext2(t, &ContextOpts{ 10508 Config: m, 10509 Providers: map[addrs.Provider]providers.Factory{ 10510 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10511 }, 10512 }) 10513 10514 _, diags := ctx.Plan() 10515 assertNoErrors(t, diags) 10516 10517 _, diags = ctx.Apply() 10518 assertNoErrors(t, diags) 10519 10520 // change the schema before refresh, to test that it errors 10521 schema.ProviderMeta = &configschema.Block{ 10522 Attributes: map[string]*configschema.Attribute{ 10523 "quux": { 10524 Type: cty.String, 10525 Required: true, 10526 }, 10527 }, 10528 } 10529 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10530 ctx = testContext2(t, &ContextOpts{ 10531 Config: m, 10532 Providers: map[addrs.Provider]providers.Factory{ 10533 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10534 }, 10535 State: ctx.State(), 10536 }) 10537 10538 _, diags = ctx.Refresh() 10539 if !diags.HasErrors() { 10540 t.Fatalf("refresh supposed to error, has no errors") 10541 } 10542 10543 var reqErr, invalidErr bool 10544 for _, diag := range diags { 10545 switch diag.Description().Summary { 10546 case "Missing required argument": 10547 if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` { 10548 reqErr = true 10549 } else { 10550 t.Errorf("Unexpected error %+v", diag.Description()) 10551 } 10552 case "Unsupported argument": 10553 if diag.Description().Detail == `An argument named "baz" is not expected here.` { 10554 invalidErr = true 10555 } else { 10556 t.Errorf("Unexpected error %+v", diag.Description()) 10557 } 10558 default: 10559 t.Errorf("Unexpected error %+v", diag.Description()) 10560 } 10561 } 10562 if !reqErr { 10563 t.Errorf("Expected missing required argument error, none received") 10564 } 10565 if !invalidErr { 10566 t.Errorf("Expected unsupported argument error, none received") 10567 } 10568} 10569 10570func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) { 10571 m := testModule(t, "provider-meta-data-set") 10572 p := testProvider("test") 10573 p.PlanResourceChangeFn = testDiffFn 10574 schema := p.ProviderSchema() 10575 schema.ProviderMeta = &configschema.Block{ 10576 Attributes: map[string]*configschema.Attribute{ 10577 "baz": { 10578 Type: cty.String, 10579 Required: true, 10580 }, 10581 }, 10582 } 10583 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10584 ctx := testContext2(t, &ContextOpts{ 10585 Config: m, 10586 Providers: map[addrs.Provider]providers.Factory{ 10587 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10588 }, 10589 }) 10590 rdsPMs := map[string]cty.Value{} 10591 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 10592 rdsPMs[req.TypeName] = req.ProviderMeta 10593 switch req.TypeName { 10594 case "test_data_source": 10595 log.Printf("[TRACE] test_data_source RDSR returning") 10596 return providers.ReadDataSourceResponse{ 10597 State: cty.ObjectVal(map[string]cty.Value{ 10598 "id": cty.StringVal("yo"), 10599 "foo": cty.StringVal("bar"), 10600 }), 10601 } 10602 case "test_file": 10603 log.Printf("[TRACE] test_file RDSR returning") 10604 return providers.ReadDataSourceResponse{ 10605 State: cty.ObjectVal(map[string]cty.Value{ 10606 "id": cty.StringVal("bar"), 10607 "rendered": cty.StringVal("baz"), 10608 "template": cty.StringVal(""), 10609 }), 10610 } 10611 default: 10612 // config drift, oops 10613 log.Printf("[TRACE] unknown request TypeName: %q", req.TypeName) 10614 return providers.ReadDataSourceResponse{} 10615 } 10616 } 10617 10618 _, diags := ctx.Plan() 10619 assertNoErrors(t, diags) 10620 10621 _, diags = ctx.Apply() 10622 assertNoErrors(t, diags) 10623 10624 _, diags = ctx.Refresh() 10625 assertNoErrors(t, diags) 10626 10627 if !p.ReadDataSourceCalled { 10628 t.Fatalf("ReadDataSource not called") 10629 } 10630 10631 expectations := map[string]cty.Value{} 10632 10633 if pm, ok := rdsPMs["test_file"]; !ok { 10634 t.Fatalf("sub-module ReadDataSource not called") 10635 } else if pm.IsNull() { 10636 t.Fatalf("null ProviderMeta in sub-module ReadDataSource") 10637 } else { 10638 expectations["quux-submodule"] = pm 10639 } 10640 10641 if pm, ok := rdsPMs["test_data_source"]; !ok { 10642 t.Fatalf("root module ReadDataSource not called") 10643 } else if pm.IsNull() { 10644 t.Fatalf("null ProviderMeta in root module ReadDataSource") 10645 } else { 10646 expectations["quux"] = pm 10647 } 10648 10649 type metaStruct struct { 10650 Baz string `cty:"baz"` 10651 } 10652 10653 for expected, v := range expectations { 10654 var meta metaStruct 10655 err := gocty.FromCtyValue(v, &meta) 10656 if err != nil { 10657 t.Fatalf("Error parsing cty value: %s", err) 10658 } 10659 if meta.Baz != expected { 10660 t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz) 10661 } 10662 } 10663} 10664 10665func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) { 10666 m := testModule(t, "provider-meta-data-unset") 10667 p := testProvider("test") 10668 p.PlanResourceChangeFn = testDiffFn 10669 schema := p.ProviderSchema() 10670 schema.ProviderMeta = &configschema.Block{ 10671 Attributes: map[string]*configschema.Attribute{ 10672 "baz": { 10673 Type: cty.String, 10674 Required: true, 10675 }, 10676 }, 10677 } 10678 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10679 ctx := testContext2(t, &ContextOpts{ 10680 Config: m, 10681 Providers: map[addrs.Provider]providers.Factory{ 10682 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10683 }, 10684 }) 10685 rdsPMs := map[string]cty.Value{} 10686 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 10687 rdsPMs[req.TypeName] = req.ProviderMeta 10688 switch req.TypeName { 10689 case "test_data_source": 10690 return providers.ReadDataSourceResponse{ 10691 State: cty.ObjectVal(map[string]cty.Value{ 10692 "id": cty.StringVal("yo"), 10693 "foo": cty.StringVal("bar"), 10694 }), 10695 } 10696 case "test_file": 10697 return providers.ReadDataSourceResponse{ 10698 State: cty.ObjectVal(map[string]cty.Value{ 10699 "id": cty.StringVal("bar"), 10700 "rendered": cty.StringVal("baz"), 10701 "template": cty.StringVal(""), 10702 }), 10703 } 10704 default: 10705 // config drift, oops 10706 return providers.ReadDataSourceResponse{} 10707 } 10708 } 10709 10710 _, diags := ctx.Plan() 10711 assertNoErrors(t, diags) 10712 10713 _, diags = ctx.Apply() 10714 assertNoErrors(t, diags) 10715 10716 if !p.ReadDataSourceCalled { 10717 t.Fatalf("ReadDataSource not called") 10718 } 10719 10720 if pm, ok := rdsPMs["test_file"]; !ok { 10721 t.Fatalf("sub-module ReadDataSource not called") 10722 } else if !pm.IsNull() { 10723 t.Fatalf("non-null ProviderMeta in sub-module ReadDataSource") 10724 } 10725 10726 if pm, ok := rdsPMs["test_data_source"]; !ok { 10727 t.Fatalf("root module ReadDataSource not called") 10728 } else if !pm.IsNull() { 10729 t.Fatalf("non-null ProviderMeta in root module ReadDataSource") 10730 } 10731} 10732 10733func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) { 10734 m := testModule(t, "provider-meta-data-set") 10735 p := testProvider("test") 10736 p.PlanResourceChangeFn = testDiffFn 10737 ctx := testContext2(t, &ContextOpts{ 10738 Config: m, 10739 Providers: map[addrs.Provider]providers.Factory{ 10740 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10741 }, 10742 }) 10743 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 10744 State: cty.ObjectVal(map[string]cty.Value{ 10745 "id": cty.StringVal("yo"), 10746 "foo": cty.StringVal("bar"), 10747 }), 10748 } 10749 10750 _, diags := ctx.Refresh() 10751 if !diags.HasErrors() { 10752 t.Fatalf("refresh supposed to error, has no errors") 10753 } 10754 10755 var rootErr, subErr bool 10756 errorSummary := "The resource data.test_%s.foo belongs to a provider that doesn't support provider_meta blocks" 10757 for _, diag := range diags { 10758 if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" { 10759 t.Errorf("Unexpected error: %+v", diag.Description()) 10760 } 10761 switch diag.Description().Detail { 10762 case fmt.Sprintf(errorSummary, "data_source"): 10763 rootErr = true 10764 case fmt.Sprintf(errorSummary, "file"): 10765 subErr = true 10766 default: 10767 t.Errorf("Unexpected error: %s", diag.Description()) 10768 } 10769 } 10770 if !rootErr { 10771 t.Errorf("Expected unsupported provider_meta block error for root module, none received") 10772 } 10773 if !subErr { 10774 t.Errorf("Expected unsupported provider_meta block error for sub-module, none received") 10775 } 10776} 10777 10778func TestContext2Apply_ProviderMeta_refreshdata_setInvalid(t *testing.T) { 10779 m := testModule(t, "provider-meta-data-set") 10780 p := testProvider("test") 10781 p.PlanResourceChangeFn = testDiffFn 10782 schema := p.ProviderSchema() 10783 schema.ProviderMeta = &configschema.Block{ 10784 Attributes: map[string]*configschema.Attribute{ 10785 "quux": { 10786 Type: cty.String, 10787 Required: true, 10788 }, 10789 }, 10790 } 10791 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) 10792 ctx := testContext2(t, &ContextOpts{ 10793 Config: m, 10794 Providers: map[addrs.Provider]providers.Factory{ 10795 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 10796 }, 10797 }) 10798 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 10799 State: cty.ObjectVal(map[string]cty.Value{ 10800 "id": cty.StringVal("yo"), 10801 "foo": cty.StringVal("bar"), 10802 }), 10803 } 10804 10805 _, diags := ctx.Refresh() 10806 if !diags.HasErrors() { 10807 t.Fatalf("refresh supposed to error, has no errors") 10808 } 10809 10810 var reqErr, invalidErr bool 10811 for _, diag := range diags { 10812 switch diag.Description().Summary { 10813 case "Missing required argument": 10814 if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` { 10815 reqErr = true 10816 } else { 10817 t.Errorf("Unexpected error %+v", diag.Description()) 10818 } 10819 case "Unsupported argument": 10820 if diag.Description().Detail == `An argument named "baz" is not expected here.` { 10821 invalidErr = true 10822 } else { 10823 t.Errorf("Unexpected error %+v", diag.Description()) 10824 } 10825 default: 10826 t.Errorf("Unexpected error %+v", diag.Description()) 10827 } 10828 } 10829 if !reqErr { 10830 t.Errorf("Expected missing required argument error, none received") 10831 } 10832 if !invalidErr { 10833 t.Errorf("Expected unsupported argument error, none received") 10834 } 10835} 10836 10837func TestContext2Apply_expandModuleVariables(t *testing.T) { 10838 m := testModuleInline(t, map[string]string{ 10839 "main.tf": ` 10840module "mod1" { 10841 for_each = toset(["a"]) 10842 source = "./mod" 10843} 10844 10845module "mod2" { 10846 source = "./mod" 10847 in = module.mod1["a"].out 10848} 10849`, 10850 "mod/main.tf": ` 10851resource "aws_instance" "foo" { 10852 foo = var.in 10853} 10854 10855variable "in" { 10856 type = string 10857 default = "default" 10858} 10859 10860output "out" { 10861 value = aws_instance.foo.id 10862} 10863`, 10864 }) 10865 10866 p := testProvider("aws") 10867 p.PlanResourceChangeFn = testDiffFn 10868 p.ApplyResourceChangeFn = testApplyFn 10869 ctx := testContext2(t, &ContextOpts{ 10870 Config: m, 10871 Providers: map[addrs.Provider]providers.Factory{ 10872 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 10873 }, 10874 }) 10875 10876 _, diags := ctx.Plan() 10877 if diags.HasErrors() { 10878 t.Fatal(diags.ErrWithWarnings()) 10879 } 10880 10881 state, diags := ctx.Apply() 10882 if diags.HasErrors() { 10883 t.Fatal(diags.ErrWithWarnings()) 10884 } 10885 10886 expected := `<no state> 10887module.mod1["a"]: 10888 aws_instance.foo: 10889 ID = foo 10890 provider = provider["registry.terraform.io/hashicorp/aws"] 10891 foo = default 10892 type = aws_instance 10893 10894 Outputs: 10895 10896 out = foo 10897module.mod2: 10898 aws_instance.foo: 10899 ID = foo 10900 provider = provider["registry.terraform.io/hashicorp/aws"] 10901 foo = foo 10902 type = aws_instance 10903 10904 Dependencies: 10905 module.mod1.aws_instance.foo` 10906 10907 if state.String() != expected { 10908 t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, state) 10909 } 10910} 10911 10912func TestContext2Apply_inheritAndStoreCBD(t *testing.T) { 10913 m := testModuleInline(t, map[string]string{ 10914 "main.tf": ` 10915resource "aws_instance" "foo" { 10916} 10917 10918resource "aws_instance" "cbd" { 10919 foo = aws_instance.foo.id 10920 lifecycle { 10921 create_before_destroy = true 10922 } 10923} 10924`, 10925 }) 10926 10927 p := testProvider("aws") 10928 p.PlanResourceChangeFn = testDiffFn 10929 ctx := testContext2(t, &ContextOpts{ 10930 Config: m, 10931 Providers: map[addrs.Provider]providers.Factory{ 10932 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 10933 }, 10934 }) 10935 10936 _, diags := ctx.Plan() 10937 if diags.HasErrors() { 10938 t.Fatal(diags.ErrWithWarnings()) 10939 } 10940 10941 state, diags := ctx.Apply() 10942 if diags.HasErrors() { 10943 t.Fatal(diags.ErrWithWarnings()) 10944 } 10945 10946 foo := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.foo")) 10947 if !foo.Current.CreateBeforeDestroy { 10948 t.Fatal("aws_instance.foo should also be create_before_destroy") 10949 } 10950} 10951 10952func TestContext2Apply_moduleDependsOn(t *testing.T) { 10953 m := testModule(t, "apply-module-depends-on") 10954 10955 p := testProvider("test") 10956 10957 // each instance being applied should happen in sequential order 10958 applied := int64(0) 10959 10960 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 10961 cfg := req.Config.AsValueMap() 10962 foo := cfg["foo"].AsString() 10963 ord := atomic.LoadInt64(&applied) 10964 10965 resp := providers.ReadDataSourceResponse{ 10966 State: cty.ObjectVal(map[string]cty.Value{ 10967 "id": cty.StringVal("data"), 10968 "foo": cfg["foo"], 10969 }), 10970 } 10971 10972 if foo == "a" && ord < 4 { 10973 // due to data source "a"'s module depending on instance 4, this 10974 // should not be less than 4 10975 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source a read too early")) 10976 } 10977 if foo == "b" && ord < 1 { 10978 // due to data source "b"'s module depending on instance 1, this 10979 // should not be less than 1 10980 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source b read too early")) 10981 } 10982 return resp 10983 } 10984 p.PlanResourceChangeFn = testDiffFn 10985 10986 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 10987 state := req.PlannedState.AsValueMap() 10988 num, _ := state["num"].AsBigFloat().Float64() 10989 ord := int64(num) 10990 if !atomic.CompareAndSwapInt64(&applied, ord-1, ord) { 10991 actual := atomic.LoadInt64(&applied) 10992 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("instance %d was applied after %d", ord, actual)) 10993 } 10994 10995 state["id"] = cty.StringVal(fmt.Sprintf("test_%d", ord)) 10996 state["type"] = cty.StringVal("test_instance") 10997 resp.NewState = cty.ObjectVal(state) 10998 10999 return resp 11000 } 11001 11002 ctx := testContext2(t, &ContextOpts{ 11003 Config: m, 11004 Providers: map[addrs.Provider]providers.Factory{ 11005 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11006 }, 11007 }) 11008 11009 _, diags := ctx.Plan() 11010 if diags.HasErrors() { 11011 t.Fatal(diags.ErrWithWarnings()) 11012 } 11013 11014 _, diags = ctx.Apply() 11015 if diags.HasErrors() { 11016 t.Fatal(diags.ErrWithWarnings()) 11017 } 11018 11019 plan, diags := ctx.Plan() 11020 if diags.HasErrors() { 11021 t.Fatal(diags.ErrWithWarnings()) 11022 } 11023 11024 for _, res := range plan.Changes.Resources { 11025 if res.Action != plans.NoOp { 11026 t.Fatalf("expected NoOp, got %s for %s", res.Action, res.Addr) 11027 } 11028 } 11029} 11030 11031func TestContext2Apply_moduleSelfReference(t *testing.T) { 11032 m := testModuleInline(t, map[string]string{ 11033 "main.tf": ` 11034module "test" { 11035 source = "./test" 11036 11037 a = module.test.b 11038} 11039 11040output "c" { 11041 value = module.test.c 11042} 11043`, 11044 "test/main.tf": ` 11045variable "a" {} 11046 11047resource "test_instance" "test" { 11048} 11049 11050output "b" { 11051 value = test_instance.test.id 11052} 11053 11054output "c" { 11055 value = var.a 11056}`}) 11057 11058 p := testProvider("test") 11059 p.PlanResourceChangeFn = testDiffFn 11060 ctx := testContext2(t, &ContextOpts{ 11061 Config: m, 11062 Providers: map[addrs.Provider]providers.Factory{ 11063 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11064 }, 11065 }) 11066 11067 _, diags := ctx.Plan() 11068 if diags.HasErrors() { 11069 t.Fatal(diags.ErrWithWarnings()) 11070 } 11071 11072 _, diags = ctx.Apply() 11073 if diags.HasErrors() { 11074 t.Fatal(diags.ErrWithWarnings()) 11075 } 11076 11077 ctx = testContext2(t, &ContextOpts{ 11078 Config: m, 11079 Providers: map[addrs.Provider]providers.Factory{ 11080 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11081 }, 11082 PlanMode: plans.DestroyMode, 11083 }) 11084 11085 _, diags = ctx.Plan() 11086 if diags.HasErrors() { 11087 t.Fatal(diags.ErrWithWarnings()) 11088 } 11089 11090 state, diags := ctx.Apply() 11091 if diags.HasErrors() { 11092 t.Fatal(diags.ErrWithWarnings()) 11093 } 11094 11095 if !state.Empty() { 11096 t.Fatal("expected empty state, got:", state) 11097 } 11098} 11099 11100func TestContext2Apply_moduleExpandDependsOn(t *testing.T) { 11101 m := testModuleInline(t, map[string]string{ 11102 "main.tf": ` 11103module "child" { 11104 count = 1 11105 source = "./child" 11106 11107 depends_on = [test_instance.a, test_instance.b] 11108} 11109 11110resource "test_instance" "a" { 11111} 11112 11113 11114resource "test_instance" "b" { 11115} 11116`, 11117 "child/main.tf": ` 11118resource "test_instance" "foo" { 11119} 11120 11121output "myoutput" { 11122 value = "literal string" 11123} 11124`}) 11125 11126 p := testProvider("test") 11127 p.PlanResourceChangeFn = testDiffFn 11128 ctx := testContext2(t, &ContextOpts{ 11129 Config: m, 11130 Providers: map[addrs.Provider]providers.Factory{ 11131 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11132 }, 11133 }) 11134 11135 _, diags := ctx.Plan() 11136 if diags.HasErrors() { 11137 t.Fatal(diags.ErrWithWarnings()) 11138 } 11139 11140 state, diags := ctx.Apply() 11141 if diags.HasErrors() { 11142 t.Fatal(diags.ErrWithWarnings()) 11143 } 11144 11145 ctx = testContext2(t, &ContextOpts{ 11146 Config: m, 11147 Providers: map[addrs.Provider]providers.Factory{ 11148 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11149 }, 11150 PlanMode: plans.DestroyMode, 11151 State: state, 11152 }) 11153 11154 _, diags = ctx.Plan() 11155 if diags.HasErrors() { 11156 t.Fatal(diags.ErrWithWarnings()) 11157 } 11158 11159 state, diags = ctx.Apply() 11160 if diags.HasErrors() { 11161 t.Fatal(diags.ErrWithWarnings()) 11162 } 11163 11164 if !state.Empty() { 11165 t.Fatal("expected empty state, got:", state) 11166 } 11167} 11168 11169func TestContext2Apply_scaleInCBD(t *testing.T) { 11170 m := testModuleInline(t, map[string]string{ 11171 "main.tf": ` 11172variable "ct" { 11173 type = number 11174} 11175 11176resource "test_instance" "a" { 11177 count = var.ct 11178} 11179 11180resource "test_instance" "b" { 11181 require_new = local.removable 11182 lifecycle { 11183 create_before_destroy = true 11184 } 11185} 11186 11187resource "test_instance" "c" { 11188 require_new = test_instance.b.id 11189 lifecycle { 11190 create_before_destroy = true 11191 } 11192} 11193 11194output "out" { 11195 value = join(".", test_instance.a[*].id) 11196} 11197 11198locals { 11199 removable = join(".", test_instance.a[*].id) 11200} 11201`}) 11202 11203 state := states.NewState() 11204 root := state.EnsureModule(addrs.RootModuleInstance) 11205 root.SetResourceInstanceCurrent( 11206 mustResourceInstanceAddr("test_instance.a[0]").Resource, 11207 &states.ResourceInstanceObjectSrc{ 11208 Status: states.ObjectReady, 11209 AttrsJSON: []byte(`{"id":"a0"}`), 11210 Dependencies: []addrs.ConfigResource{}, 11211 CreateBeforeDestroy: true, 11212 }, 11213 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11214 ) 11215 root.SetResourceInstanceCurrent( 11216 mustResourceInstanceAddr("test_instance.a[1]").Resource, 11217 &states.ResourceInstanceObjectSrc{ 11218 Status: states.ObjectReady, 11219 AttrsJSON: []byte(`{"id":"a1"}`), 11220 Dependencies: []addrs.ConfigResource{}, 11221 CreateBeforeDestroy: true, 11222 }, 11223 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11224 ) 11225 root.SetResourceInstanceCurrent( 11226 mustResourceInstanceAddr("test_instance.b").Resource, 11227 &states.ResourceInstanceObjectSrc{ 11228 Status: states.ObjectReady, 11229 AttrsJSON: []byte(`{"id":"b", "require_new":"old.old"}`), 11230 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_instance.a")}, 11231 CreateBeforeDestroy: true, 11232 }, 11233 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11234 ) 11235 root.SetResourceInstanceCurrent( 11236 mustResourceInstanceAddr("test_instance.c").Resource, 11237 &states.ResourceInstanceObjectSrc{ 11238 Status: states.ObjectReady, 11239 AttrsJSON: []byte(`{"id":"c", "require_new":"b"}`), 11240 Dependencies: []addrs.ConfigResource{ 11241 mustConfigResourceAddr("test_instance.a"), 11242 mustConfigResourceAddr("test_instance.b"), 11243 }, 11244 CreateBeforeDestroy: true, 11245 }, 11246 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 11247 ) 11248 11249 p := testProvider("test") 11250 11251 p.PlanResourceChangeFn = func(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 11252 n := r.ProposedNewState.AsValueMap() 11253 11254 if r.PriorState.IsNull() { 11255 n["id"] = cty.UnknownVal(cty.String) 11256 resp.PlannedState = cty.ObjectVal(n) 11257 return resp 11258 } 11259 11260 p := r.PriorState.AsValueMap() 11261 11262 priorRN := p["require_new"] 11263 newRN := n["require_new"] 11264 11265 if eq := priorRN.Equals(newRN); !eq.IsKnown() || eq.False() { 11266 resp.RequiresReplace = []cty.Path{{cty.GetAttrStep{Name: "require_new"}}} 11267 n["id"] = cty.UnknownVal(cty.String) 11268 } 11269 11270 resp.PlannedState = cty.ObjectVal(n) 11271 return resp 11272 } 11273 11274 // reduce the count to 1 11275 ctx := testContext2(t, &ContextOpts{ 11276 Variables: InputValues{ 11277 "ct": &InputValue{ 11278 Value: cty.NumberIntVal(1), 11279 SourceType: ValueFromCaller, 11280 }, 11281 }, 11282 Config: m, 11283 Providers: map[addrs.Provider]providers.Factory{ 11284 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11285 }, 11286 State: state, 11287 }) 11288 11289 _, diags := ctx.Plan() 11290 if diags.HasErrors() { 11291 t.Fatal(diags.ErrWithWarnings()) 11292 } 11293 11294 state, diags = ctx.Apply() 11295 if diags.HasErrors() { 11296 log.Fatal(diags.ErrWithWarnings()) 11297 } 11298 11299 // check the output, as those can't cause an error planning the value 11300 out := state.RootModule().OutputValues["out"].Value.AsString() 11301 if out != "a0" { 11302 t.Fatalf(`expected output "a0", got: %q`, out) 11303 } 11304 11305 // reduce the count to 0 11306 ctx = testContext2(t, &ContextOpts{ 11307 Variables: InputValues{ 11308 "ct": &InputValue{ 11309 Value: cty.NumberIntVal(0), 11310 SourceType: ValueFromCaller, 11311 }, 11312 }, 11313 Config: m, 11314 Providers: map[addrs.Provider]providers.Factory{ 11315 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11316 }, 11317 State: state, 11318 }) 11319 11320 _, diags = ctx.Plan() 11321 if diags.HasErrors() { 11322 t.Fatal(diags.ErrWithWarnings()) 11323 } 11324 11325 state, diags = ctx.Apply() 11326 if diags.HasErrors() { 11327 t.Fatal(diags.ErrWithWarnings()) 11328 } 11329 11330 // check the output, as those can't cause an error planning the value 11331 out = state.RootModule().OutputValues["out"].Value.AsString() 11332 if out != "" { 11333 t.Fatalf(`expected output "", got: %q`, out) 11334 } 11335} 11336 11337// Ensure that we can destroy when a provider references a resource that will 11338// also be destroyed 11339func TestContext2Apply_destroyProviderReference(t *testing.T) { 11340 m := testModuleInline(t, map[string]string{ 11341 "main.tf": ` 11342provider "null" { 11343 value = "" 11344} 11345 11346module "mod" { 11347 source = "./mod" 11348} 11349 11350provider "test" { 11351 value = module.mod.output 11352} 11353 11354resource "test_instance" "bar" { 11355} 11356`, 11357 "mod/main.tf": ` 11358data "null_data_source" "foo" { 11359 count = 1 11360} 11361 11362 11363output "output" { 11364 value = data.null_data_source.foo[0].output 11365} 11366`}) 11367 11368 schemaFn := func(name string) *ProviderSchema { 11369 return &ProviderSchema{ 11370 Provider: &configschema.Block{ 11371 Attributes: map[string]*configschema.Attribute{ 11372 "value": { 11373 Type: cty.String, 11374 Required: true, 11375 }, 11376 }, 11377 }, 11378 ResourceTypes: map[string]*configschema.Block{ 11379 name + "_instance": { 11380 Attributes: map[string]*configschema.Attribute{ 11381 "id": { 11382 Type: cty.String, 11383 Computed: true, 11384 }, 11385 "foo": { 11386 Type: cty.String, 11387 Optional: true, 11388 }, 11389 }, 11390 }, 11391 }, 11392 DataSources: map[string]*configschema.Block{ 11393 name + "_data_source": { 11394 Attributes: map[string]*configschema.Attribute{ 11395 "id": { 11396 Type: cty.String, 11397 Computed: true, 11398 }, 11399 "output": { 11400 Type: cty.String, 11401 Computed: true, 11402 }, 11403 }, 11404 }, 11405 }, 11406 } 11407 } 11408 11409 testP := new(MockProvider) 11410 testP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 11411 return providers.ReadResourceResponse{NewState: req.PriorState} 11412 } 11413 testP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("test")) 11414 11415 providerConfig := "" 11416 testP.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 11417 value := req.Config.GetAttr("value") 11418 if value.IsKnown() && !value.IsNull() { 11419 providerConfig = value.AsString() 11420 } else { 11421 providerConfig = "" 11422 } 11423 return resp 11424 } 11425 testP.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 11426 if providerConfig != "valid" { 11427 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provider config is %q", providerConfig)) 11428 return 11429 } 11430 return testApplyFn(req) 11431 } 11432 testP.PlanResourceChangeFn = testDiffFn 11433 11434 nullP := new(MockProvider) 11435 nullP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 11436 return providers.ReadResourceResponse{NewState: req.PriorState} 11437 } 11438 nullP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("null")) 11439 11440 nullP.ApplyResourceChangeFn = testApplyFn 11441 nullP.PlanResourceChangeFn = testDiffFn 11442 11443 nullP.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 11444 State: cty.ObjectVal(map[string]cty.Value{ 11445 "id": cty.StringVal("ID"), 11446 "output": cty.StringVal("valid"), 11447 }), 11448 } 11449 11450 ctx := testContext2(t, &ContextOpts{ 11451 Config: m, 11452 Providers: map[addrs.Provider]providers.Factory{ 11453 addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), 11454 addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), 11455 }, 11456 }) 11457 11458 if _, diags := ctx.Plan(); diags.HasErrors() { 11459 t.Fatalf("plan errors: %s", diags.Err()) 11460 } 11461 11462 state, diags := ctx.Apply() 11463 if diags.HasErrors() { 11464 t.Fatalf("apply errors: %s", diags.Err()) 11465 } 11466 11467 ctx = testContext2(t, &ContextOpts{ 11468 Config: m, 11469 Providers: map[addrs.Provider]providers.Factory{ 11470 addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), 11471 addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), 11472 }, 11473 11474 State: state, 11475 PlanMode: plans.DestroyMode, 11476 }) 11477 11478 if _, diags := ctx.Plan(); diags.HasErrors() { 11479 t.Fatalf("destroy plan errors: %s", diags.Err()) 11480 } 11481 11482 if _, diags := ctx.Apply(); diags.HasErrors() { 11483 t.Fatalf("destroy apply errors: %s", diags.Err()) 11484 } 11485} 11486 11487// Destroying properly requires pruning out all unneeded config nodes to 11488// prevent incorrect expansion evaluation. 11489func TestContext2Apply_destroyInterModuleExpansion(t *testing.T) { 11490 m := testModuleInline(t, map[string]string{ 11491 "main.tf": ` 11492data "test_data_source" "a" { 11493 for_each = { 11494 one = "thing" 11495 } 11496} 11497 11498locals { 11499 module_input = { 11500 for k, v in data.test_data_source.a : k => v.id 11501 } 11502} 11503 11504module "mod1" { 11505 source = "./mod" 11506 input = local.module_input 11507} 11508 11509module "mod2" { 11510 source = "./mod" 11511 input = module.mod1.outputs 11512} 11513 11514resource "test_instance" "bar" { 11515 for_each = module.mod2.outputs 11516} 11517 11518output "module_output" { 11519 value = module.mod2.outputs 11520} 11521output "test_instances" { 11522 value = test_instance.bar 11523} 11524`, 11525 "mod/main.tf": ` 11526variable "input" { 11527} 11528 11529data "test_data_source" "foo" { 11530 for_each = var.input 11531} 11532 11533output "outputs" { 11534 value = data.test_data_source.foo 11535} 11536`}) 11537 11538 p := testProvider("test") 11539 p.PlanResourceChangeFn = testDiffFn 11540 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 11541 return providers.ReadDataSourceResponse{ 11542 State: cty.ObjectVal(map[string]cty.Value{ 11543 "id": cty.StringVal("data_source"), 11544 "foo": cty.StringVal("output"), 11545 }), 11546 } 11547 } 11548 11549 ctx := testContext2(t, &ContextOpts{ 11550 Config: m, 11551 Providers: map[addrs.Provider]providers.Factory{ 11552 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11553 }, 11554 }) 11555 11556 if _, diags := ctx.Plan(); diags.HasErrors() { 11557 t.Fatalf("plan errors: %s", diags.Err()) 11558 } 11559 11560 state, diags := ctx.Apply() 11561 if diags.HasErrors() { 11562 t.Fatalf("apply errors: %s", diags.Err()) 11563 } 11564 11565 destroy := func() { 11566 ctx = testContext2(t, &ContextOpts{ 11567 Config: m, 11568 Providers: map[addrs.Provider]providers.Factory{ 11569 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11570 }, 11571 11572 State: state, 11573 PlanMode: plans.DestroyMode, 11574 }) 11575 11576 if _, diags := ctx.Plan(); diags.HasErrors() { 11577 t.Fatalf("destroy plan errors: %s", diags.Err()) 11578 } 11579 11580 state, diags = ctx.Apply() 11581 if diags.HasErrors() { 11582 t.Fatalf("destroy apply errors: %s", diags.Err()) 11583 } 11584 } 11585 11586 destroy() 11587 // Destroying again from the empty state should not cause any errors either 11588 destroy() 11589} 11590 11591func TestContext2Apply_createBeforeDestroyWithModule(t *testing.T) { 11592 m := testModuleInline(t, map[string]string{ 11593 "main.tf": ` 11594variable "v" {} 11595 11596module "mod" { 11597 source = "./mod" 11598 in = var.v 11599} 11600 11601resource "test_resource" "a" { 11602 value = var.v 11603 depends_on = [module.mod] 11604 lifecycle { 11605 create_before_destroy = true 11606 } 11607} 11608`, 11609 "mod/main.tf": ` 11610variable "in" {} 11611 11612resource "test_resource" "a" { 11613 value = var.in 11614} 11615`}) 11616 11617 p := testProvider("test") 11618 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 11619 proposed := req.ProposedNewState.AsValueMap() 11620 proposed["id"] = cty.UnknownVal(cty.String) 11621 return providers.PlanResourceChangeResponse{ 11622 PlannedState: cty.ObjectVal(proposed), 11623 RequiresReplace: []cty.Path{cty.Path{cty.GetAttrStep{Name: "value"}}}, 11624 } 11625 } 11626 11627 ctx := testContext2(t, &ContextOpts{ 11628 Config: m, 11629 Providers: map[addrs.Provider]providers.Factory{ 11630 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11631 }, 11632 Variables: InputValues{ 11633 "v": &InputValue{ 11634 Value: cty.StringVal("A"), 11635 }, 11636 }, 11637 }) 11638 11639 if _, diags := ctx.Plan(); diags.HasErrors() { 11640 t.Fatalf("plan errors: %s", diags.Err()) 11641 } 11642 11643 state, diags := ctx.Apply() 11644 if diags.HasErrors() { 11645 t.Fatalf("apply errors: %s", diags.Err()) 11646 } 11647 11648 ctx = testContext2(t, &ContextOpts{ 11649 Config: m, 11650 Providers: map[addrs.Provider]providers.Factory{ 11651 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11652 }, 11653 Variables: InputValues{ 11654 "v": &InputValue{ 11655 Value: cty.StringVal("B"), 11656 }, 11657 }, 11658 State: state, 11659 }) 11660 11661 if _, diags := ctx.Plan(); diags.HasErrors() { 11662 t.Fatalf("plan errors: %s", diags.Err()) 11663 } 11664 11665 _, diags = ctx.Apply() 11666 if diags.HasErrors() { 11667 t.Fatalf("apply errors: %s", diags.Err()) 11668 } 11669} 11670 11671func TestContext2Apply_forcedCBD(t *testing.T) { 11672 m := testModuleInline(t, map[string]string{ 11673 "main.tf": ` 11674variable "v" {} 11675 11676resource "test_instance" "a" { 11677 require_new = var.v 11678} 11679 11680resource "test_instance" "b" { 11681 depends_on = [test_instance.a] 11682 lifecycle { 11683 create_before_destroy = true 11684 } 11685} 11686`}) 11687 11688 p := testProvider("test") 11689 p.PlanResourceChangeFn = testDiffFn 11690 11691 ctx := testContext2(t, &ContextOpts{ 11692 Config: m, 11693 Providers: map[addrs.Provider]providers.Factory{ 11694 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11695 }, 11696 Variables: InputValues{ 11697 "v": &InputValue{ 11698 Value: cty.StringVal("A"), 11699 }, 11700 }, 11701 }) 11702 11703 if _, diags := ctx.Plan(); diags.HasErrors() { 11704 t.Fatalf("plan errors: %s", diags.Err()) 11705 } 11706 11707 state, diags := ctx.Apply() 11708 if diags.HasErrors() { 11709 t.Fatalf("apply errors: %s", diags.Err()) 11710 } 11711 11712 ctx = testContext2(t, &ContextOpts{ 11713 Config: m, 11714 Providers: map[addrs.Provider]providers.Factory{ 11715 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11716 }, 11717 Variables: InputValues{ 11718 "v": &InputValue{ 11719 Value: cty.StringVal("B"), 11720 }, 11721 }, 11722 State: state, 11723 }) 11724 11725 if _, diags := ctx.Plan(); diags.HasErrors() { 11726 t.Fatalf("plan errors: %s", diags.Err()) 11727 } 11728 11729 _, diags = ctx.Apply() 11730 if diags.HasErrors() { 11731 t.Fatalf("apply errors: %s", diags.Err()) 11732 } 11733} 11734 11735func TestContext2Apply_removeReferencedResource(t *testing.T) { 11736 m := testModuleInline(t, map[string]string{ 11737 "main.tf": ` 11738variable "ct" { 11739} 11740 11741resource "test_resource" "to_remove" { 11742 count = var.ct 11743} 11744 11745resource "test_resource" "c" { 11746 value = join("", test_resource.to_remove[*].id) 11747} 11748`}) 11749 11750 p := testProvider("test") 11751 p.PlanResourceChangeFn = testDiffFn 11752 11753 ctx := testContext2(t, &ContextOpts{ 11754 Config: m, 11755 Providers: map[addrs.Provider]providers.Factory{ 11756 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11757 }, 11758 Variables: InputValues{ 11759 "ct": &InputValue{ 11760 Value: cty.NumberIntVal(1), 11761 }, 11762 }, 11763 }) 11764 11765 if _, diags := ctx.Plan(); diags.HasErrors() { 11766 t.Fatalf("plan errors: %s", diags.Err()) 11767 } 11768 11769 state, diags := ctx.Apply() 11770 if diags.HasErrors() { 11771 t.Fatalf("apply errors: %s", diags.Err()) 11772 } 11773 11774 ctx = testContext2(t, &ContextOpts{ 11775 Config: m, 11776 Providers: map[addrs.Provider]providers.Factory{ 11777 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11778 }, 11779 Variables: InputValues{ 11780 "ct": &InputValue{ 11781 Value: cty.NumberIntVal(0), 11782 }, 11783 }, 11784 State: state, 11785 }) 11786 11787 if _, diags := ctx.Plan(); diags.HasErrors() { 11788 t.Fatalf("plan errors: %s", diags.Err()) 11789 } 11790 11791 _, diags = ctx.Apply() 11792 if diags.HasErrors() { 11793 t.Fatalf("apply errors: %s", diags.Err()) 11794 } 11795} 11796 11797func TestContext2Apply_variableSensitivity(t *testing.T) { 11798 m := testModuleInline(t, map[string]string{ 11799 "main.tf": ` 11800variable "sensitive_var" { 11801 default = "foo" 11802 sensitive = true 11803} 11804 11805variable "sensitive_id" { 11806 default = "secret id" 11807 sensitive = true 11808} 11809 11810resource "test_resource" "foo" { 11811 value = var.sensitive_var 11812 11813 network_interface { 11814 network_interface_id = var.sensitive_id 11815 } 11816}`, 11817 }) 11818 11819 p := new(MockProvider) 11820 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 11821 return providers.ReadResourceResponse{NewState: req.PriorState} 11822 } 11823 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 11824 Provider: &configschema.Block{}, 11825 ResourceTypes: map[string]*configschema.Block{ 11826 "test_resource": { 11827 Attributes: map[string]*configschema.Attribute{ 11828 "id": { 11829 Type: cty.String, 11830 Computed: true, 11831 }, 11832 "value": { 11833 Type: cty.String, 11834 Optional: true, 11835 Computed: true, 11836 }, 11837 }, 11838 BlockTypes: map[string]*configschema.NestedBlock{ 11839 "network_interface": { 11840 Block: configschema.Block{ 11841 Attributes: map[string]*configschema.Attribute{ 11842 "network_interface_id": {Type: cty.String, Optional: true}, 11843 "device_index": {Type: cty.Number, Optional: true}, 11844 }, 11845 }, 11846 Nesting: configschema.NestingSet, 11847 }, 11848 }, 11849 }, 11850 }, 11851 }) 11852 p.PlanResourceChangeFn = testDiffFn 11853 11854 ctx := testContext2(t, &ContextOpts{ 11855 Config: m, 11856 Providers: map[addrs.Provider]providers.Factory{ 11857 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11858 }, 11859 }) 11860 11861 if _, diags := ctx.Plan(); diags.HasErrors() { 11862 t.Fatalf("plan errors: %s", diags.Err()) 11863 } 11864 11865 state, diags := ctx.Apply() 11866 if diags.HasErrors() { 11867 t.Fatalf("apply errors: %s", diags.Err()) 11868 } 11869 11870 // Run a second apply with no changes 11871 ctx = testContext2(t, &ContextOpts{ 11872 Config: m, 11873 Providers: map[addrs.Provider]providers.Factory{ 11874 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11875 }, 11876 State: state, 11877 }) 11878 11879 if _, diags := ctx.Plan(); diags.HasErrors() { 11880 t.Fatalf("plan errors: %s", diags.Err()) 11881 } 11882 11883 state, diags = ctx.Apply() 11884 if diags.HasErrors() { 11885 t.Fatalf("apply errors: %s", diags.Err()) 11886 } 11887 11888 // Now change the variable value for sensitive_var 11889 ctx = testContext2(t, &ContextOpts{ 11890 Config: m, 11891 Providers: map[addrs.Provider]providers.Factory{ 11892 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11893 }, 11894 Variables: InputValues{ 11895 "sensitive_var": &InputValue{ 11896 Value: cty.StringVal("bar"), 11897 }, 11898 }, 11899 State: state, 11900 }) 11901 11902 if _, diags := ctx.Plan(); diags.HasErrors() { 11903 t.Fatalf("plan errors: %s", diags.Err()) 11904 } 11905 11906 _, diags = ctx.Apply() 11907 if diags.HasErrors() { 11908 t.Fatalf("apply errors: %s", diags.Err()) 11909 } 11910} 11911 11912func TestContext2Apply_variableSensitivityPropagation(t *testing.T) { 11913 m := testModuleInline(t, map[string]string{ 11914 "main.tf": ` 11915variable "sensitive_map" { 11916 type = map(string) 11917 default = { 11918 "x" = "foo" 11919 } 11920 sensitive = true 11921} 11922 11923resource "test_resource" "foo" { 11924 value = var.sensitive_map.x 11925} 11926`, 11927 }) 11928 11929 p := testProvider("test") 11930 p.PlanResourceChangeFn = testDiffFn 11931 11932 ctx := testContext2(t, &ContextOpts{ 11933 Config: m, 11934 Providers: map[addrs.Provider]providers.Factory{ 11935 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 11936 }, 11937 }) 11938 11939 plan, diags := ctx.Plan() 11940 if diags.HasErrors() { 11941 t.Fatalf("plan errors: %s", diags.Err()) 11942 } 11943 11944 verifySensitiveValue := func(pvms []cty.PathValueMarks) { 11945 if len(pvms) != 1 { 11946 t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) 11947 } 11948 pvm := pvms[0] 11949 if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) { 11950 t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) 11951 } 11952 if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) { 11953 t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) 11954 } 11955 } 11956 11957 addr := mustResourceInstanceAddr("test_resource.foo") 11958 fooChangeSrc := plan.Changes.ResourceInstance(addr) 11959 verifySensitiveValue(fooChangeSrc.AfterValMarks) 11960 11961 state, diags := ctx.Apply() 11962 if diags.HasErrors() { 11963 t.Fatalf("apply errors: %s", diags.Err()) 11964 } 11965 11966 fooState := state.ResourceInstance(addr) 11967 verifySensitiveValue(fooState.Current.AttrSensitivePaths) 11968} 11969 11970func TestContext2Apply_variableSensitivityProviders(t *testing.T) { 11971 m := testModuleInline(t, map[string]string{ 11972 "main.tf": ` 11973resource "test_resource" "foo" { 11974 sensitive_value = "should get marked" 11975} 11976 11977resource "test_resource" "bar" { 11978 value = test_resource.foo.sensitive_value 11979 random = test_resource.foo.id # not sensitive 11980 11981 nesting_single { 11982 value = "abc" 11983 sensitive_value = "xyz" 11984 } 11985} 11986 11987resource "test_resource" "baz" { 11988 value = test_resource.bar.nesting_single.sensitive_value 11989} 11990`, 11991 }) 11992 11993 p := testProvider("test") 11994 p.PlanResourceChangeFn = testDiffFn 11995 11996 ctx := testContext2(t, &ContextOpts{ 11997 Config: m, 11998 Providers: map[addrs.Provider]providers.Factory{ 11999 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12000 }, 12001 }) 12002 12003 plan, diags := ctx.Plan() 12004 if diags.HasErrors() { 12005 t.Fatalf("plan errors: %s", diags.Err()) 12006 } 12007 12008 verifySensitiveValue := func(pvms []cty.PathValueMarks) { 12009 if len(pvms) != 1 { 12010 t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) 12011 } 12012 pvm := pvms[0] 12013 if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) { 12014 t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) 12015 } 12016 if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) { 12017 t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) 12018 } 12019 } 12020 12021 // Sensitive attributes (defined by the provider) are marked 12022 // as sensitive when referenced from another resource 12023 // "bar" references sensitive resources in "foo" 12024 barAddr := mustResourceInstanceAddr("test_resource.bar") 12025 barChangeSrc := plan.Changes.ResourceInstance(barAddr) 12026 verifySensitiveValue(barChangeSrc.AfterValMarks) 12027 12028 bazAddr := mustResourceInstanceAddr("test_resource.baz") 12029 bazChangeSrc := plan.Changes.ResourceInstance(bazAddr) 12030 verifySensitiveValue(bazChangeSrc.AfterValMarks) 12031 12032 state, diags := ctx.Apply() 12033 if diags.HasErrors() { 12034 t.Fatalf("apply errors: %s", diags.Err()) 12035 } 12036 12037 barState := state.ResourceInstance(barAddr) 12038 verifySensitiveValue(barState.Current.AttrSensitivePaths) 12039 12040 bazState := state.ResourceInstance(bazAddr) 12041 verifySensitiveValue(bazState.Current.AttrSensitivePaths) 12042} 12043 12044func TestContext2Apply_variableSensitivityChange(t *testing.T) { 12045 m := testModuleInline(t, map[string]string{ 12046 "main.tf": ` 12047variable "sensitive_var" { 12048 default = "hello" 12049 sensitive = true 12050} 12051 12052resource "test_resource" "foo" { 12053 value = var.sensitive_var 12054}`, 12055 }) 12056 12057 p := testProvider("test") 12058 p.PlanResourceChangeFn = testDiffFn 12059 12060 ctx := testContext2(t, &ContextOpts{ 12061 Config: m, 12062 Providers: map[addrs.Provider]providers.Factory{ 12063 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12064 }, 12065 State: states.BuildState(func(s *states.SyncState) { 12066 s.SetResourceInstanceCurrent( 12067 addrs.Resource{ 12068 Mode: addrs.ManagedResourceMode, 12069 Type: "test_resource", 12070 Name: "foo", 12071 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 12072 &states.ResourceInstanceObjectSrc{ 12073 Status: states.ObjectReady, 12074 AttrsJSON: []byte(`{"id":"foo", "value":"hello"}`), 12075 // No AttrSensitivePaths present 12076 }, 12077 addrs.AbsProviderConfig{ 12078 Provider: addrs.NewDefaultProvider("test"), 12079 Module: addrs.RootModule, 12080 }, 12081 ) 12082 }), 12083 }) 12084 12085 _, diags := ctx.Plan() 12086 assertNoErrors(t, diags) 12087 12088 addr := mustResourceInstanceAddr("test_resource.foo") 12089 12090 state, diags := ctx.Apply() 12091 assertNoErrors(t, diags) 12092 12093 fooState := state.ResourceInstance(addr) 12094 12095 if len(fooState.Current.AttrSensitivePaths) != 1 { 12096 t.Fatalf("wrong number of sensitive paths, expected 1, got, %v", len(fooState.Current.AttrSensitivePaths)) 12097 } 12098 got := fooState.Current.AttrSensitivePaths[0] 12099 want := cty.PathValueMarks{ 12100 Path: cty.GetAttrPath("value"), 12101 Marks: cty.NewValueMarks("sensitive"), 12102 } 12103 12104 if !got.Equal(want) { 12105 t.Fatalf("wrong value marks; got:\n%#v\n\nwant:\n%#v\n", got, want) 12106 } 12107 12108 m2 := testModuleInline(t, map[string]string{ 12109 "main.tf": ` 12110variable "sensitive_var" { 12111 default = "hello" 12112 sensitive = false 12113} 12114 12115resource "test_resource" "foo" { 12116 value = var.sensitive_var 12117}`, 12118 }) 12119 12120 ctx2 := testContext2(t, &ContextOpts{ 12121 Config: m2, 12122 Providers: map[addrs.Provider]providers.Factory{ 12123 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12124 }, 12125 State: state, 12126 }) 12127 12128 _, diags = ctx2.Plan() 12129 assertNoErrors(t, diags) 12130 12131 stateWithoutSensitive, diags := ctx.Apply() 12132 assertNoErrors(t, diags) 12133 12134 fooState2 := stateWithoutSensitive.ResourceInstance(addr) 12135 if len(fooState2.Current.AttrSensitivePaths) > 0 { 12136 t.Fatalf("wrong number of sensitive paths, expected 0, got, %v", len(fooState2.Current.AttrSensitivePaths)) 12137 } 12138} 12139 12140func TestContext2Apply_moduleVariableOptionalAttributes(t *testing.T) { 12141 m := testModuleInline(t, map[string]string{ 12142 "main.tf": ` 12143terraform { 12144 experiments = [module_variable_optional_attrs] 12145} 12146 12147variable "in" { 12148 type = object({ 12149 required = string 12150 optional = optional(string) 12151 }) 12152} 12153 12154output "out" { 12155 value = var.in 12156} 12157`}) 12158 12159 ctx := testContext2(t, &ContextOpts{ 12160 Variables: InputValues{ 12161 "in": &InputValue{ 12162 Value: cty.MapVal(map[string]cty.Value{ 12163 "required": cty.StringVal("boop"), 12164 }), 12165 SourceType: ValueFromCaller, 12166 }, 12167 }, 12168 Config: m, 12169 }) 12170 12171 _, diags := ctx.Plan() 12172 if diags.HasErrors() { 12173 t.Fatal(diags.ErrWithWarnings()) 12174 } 12175 12176 state, diags := ctx.Apply() 12177 if diags.HasErrors() { 12178 t.Fatal(diags.ErrWithWarnings()) 12179 } 12180 12181 got := state.RootModule().OutputValues["out"].Value 12182 want := cty.ObjectVal(map[string]cty.Value{ 12183 "required": cty.StringVal("boop"), 12184 12185 // Because "optional" was marked as optional, it got silently filled 12186 // in as a null value of string type rather than returning an error. 12187 "optional": cty.NullVal(cty.String), 12188 }) 12189 if !want.RawEquals(got) { 12190 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, want) 12191 } 12192} 12193 12194func TestContext2Apply_provisionerSensitive(t *testing.T) { 12195 m := testModule(t, "apply-provisioner-sensitive") 12196 p := testProvider("aws") 12197 12198 pr := testProvisioner() 12199 pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { 12200 if req.Config.ContainsMarked() { 12201 t.Fatalf("unexpectedly marked config value: %#v", req.Config) 12202 } 12203 command := req.Config.GetAttr("command") 12204 if command.IsMarked() { 12205 t.Fatalf("unexpectedly marked command argument: %#v", command.Marks()) 12206 } 12207 req.UIOutput.Output(fmt.Sprintf("Executing: %q", command.AsString())) 12208 return 12209 } 12210 p.PlanResourceChangeFn = testDiffFn 12211 p.ApplyResourceChangeFn = testApplyFn 12212 12213 h := new(MockHook) 12214 ctx := testContext2(t, &ContextOpts{ 12215 Config: m, 12216 Hooks: []Hook{h}, 12217 Providers: map[addrs.Provider]providers.Factory{ 12218 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 12219 }, 12220 Provisioners: map[string]provisioners.Factory{ 12221 "shell": testProvisionerFuncFixed(pr), 12222 }, 12223 Variables: InputValues{ 12224 "password": &InputValue{ 12225 Value: cty.StringVal("secret"), 12226 SourceType: ValueFromCaller, 12227 }, 12228 }, 12229 }) 12230 12231 if _, diags := ctx.Plan(); diags.HasErrors() { 12232 logDiagnostics(t, diags) 12233 t.Fatal("plan failed") 12234 } 12235 12236 // "restart" provisioner 12237 pr.CloseCalled = false 12238 12239 state, diags := ctx.Apply() 12240 if diags.HasErrors() { 12241 logDiagnostics(t, diags) 12242 t.Fatal("apply failed") 12243 } 12244 12245 actual := strings.TrimSpace(state.String()) 12246 expected := strings.TrimSpace(testTerraformApplyProvisionerSensitiveStr) 12247 if actual != expected { 12248 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 12249 } 12250 12251 // Verify apply was invoked 12252 if !pr.ProvisionResourceCalled { 12253 t.Fatalf("provisioner was not called on apply") 12254 } 12255 12256 // Verify output was suppressed 12257 if !h.ProvisionOutputCalled { 12258 t.Fatalf("ProvisionOutput hook not called") 12259 } 12260 if got, doNotWant := h.ProvisionOutputMessage, "secret"; strings.Contains(got, doNotWant) { 12261 t.Errorf("sensitive value %q included in output:\n%s", doNotWant, got) 12262 } 12263 if got, want := h.ProvisionOutputMessage, "output suppressed"; !strings.Contains(got, want) { 12264 t.Errorf("expected hook to be called with %q, but was:\n%s", want, got) 12265 } 12266} 12267 12268func TestContext2Apply_warnings(t *testing.T) { 12269 m := testModuleInline(t, map[string]string{ 12270 "main.tf": ` 12271resource "test_resource" "foo" { 12272}`, 12273 }) 12274 12275 p := testProvider("test") 12276 p.PlanResourceChangeFn = testDiffFn 12277 12278 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 12279 resp := testApplyFn(req) 12280 12281 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warning")) 12282 return resp 12283 } 12284 12285 ctx := testContext2(t, &ContextOpts{ 12286 Config: m, 12287 Providers: map[addrs.Provider]providers.Factory{ 12288 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12289 }, 12290 }) 12291 12292 if _, diags := ctx.Plan(); diags.HasErrors() { 12293 t.Fatalf("plan errors: %s", diags.Err()) 12294 } 12295 12296 state, diags := ctx.Apply() 12297 if diags.HasErrors() { 12298 t.Fatalf("diags: %s", diags.Err()) 12299 } 12300 12301 inst := state.ResourceInstance(mustResourceInstanceAddr("test_resource.foo")) 12302 if inst == nil { 12303 t.Fatal("missing 'test_resource.foo' in state:", state) 12304 } 12305} 12306 12307func TestContext2Apply_rpcDiagnostics(t *testing.T) { 12308 m := testModuleInline(t, map[string]string{ 12309 "main.tf": ` 12310resource "test_instance" "a" { 12311} 12312`, 12313 }) 12314 12315 p := testProvider("test") 12316 p.PlanResourceChangeFn = testDiffFn 12317 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 12318 resp = testApplyFn(req) 12319 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("don't frobble")) 12320 return resp 12321 } 12322 12323 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 12324 ResourceTypes: map[string]*configschema.Block{ 12325 "test_instance": { 12326 Attributes: map[string]*configschema.Attribute{ 12327 "id": {Type: cty.String, Computed: true}, 12328 }, 12329 }, 12330 }, 12331 }) 12332 12333 ctx := testContext2(t, &ContextOpts{ 12334 Config: m, 12335 Providers: map[addrs.Provider]providers.Factory{ 12336 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12337 }, 12338 }) 12339 _, diags := ctx.Plan() 12340 if diags.HasErrors() { 12341 t.Fatal(diags.Err()) 12342 } 12343 12344 _, diags = ctx.Apply() 12345 if diags.HasErrors() { 12346 t.Fatal(diags.Err()) 12347 } 12348 12349 if len(diags) == 0 { 12350 t.Fatal("expected warnings") 12351 } 12352 12353 for _, d := range diags { 12354 des := d.Description().Summary 12355 if !strings.Contains(des, "frobble") { 12356 t.Fatalf(`expected frobble, got %q`, des) 12357 } 12358 } 12359} 12360 12361func TestContext2Apply_dataSensitive(t *testing.T) { 12362 m := testModule(t, "apply-data-sensitive") 12363 p := testProvider("null") 12364 p.PlanResourceChangeFn = testDiffFn 12365 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 12366 // add the required id 12367 m := req.Config.AsValueMap() 12368 m["id"] = cty.StringVal("foo") 12369 12370 return providers.ReadDataSourceResponse{ 12371 State: cty.ObjectVal(m), 12372 } 12373 } 12374 12375 ctx := testContext2(t, &ContextOpts{ 12376 Config: m, 12377 Providers: map[addrs.Provider]providers.Factory{ 12378 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 12379 }, 12380 }) 12381 12382 if p, diags := ctx.Plan(); diags.HasErrors() { 12383 t.Fatalf("diags: %s", diags.Err()) 12384 } else { 12385 t.Logf(legacyDiffComparisonString(p.Changes)) 12386 } 12387 12388 state, diags := ctx.Apply() 12389 assertNoErrors(t, diags) 12390 12391 addr := mustResourceInstanceAddr("data.null_data_source.testing") 12392 12393 dataSourceState := state.ResourceInstance(addr) 12394 pvms := dataSourceState.Current.AttrSensitivePaths 12395 if len(pvms) != 1 { 12396 t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) 12397 } 12398 pvm := pvms[0] 12399 if gotPath, wantPath := pvm.Path, cty.GetAttrPath("foo"); !gotPath.Equals(wantPath) { 12400 t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) 12401 } 12402 if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) { 12403 t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) 12404 } 12405} 12406 12407func TestContext2Apply_errorRestorePrivateData(t *testing.T) { 12408 // empty config to remove our resource 12409 m := testModuleInline(t, map[string]string{ 12410 "main.tf": "", 12411 }) 12412 12413 p := simpleMockProvider() 12414 p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ 12415 // we error during apply, which will trigger core to preserve the last 12416 // known state, including private data 12417 Diagnostics: tfdiags.Diagnostics(nil).Append(errors.New("oops")), 12418 } 12419 12420 addr := mustResourceInstanceAddr("test_object.a") 12421 12422 state := states.BuildState(func(s *states.SyncState) { 12423 s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ 12424 Status: states.ObjectReady, 12425 AttrsJSON: []byte(`{"id":"foo"}`), 12426 Private: []byte("private"), 12427 }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) 12428 }) 12429 12430 ctx := testContext2(t, &ContextOpts{ 12431 Config: m, 12432 State: state, 12433 Providers: map[addrs.Provider]providers.Factory{ 12434 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12435 }, 12436 }) 12437 12438 _, diags := ctx.Plan() 12439 if diags.HasErrors() { 12440 t.Fatal(diags.Err()) 12441 } 12442 12443 state, _ = ctx.Apply() 12444 if string(state.ResourceInstance(addr).Current.Private) != "private" { 12445 t.Fatal("missing private data in state") 12446 } 12447} 12448 12449func TestContext2Apply_errorRestoreStatus(t *testing.T) { 12450 // empty config to remove our resource 12451 m := testModuleInline(t, map[string]string{ 12452 "main.tf": "", 12453 }) 12454 12455 p := simpleMockProvider() 12456 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 12457 // We error during apply, but return the current object state. 12458 resp.Diagnostics = resp.Diagnostics.Append(errors.New("oops")) 12459 // return a warning too to make sure it isn't dropped 12460 resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warned")) 12461 resp.NewState = req.PriorState 12462 resp.Private = req.PlannedPrivate 12463 return resp 12464 } 12465 12466 addr := mustResourceInstanceAddr("test_object.a") 12467 12468 state := states.BuildState(func(s *states.SyncState) { 12469 s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ 12470 Status: states.ObjectTainted, 12471 AttrsJSON: []byte(`{"test_string":"foo"}`), 12472 Private: []byte("private"), 12473 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.b")}, 12474 }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) 12475 }) 12476 12477 ctx := testContext2(t, &ContextOpts{ 12478 Config: m, 12479 State: state, 12480 Providers: map[addrs.Provider]providers.Factory{ 12481 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12482 }, 12483 }) 12484 12485 _, diags := ctx.Plan() 12486 if diags.HasErrors() { 12487 t.Fatal(diags.Err()) 12488 } 12489 12490 state, diags = ctx.Apply() 12491 12492 errString := diags.ErrWithWarnings().Error() 12493 if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") { 12494 t.Fatalf("error missing expected info: %q", errString) 12495 } 12496 12497 if len(diags) != 2 { 12498 t.Fatalf("expected 1 error and 1 warning, got: %q", errString) 12499 } 12500 12501 res := state.ResourceInstance(addr) 12502 if res == nil { 12503 t.Fatal("resource was removed from state") 12504 } 12505 12506 if res.Current.Status != states.ObjectTainted { 12507 t.Fatal("resource should still be tainted in the state") 12508 } 12509 12510 if len(res.Current.Dependencies) != 1 || !res.Current.Dependencies[0].Equal(mustConfigResourceAddr("test_object.b")) { 12511 t.Fatalf("incorrect dependencies, got %q", res.Current.Dependencies) 12512 } 12513 12514 if string(res.Current.Private) != "private" { 12515 t.Fatalf("incorrect private data, got %q", res.Current.Private) 12516 } 12517} 12518 12519func TestContext2Apply_nonConformingResponse(t *testing.T) { 12520 // empty config to remove our resource 12521 m := testModuleInline(t, map[string]string{ 12522 "main.tf": ` 12523resource "test_object" "a" { 12524 test_string = "x" 12525} 12526`, 12527 }) 12528 12529 p := simpleMockProvider() 12530 respDiags := tfdiags.Diagnostics(nil).Append(tfdiags.SimpleWarning("warned")) 12531 respDiags = respDiags.Append(errors.New("oops")) 12532 p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ 12533 // Don't lose these diagnostics 12534 Diagnostics: respDiags, 12535 // This state is missing required attributes, and should produce an error 12536 NewState: cty.ObjectVal(map[string]cty.Value{ 12537 "test_string": cty.StringVal("x"), 12538 }), 12539 } 12540 12541 ctx := testContext2(t, &ContextOpts{ 12542 Config: m, 12543 Providers: map[addrs.Provider]providers.Factory{ 12544 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12545 }, 12546 }) 12547 12548 _, diags := ctx.Plan() 12549 if diags.HasErrors() { 12550 t.Fatal(diags.Err()) 12551 } 12552 12553 _, diags = ctx.Apply() 12554 errString := diags.ErrWithWarnings().Error() 12555 if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") { 12556 t.Fatalf("error missing expected info: %q", errString) 12557 } 12558 12559 // we should have more than the ones returned from the provider, and they 12560 // should not be coalesced into a single value 12561 if len(diags) < 3 { 12562 t.Fatalf("incorrect diagnostics, got %d values with %s", len(diags), diags.ErrWithWarnings()) 12563 } 12564} 12565 12566func TestContext2Apply_nilResponse(t *testing.T) { 12567 // empty config to remove our resource 12568 m := testModuleInline(t, map[string]string{ 12569 "main.tf": ` 12570resource "test_object" "a" { 12571} 12572`, 12573 }) 12574 12575 p := simpleMockProvider() 12576 p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{} 12577 12578 ctx := testContext2(t, &ContextOpts{ 12579 Config: m, 12580 Providers: map[addrs.Provider]providers.Factory{ 12581 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 12582 }, 12583 }) 12584 12585 _, diags := ctx.Plan() 12586 if diags.HasErrors() { 12587 t.Fatal(diags.Err()) 12588 } 12589 12590 _, diags = ctx.Apply() 12591 if !diags.HasErrors() { 12592 t.Fatal("expected and error") 12593 } 12594 12595 errString := diags.ErrWithWarnings().Error() 12596 if !strings.Contains(errString, "invalid nil value") { 12597 t.Fatalf("error missing expected info: %q", errString) 12598 } 12599} 12600 12601//////////////////////////////////////////////////////////////////////////////// 12602// NOTE: Due to the size of this file, new tests should be added to 12603// context_apply2_test.go. 12604//////////////////////////////////////////////////////////////////////////////// 12605