github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/context_apply_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "log" 9 "reflect" 10 "runtime" 11 "sort" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "testing" 16 "time" 17 18 "github.com/davecgh/go-spew/spew" 19 "github.com/go-test/deep" 20 "github.com/google/go-cmp/cmp" 21 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 22 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 23 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 24 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" 25 "github.com/hashicorp/terraform-plugin-sdk/internal/plans" 26 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 27 "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" 28 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 29 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 30 "github.com/zclconf/go-cty/cty" 31 ) 32 33 func TestContext2Apply_basic(t *testing.T) { 34 m := testModule(t, "apply-good") 35 p := testProvider("aws") 36 p.ApplyFn = testApplyFn 37 p.DiffFn = testDiffFn 38 ctx := testContext2(t, &ContextOpts{ 39 Config: m, 40 ProviderResolver: providers.ResolverFixed( 41 map[string]providers.Factory{ 42 "aws": testProviderFuncFixed(p), 43 }, 44 ), 45 }) 46 47 if _, diags := ctx.Plan(); diags.HasErrors() { 48 t.Fatalf("plan errors: %s", diags.Err()) 49 } 50 51 state, diags := ctx.Apply() 52 if diags.HasErrors() { 53 t.Fatalf("diags: %s", diags.Err()) 54 } 55 56 mod := state.RootModule() 57 if len(mod.Resources) < 2 { 58 t.Fatalf("bad: %#v", mod.Resources) 59 } 60 61 actual := strings.TrimSpace(state.String()) 62 expected := strings.TrimSpace(testTerraformApplyStr) 63 if actual != expected { 64 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 65 } 66 } 67 68 func TestContext2Apply_unstable(t *testing.T) { 69 // This tests behavior when the configuration contains an unstable value, 70 // such as the result of uuid() or timestamp(), where each call produces 71 // a different result. 72 // 73 // This is an important case to test because we need to ensure that 74 // we don't re-call the function during the apply phase: the value should 75 // be fixed during plan 76 77 m := testModule(t, "apply-unstable") 78 p := testProvider("test") 79 p.ApplyFn = testApplyFn 80 p.DiffFn = testDiffFn 81 ctx := testContext2(t, &ContextOpts{ 82 Config: m, 83 ProviderResolver: providers.ResolverFixed( 84 map[string]providers.Factory{ 85 "test": testProviderFuncFixed(p), 86 }, 87 ), 88 }) 89 90 plan, diags := ctx.Plan() 91 if diags.HasErrors() { 92 t.Fatalf("unexpected error during Plan: %s", diags.Err()) 93 } 94 95 addr := addrs.Resource{ 96 Mode: addrs.ManagedResourceMode, 97 Type: "test_resource", 98 Name: "foo", 99 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) 100 schema := p.GetSchemaReturn.ResourceTypes["test_resource"] // automatically available in mock 101 rds := plan.Changes.ResourceInstance(addr) 102 rd, err := rds.Decode(schema.ImpliedType()) 103 if err != nil { 104 t.Fatal(err) 105 } 106 if rd.After.GetAttr("random").IsKnown() { 107 t.Fatalf("Attribute 'random' has known value %#v; should be unknown in plan", rd.After.GetAttr("random")) 108 } 109 110 state, diags := ctx.Apply() 111 if diags.HasErrors() { 112 t.Fatalf("unexpected error during Apply: %s", diags.Err()) 113 } 114 115 mod := state.Module(addr.Module) 116 rss := state.ResourceInstance(addr) 117 118 if len(mod.Resources) != 1 { 119 t.Fatalf("wrong number of resources %d; want 1", len(mod.Resources)) 120 } 121 122 rs, err := rss.Current.Decode(schema.ImpliedType()) 123 got := rs.Value.GetAttr("random") 124 if !got.IsKnown() { 125 t.Fatalf("random is still unknown after apply") 126 } 127 if got, want := len(got.AsString()), 36; got != want { 128 t.Fatalf("random string has wrong length %d; want %d", got, want) 129 } 130 } 131 132 func TestContext2Apply_escape(t *testing.T) { 133 m := testModule(t, "apply-escape") 134 p := testProvider("aws") 135 p.ApplyFn = testApplyFn 136 p.DiffFn = testDiffFn 137 ctx := testContext2(t, &ContextOpts{ 138 Config: m, 139 ProviderResolver: providers.ResolverFixed( 140 map[string]providers.Factory{ 141 "aws": testProviderFuncFixed(p), 142 }, 143 ), 144 }) 145 146 if _, diags := ctx.Plan(); diags.HasErrors() { 147 t.Fatalf("plan errors: %s", diags.Err()) 148 } 149 150 state, diags := ctx.Apply() 151 if diags.HasErrors() { 152 t.Fatalf("diags: %s", diags.Err()) 153 } 154 155 checkStateString(t, state, ` 156 aws_instance.bar: 157 ID = foo 158 provider = provider.aws 159 foo = "bar" 160 type = aws_instance 161 `) 162 } 163 164 func TestContext2Apply_resourceCountOneList(t *testing.T) { 165 m := testModule(t, "apply-resource-count-one-list") 166 p := testProvider("null") 167 p.ApplyFn = testApplyFn 168 p.DiffFn = testDiffFn 169 ctx := testContext2(t, &ContextOpts{ 170 Config: m, 171 ProviderResolver: providers.ResolverFixed( 172 map[string]providers.Factory{ 173 "null": testProviderFuncFixed(p), 174 }, 175 ), 176 }) 177 178 if _, diags := ctx.Plan(); diags.HasErrors() { 179 t.Fatalf("plan errors: %s", diags.Err()) 180 } 181 182 state, diags := ctx.Apply() 183 assertNoDiagnostics(t, diags) 184 185 got := strings.TrimSpace(state.String()) 186 want := strings.TrimSpace(`null_resource.foo.0: 187 ID = foo 188 provider = provider.null 189 190 Outputs: 191 192 test = [foo]`) 193 if got != want { 194 t.Fatalf("got:\n%s\n\nwant:\n%s\n", got, want) 195 } 196 } 197 func TestContext2Apply_resourceCountZeroList(t *testing.T) { 198 m := testModule(t, "apply-resource-count-zero-list") 199 p := testProvider("null") 200 p.ApplyFn = testApplyFn 201 p.DiffFn = testDiffFn 202 ctx := testContext2(t, &ContextOpts{ 203 Config: m, 204 ProviderResolver: providers.ResolverFixed( 205 map[string]providers.Factory{ 206 "null": testProviderFuncFixed(p), 207 }, 208 ), 209 }) 210 211 if _, diags := ctx.Plan(); diags.HasErrors() { 212 t.Fatalf("plan errors: %s", diags.Err()) 213 } 214 215 state, diags := ctx.Apply() 216 if diags.HasErrors() { 217 t.Fatalf("diags: %s", diags.Err()) 218 } 219 220 got := strings.TrimSpace(state.String()) 221 want := strings.TrimSpace(`Outputs: 222 223 test = []`) 224 if got != want { 225 t.Fatalf("wrong state\n\ngot:\n%s\n\nwant:\n%s\n", got, want) 226 } 227 } 228 229 func TestContext2Apply_resourceDependsOnModule(t *testing.T) { 230 m := testModule(t, "apply-resource-depends-on-module") 231 p := testProvider("aws") 232 p.DiffFn = testDiffFn 233 234 // verify the apply happens in the correct order 235 var mu sync.Mutex 236 var order []string 237 238 p.ApplyFn = func( 239 info *InstanceInfo, 240 is *InstanceState, 241 id *InstanceDiff) (*InstanceState, error) { 242 243 if id.Attributes["ami"].New == "child" { 244 245 // make the child slower than the parent 246 time.Sleep(50 * time.Millisecond) 247 248 mu.Lock() 249 order = append(order, "child") 250 mu.Unlock() 251 } else { 252 mu.Lock() 253 order = append(order, "parent") 254 mu.Unlock() 255 } 256 257 return testApplyFn(info, is, id) 258 } 259 260 ctx := testContext2(t, &ContextOpts{ 261 Config: m, 262 ProviderResolver: providers.ResolverFixed( 263 map[string]providers.Factory{ 264 "aws": testProviderFuncFixed(p), 265 }, 266 ), 267 }) 268 269 if _, diags := ctx.Plan(); diags.HasErrors() { 270 t.Fatalf("plan errors: %s", diags.Err()) 271 } 272 273 state, diags := ctx.Apply() 274 if diags.HasErrors() { 275 t.Fatalf("diags: %s", diags.Err()) 276 } 277 278 if !reflect.DeepEqual(order, []string{"child", "parent"}) { 279 t.Fatal("resources applied out of order") 280 } 281 282 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleStr) 283 } 284 285 // Test that without a config, the Dependencies in the state are enough 286 // to maintain proper ordering. 287 func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) { 288 m := testModule(t, "apply-resource-depends-on-module-empty") 289 p := testProvider("aws") 290 p.DiffFn = testDiffFn 291 292 state := MustShimLegacyState(&State{ 293 Modules: []*ModuleState{ 294 { 295 Path: rootModulePath, 296 Resources: map[string]*ResourceState{ 297 "aws_instance.a": { 298 Type: "aws_instance", 299 Primary: &InstanceState{ 300 ID: "parent", 301 }, 302 Dependencies: []string{"module.child"}, 303 Provider: "provider.aws", 304 }, 305 }, 306 }, 307 { 308 Path: []string{"root", "child"}, 309 Resources: map[string]*ResourceState{ 310 "aws_instance.child": { 311 Type: "aws_instance", 312 Primary: &InstanceState{ 313 ID: "child", 314 }, 315 Provider: "provider.aws", 316 }, 317 }, 318 }, 319 }, 320 }) 321 322 { 323 // verify the apply happens in the correct order 324 var mu sync.Mutex 325 var order []string 326 327 p.ApplyFn = func( 328 info *InstanceInfo, 329 is *InstanceState, 330 id *InstanceDiff) (*InstanceState, error) { 331 332 if is.ID == "parent" { 333 // make the dep slower than the parent 334 time.Sleep(50 * time.Millisecond) 335 336 mu.Lock() 337 order = append(order, "child") 338 mu.Unlock() 339 } else { 340 mu.Lock() 341 order = append(order, "parent") 342 mu.Unlock() 343 } 344 345 return testApplyFn(info, is, id) 346 } 347 348 ctx := testContext2(t, &ContextOpts{ 349 Config: m, 350 ProviderResolver: providers.ResolverFixed( 351 map[string]providers.Factory{ 352 "aws": testProviderFuncFixed(p), 353 }, 354 ), 355 State: state, 356 }) 357 358 if _, diags := ctx.Plan(); diags.HasErrors() { 359 t.Fatalf("diags: %s", diags.Err()) 360 } 361 362 state, diags := ctx.Apply() 363 assertNoErrors(t, diags) 364 365 if !reflect.DeepEqual(order, []string{"child", "parent"}) { 366 t.Fatal("resources applied out of order") 367 } 368 369 checkStateString(t, state, "<no state>") 370 } 371 } 372 373 func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) { 374 m := testModule(t, "apply-resource-depends-on-module") 375 p := testProvider("aws") 376 p.DiffFn = testDiffFn 377 378 var globalState *states.State 379 { 380 p.ApplyFn = testApplyFn 381 ctx := testContext2(t, &ContextOpts{ 382 Config: m, 383 ProviderResolver: providers.ResolverFixed( 384 map[string]providers.Factory{ 385 "aws": testProviderFuncFixed(p), 386 }, 387 ), 388 }) 389 390 if _, diags := ctx.Plan(); diags.HasErrors() { 391 t.Fatalf("diags: %s", diags.Err()) 392 } 393 394 state, diags := ctx.Apply() 395 if diags.HasErrors() { 396 t.Fatalf("diags: %s", diags.Err()) 397 } 398 399 globalState = state 400 } 401 402 { 403 // Wait for the dependency, sleep, and verify the graph never 404 // called a child. 405 var called int32 406 var checked bool 407 p.ApplyFn = func( 408 info *InstanceInfo, 409 is *InstanceState, 410 id *InstanceDiff) (*InstanceState, error) { 411 412 if is.Attributes["ami"] == "parent" { 413 checked = true 414 415 // Sleep to allow parallel execution 416 time.Sleep(50 * time.Millisecond) 417 418 // Verify that called is 0 (dep not called) 419 if atomic.LoadInt32(&called) != 0 { 420 return nil, fmt.Errorf("module child should not be called") 421 } 422 } 423 424 atomic.AddInt32(&called, 1) 425 return testApplyFn(info, is, id) 426 } 427 428 ctx := testContext2(t, &ContextOpts{ 429 Config: m, 430 ProviderResolver: providers.ResolverFixed( 431 map[string]providers.Factory{ 432 "aws": testProviderFuncFixed(p), 433 }, 434 ), 435 State: globalState, 436 Destroy: true, 437 }) 438 439 if _, diags := ctx.Plan(); diags.HasErrors() { 440 t.Fatalf("diags: %s", diags.Err()) 441 } 442 443 state, diags := ctx.Apply() 444 if diags.HasErrors() { 445 t.Fatalf("diags: %s", diags.Err()) 446 } 447 448 if !checked { 449 t.Fatal("should check") 450 } 451 452 checkStateString(t, state, `<no state>`) 453 } 454 } 455 456 func TestContext2Apply_resourceDependsOnModuleGrandchild(t *testing.T) { 457 m := testModule(t, "apply-resource-depends-on-module-deep") 458 p := testProvider("aws") 459 p.DiffFn = testDiffFn 460 461 { 462 // Wait for the dependency, sleep, and verify the graph never 463 // called a child. 464 var called int32 465 var checked bool 466 p.ApplyFn = func( 467 info *InstanceInfo, 468 is *InstanceState, 469 id *InstanceDiff) (*InstanceState, error) { 470 471 if id.Attributes["ami"].New == "grandchild" { 472 checked = true 473 474 // Sleep to allow parallel execution 475 time.Sleep(50 * time.Millisecond) 476 477 // Verify that called is 0 (dep not called) 478 if atomic.LoadInt32(&called) != 0 { 479 return nil, fmt.Errorf("aws_instance.a should not be called") 480 } 481 } 482 483 atomic.AddInt32(&called, 1) 484 return testApplyFn(info, is, id) 485 } 486 487 ctx := testContext2(t, &ContextOpts{ 488 Config: m, 489 ProviderResolver: providers.ResolverFixed( 490 map[string]providers.Factory{ 491 "aws": testProviderFuncFixed(p), 492 }, 493 ), 494 }) 495 496 if _, diags := ctx.Plan(); diags.HasErrors() { 497 t.Fatalf("diags: %s", diags.Err()) 498 } 499 500 state, diags := ctx.Apply() 501 if diags.HasErrors() { 502 t.Fatalf("diags: %s", diags.Err()) 503 } 504 505 if !checked { 506 t.Fatal("should check") 507 } 508 509 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleDeepStr) 510 } 511 } 512 513 func TestContext2Apply_resourceDependsOnModuleInModule(t *testing.T) { 514 m := testModule(t, "apply-resource-depends-on-module-in-module") 515 p := testProvider("aws") 516 p.DiffFn = testDiffFn 517 518 { 519 // Wait for the dependency, sleep, and verify the graph never 520 // called a child. 521 var called int32 522 var checked bool 523 p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { 524 if id.Attributes["ami"].New == "grandchild" { 525 checked = true 526 527 // Sleep to allow parallel execution 528 time.Sleep(50 * time.Millisecond) 529 530 // Verify that called is 0 (dep not called) 531 if atomic.LoadInt32(&called) != 0 { 532 return nil, fmt.Errorf("something else was applied before grandchild; grandchild should be first") 533 } 534 } 535 536 atomic.AddInt32(&called, 1) 537 return testApplyFn(info, is, id) 538 } 539 540 ctx := testContext2(t, &ContextOpts{ 541 Config: m, 542 ProviderResolver: providers.ResolverFixed( 543 map[string]providers.Factory{ 544 "aws": testProviderFuncFixed(p), 545 }, 546 ), 547 }) 548 549 if _, diags := ctx.Plan(); diags.HasErrors() { 550 t.Fatalf("diags: %s", diags.Err()) 551 } 552 553 state, diags := ctx.Apply() 554 if diags.HasErrors() { 555 t.Fatalf("diags: %s", diags.Err()) 556 } 557 558 if !checked { 559 t.Fatal("should check") 560 } 561 562 checkStateString(t, state, testTerraformApplyResourceDependsOnModuleInModuleStr) 563 } 564 } 565 566 func TestContext2Apply_mapVarBetweenModules(t *testing.T) { 567 m := testModule(t, "apply-map-var-through-module") 568 p := testProvider("null") 569 p.ApplyFn = testApplyFn 570 p.DiffFn = testDiffFn 571 ctx := testContext2(t, &ContextOpts{ 572 Config: m, 573 ProviderResolver: providers.ResolverFixed( 574 map[string]providers.Factory{ 575 "null": testProviderFuncFixed(p), 576 }, 577 ), 578 }) 579 580 if _, diags := ctx.Plan(); diags.HasErrors() { 581 t.Fatalf("plan errors: %s", diags.Err()) 582 } 583 584 state, diags := ctx.Apply() 585 if diags.HasErrors() { 586 t.Fatalf("diags: %s", diags.Err()) 587 } 588 589 actual := strings.TrimSpace(state.String()) 590 expected := strings.TrimSpace(`<no state> 591 Outputs: 592 593 amis_from_module = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 } 594 595 module.test: 596 null_resource.noop: 597 ID = foo 598 provider = provider.null 599 600 Outputs: 601 602 amis_out = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }`) 603 if actual != expected { 604 t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual) 605 } 606 } 607 608 func TestContext2Apply_refCount(t *testing.T) { 609 m := testModule(t, "apply-ref-count") 610 p := testProvider("aws") 611 p.ApplyFn = testApplyFn 612 p.DiffFn = testDiffFn 613 ctx := testContext2(t, &ContextOpts{ 614 Config: m, 615 ProviderResolver: providers.ResolverFixed( 616 map[string]providers.Factory{ 617 "aws": testProviderFuncFixed(p), 618 }, 619 ), 620 }) 621 622 if _, diags := ctx.Plan(); diags.HasErrors() { 623 t.Fatalf("plan errors: %s", diags.Err()) 624 } 625 626 state, diags := ctx.Apply() 627 if diags.HasErrors() { 628 t.Fatalf("diags: %s", diags.Err()) 629 } 630 631 mod := state.RootModule() 632 if len(mod.Resources) < 2 { 633 t.Fatalf("bad: %#v", mod.Resources) 634 } 635 636 actual := strings.TrimSpace(state.String()) 637 expected := strings.TrimSpace(testTerraformApplyRefCountStr) 638 if actual != expected { 639 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 640 } 641 } 642 643 func TestContext2Apply_providerAlias(t *testing.T) { 644 m := testModule(t, "apply-provider-alias") 645 p := testProvider("aws") 646 p.ApplyFn = testApplyFn 647 p.DiffFn = testDiffFn 648 ctx := testContext2(t, &ContextOpts{ 649 Config: m, 650 ProviderResolver: providers.ResolverFixed( 651 map[string]providers.Factory{ 652 "aws": testProviderFuncFixed(p), 653 }, 654 ), 655 }) 656 657 if _, diags := ctx.Plan(); diags.HasErrors() { 658 t.Fatalf("plan errors: %s", diags.Err()) 659 } 660 661 state, diags := ctx.Apply() 662 if diags.HasErrors() { 663 t.Fatalf("diags: %s", diags.Err()) 664 } 665 666 mod := state.RootModule() 667 if len(mod.Resources) < 2 { 668 t.Fatalf("bad: %#v", mod.Resources) 669 } 670 671 actual := strings.TrimSpace(state.String()) 672 expected := strings.TrimSpace(testTerraformApplyProviderAliasStr) 673 if actual != expected { 674 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 675 } 676 } 677 678 // Two providers that are configured should both be configured prior to apply 679 func TestContext2Apply_providerAliasConfigure(t *testing.T) { 680 m := testModule(t, "apply-provider-alias-configure") 681 682 p2 := testProvider("another") 683 p2.ApplyFn = testApplyFn 684 p2.DiffFn = testDiffFn 685 686 ctx := testContext2(t, &ContextOpts{ 687 Config: m, 688 ProviderResolver: providers.ResolverFixed( 689 map[string]providers.Factory{ 690 "another": testProviderFuncFixed(p2), 691 }, 692 ), 693 }) 694 695 if p, diags := ctx.Plan(); diags.HasErrors() { 696 t.Fatalf("diags: %s", diags.Err()) 697 } else { 698 t.Logf(legacyDiffComparisonString(p.Changes)) 699 } 700 701 // Configure to record calls AFTER Plan above 702 var configCount int32 703 p2.ConfigureFn = func(c *ResourceConfig) error { 704 atomic.AddInt32(&configCount, 1) 705 706 foo, ok := c.Get("foo") 707 if !ok { 708 return fmt.Errorf("foo is not found") 709 } 710 711 if foo != "bar" { 712 return fmt.Errorf("foo: %#v", foo) 713 } 714 715 return nil 716 } 717 718 state, diags := ctx.Apply() 719 if diags.HasErrors() { 720 t.Fatalf("diags: %s", diags.Err()) 721 } 722 723 if configCount != 2 { 724 t.Fatalf("provider config expected 2 calls, got: %d", configCount) 725 } 726 727 actual := strings.TrimSpace(state.String()) 728 expected := strings.TrimSpace(testTerraformApplyProviderAliasConfigStr) 729 if actual != expected { 730 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 731 } 732 } 733 734 // GH-2870 735 func TestContext2Apply_providerWarning(t *testing.T) { 736 m := testModule(t, "apply-provider-warning") 737 p := testProvider("aws") 738 p.ApplyFn = testApplyFn 739 p.DiffFn = testDiffFn 740 p.ValidateFn = func(c *ResourceConfig) (ws []string, es []error) { 741 ws = append(ws, "Just a warning") 742 return 743 } 744 ctx := testContext2(t, &ContextOpts{ 745 Config: m, 746 ProviderResolver: providers.ResolverFixed( 747 map[string]providers.Factory{ 748 "aws": testProviderFuncFixed(p), 749 }, 750 ), 751 }) 752 753 if _, diags := ctx.Plan(); diags.HasErrors() { 754 t.Fatalf("plan errors: %s", diags.Err()) 755 } 756 757 state, diags := ctx.Apply() 758 if diags.HasErrors() { 759 t.Fatalf("diags: %s", diags.Err()) 760 } 761 762 actual := strings.TrimSpace(state.String()) 763 expected := strings.TrimSpace(` 764 aws_instance.foo: 765 ID = foo 766 provider = provider.aws 767 `) 768 if actual != expected { 769 t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected) 770 } 771 772 if !p.ConfigureCalled { 773 t.Fatalf("provider Configure() was never called!") 774 } 775 } 776 777 func TestContext2Apply_emptyModule(t *testing.T) { 778 m := testModule(t, "apply-empty-module") 779 p := testProvider("aws") 780 p.ApplyFn = testApplyFn 781 p.DiffFn = testDiffFn 782 ctx := testContext2(t, &ContextOpts{ 783 Config: m, 784 ProviderResolver: providers.ResolverFixed( 785 map[string]providers.Factory{ 786 "aws": testProviderFuncFixed(p), 787 }, 788 ), 789 }) 790 791 if _, diags := ctx.Plan(); diags.HasErrors() { 792 t.Fatalf("plan errors: %s", diags.Err()) 793 } 794 795 state, diags := ctx.Apply() 796 if diags.HasErrors() { 797 t.Fatalf("diags: %s", diags.Err()) 798 } 799 800 actual := strings.TrimSpace(state.String()) 801 actual = strings.Replace(actual, " ", "", -1) 802 expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr) 803 if actual != expected { 804 t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected) 805 } 806 } 807 808 func TestContext2Apply_createBeforeDestroy(t *testing.T) { 809 m := testModule(t, "apply-good-create-before") 810 p := testProvider("aws") 811 p.ApplyFn = testApplyFn 812 p.DiffFn = testDiffFn 813 state := MustShimLegacyState(&State{ 814 Modules: []*ModuleState{ 815 { 816 Path: rootModulePath, 817 Resources: map[string]*ResourceState{ 818 "aws_instance.bar": { 819 Type: "aws_instance", 820 Primary: &InstanceState{ 821 ID: "bar", 822 Attributes: map[string]string{ 823 "require_new": "abc", 824 }, 825 }, 826 }, 827 }, 828 }, 829 }, 830 }) 831 ctx := testContext2(t, &ContextOpts{ 832 Config: m, 833 ProviderResolver: providers.ResolverFixed( 834 map[string]providers.Factory{ 835 "aws": testProviderFuncFixed(p), 836 }, 837 ), 838 State: state, 839 }) 840 841 if p, diags := ctx.Plan(); diags.HasErrors() { 842 t.Fatalf("diags: %s", diags.Err()) 843 } else { 844 t.Logf(legacyDiffComparisonString(p.Changes)) 845 } 846 847 state, diags := ctx.Apply() 848 if diags.HasErrors() { 849 t.Fatalf("diags: %s", diags.Err()) 850 } 851 852 mod := state.RootModule() 853 if got, want := len(mod.Resources), 1; got != want { 854 t.Logf("state:\n%s", state) 855 t.Fatalf("wrong number of resources %d; want %d", got, want) 856 } 857 858 actual := strings.TrimSpace(state.String()) 859 expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr) 860 if actual != expected { 861 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 862 } 863 } 864 865 func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) { 866 m := testModule(t, "apply-good-create-before-update") 867 p := testProvider("aws") 868 p.ApplyFn = testApplyFn 869 p.DiffFn = testDiffFn 870 state := MustShimLegacyState(&State{ 871 Modules: []*ModuleState{ 872 { 873 Path: rootModulePath, 874 Resources: map[string]*ResourceState{ 875 "aws_instance.bar": { 876 Type: "aws_instance", 877 Primary: &InstanceState{ 878 ID: "bar", 879 Attributes: map[string]string{ 880 "foo": "bar", 881 }, 882 }, 883 }, 884 }, 885 }, 886 }, 887 }) 888 ctx := testContext2(t, &ContextOpts{ 889 Config: m, 890 ProviderResolver: providers.ResolverFixed( 891 map[string]providers.Factory{ 892 "aws": testProviderFuncFixed(p), 893 }, 894 ), 895 State: state, 896 }) 897 898 if p, diags := ctx.Plan(); diags.HasErrors() { 899 t.Fatalf("diags: %s", diags.Err()) 900 } else { 901 t.Logf(legacyDiffComparisonString(p.Changes)) 902 } 903 904 state, diags := ctx.Apply() 905 if diags.HasErrors() { 906 t.Fatalf("diags: %s", diags.Err()) 907 } 908 909 mod := state.RootModule() 910 if len(mod.Resources) != 1 { 911 t.Fatalf("bad: %s", state) 912 } 913 914 actual := strings.TrimSpace(state.String()) 915 expected := strings.TrimSpace(testTerraformApplyCreateBeforeUpdateStr) 916 if actual != expected { 917 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 918 } 919 } 920 921 // This tests that when a CBD resource depends on a non-CBD resource, 922 // we can still properly apply changes that require new for both. 923 func TestContext2Apply_createBeforeDestroy_dependsNonCBD(t *testing.T) { 924 m := testModule(t, "apply-cbd-depends-non-cbd") 925 p := testProvider("aws") 926 p.ApplyFn = testApplyFn 927 p.DiffFn = testDiffFn 928 state := MustShimLegacyState(&State{ 929 Modules: []*ModuleState{ 930 { 931 Path: rootModulePath, 932 Resources: map[string]*ResourceState{ 933 "aws_instance.bar": { 934 Type: "aws_instance", 935 Primary: &InstanceState{ 936 ID: "bar", 937 Attributes: map[string]string{ 938 "require_new": "abc", 939 }, 940 }, 941 }, 942 943 "aws_instance.foo": { 944 Type: "aws_instance", 945 Primary: &InstanceState{ 946 ID: "foo", 947 Attributes: map[string]string{ 948 "require_new": "abc", 949 }, 950 }, 951 }, 952 }, 953 }, 954 }, 955 }) 956 ctx := testContext2(t, &ContextOpts{ 957 Config: m, 958 ProviderResolver: providers.ResolverFixed( 959 map[string]providers.Factory{ 960 "aws": testProviderFuncFixed(p), 961 }, 962 ), 963 State: state, 964 }) 965 966 if p, diags := ctx.Plan(); diags.HasErrors() { 967 t.Fatalf("diags: %s", diags.Err()) 968 } else { 969 t.Logf(legacyDiffComparisonString(p.Changes)) 970 } 971 972 state, diags := ctx.Apply() 973 if diags.HasErrors() { 974 t.Fatalf("diags: %s", diags.Err()) 975 } 976 977 checkStateString(t, state, ` 978 aws_instance.bar: 979 ID = foo 980 provider = provider.aws 981 require_new = yes 982 type = aws_instance 983 value = foo 984 985 Dependencies: 986 aws_instance.foo 987 aws_instance.foo: 988 ID = foo 989 provider = provider.aws 990 require_new = yes 991 type = aws_instance 992 `) 993 } 994 995 func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) { 996 h := new(MockHook) 997 m := testModule(t, "apply-good-create-before") 998 p := testProvider("aws") 999 p.ApplyFn = testApplyFn 1000 p.DiffFn = testDiffFn 1001 state := MustShimLegacyState(&State{ 1002 Modules: []*ModuleState{ 1003 { 1004 Path: rootModulePath, 1005 Resources: map[string]*ResourceState{ 1006 "aws_instance.bar": { 1007 Type: "aws_instance", 1008 Primary: &InstanceState{ 1009 ID: "bar", 1010 Attributes: map[string]string{ 1011 "require_new": "abc", 1012 }, 1013 }, 1014 Provider: "provider.aws", 1015 }, 1016 }, 1017 }, 1018 }, 1019 }) 1020 1021 var actual []cty.Value 1022 var actualLock sync.Mutex 1023 h.PostApplyFn = func(addr addrs.AbsResourceInstance, gen states.Generation, sv cty.Value, e error) (HookAction, error) { 1024 actualLock.Lock() 1025 1026 defer actualLock.Unlock() 1027 actual = append(actual, sv) 1028 return HookActionContinue, nil 1029 } 1030 1031 ctx := testContext2(t, &ContextOpts{ 1032 Config: m, 1033 Hooks: []Hook{h}, 1034 ProviderResolver: providers.ResolverFixed( 1035 map[string]providers.Factory{ 1036 "aws": testProviderFuncFixed(p), 1037 }, 1038 ), 1039 State: state, 1040 }) 1041 1042 if p, diags := ctx.Plan(); diags.HasErrors() { 1043 t.Fatalf("diags: %s", diags.Err()) 1044 } else { 1045 t.Logf(legacyDiffComparisonString(p.Changes)) 1046 } 1047 1048 if _, diags := ctx.Apply(); diags.HasErrors() { 1049 t.Fatalf("apply errors: %s", diags.Err()) 1050 } 1051 1052 expected := []cty.Value{ 1053 cty.ObjectVal(map[string]cty.Value{ 1054 "id": cty.StringVal("foo"), 1055 "require_new": cty.StringVal("xyz"), 1056 "type": cty.StringVal("aws_instance"), 1057 }), 1058 cty.NullVal(cty.DynamicPseudoType), 1059 } 1060 1061 cmpOpt := cmp.Transformer("ctyshim", hcl2shim.ConfigValueFromHCL2) 1062 if !cmp.Equal(actual, expected, cmpOpt) { 1063 t.Fatalf("wrong state snapshot sequence\n%s", cmp.Diff(expected, actual, cmpOpt)) 1064 } 1065 } 1066 1067 // Test that we can perform an apply with CBD in a count with deposed instances. 1068 func TestContext2Apply_createBeforeDestroy_deposedCount(t *testing.T) { 1069 m := testModule(t, "apply-cbd-count") 1070 p := testProvider("aws") 1071 p.ApplyFn = testApplyFn 1072 p.DiffFn = testDiffFn 1073 1074 state := MustShimLegacyState(&State{ 1075 Modules: []*ModuleState{ 1076 { 1077 Path: rootModulePath, 1078 Resources: map[string]*ResourceState{ 1079 "aws_instance.bar.0": { 1080 Type: "aws_instance", 1081 Primary: &InstanceState{ 1082 ID: "bar", 1083 Tainted: true, 1084 }, 1085 1086 Deposed: []*InstanceState{ 1087 { 1088 ID: "foo", 1089 }, 1090 }, 1091 }, 1092 "aws_instance.bar.1": { 1093 Type: "aws_instance", 1094 Primary: &InstanceState{ 1095 ID: "bar", 1096 Tainted: true, 1097 }, 1098 1099 Deposed: []*InstanceState{ 1100 { 1101 ID: "bar", 1102 }, 1103 }, 1104 }, 1105 }, 1106 }, 1107 }, 1108 }) 1109 1110 ctx := testContext2(t, &ContextOpts{ 1111 Config: m, 1112 ProviderResolver: providers.ResolverFixed( 1113 map[string]providers.Factory{ 1114 "aws": testProviderFuncFixed(p), 1115 }, 1116 ), 1117 State: state, 1118 }) 1119 1120 if p, diags := ctx.Plan(); diags.HasErrors() { 1121 t.Fatalf("diags: %s", diags.Err()) 1122 } else { 1123 t.Logf(legacyDiffComparisonString(p.Changes)) 1124 } 1125 1126 state, diags := ctx.Apply() 1127 if diags.HasErrors() { 1128 t.Fatalf("diags: %s", diags.Err()) 1129 } 1130 1131 checkStateString(t, state, ` 1132 aws_instance.bar.0: 1133 ID = foo 1134 provider = provider.aws 1135 foo = bar 1136 type = aws_instance 1137 aws_instance.bar.1: 1138 ID = foo 1139 provider = provider.aws 1140 foo = bar 1141 type = aws_instance 1142 `) 1143 } 1144 1145 // Test that when we have a deposed instance but a good primary, we still 1146 // destroy the deposed instance. 1147 func TestContext2Apply_createBeforeDestroy_deposedOnly(t *testing.T) { 1148 m := testModule(t, "apply-cbd-deposed-only") 1149 p := testProvider("aws") 1150 p.ApplyFn = testApplyFn 1151 p.DiffFn = testDiffFn 1152 1153 state := MustShimLegacyState(&State{ 1154 Modules: []*ModuleState{ 1155 { 1156 Path: rootModulePath, 1157 Resources: map[string]*ResourceState{ 1158 "aws_instance.bar": { 1159 Type: "aws_instance", 1160 Primary: &InstanceState{ 1161 ID: "bar", 1162 }, 1163 1164 Deposed: []*InstanceState{ 1165 { 1166 ID: "foo", 1167 }, 1168 }, 1169 Provider: "provider.aws", 1170 }, 1171 }, 1172 }, 1173 }, 1174 }) 1175 1176 ctx := testContext2(t, &ContextOpts{ 1177 Config: m, 1178 ProviderResolver: providers.ResolverFixed( 1179 map[string]providers.Factory{ 1180 "aws": testProviderFuncFixed(p), 1181 }, 1182 ), 1183 State: state, 1184 }) 1185 1186 if p, diags := ctx.Plan(); diags.HasErrors() { 1187 t.Fatalf("diags: %s", diags.Err()) 1188 } else { 1189 t.Logf(legacyDiffComparisonString(p.Changes)) 1190 } 1191 1192 state, diags := ctx.Apply() 1193 if diags.HasErrors() { 1194 t.Fatalf("diags: %s", diags.Err()) 1195 } 1196 1197 checkStateString(t, state, ` 1198 aws_instance.bar: 1199 ID = bar 1200 provider = provider.aws 1201 `) 1202 } 1203 1204 func TestContext2Apply_destroyComputed(t *testing.T) { 1205 m := testModule(t, "apply-destroy-computed") 1206 p := testProvider("aws") 1207 p.ApplyFn = testApplyFn 1208 p.DiffFn = testDiffFn 1209 state := MustShimLegacyState(&State{ 1210 Modules: []*ModuleState{ 1211 { 1212 Path: rootModulePath, 1213 Resources: map[string]*ResourceState{ 1214 "aws_instance.foo": { 1215 Type: "aws_instance", 1216 Primary: &InstanceState{ 1217 ID: "foo", 1218 Attributes: map[string]string{ 1219 "output": "value", 1220 }, 1221 }, 1222 Provider: "provider.aws", 1223 }, 1224 }, 1225 }, 1226 }, 1227 }) 1228 ctx := testContext2(t, &ContextOpts{ 1229 Config: m, 1230 ProviderResolver: providers.ResolverFixed( 1231 map[string]providers.Factory{ 1232 "aws": testProviderFuncFixed(p), 1233 }, 1234 ), 1235 State: state, 1236 Destroy: true, 1237 }) 1238 1239 if p, diags := ctx.Plan(); diags.HasErrors() { 1240 logDiagnostics(t, diags) 1241 t.Fatal("plan failed") 1242 } else { 1243 t.Logf("plan:\n\n%s", legacyDiffComparisonString(p.Changes)) 1244 } 1245 1246 if _, diags := ctx.Apply(); diags.HasErrors() { 1247 logDiagnostics(t, diags) 1248 t.Fatal("apply failed") 1249 } 1250 } 1251 1252 // Test that the destroy operation uses depends_on as a source of ordering. 1253 func TestContext2Apply_destroyDependsOn(t *testing.T) { 1254 // It is possible for this to be racy, so we loop a number of times 1255 // just to check. 1256 for i := 0; i < 10; i++ { 1257 testContext2Apply_destroyDependsOn(t) 1258 } 1259 } 1260 1261 func testContext2Apply_destroyDependsOn(t *testing.T) { 1262 m := testModule(t, "apply-destroy-depends-on") 1263 p := testProvider("aws") 1264 p.ApplyFn = testApplyFn 1265 p.DiffFn = testDiffFn 1266 state := MustShimLegacyState(&State{ 1267 Modules: []*ModuleState{ 1268 { 1269 Path: rootModulePath, 1270 Resources: map[string]*ResourceState{ 1271 "aws_instance.foo": { 1272 Type: "aws_instance", 1273 Primary: &InstanceState{ 1274 ID: "foo", 1275 Attributes: map[string]string{}, 1276 }, 1277 }, 1278 1279 "aws_instance.bar": { 1280 Type: "aws_instance", 1281 Primary: &InstanceState{ 1282 ID: "bar", 1283 Attributes: map[string]string{}, 1284 }, 1285 }, 1286 }, 1287 }, 1288 }, 1289 }) 1290 1291 // Record the order we see Apply 1292 var actual []string 1293 var actualLock sync.Mutex 1294 p.ApplyFn = func( 1295 _ *InstanceInfo, is *InstanceState, _ *InstanceDiff) (*InstanceState, error) { 1296 actualLock.Lock() 1297 defer actualLock.Unlock() 1298 actual = append(actual, is.ID) 1299 return nil, nil 1300 } 1301 1302 ctx := testContext2(t, &ContextOpts{ 1303 Config: m, 1304 ProviderResolver: providers.ResolverFixed( 1305 map[string]providers.Factory{ 1306 "aws": testProviderFuncFixed(p), 1307 }, 1308 ), 1309 State: state, 1310 Destroy: true, 1311 Parallelism: 1, // To check ordering 1312 }) 1313 1314 if _, diags := ctx.Plan(); diags.HasErrors() { 1315 t.Fatalf("plan errors: %s", diags.Err()) 1316 } 1317 1318 if _, diags := ctx.Apply(); diags.HasErrors() { 1319 t.Fatalf("apply errors: %s", diags.Err()) 1320 } 1321 1322 expected := []string{"foo", "bar"} 1323 if !reflect.DeepEqual(actual, expected) { 1324 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1325 } 1326 } 1327 1328 // Test that destroy ordering is correct with dependencies only 1329 // in the state. 1330 func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) { 1331 // It is possible for this to be racy, so we loop a number of times 1332 // just to check. 1333 for i := 0; i < 10; i++ { 1334 testContext2Apply_destroyDependsOnStateOnly(t) 1335 } 1336 } 1337 1338 func testContext2Apply_destroyDependsOnStateOnly(t *testing.T) { 1339 m := testModule(t, "empty") 1340 p := testProvider("aws") 1341 p.ApplyFn = testApplyFn 1342 p.DiffFn = testDiffFn 1343 state := MustShimLegacyState(&State{ 1344 Modules: []*ModuleState{ 1345 { 1346 Path: rootModulePath, 1347 Resources: map[string]*ResourceState{ 1348 "aws_instance.foo": { 1349 Type: "aws_instance", 1350 Primary: &InstanceState{ 1351 ID: "foo", 1352 Attributes: map[string]string{}, 1353 }, 1354 Provider: "provider.aws", 1355 }, 1356 1357 "aws_instance.bar": { 1358 Type: "aws_instance", 1359 Primary: &InstanceState{ 1360 ID: "bar", 1361 Attributes: map[string]string{}, 1362 }, 1363 Dependencies: []string{"aws_instance.foo"}, 1364 Provider: "provider.aws", 1365 }, 1366 }, 1367 }, 1368 }, 1369 }) 1370 1371 // Record the order we see Apply 1372 var actual []string 1373 var actualLock sync.Mutex 1374 p.ApplyFn = func( 1375 _ *InstanceInfo, is *InstanceState, _ *InstanceDiff) (*InstanceState, error) { 1376 actualLock.Lock() 1377 defer actualLock.Unlock() 1378 actual = append(actual, is.ID) 1379 return nil, nil 1380 } 1381 1382 ctx := testContext2(t, &ContextOpts{ 1383 Config: m, 1384 ProviderResolver: providers.ResolverFixed( 1385 map[string]providers.Factory{ 1386 "aws": testProviderFuncFixed(p), 1387 }, 1388 ), 1389 State: state, 1390 Destroy: true, 1391 Parallelism: 1, // To check ordering 1392 }) 1393 1394 if _, diags := ctx.Plan(); diags.HasErrors() { 1395 t.Fatalf("plan errors: %s", diags.Err()) 1396 } 1397 1398 if _, diags := ctx.Apply(); diags.HasErrors() { 1399 t.Fatalf("apply errors: %s", diags.Err()) 1400 } 1401 1402 expected := []string{"bar", "foo"} 1403 if !reflect.DeepEqual(actual, expected) { 1404 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1405 } 1406 } 1407 1408 // Test that destroy ordering is correct with dependencies only 1409 // in the state within a module (GH-11749) 1410 func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) { 1411 // It is possible for this to be racy, so we loop a number of times 1412 // just to check. 1413 for i := 0; i < 10; i++ { 1414 testContext2Apply_destroyDependsOnStateOnlyModule(t) 1415 } 1416 } 1417 1418 func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) { 1419 m := testModule(t, "empty") 1420 p := testProvider("aws") 1421 p.ApplyFn = testApplyFn 1422 p.DiffFn = testDiffFn 1423 state := MustShimLegacyState(&State{ 1424 Modules: []*ModuleState{ 1425 { 1426 Path: []string{"root", "child"}, 1427 Resources: map[string]*ResourceState{ 1428 "aws_instance.foo": { 1429 Type: "aws_instance", 1430 Primary: &InstanceState{ 1431 ID: "foo", 1432 Attributes: map[string]string{}, 1433 }, 1434 Provider: "provider.aws", 1435 }, 1436 1437 "aws_instance.bar": { 1438 Type: "aws_instance", 1439 Primary: &InstanceState{ 1440 ID: "bar", 1441 Attributes: map[string]string{}, 1442 }, 1443 Dependencies: []string{"aws_instance.foo"}, 1444 Provider: "provider.aws", 1445 }, 1446 }, 1447 }, 1448 }, 1449 }) 1450 1451 // Record the order we see Apply 1452 var actual []string 1453 var actualLock sync.Mutex 1454 p.ApplyFn = func( 1455 _ *InstanceInfo, is *InstanceState, _ *InstanceDiff) (*InstanceState, error) { 1456 actualLock.Lock() 1457 defer actualLock.Unlock() 1458 actual = append(actual, is.ID) 1459 return nil, nil 1460 } 1461 1462 ctx := testContext2(t, &ContextOpts{ 1463 Config: m, 1464 ProviderResolver: providers.ResolverFixed( 1465 map[string]providers.Factory{ 1466 "aws": testProviderFuncFixed(p), 1467 }, 1468 ), 1469 State: state, 1470 Destroy: true, 1471 Parallelism: 1, // To check ordering 1472 }) 1473 1474 if _, diags := ctx.Plan(); diags.HasErrors() { 1475 t.Fatalf("plan errors: %s", diags.Err()) 1476 } 1477 1478 if _, diags := ctx.Apply(); diags.HasErrors() { 1479 t.Fatalf("apply errors: %s", diags.Err()) 1480 } 1481 1482 expected := []string{"bar", "foo"} 1483 if !reflect.DeepEqual(actual, expected) { 1484 t.Fatalf("wrong order\ngot: %#v\nwant: %#v", actual, expected) 1485 } 1486 } 1487 1488 func TestContext2Apply_dataBasic(t *testing.T) { 1489 m := testModule(t, "apply-data-basic") 1490 p := testProvider("null") 1491 p.ApplyFn = testApplyFn 1492 p.DiffFn = testDiffFn 1493 p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ 1494 State: cty.ObjectVal(map[string]cty.Value{ 1495 "id": cty.StringVal("yo"), 1496 "foo": cty.NullVal(cty.String), 1497 }), 1498 } 1499 1500 ctx := testContext2(t, &ContextOpts{ 1501 Config: m, 1502 ProviderResolver: providers.ResolverFixed( 1503 map[string]providers.Factory{ 1504 "null": testProviderFuncFixed(p), 1505 }, 1506 ), 1507 }) 1508 1509 if p, diags := ctx.Plan(); diags.HasErrors() { 1510 t.Fatalf("diags: %s", diags.Err()) 1511 } else { 1512 t.Logf(legacyDiffComparisonString(p.Changes)) 1513 } 1514 1515 state, diags := ctx.Apply() 1516 assertNoErrors(t, diags) 1517 1518 actual := strings.TrimSpace(state.String()) 1519 expected := strings.TrimSpace(testTerraformApplyDataBasicStr) 1520 if actual != expected { 1521 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1522 } 1523 } 1524 1525 func TestContext2Apply_destroyData(t *testing.T) { 1526 m := testModule(t, "apply-destroy-data-resource") 1527 p := testProvider("null") 1528 p.ApplyFn = testApplyFn 1529 p.DiffFn = testDiffFn 1530 state := MustShimLegacyState(&State{ 1531 Modules: []*ModuleState{ 1532 { 1533 Path: rootModulePath, 1534 Resources: map[string]*ResourceState{ 1535 "data.null_data_source.testing": { 1536 Type: "null_data_source", 1537 Primary: &InstanceState{ 1538 ID: "-", 1539 Attributes: map[string]string{ 1540 "inputs.#": "1", 1541 "inputs.test": "yes", 1542 }, 1543 }, 1544 }, 1545 }, 1546 }, 1547 }, 1548 }) 1549 hook := &testHook{} 1550 ctx := testContext2(t, &ContextOpts{ 1551 Config: m, 1552 ProviderResolver: providers.ResolverFixed( 1553 map[string]providers.Factory{ 1554 "null": testProviderFuncFixed(p), 1555 }, 1556 ), 1557 State: state, 1558 Destroy: true, 1559 Hooks: []Hook{hook}, 1560 }) 1561 1562 if p, diags := ctx.Plan(); diags.HasErrors() { 1563 t.Fatalf("diags: %s", diags.Err()) 1564 } else { 1565 t.Logf(legacyDiffComparisonString(p.Changes)) 1566 } 1567 1568 newState, diags := ctx.Apply() 1569 if diags.HasErrors() { 1570 t.Fatalf("diags: %s", diags.Err()) 1571 } 1572 1573 if got := len(newState.Modules); got != 1 { 1574 t.Fatalf("state has %d modules after destroy; want 1", got) 1575 } 1576 1577 if got := len(newState.RootModule().Resources); got != 0 { 1578 t.Fatalf("state has %d resources after destroy; want 0", got) 1579 } 1580 1581 wantHookCalls := []*testHookCall{ 1582 {"PreDiff", "data.null_data_source.testing"}, 1583 {"PostDiff", "data.null_data_source.testing"}, 1584 {"PostStateUpdate", ""}, 1585 } 1586 if !reflect.DeepEqual(hook.Calls, wantHookCalls) { 1587 t.Errorf("wrong hook calls\ngot: %swant: %s", spew.Sdump(hook.Calls), spew.Sdump(wantHookCalls)) 1588 } 1589 } 1590 1591 // https://github.com/hashicorp/terraform-plugin-sdk/pull/5096 1592 func TestContext2Apply_destroySkipsCBD(t *testing.T) { 1593 // Config contains CBD resource depending on non-CBD resource, which triggers 1594 // a cycle if they are both replaced, but should _not_ trigger a cycle when 1595 // just doing a `terraform destroy`. 1596 m := testModule(t, "apply-destroy-cbd") 1597 p := testProvider("aws") 1598 p.ApplyFn = testApplyFn 1599 p.DiffFn = testDiffFn 1600 state := MustShimLegacyState(&State{ 1601 Modules: []*ModuleState{ 1602 { 1603 Path: rootModulePath, 1604 Resources: map[string]*ResourceState{ 1605 "aws_instance.foo": { 1606 Type: "aws_instance", 1607 Primary: &InstanceState{ 1608 ID: "foo", 1609 }, 1610 }, 1611 "aws_instance.bar": { 1612 Type: "aws_instance", 1613 Primary: &InstanceState{ 1614 ID: "foo", 1615 }, 1616 }, 1617 }, 1618 }, 1619 }, 1620 }) 1621 ctx := testContext2(t, &ContextOpts{ 1622 Config: m, 1623 ProviderResolver: providers.ResolverFixed( 1624 map[string]providers.Factory{ 1625 "aws": testProviderFuncFixed(p), 1626 }, 1627 ), 1628 State: state, 1629 Destroy: true, 1630 }) 1631 1632 if p, diags := ctx.Plan(); diags.HasErrors() { 1633 t.Fatalf("diags: %s", diags.Err()) 1634 } else { 1635 t.Logf(legacyDiffComparisonString(p.Changes)) 1636 } 1637 1638 if _, diags := ctx.Apply(); diags.HasErrors() { 1639 t.Fatalf("apply errors: %s", diags.Err()) 1640 } 1641 } 1642 1643 func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) { 1644 m := testModule(t, "apply-destroy-mod-var-provider-config") 1645 p := testProvider("aws") 1646 p.ApplyFn = testApplyFn 1647 p.DiffFn = testDiffFn 1648 state := MustShimLegacyState(&State{ 1649 Modules: []*ModuleState{ 1650 { 1651 Path: []string{"root", "child"}, 1652 Resources: map[string]*ResourceState{ 1653 "aws_instance.foo": { 1654 Type: "aws_instance", 1655 Primary: &InstanceState{ 1656 ID: "foo", 1657 }, 1658 }, 1659 }, 1660 }, 1661 }, 1662 }) 1663 ctx := testContext2(t, &ContextOpts{ 1664 Config: m, 1665 ProviderResolver: providers.ResolverFixed( 1666 map[string]providers.Factory{ 1667 "aws": testProviderFuncFixed(p), 1668 }, 1669 ), 1670 State: state, 1671 Destroy: true, 1672 }) 1673 1674 if _, diags := ctx.Plan(); diags.HasErrors() { 1675 t.Fatalf("plan errors: %s", diags.Err()) 1676 } 1677 1678 _, diags := ctx.Apply() 1679 if diags.HasErrors() { 1680 t.Fatalf("diags: %s", diags.Err()) 1681 } 1682 } 1683 1684 // https://github.com/hashicorp/terraform-plugin-sdk/issues/2892 1685 func TestContext2Apply_destroyCrossProviders(t *testing.T) { 1686 m := testModule(t, "apply-destroy-cross-providers") 1687 1688 p_aws := testProvider("aws") 1689 p_aws.ApplyFn = testApplyFn 1690 p_aws.DiffFn = testDiffFn 1691 p_aws.GetSchemaReturn = &ProviderSchema{ 1692 ResourceTypes: map[string]*configschema.Block{ 1693 "aws_instance": { 1694 Attributes: map[string]*configschema.Attribute{ 1695 "id": { 1696 Type: cty.String, 1697 Computed: true, 1698 }, 1699 }, 1700 }, 1701 "aws_vpc": { 1702 Attributes: map[string]*configschema.Attribute{ 1703 "value": { 1704 Type: cty.String, 1705 Optional: true, 1706 }, 1707 }, 1708 }, 1709 }, 1710 } 1711 1712 providers := map[string]providers.Factory{ 1713 "aws": testProviderFuncFixed(p_aws), 1714 } 1715 1716 // Bug only appears from time to time, 1717 // so we run this test multiple times 1718 // to check for the race-condition 1719 1720 // FIXME: this test flaps now, so run it more times 1721 for i := 0; i <= 100; i++ { 1722 ctx := getContextForApply_destroyCrossProviders(t, m, providers) 1723 1724 if _, diags := ctx.Plan(); diags.HasErrors() { 1725 logDiagnostics(t, diags) 1726 t.Fatal("plan failed") 1727 } 1728 1729 if _, diags := ctx.Apply(); diags.HasErrors() { 1730 logDiagnostics(t, diags) 1731 t.Fatal("apply failed") 1732 } 1733 } 1734 } 1735 1736 func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, providerFactories map[string]providers.Factory) *Context { 1737 state := MustShimLegacyState(&State{ 1738 Modules: []*ModuleState{ 1739 { 1740 Path: rootModulePath, 1741 Resources: map[string]*ResourceState{ 1742 "aws_instance.shared": { 1743 Type: "aws_instance", 1744 Primary: &InstanceState{ 1745 ID: "remote-2652591293", 1746 Attributes: map[string]string{ 1747 "id": "test", 1748 }, 1749 }, 1750 Provider: "provider.aws", 1751 }, 1752 }, 1753 }, 1754 { 1755 Path: []string{"root", "child"}, 1756 Resources: map[string]*ResourceState{ 1757 "aws_vpc.bar": { 1758 Type: "aws_vpc", 1759 Primary: &InstanceState{ 1760 ID: "vpc-aaabbb12", 1761 Attributes: map[string]string{ 1762 "value": "test", 1763 }, 1764 }, 1765 Provider: "provider.aws", 1766 }, 1767 }, 1768 }, 1769 }, 1770 }) 1771 ctx := testContext2(t, &ContextOpts{ 1772 Config: m, 1773 ProviderResolver: providers.ResolverFixed(providerFactories), 1774 State: state, 1775 Destroy: true, 1776 }) 1777 1778 return ctx 1779 } 1780 1781 func TestContext2Apply_minimal(t *testing.T) { 1782 m := testModule(t, "apply-minimal") 1783 p := testProvider("aws") 1784 p.ApplyFn = testApplyFn 1785 p.DiffFn = testDiffFn 1786 ctx := testContext2(t, &ContextOpts{ 1787 Config: m, 1788 ProviderResolver: providers.ResolverFixed( 1789 map[string]providers.Factory{ 1790 "aws": testProviderFuncFixed(p), 1791 }, 1792 ), 1793 }) 1794 1795 if _, diags := ctx.Plan(); diags.HasErrors() { 1796 t.Fatalf("plan errors: %s", diags.Err()) 1797 } 1798 1799 state, diags := ctx.Apply() 1800 if diags.HasErrors() { 1801 t.Fatalf("diags: %s", diags.Err()) 1802 } 1803 1804 actual := strings.TrimSpace(state.String()) 1805 expected := strings.TrimSpace(testTerraformApplyMinimalStr) 1806 if actual != expected { 1807 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1808 } 1809 } 1810 1811 func TestContext2Apply_badDiff(t *testing.T) { 1812 m := testModule(t, "apply-good") 1813 p := testProvider("aws") 1814 p.ApplyFn = testApplyFn 1815 p.DiffFn = testDiffFn 1816 ctx := testContext2(t, &ContextOpts{ 1817 Config: m, 1818 ProviderResolver: providers.ResolverFixed( 1819 map[string]providers.Factory{ 1820 "aws": testProviderFuncFixed(p), 1821 }, 1822 ), 1823 }) 1824 1825 if _, diags := ctx.Plan(); diags.HasErrors() { 1826 t.Fatalf("plan errors: %s", diags.Err()) 1827 } 1828 1829 p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { 1830 return &InstanceDiff{ 1831 Attributes: map[string]*ResourceAttrDiff{ 1832 "newp": { 1833 Old: "", 1834 New: "", 1835 NewComputed: true, 1836 }, 1837 }, 1838 }, nil 1839 } 1840 1841 if _, diags := ctx.Apply(); diags == nil { 1842 t.Fatal("should error") 1843 } 1844 } 1845 1846 func TestContext2Apply_cancel(t *testing.T) { 1847 stopped := false 1848 1849 m := testModule(t, "apply-cancel") 1850 p := testProvider("aws") 1851 ctx := testContext2(t, &ContextOpts{ 1852 Config: m, 1853 ProviderResolver: providers.ResolverFixed( 1854 map[string]providers.Factory{ 1855 "aws": testProviderFuncFixed(p), 1856 }, 1857 ), 1858 }) 1859 1860 p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { 1861 if !stopped { 1862 stopped = true 1863 go ctx.Stop() 1864 1865 for { 1866 if ctx.sh.Stopped() { 1867 break 1868 } 1869 time.Sleep(10 * time.Millisecond) 1870 } 1871 } 1872 1873 return &InstanceState{ 1874 ID: "foo", 1875 Attributes: map[string]string{ 1876 "value": "2", 1877 }, 1878 }, nil 1879 } 1880 p.DiffFn = func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 1881 d := &InstanceDiff{ 1882 Attributes: map[string]*ResourceAttrDiff{}, 1883 } 1884 if new, ok := rc.Get("value"); ok { 1885 d.Attributes["value"] = &ResourceAttrDiff{ 1886 New: new.(string), 1887 } 1888 } 1889 if new, ok := rc.Get("foo"); ok { 1890 d.Attributes["foo"] = &ResourceAttrDiff{ 1891 New: new.(string), 1892 } 1893 } 1894 return d, nil 1895 } 1896 1897 if _, diags := ctx.Plan(); diags.HasErrors() { 1898 t.Fatalf("plan errors: %s", diags.Err()) 1899 } 1900 1901 // Start the Apply in a goroutine 1902 var applyDiags tfdiags.Diagnostics 1903 stateCh := make(chan *states.State) 1904 go func() { 1905 state, diags := ctx.Apply() 1906 applyDiags = diags 1907 1908 stateCh <- state 1909 }() 1910 1911 state := <-stateCh 1912 if applyDiags.HasErrors() { 1913 t.Fatalf("unexpected errors: %s", applyDiags.Err()) 1914 } 1915 1916 actual := strings.TrimSpace(state.String()) 1917 expected := strings.TrimSpace(testTerraformApplyCancelStr) 1918 if actual != expected { 1919 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1920 } 1921 1922 if !p.StopCalled { 1923 t.Fatal("stop should be called") 1924 } 1925 } 1926 1927 func TestContext2Apply_cancelBlock(t *testing.T) { 1928 m := testModule(t, "apply-cancel-block") 1929 p := testProvider("aws") 1930 ctx := testContext2(t, &ContextOpts{ 1931 Config: m, 1932 ProviderResolver: providers.ResolverFixed( 1933 map[string]providers.Factory{ 1934 "aws": testProviderFuncFixed(p), 1935 }, 1936 ), 1937 }) 1938 1939 applyCh := make(chan struct{}) 1940 p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { 1941 return &InstanceDiff{ 1942 Attributes: map[string]*ResourceAttrDiff{ 1943 "id": { 1944 New: "foo", 1945 }, 1946 "num": { 1947 New: "2", 1948 }, 1949 }, 1950 }, nil 1951 } 1952 p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { 1953 close(applyCh) 1954 1955 for !ctx.sh.Stopped() { 1956 // Wait for stop to be called. We call Gosched here so that 1957 // the other goroutines can always be scheduled to set Stopped. 1958 runtime.Gosched() 1959 } 1960 1961 // Sleep 1962 time.Sleep(100 * time.Millisecond) 1963 1964 return &InstanceState{ 1965 ID: "foo", 1966 Attributes: map[string]string{ 1967 "num": "2", 1968 }, 1969 }, nil 1970 } 1971 1972 if _, diags := ctx.Plan(); diags.HasErrors() { 1973 t.Fatalf("plan errors: %s", diags.Err()) 1974 } 1975 1976 // Start the Apply in a goroutine 1977 var applyDiags tfdiags.Diagnostics 1978 stateCh := make(chan *states.State) 1979 go func() { 1980 state, diags := ctx.Apply() 1981 applyDiags = diags 1982 1983 stateCh <- state 1984 }() 1985 1986 stopDone := make(chan struct{}) 1987 go func() { 1988 defer close(stopDone) 1989 <-applyCh 1990 ctx.Stop() 1991 }() 1992 1993 // Make sure that stop blocks 1994 select { 1995 case <-stopDone: 1996 t.Fatal("stop should block") 1997 case <-time.After(10 * time.Millisecond): 1998 } 1999 2000 // Wait for stop 2001 select { 2002 case <-stopDone: 2003 case <-time.After(500 * time.Millisecond): 2004 t.Fatal("stop should be done") 2005 } 2006 2007 // Wait for apply to complete 2008 state := <-stateCh 2009 if applyDiags.HasErrors() { 2010 t.Fatalf("unexpected error: %s", applyDiags.Err()) 2011 } 2012 2013 checkStateString(t, state, ` 2014 aws_instance.foo: 2015 ID = foo 2016 provider = provider.aws 2017 num = 2 2018 `) 2019 } 2020 2021 func TestContext2Apply_cancelProvisioner(t *testing.T) { 2022 m := testModule(t, "apply-cancel-provisioner") 2023 p := testProvider("aws") 2024 p.ApplyFn = testApplyFn 2025 p.DiffFn = testDiffFn 2026 2027 pr := testProvisioner() 2028 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 2029 Provisioner: &configschema.Block{ 2030 Attributes: map[string]*configschema.Attribute{ 2031 "foo": { 2032 Type: cty.String, 2033 Optional: true, 2034 }, 2035 }, 2036 }, 2037 } 2038 2039 ctx := testContext2(t, &ContextOpts{ 2040 Config: m, 2041 ProviderResolver: providers.ResolverFixed( 2042 map[string]providers.Factory{ 2043 "aws": testProviderFuncFixed(p), 2044 }, 2045 ), 2046 Provisioners: map[string]ProvisionerFactory{ 2047 "shell": testProvisionerFuncFixed(pr), 2048 }, 2049 }) 2050 2051 prStopped := make(chan struct{}) 2052 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 2053 // Start the stop process 2054 go ctx.Stop() 2055 2056 <-prStopped 2057 return nil 2058 } 2059 pr.StopFn = func() error { 2060 close(prStopped) 2061 return nil 2062 } 2063 2064 if _, diags := ctx.Plan(); diags.HasErrors() { 2065 t.Fatalf("plan errors: %s", diags.Err()) 2066 } 2067 2068 // Start the Apply in a goroutine 2069 var applyDiags tfdiags.Diagnostics 2070 stateCh := make(chan *states.State) 2071 go func() { 2072 state, diags := ctx.Apply() 2073 applyDiags = diags 2074 2075 stateCh <- state 2076 }() 2077 2078 // Wait for completion 2079 state := <-stateCh 2080 assertNoErrors(t, applyDiags) 2081 2082 checkStateString(t, state, ` 2083 aws_instance.foo: (tainted) 2084 ID = foo 2085 provider = provider.aws 2086 num = 2 2087 type = aws_instance 2088 `) 2089 2090 if !pr.StopCalled { 2091 t.Fatal("stop should be called") 2092 } 2093 } 2094 2095 func TestContext2Apply_compute(t *testing.T) { 2096 m := testModule(t, "apply-compute") 2097 p := testProvider("aws") 2098 p.ApplyFn = testApplyFn 2099 p.DiffFn = testDiffFn 2100 p.GetSchemaReturn = &ProviderSchema{ 2101 ResourceTypes: map[string]*configschema.Block{ 2102 "aws_instance": { 2103 Attributes: map[string]*configschema.Attribute{ 2104 "num": { 2105 Type: cty.Number, 2106 Optional: true, 2107 }, 2108 "compute": { 2109 Type: cty.String, 2110 Optional: true, 2111 }, 2112 "compute_value": { 2113 Type: cty.String, 2114 Optional: true, 2115 }, 2116 "foo": { 2117 Type: cty.String, 2118 Optional: true, 2119 }, 2120 "id": { 2121 Type: cty.String, 2122 Computed: true, 2123 }, 2124 "type": { 2125 Type: cty.String, 2126 Computed: true, 2127 }, 2128 "value": { // Populated from compute_value because compute = "value" in the config fixture 2129 Type: cty.String, 2130 Computed: true, 2131 }, 2132 }, 2133 }, 2134 }, 2135 } 2136 2137 ctx := testContext2(t, &ContextOpts{ 2138 Config: m, 2139 ProviderResolver: providers.ResolverFixed( 2140 map[string]providers.Factory{ 2141 "aws": testProviderFuncFixed(p), 2142 }, 2143 ), 2144 }) 2145 2146 ctx.variables = InputValues{ 2147 "value": &InputValue{ 2148 Value: cty.NumberIntVal(1), 2149 SourceType: ValueFromCaller, 2150 }, 2151 } 2152 2153 if _, diags := ctx.Plan(); diags.HasErrors() { 2154 t.Fatalf("plan errors: %s", diags.Err()) 2155 } 2156 2157 state, diags := ctx.Apply() 2158 if diags.HasErrors() { 2159 t.Fatalf("unexpected errors: %s", diags.Err()) 2160 } 2161 2162 actual := strings.TrimSpace(state.String()) 2163 expected := strings.TrimSpace(testTerraformApplyComputeStr) 2164 if actual != expected { 2165 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2166 } 2167 } 2168 2169 func TestContext2Apply_countDecrease(t *testing.T) { 2170 m := testModule(t, "apply-count-dec") 2171 p := testProvider("aws") 2172 p.DiffFn = testDiffFn 2173 p.ApplyFn = testApplyFn 2174 s := MustShimLegacyState(&State{ 2175 Modules: []*ModuleState{ 2176 { 2177 Path: rootModulePath, 2178 Resources: map[string]*ResourceState{ 2179 "aws_instance.foo.0": { 2180 Type: "aws_instance", 2181 Primary: &InstanceState{ 2182 ID: "bar", 2183 Attributes: map[string]string{ 2184 "foo": "foo", 2185 "type": "aws_instance", 2186 }, 2187 }, 2188 }, 2189 "aws_instance.foo.1": { 2190 Type: "aws_instance", 2191 Primary: &InstanceState{ 2192 ID: "bar", 2193 Attributes: map[string]string{ 2194 "foo": "foo", 2195 "type": "aws_instance", 2196 }, 2197 }, 2198 }, 2199 "aws_instance.foo.2": { 2200 Type: "aws_instance", 2201 Primary: &InstanceState{ 2202 ID: "bar", 2203 Attributes: map[string]string{ 2204 "foo": "foo", 2205 "type": "aws_instance", 2206 }, 2207 }, 2208 }, 2209 }, 2210 }, 2211 }, 2212 }) 2213 ctx := testContext2(t, &ContextOpts{ 2214 Config: m, 2215 ProviderResolver: providers.ResolverFixed( 2216 map[string]providers.Factory{ 2217 "aws": testProviderFuncFixed(p), 2218 }, 2219 ), 2220 State: s, 2221 }) 2222 2223 if _, diags := ctx.Plan(); diags.HasErrors() { 2224 logDiagnostics(t, diags) 2225 t.Fatal("plan failed") 2226 } 2227 2228 state, diags := ctx.Apply() 2229 assertNoErrors(t, diags) 2230 2231 actual := strings.TrimSpace(state.String()) 2232 expected := strings.TrimSpace(testTerraformApplyCountDecStr) 2233 if actual != expected { 2234 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2235 } 2236 } 2237 2238 func TestContext2Apply_countDecreaseToOneX(t *testing.T) { 2239 m := testModule(t, "apply-count-dec-one") 2240 p := testProvider("aws") 2241 p.ApplyFn = testApplyFn 2242 p.DiffFn = testDiffFn 2243 s := MustShimLegacyState(&State{ 2244 Modules: []*ModuleState{ 2245 { 2246 Path: rootModulePath, 2247 Resources: map[string]*ResourceState{ 2248 "aws_instance.foo.0": { 2249 Type: "aws_instance", 2250 Primary: &InstanceState{ 2251 ID: "bar", 2252 Attributes: map[string]string{ 2253 "foo": "foo", 2254 "type": "aws_instance", 2255 }, 2256 }, 2257 }, 2258 "aws_instance.foo.1": { 2259 Type: "aws_instance", 2260 Primary: &InstanceState{ 2261 ID: "bar", 2262 }, 2263 }, 2264 "aws_instance.foo.2": { 2265 Type: "aws_instance", 2266 Primary: &InstanceState{ 2267 ID: "bar", 2268 }, 2269 }, 2270 }, 2271 }, 2272 }, 2273 }) 2274 ctx := testContext2(t, &ContextOpts{ 2275 Config: m, 2276 ProviderResolver: providers.ResolverFixed( 2277 map[string]providers.Factory{ 2278 "aws": testProviderFuncFixed(p), 2279 }, 2280 ), 2281 State: s, 2282 }) 2283 2284 if _, diags := ctx.Plan(); diags.HasErrors() { 2285 t.Fatalf("plan errors: %s", diags.Err()) 2286 } 2287 2288 state, diags := ctx.Apply() 2289 if diags.HasErrors() { 2290 t.Fatalf("diags: %s", diags.Err()) 2291 } 2292 2293 actual := strings.TrimSpace(state.String()) 2294 expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr) 2295 if actual != expected { 2296 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2297 } 2298 } 2299 2300 // https://github.com/PeoplePerHour/terraform/pull/11 2301 // 2302 // This tests a case where both a "resource" and "resource.0" are in 2303 // the state file, which apparently is a reasonable backwards compatibility 2304 // concern found in the above 3rd party repo. 2305 func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) { 2306 m := testModule(t, "apply-count-dec-one") 2307 p := testProvider("aws") 2308 p.ApplyFn = testApplyFn 2309 p.DiffFn = testDiffFn 2310 s := MustShimLegacyState(&State{ 2311 Modules: []*ModuleState{ 2312 { 2313 Path: rootModulePath, 2314 Resources: map[string]*ResourceState{ 2315 "aws_instance.foo": { 2316 Type: "aws_instance", 2317 Primary: &InstanceState{ 2318 ID: "bar", 2319 Attributes: map[string]string{ 2320 "foo": "foo", 2321 "type": "aws_instance", 2322 }, 2323 }, 2324 }, 2325 "aws_instance.foo.0": { 2326 Type: "aws_instance", 2327 Primary: &InstanceState{ 2328 ID: "baz", 2329 Attributes: map[string]string{ 2330 "type": "aws_instance", 2331 }, 2332 }, 2333 }, 2334 }, 2335 }, 2336 }, 2337 }) 2338 ctx := testContext2(t, &ContextOpts{ 2339 Config: m, 2340 ProviderResolver: providers.ResolverFixed( 2341 map[string]providers.Factory{ 2342 "aws": testProviderFuncFixed(p), 2343 }, 2344 ), 2345 State: s, 2346 }) 2347 2348 if p, diags := ctx.Plan(); diags.HasErrors() { 2349 t.Fatalf("diags: %s", diags.Err()) 2350 } else { 2351 got := strings.TrimSpace(legacyPlanComparisonString(ctx.State(), p.Changes)) 2352 want := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedPlanStr) 2353 if got != want { 2354 t.Fatalf("wrong plan result\ngot:\n%s\nwant:\n%s", got, want) 2355 } 2356 } 2357 2358 state, diags := ctx.Apply() 2359 if diags.HasErrors() { 2360 t.Fatalf("diags: %s", diags.Err()) 2361 } 2362 2363 actual := strings.TrimSpace(state.String()) 2364 expected := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedStr) 2365 if actual != expected { 2366 t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2367 } 2368 } 2369 2370 func TestContext2Apply_countTainted(t *testing.T) { 2371 m := testModule(t, "apply-count-tainted") 2372 p := testProvider("aws") 2373 p.DiffFn = testDiffFn 2374 p.ApplyFn = testApplyFn 2375 s := MustShimLegacyState(&State{ 2376 Modules: []*ModuleState{ 2377 { 2378 Path: rootModulePath, 2379 Resources: map[string]*ResourceState{ 2380 "aws_instance.foo.0": { 2381 Type: "aws_instance", 2382 Primary: &InstanceState{ 2383 ID: "bar", 2384 Attributes: map[string]string{ 2385 "foo": "foo", 2386 "type": "aws_instance", 2387 }, 2388 Tainted: true, 2389 }, 2390 }, 2391 }, 2392 }, 2393 }, 2394 }) 2395 ctx := testContext2(t, &ContextOpts{ 2396 Config: m, 2397 ProviderResolver: providers.ResolverFixed( 2398 map[string]providers.Factory{ 2399 "aws": testProviderFuncFixed(p), 2400 }, 2401 ), 2402 State: s, 2403 }) 2404 2405 { 2406 plan, diags := ctx.Plan() 2407 assertNoErrors(t, diags) 2408 got := strings.TrimSpace(legacyDiffComparisonString(plan.Changes)) 2409 want := strings.TrimSpace(` 2410 DESTROY/CREATE: aws_instance.foo[0] 2411 foo: "foo" => "foo" 2412 id: "bar" => "<computed>" 2413 type: "aws_instance" => "aws_instance" 2414 CREATE: aws_instance.foo[1] 2415 foo: "" => "foo" 2416 id: "" => "<computed>" 2417 type: "" => "aws_instance" 2418 `) 2419 if got != want { 2420 t.Fatalf("wrong plan\n\ngot:\n%s\n\nwant:\n%s", got, want) 2421 } 2422 } 2423 2424 state, diags := ctx.Apply() 2425 assertNoErrors(t, diags) 2426 2427 got := strings.TrimSpace(state.String()) 2428 want := strings.TrimSpace(` 2429 aws_instance.foo.0: 2430 ID = foo 2431 provider = provider.aws 2432 foo = foo 2433 type = aws_instance 2434 aws_instance.foo.1: 2435 ID = foo 2436 provider = provider.aws 2437 foo = foo 2438 type = aws_instance 2439 `) 2440 if got != want { 2441 t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", got, want) 2442 } 2443 } 2444 2445 func TestContext2Apply_countVariable(t *testing.T) { 2446 m := testModule(t, "apply-count-variable") 2447 p := testProvider("aws") 2448 p.ApplyFn = testApplyFn 2449 p.DiffFn = testDiffFn 2450 ctx := testContext2(t, &ContextOpts{ 2451 Config: m, 2452 ProviderResolver: providers.ResolverFixed( 2453 map[string]providers.Factory{ 2454 "aws": testProviderFuncFixed(p), 2455 }, 2456 ), 2457 }) 2458 2459 if _, diags := ctx.Plan(); diags.HasErrors() { 2460 t.Fatalf("plan errors: %s", diags.Err()) 2461 } 2462 2463 state, diags := ctx.Apply() 2464 if diags.HasErrors() { 2465 t.Fatalf("diags: %s", diags.Err()) 2466 } 2467 2468 actual := strings.TrimSpace(state.String()) 2469 expected := strings.TrimSpace(testTerraformApplyCountVariableStr) 2470 if actual != expected { 2471 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2472 } 2473 } 2474 2475 func TestContext2Apply_countVariableRef(t *testing.T) { 2476 m := testModule(t, "apply-count-variable-ref") 2477 p := testProvider("aws") 2478 p.ApplyFn = testApplyFn 2479 p.DiffFn = testDiffFn 2480 ctx := testContext2(t, &ContextOpts{ 2481 Config: m, 2482 ProviderResolver: providers.ResolverFixed( 2483 map[string]providers.Factory{ 2484 "aws": testProviderFuncFixed(p), 2485 }, 2486 ), 2487 }) 2488 2489 if _, diags := ctx.Plan(); diags.HasErrors() { 2490 t.Fatalf("plan errors: %s", diags.Err()) 2491 } 2492 2493 state, diags := ctx.Apply() 2494 if diags.HasErrors() { 2495 t.Fatalf("diags: %s", diags.Err()) 2496 } 2497 2498 actual := strings.TrimSpace(state.String()) 2499 expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr) 2500 if actual != expected { 2501 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2502 } 2503 } 2504 2505 func TestContext2Apply_provisionerInterpCount(t *testing.T) { 2506 // This test ensures that a provisioner can interpolate a resource count 2507 // even though the provisioner expression is evaluated during the plan 2508 // walk. https://github.com/hashicorp/terraform-plugin-sdk/issues/16840 2509 2510 m, snap := testModuleWithSnapshot(t, "apply-provisioner-interp-count") 2511 2512 p := testProvider("aws") 2513 p.ApplyFn = testApplyFn 2514 p.DiffFn = testDiffFn 2515 2516 pr := testProvisioner() 2517 2518 providerResolver := providers.ResolverFixed( 2519 map[string]providers.Factory{ 2520 "aws": testProviderFuncFixed(p), 2521 }, 2522 ) 2523 provisioners := map[string]ProvisionerFactory{ 2524 "local-exec": testProvisionerFuncFixed(pr), 2525 } 2526 ctx := testContext2(t, &ContextOpts{ 2527 Config: m, 2528 ProviderResolver: providerResolver, 2529 Provisioners: provisioners, 2530 }) 2531 2532 plan, diags := ctx.Plan() 2533 if diags.HasErrors() { 2534 t.Fatalf("plan failed unexpectedly: %s", diags.Err()) 2535 } 2536 2537 state := ctx.State() 2538 2539 // We'll marshal and unmarshal the plan here, to ensure that we have 2540 // a clean new context as would be created if we separately ran 2541 // terraform plan -out=tfplan && terraform apply tfplan 2542 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 2543 if err != nil { 2544 t.Fatal(err) 2545 } 2546 ctxOpts.ProviderResolver = providerResolver 2547 ctxOpts.Provisioners = provisioners 2548 ctx, diags = NewContext(ctxOpts) 2549 if diags.HasErrors() { 2550 t.Fatalf("failed to create context for plan: %s", diags.Err()) 2551 } 2552 2553 // Applying the plan should now succeed 2554 _, diags = ctx.Apply() 2555 if diags.HasErrors() { 2556 t.Fatalf("apply failed unexpectedly: %s", diags.Err()) 2557 } 2558 2559 // Verify apply was invoked 2560 if !pr.ProvisionResourceCalled { 2561 t.Fatalf("provisioner was not called") 2562 } 2563 } 2564 2565 func TestContext2Apply_foreachVariable(t *testing.T) { 2566 m := testModule(t, "plan-for-each-unknown-value") 2567 p := testProvider("aws") 2568 p.ApplyFn = testApplyFn 2569 p.DiffFn = testDiffFn 2570 ctx := testContext2(t, &ContextOpts{ 2571 Config: m, 2572 ProviderResolver: providers.ResolverFixed( 2573 map[string]providers.Factory{ 2574 "aws": testProviderFuncFixed(p), 2575 }, 2576 ), 2577 Variables: InputValues{ 2578 "foo": &InputValue{ 2579 Value: cty.StringVal("hello"), 2580 }, 2581 }, 2582 }) 2583 2584 if _, diags := ctx.Plan(); diags.HasErrors() { 2585 t.Fatalf("plan errors: %s", diags.Err()) 2586 } 2587 2588 state, diags := ctx.Apply() 2589 if diags.HasErrors() { 2590 t.Fatalf("diags: %s", diags.Err()) 2591 } 2592 2593 actual := strings.TrimSpace(state.String()) 2594 expected := strings.TrimSpace(testTerraformApplyForEachVariableStr) 2595 if actual != expected { 2596 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2597 } 2598 } 2599 2600 func TestContext2Apply_moduleBasic(t *testing.T) { 2601 m := testModule(t, "apply-module") 2602 p := testProvider("aws") 2603 p.ApplyFn = testApplyFn 2604 p.DiffFn = testDiffFn 2605 ctx := testContext2(t, &ContextOpts{ 2606 Config: m, 2607 ProviderResolver: providers.ResolverFixed( 2608 map[string]providers.Factory{ 2609 "aws": testProviderFuncFixed(p), 2610 }, 2611 ), 2612 }) 2613 2614 if _, diags := ctx.Plan(); diags.HasErrors() { 2615 t.Fatalf("plan errors: %s", diags.Err()) 2616 } 2617 2618 state, diags := ctx.Apply() 2619 if diags.HasErrors() { 2620 t.Fatalf("diags: %s", diags.Err()) 2621 } 2622 2623 actual := strings.TrimSpace(state.String()) 2624 expected := strings.TrimSpace(testTerraformApplyModuleStr) 2625 if actual != expected { 2626 t.Fatalf("bad, expected:\n%s\n\nactual:\n%s", expected, actual) 2627 } 2628 } 2629 2630 func TestContext2Apply_moduleDestroyOrder(t *testing.T) { 2631 m := testModule(t, "apply-module-destroy-order") 2632 p := testProvider("aws") 2633 p.DiffFn = testDiffFn 2634 2635 // Create a custom apply function to track the order they were destroyed 2636 var order []string 2637 var orderLock sync.Mutex 2638 p.ApplyFn = func( 2639 info *InstanceInfo, 2640 is *InstanceState, 2641 id *InstanceDiff) (*InstanceState, error) { 2642 2643 if is.ID == "b" { 2644 // Pause briefly to make any race conditions more visible, since 2645 // missing edges here can cause undeterministic ordering. 2646 time.Sleep(100 * time.Millisecond) 2647 } 2648 2649 orderLock.Lock() 2650 defer orderLock.Unlock() 2651 2652 order = append(order, is.ID) 2653 return nil, nil 2654 } 2655 2656 p.GetSchemaReturn = &ProviderSchema{ 2657 ResourceTypes: map[string]*configschema.Block{ 2658 "aws_instance": { 2659 Attributes: map[string]*configschema.Attribute{ 2660 "id": {Type: cty.String, Required: true}, 2661 "blah": {Type: cty.String, Optional: true}, 2662 "value": {Type: cty.String, Optional: true}, 2663 }, 2664 }, 2665 }, 2666 } 2667 2668 state := MustShimLegacyState(&State{ 2669 Modules: []*ModuleState{ 2670 { 2671 Path: rootModulePath, 2672 Resources: map[string]*ResourceState{ 2673 "aws_instance.b": resourceState("aws_instance", "b"), 2674 }, 2675 }, 2676 2677 { 2678 Path: []string{"root", "child"}, 2679 Resources: map[string]*ResourceState{ 2680 "aws_instance.a": resourceState("aws_instance", "a"), 2681 }, 2682 Outputs: map[string]*OutputState{ 2683 "a_output": { 2684 Type: "string", 2685 Sensitive: false, 2686 Value: "a", 2687 }, 2688 }, 2689 }, 2690 }, 2691 }) 2692 2693 ctx := testContext2(t, &ContextOpts{ 2694 Config: m, 2695 ProviderResolver: providers.ResolverFixed( 2696 map[string]providers.Factory{ 2697 "aws": testProviderFuncFixed(p), 2698 }, 2699 ), 2700 State: state, 2701 Destroy: true, 2702 }) 2703 2704 if _, diags := ctx.Plan(); diags.HasErrors() { 2705 t.Fatalf("plan errors: %s", diags.Err()) 2706 } 2707 2708 state, diags := ctx.Apply() 2709 if diags.HasErrors() { 2710 t.Fatalf("diags: %s", diags.Err()) 2711 } 2712 2713 expected := []string{"b", "a"} 2714 if !reflect.DeepEqual(order, expected) { 2715 t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) 2716 } 2717 2718 { 2719 actual := strings.TrimSpace(state.String()) 2720 expected := strings.TrimSpace(testTerraformApplyModuleDestroyOrderStr) 2721 if actual != expected { 2722 t.Errorf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 2723 } 2724 } 2725 } 2726 2727 func TestContext2Apply_moduleInheritAlias(t *testing.T) { 2728 m := testModule(t, "apply-module-provider-inherit-alias") 2729 p := testProvider("aws") 2730 p.ApplyFn = testApplyFn 2731 p.DiffFn = testDiffFn 2732 2733 p.ConfigureFn = func(c *ResourceConfig) error { 2734 if _, ok := c.Get("value"); !ok { 2735 return nil 2736 } 2737 2738 if _, ok := c.Get("root"); ok { 2739 return fmt.Errorf("child should not get root") 2740 } 2741 2742 return nil 2743 } 2744 2745 ctx := testContext2(t, &ContextOpts{ 2746 Config: m, 2747 ProviderResolver: providers.ResolverFixed( 2748 map[string]providers.Factory{ 2749 "aws": testProviderFuncFixed(p), 2750 }, 2751 ), 2752 }) 2753 2754 if _, diags := ctx.Plan(); diags.HasErrors() { 2755 t.Fatalf("plan errors: %s", diags.Err()) 2756 } 2757 2758 state, diags := ctx.Apply() 2759 if diags.HasErrors() { 2760 t.Fatalf("diags: %s", diags.Err()) 2761 } 2762 2763 checkStateString(t, state, ` 2764 <no state> 2765 module.child: 2766 aws_instance.foo: 2767 ID = foo 2768 provider = provider.aws.eu 2769 `) 2770 } 2771 2772 func TestContext2Apply_orphanResource(t *testing.T) { 2773 // This is a two-step test: 2774 // 1. Apply a configuration with resources that have count set. 2775 // This should place the empty resource object in the state to record 2776 // that each exists, and record any instances. 2777 // 2. Apply an empty configuration against the same state, which should 2778 // then clean up both the instances and the containing resource objects. 2779 p := testProvider("test") 2780 p.ApplyFn = testApplyFn 2781 p.DiffFn = testDiffFn 2782 p.GetSchemaReturn = &ProviderSchema{ 2783 ResourceTypes: map[string]*configschema.Block{ 2784 "test_thing": {}, 2785 }, 2786 } 2787 2788 // Step 1: create the resources and instances 2789 m := testModule(t, "apply-orphan-resource") 2790 ctx := testContext2(t, &ContextOpts{ 2791 Config: m, 2792 ProviderResolver: providers.ResolverFixed( 2793 map[string]providers.Factory{ 2794 "test": testProviderFuncFixed(p), 2795 }, 2796 ), 2797 }) 2798 _, diags := ctx.Plan() 2799 assertNoErrors(t, diags) 2800 state, diags := ctx.Apply() 2801 assertNoErrors(t, diags) 2802 2803 // At this point both resources should be recorded in the state, along 2804 // with the single instance associated with test_thing.one. 2805 want := states.BuildState(func(s *states.SyncState) { 2806 providerAddr := addrs.ProviderConfig{ 2807 Type: "test", 2808 }.Absolute(addrs.RootModuleInstance) 2809 zeroAddr := addrs.Resource{ 2810 Mode: addrs.ManagedResourceMode, 2811 Type: "test_thing", 2812 Name: "zero", 2813 }.Absolute(addrs.RootModuleInstance) 2814 oneAddr := addrs.Resource{ 2815 Mode: addrs.ManagedResourceMode, 2816 Type: "test_thing", 2817 Name: "one", 2818 }.Absolute(addrs.RootModuleInstance) 2819 s.SetResourceMeta(zeroAddr, states.EachList, providerAddr) 2820 s.SetResourceMeta(oneAddr, states.EachList, providerAddr) 2821 s.SetResourceInstanceCurrent(oneAddr.Instance(addrs.IntKey(0)), &states.ResourceInstanceObjectSrc{ 2822 Status: states.ObjectReady, 2823 AttrsJSON: []byte(`{}`), 2824 }, providerAddr) 2825 }) 2826 if !cmp.Equal(state, want) { 2827 t.Fatalf("wrong state after step 1\n%s", cmp.Diff(want, state)) 2828 } 2829 2830 // Step 2: update with an empty config, to destroy everything 2831 m = testModule(t, "empty") 2832 ctx = testContext2(t, &ContextOpts{ 2833 Config: m, 2834 State: state, 2835 ProviderResolver: providers.ResolverFixed( 2836 map[string]providers.Factory{ 2837 "test": testProviderFuncFixed(p), 2838 }, 2839 ), 2840 }) 2841 _, diags = ctx.Plan() 2842 assertNoErrors(t, diags) 2843 state, diags = ctx.Apply() 2844 assertNoErrors(t, diags) 2845 2846 // The state should now be _totally_ empty, with just an empty root module 2847 // (since that always exists) and no resources at all. 2848 want = states.NewState() 2849 if !cmp.Equal(state, want) { 2850 t.Fatalf("wrong state after step 2\ngot: %swant: %s", spew.Sdump(state), spew.Sdump(want)) 2851 } 2852 2853 } 2854 2855 func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) { 2856 m := testModule(t, "apply-module-provider-inherit-alias-orphan") 2857 p := testProvider("aws") 2858 p.ApplyFn = testApplyFn 2859 p.DiffFn = testDiffFn 2860 2861 called := false 2862 p.ConfigureFn = func(c *ResourceConfig) error { 2863 called = true 2864 2865 if _, ok := c.Get("child"); !ok { 2866 return nil 2867 } 2868 2869 if _, ok := c.Get("root"); ok { 2870 return fmt.Errorf("child should not get root") 2871 } 2872 2873 return nil 2874 } 2875 2876 // Create a state with an orphan module 2877 state := MustShimLegacyState(&State{ 2878 Modules: []*ModuleState{ 2879 { 2880 Path: []string{"root", "child"}, 2881 Resources: map[string]*ResourceState{ 2882 "aws_instance.bar": { 2883 Type: "aws_instance", 2884 Primary: &InstanceState{ 2885 ID: "bar", 2886 }, 2887 Provider: "provider.aws.eu", 2888 }, 2889 }, 2890 }, 2891 }, 2892 }) 2893 2894 ctx := testContext2(t, &ContextOpts{ 2895 Config: m, 2896 State: state, 2897 ProviderResolver: providers.ResolverFixed( 2898 map[string]providers.Factory{ 2899 "aws": testProviderFuncFixed(p), 2900 }, 2901 ), 2902 }) 2903 2904 if _, diags := ctx.Plan(); diags.HasErrors() { 2905 t.Fatalf("plan errors: %s", diags.Err()) 2906 } 2907 2908 state, diags := ctx.Apply() 2909 if diags.HasErrors() { 2910 t.Fatalf("diags: %s", diags.Err()) 2911 } 2912 2913 if !called { 2914 t.Fatal("must call configure") 2915 } 2916 2917 checkStateString(t, state, "<no state>") 2918 } 2919 2920 func TestContext2Apply_moduleOrphanProvider(t *testing.T) { 2921 m := testModule(t, "apply-module-orphan-provider-inherit") 2922 p := testProvider("aws") 2923 p.ApplyFn = testApplyFn 2924 p.DiffFn = testDiffFn 2925 2926 p.ConfigureFn = func(c *ResourceConfig) error { 2927 if _, ok := c.Get("value"); !ok { 2928 return fmt.Errorf("value is not found") 2929 } 2930 2931 return nil 2932 } 2933 2934 // Create a state with an orphan module 2935 state := MustShimLegacyState(&State{ 2936 Modules: []*ModuleState{ 2937 { 2938 Path: []string{"root", "child"}, 2939 Resources: map[string]*ResourceState{ 2940 "aws_instance.bar": { 2941 Type: "aws_instance", 2942 Primary: &InstanceState{ 2943 ID: "bar", 2944 }, 2945 Provider: "provider.aws", 2946 }, 2947 }, 2948 }, 2949 }, 2950 }) 2951 2952 ctx := testContext2(t, &ContextOpts{ 2953 Config: m, 2954 State: state, 2955 ProviderResolver: providers.ResolverFixed( 2956 map[string]providers.Factory{ 2957 "aws": testProviderFuncFixed(p), 2958 }, 2959 ), 2960 }) 2961 2962 if _, diags := ctx.Plan(); diags.HasErrors() { 2963 t.Fatalf("plan errors: %s", diags.Err()) 2964 } 2965 2966 if _, diags := ctx.Apply(); diags.HasErrors() { 2967 t.Fatalf("apply errors: %s", diags.Err()) 2968 } 2969 } 2970 2971 func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) { 2972 m := testModule(t, "apply-module-orphan-provider-inherit") 2973 p := testProvider("aws") 2974 p.ApplyFn = testApplyFn 2975 p.DiffFn = testDiffFn 2976 2977 p.ConfigureFn = func(c *ResourceConfig) error { 2978 if _, ok := c.Get("value"); !ok { 2979 return fmt.Errorf("value is not found") 2980 } 2981 2982 return nil 2983 } 2984 2985 // Create a state with an orphan module that is nested (grandchild) 2986 state := MustShimLegacyState(&State{ 2987 Modules: []*ModuleState{ 2988 { 2989 Path: []string{"root", "parent", "child"}, 2990 Resources: map[string]*ResourceState{ 2991 "aws_instance.bar": { 2992 Type: "aws_instance", 2993 Primary: &InstanceState{ 2994 ID: "bar", 2995 }, 2996 Provider: "provider.aws", 2997 }, 2998 }, 2999 }, 3000 }, 3001 }) 3002 3003 ctx := testContext2(t, &ContextOpts{ 3004 Config: m, 3005 State: state, 3006 ProviderResolver: providers.ResolverFixed( 3007 map[string]providers.Factory{ 3008 "aws": testProviderFuncFixed(p), 3009 }, 3010 ), 3011 }) 3012 3013 if _, diags := ctx.Plan(); diags.HasErrors() { 3014 t.Fatalf("plan errors: %s", diags.Err()) 3015 } 3016 3017 if _, diags := ctx.Apply(); diags.HasErrors() { 3018 t.Fatalf("apply errors: %s", diags.Err()) 3019 } 3020 } 3021 3022 func TestContext2Apply_moduleGrandchildProvider(t *testing.T) { 3023 m := testModule(t, "apply-module-grandchild-provider-inherit") 3024 p := testProvider("aws") 3025 p.ApplyFn = testApplyFn 3026 p.DiffFn = testDiffFn 3027 3028 var callLock sync.Mutex 3029 called := false 3030 p.ConfigureFn = func(c *ResourceConfig) error { 3031 if _, ok := c.Get("value"); !ok { 3032 return fmt.Errorf("value is not found") 3033 } 3034 callLock.Lock() 3035 called = true 3036 callLock.Unlock() 3037 3038 return nil 3039 } 3040 3041 ctx := testContext2(t, &ContextOpts{ 3042 Config: m, 3043 ProviderResolver: providers.ResolverFixed( 3044 map[string]providers.Factory{ 3045 "aws": testProviderFuncFixed(p), 3046 }, 3047 ), 3048 }) 3049 3050 if _, diags := ctx.Plan(); diags.HasErrors() { 3051 t.Fatalf("plan errors: %s", diags.Err()) 3052 } 3053 3054 if _, diags := ctx.Apply(); diags.HasErrors() { 3055 t.Fatalf("apply errors: %s", diags.Err()) 3056 } 3057 3058 callLock.Lock() 3059 defer callLock.Unlock() 3060 if called != true { 3061 t.Fatalf("err: configure never called") 3062 } 3063 } 3064 3065 // This tests an issue where all the providers in a module but not 3066 // in the root weren't being added to the root properly. In this test 3067 // case: aws is explicitly added to root, but "test" should be added to. 3068 // With the bug, it wasn't. 3069 func TestContext2Apply_moduleOnlyProvider(t *testing.T) { 3070 m := testModule(t, "apply-module-only-provider") 3071 p := testProvider("aws") 3072 p.ApplyFn = testApplyFn 3073 p.DiffFn = testDiffFn 3074 pTest := testProvider("test") 3075 pTest.ApplyFn = testApplyFn 3076 pTest.DiffFn = testDiffFn 3077 3078 ctx := testContext2(t, &ContextOpts{ 3079 Config: m, 3080 ProviderResolver: providers.ResolverFixed( 3081 map[string]providers.Factory{ 3082 "aws": testProviderFuncFixed(p), 3083 "test": testProviderFuncFixed(pTest), 3084 }, 3085 ), 3086 }) 3087 3088 if _, diags := ctx.Plan(); diags.HasErrors() { 3089 t.Fatalf("plan errors: %s", diags.Err()) 3090 } 3091 3092 state, diags := ctx.Apply() 3093 if diags.HasErrors() { 3094 t.Fatalf("diags: %s", diags.Err()) 3095 } 3096 3097 actual := strings.TrimSpace(state.String()) 3098 expected := strings.TrimSpace(testTerraformApplyModuleOnlyProviderStr) 3099 if actual != expected { 3100 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3101 } 3102 } 3103 3104 func TestContext2Apply_moduleProviderAlias(t *testing.T) { 3105 m := testModule(t, "apply-module-provider-alias") 3106 p := testProvider("aws") 3107 p.ApplyFn = testApplyFn 3108 p.DiffFn = testDiffFn 3109 ctx := testContext2(t, &ContextOpts{ 3110 Config: m, 3111 ProviderResolver: providers.ResolverFixed( 3112 map[string]providers.Factory{ 3113 "aws": testProviderFuncFixed(p), 3114 }, 3115 ), 3116 }) 3117 3118 if _, diags := ctx.Plan(); diags.HasErrors() { 3119 t.Fatalf("plan errors: %s", diags.Err()) 3120 } 3121 3122 state, diags := ctx.Apply() 3123 if diags.HasErrors() { 3124 t.Fatalf("diags: %s", diags.Err()) 3125 } 3126 3127 actual := strings.TrimSpace(state.String()) 3128 expected := strings.TrimSpace(testTerraformApplyModuleProviderAliasStr) 3129 if actual != expected { 3130 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3131 } 3132 } 3133 3134 func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) { 3135 m := testModule(t, "apply-module-provider-alias") 3136 p := testProvider("aws") 3137 p.ApplyFn = testApplyFn 3138 p.DiffFn = testDiffFn 3139 ctx := testContext2(t, &ContextOpts{ 3140 Config: m, 3141 ProviderResolver: providers.ResolverFixed( 3142 map[string]providers.Factory{ 3143 "aws": testProviderFuncFixed(p), 3144 }, 3145 ), 3146 Targets: []addrs.Targetable{ 3147 addrs.AbsResource{ 3148 Module: addrs.RootModuleInstance, 3149 Resource: addrs.Resource{ 3150 Mode: addrs.ManagedResourceMode, 3151 Type: "nonexistent", 3152 Name: "thing", 3153 }, 3154 }, 3155 }, 3156 }) 3157 3158 if _, diags := ctx.Plan(); diags.HasErrors() { 3159 t.Fatalf("plan errors: %s", diags.Err()) 3160 } 3161 3162 state, diags := ctx.Apply() 3163 if diags.HasErrors() { 3164 t.Fatalf("diags: %s", diags.Err()) 3165 } 3166 3167 actual := strings.TrimSpace(state.String()) 3168 expected := strings.TrimSpace(` 3169 <no state> 3170 `) 3171 if actual != expected { 3172 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3173 } 3174 } 3175 3176 func TestContext2Apply_moduleProviderCloseNested(t *testing.T) { 3177 m := testModule(t, "apply-module-provider-close-nested") 3178 p := testProvider("aws") 3179 p.ApplyFn = testApplyFn 3180 p.DiffFn = testDiffFn 3181 ctx := testContext2(t, &ContextOpts{ 3182 Config: m, 3183 ProviderResolver: providers.ResolverFixed( 3184 map[string]providers.Factory{ 3185 "aws": testProviderFuncFixed(p), 3186 }, 3187 ), 3188 State: MustShimLegacyState(&State{ 3189 Modules: []*ModuleState{ 3190 { 3191 Path: []string{"root", "child", "subchild"}, 3192 Resources: map[string]*ResourceState{ 3193 "aws_instance.foo": { 3194 Type: "aws_instance", 3195 Primary: &InstanceState{ 3196 ID: "bar", 3197 }, 3198 }, 3199 }, 3200 }, 3201 }, 3202 }), 3203 Destroy: true, 3204 }) 3205 3206 if _, diags := ctx.Plan(); diags.HasErrors() { 3207 t.Fatalf("plan errors: %s", diags.Err()) 3208 } 3209 3210 if _, diags := ctx.Apply(); diags.HasErrors() { 3211 t.Fatalf("apply errors: %s", diags.Err()) 3212 } 3213 } 3214 3215 // Tests that variables used as module vars that reference data that 3216 // already exists in the state and requires no diff works properly. This 3217 // fixes an issue faced where module variables were pruned because they were 3218 // accessing "non-existent" resources (they existed, just not in the graph 3219 // cause they weren't in the diff). 3220 func TestContext2Apply_moduleVarRefExisting(t *testing.T) { 3221 m := testModule(t, "apply-ref-existing") 3222 p := testProvider("aws") 3223 p.ApplyFn = testApplyFn 3224 p.DiffFn = testDiffFn 3225 3226 state := MustShimLegacyState(&State{ 3227 Modules: []*ModuleState{ 3228 { 3229 Path: rootModulePath, 3230 Resources: map[string]*ResourceState{ 3231 "aws_instance.foo": { 3232 Type: "aws_instance", 3233 Primary: &InstanceState{ 3234 ID: "foo", 3235 Attributes: map[string]string{ 3236 "foo": "bar", 3237 }, 3238 }, 3239 Provider: "provider.aws", 3240 }, 3241 }, 3242 }, 3243 }, 3244 }) 3245 3246 ctx := testContext2(t, &ContextOpts{ 3247 Config: m, 3248 ProviderResolver: providers.ResolverFixed( 3249 map[string]providers.Factory{ 3250 "aws": testProviderFuncFixed(p), 3251 }, 3252 ), 3253 State: state, 3254 }) 3255 3256 if _, diags := ctx.Plan(); diags.HasErrors() { 3257 t.Fatalf("plan errors: %s", diags.Err()) 3258 } 3259 3260 state, diags := ctx.Apply() 3261 if diags.HasErrors() { 3262 t.Fatalf("diags: %s", diags.Err()) 3263 } 3264 3265 actual := strings.TrimSpace(state.String()) 3266 expected := strings.TrimSpace(testTerraformApplyModuleVarRefExistingStr) 3267 if actual != expected { 3268 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3269 } 3270 } 3271 3272 func TestContext2Apply_moduleVarResourceCount(t *testing.T) { 3273 m := testModule(t, "apply-module-var-resource-count") 3274 p := testProvider("aws") 3275 p.ApplyFn = testApplyFn 3276 p.DiffFn = testDiffFn 3277 ctx := testContext2(t, &ContextOpts{ 3278 Config: m, 3279 ProviderResolver: providers.ResolverFixed( 3280 map[string]providers.Factory{ 3281 "aws": testProviderFuncFixed(p), 3282 }, 3283 ), 3284 Variables: InputValues{ 3285 "num": &InputValue{ 3286 Value: cty.NumberIntVal(2), 3287 SourceType: ValueFromCaller, 3288 }, 3289 }, 3290 Destroy: true, 3291 }) 3292 3293 if _, diags := ctx.Plan(); diags.HasErrors() { 3294 t.Fatalf("plan errors: %s", diags.Err()) 3295 } 3296 3297 if _, diags := ctx.Apply(); diags.HasErrors() { 3298 t.Fatalf("apply errors: %s", diags.Err()) 3299 } 3300 3301 ctx = testContext2(t, &ContextOpts{ 3302 Config: m, 3303 ProviderResolver: providers.ResolverFixed( 3304 map[string]providers.Factory{ 3305 "aws": testProviderFuncFixed(p), 3306 }, 3307 ), 3308 Variables: InputValues{ 3309 "num": &InputValue{ 3310 Value: cty.NumberIntVal(5), 3311 SourceType: ValueFromCaller, 3312 }, 3313 }, 3314 }) 3315 3316 if _, diags := ctx.Plan(); diags.HasErrors() { 3317 t.Fatalf("plan errors: %s", diags.Err()) 3318 } 3319 3320 if _, diags := ctx.Apply(); diags.HasErrors() { 3321 t.Fatalf("apply errors: %s", diags.Err()) 3322 } 3323 } 3324 3325 // GH-819 3326 func TestContext2Apply_moduleBool(t *testing.T) { 3327 m := testModule(t, "apply-module-bool") 3328 p := testProvider("aws") 3329 p.ApplyFn = testApplyFn 3330 p.DiffFn = testDiffFn 3331 ctx := testContext2(t, &ContextOpts{ 3332 Config: m, 3333 ProviderResolver: providers.ResolverFixed( 3334 map[string]providers.Factory{ 3335 "aws": testProviderFuncFixed(p), 3336 }, 3337 ), 3338 }) 3339 3340 if _, diags := ctx.Plan(); diags.HasErrors() { 3341 t.Fatalf("plan errors: %s", diags.Err()) 3342 } 3343 3344 state, diags := ctx.Apply() 3345 if diags.HasErrors() { 3346 t.Fatalf("diags: %s", diags.Err()) 3347 } 3348 3349 actual := strings.TrimSpace(state.String()) 3350 expected := strings.TrimSpace(testTerraformApplyModuleBoolStr) 3351 if actual != expected { 3352 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3353 } 3354 } 3355 3356 // Tests that a module can be targeted and everything is properly created. 3357 // This adds to the plan test to also just verify that apply works. 3358 func TestContext2Apply_moduleTarget(t *testing.T) { 3359 m := testModule(t, "plan-targeted-cross-module") 3360 p := testProvider("aws") 3361 p.ApplyFn = testApplyFn 3362 p.DiffFn = testDiffFn 3363 ctx := testContext2(t, &ContextOpts{ 3364 Config: m, 3365 ProviderResolver: providers.ResolverFixed( 3366 map[string]providers.Factory{ 3367 "aws": testProviderFuncFixed(p), 3368 }, 3369 ), 3370 Targets: []addrs.Targetable{ 3371 addrs.RootModuleInstance.Child("B", addrs.NoKey), 3372 }, 3373 }) 3374 3375 if _, diags := ctx.Plan(); diags.HasErrors() { 3376 t.Fatalf("plan errors: %s", diags.Err()) 3377 } 3378 3379 state, diags := ctx.Apply() 3380 if diags.HasErrors() { 3381 t.Fatalf("diags: %s", diags.Err()) 3382 } 3383 3384 checkStateString(t, state, ` 3385 <no state> 3386 module.A: 3387 aws_instance.foo: 3388 ID = foo 3389 provider = provider.aws 3390 foo = bar 3391 type = aws_instance 3392 3393 Outputs: 3394 3395 value = foo 3396 module.B: 3397 aws_instance.bar: 3398 ID = foo 3399 provider = provider.aws 3400 foo = foo 3401 type = aws_instance 3402 `) 3403 } 3404 3405 func TestContext2Apply_multiProvider(t *testing.T) { 3406 m := testModule(t, "apply-multi-provider") 3407 p := testProvider("aws") 3408 p.ApplyFn = testApplyFn 3409 p.DiffFn = testDiffFn 3410 3411 pDO := testProvider("do") 3412 pDO.ApplyFn = testApplyFn 3413 pDO.DiffFn = testDiffFn 3414 3415 ctx := testContext2(t, &ContextOpts{ 3416 Config: m, 3417 ProviderResolver: providers.ResolverFixed( 3418 map[string]providers.Factory{ 3419 "aws": testProviderFuncFixed(p), 3420 "do": testProviderFuncFixed(pDO), 3421 }, 3422 ), 3423 }) 3424 3425 if _, diags := ctx.Plan(); diags.HasErrors() { 3426 t.Fatalf("plan errors: %s", diags.Err()) 3427 } 3428 3429 state, diags := ctx.Apply() 3430 if diags.HasErrors() { 3431 t.Fatalf("diags: %s", diags.Err()) 3432 } 3433 3434 mod := state.RootModule() 3435 if len(mod.Resources) < 2 { 3436 t.Fatalf("bad: %#v", mod.Resources) 3437 } 3438 3439 actual := strings.TrimSpace(state.String()) 3440 expected := strings.TrimSpace(testTerraformApplyMultiProviderStr) 3441 if actual != expected { 3442 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 3443 } 3444 } 3445 3446 func TestContext2Apply_multiProviderDestroy(t *testing.T) { 3447 m := testModule(t, "apply-multi-provider-destroy") 3448 p := testProvider("aws") 3449 p.ApplyFn = testApplyFn 3450 p.DiffFn = testDiffFn 3451 p.GetSchemaReturn = &ProviderSchema{ 3452 Provider: &configschema.Block{ 3453 Attributes: map[string]*configschema.Attribute{ 3454 "addr": {Type: cty.String, Optional: true}, 3455 }, 3456 }, 3457 ResourceTypes: map[string]*configschema.Block{ 3458 "aws_instance": { 3459 Attributes: map[string]*configschema.Attribute{ 3460 "foo": {Type: cty.String, Optional: true}, 3461 }, 3462 }, 3463 }, 3464 } 3465 3466 p2 := testProvider("vault") 3467 p2.ApplyFn = testApplyFn 3468 p2.DiffFn = testDiffFn 3469 p2.GetSchemaReturn = &ProviderSchema{ 3470 ResourceTypes: map[string]*configschema.Block{ 3471 "vault_instance": { 3472 Attributes: map[string]*configschema.Attribute{ 3473 "id": {Type: cty.String, Computed: true}, 3474 }, 3475 }, 3476 }, 3477 } 3478 3479 var state *states.State 3480 3481 // First, create the instances 3482 { 3483 ctx := testContext2(t, &ContextOpts{ 3484 Config: m, 3485 ProviderResolver: providers.ResolverFixed( 3486 map[string]providers.Factory{ 3487 "aws": testProviderFuncFixed(p), 3488 "vault": testProviderFuncFixed(p2), 3489 }, 3490 ), 3491 }) 3492 3493 if _, diags := ctx.Plan(); diags.HasErrors() { 3494 t.Fatalf("errors during create plan: %s", diags.Err()) 3495 } 3496 3497 s, diags := ctx.Apply() 3498 if diags.HasErrors() { 3499 t.Fatalf("errors during create apply: %s", diags.Err()) 3500 } 3501 3502 state = s 3503 } 3504 3505 // Destroy them 3506 { 3507 // Verify that aws_instance.bar is destroyed first 3508 var checked bool 3509 var called int32 3510 var lock sync.Mutex 3511 applyFn := func( 3512 info *InstanceInfo, 3513 is *InstanceState, 3514 id *InstanceDiff) (*InstanceState, error) { 3515 lock.Lock() 3516 defer lock.Unlock() 3517 3518 if info.Type == "aws_instance" { 3519 checked = true 3520 3521 // Sleep to allow parallel execution 3522 time.Sleep(50 * time.Millisecond) 3523 3524 // Verify that called is 0 (dep not called) 3525 if atomic.LoadInt32(&called) != 0 { 3526 return nil, fmt.Errorf("nothing else should be called") 3527 } 3528 } 3529 3530 atomic.AddInt32(&called, 1) 3531 return testApplyFn(info, is, id) 3532 } 3533 3534 // Set the apply functions 3535 p.ApplyFn = applyFn 3536 p2.ApplyFn = applyFn 3537 3538 ctx := testContext2(t, &ContextOpts{ 3539 Destroy: true, 3540 State: state, 3541 Config: m, 3542 ProviderResolver: providers.ResolverFixed( 3543 map[string]providers.Factory{ 3544 "aws": testProviderFuncFixed(p), 3545 "vault": testProviderFuncFixed(p2), 3546 }, 3547 ), 3548 }) 3549 3550 if _, diags := ctx.Plan(); diags.HasErrors() { 3551 t.Fatalf("errors during destroy plan: %s", diags.Err()) 3552 } 3553 3554 s, diags := ctx.Apply() 3555 if diags.HasErrors() { 3556 t.Fatalf("errors during destroy apply: %s", diags.Err()) 3557 } 3558 3559 if !checked { 3560 t.Fatal("should be checked") 3561 } 3562 3563 state = s 3564 } 3565 3566 checkStateString(t, state, `<no state>`) 3567 } 3568 3569 // This is like the multiProviderDestroy test except it tests that 3570 // dependent resources within a child module that inherit provider 3571 // configuration are still destroyed first. 3572 func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { 3573 m := testModule(t, "apply-multi-provider-destroy-child") 3574 p := testProvider("aws") 3575 p.ApplyFn = testApplyFn 3576 p.DiffFn = testDiffFn 3577 p.GetSchemaReturn = &ProviderSchema{ 3578 Provider: &configschema.Block{ 3579 Attributes: map[string]*configschema.Attribute{ 3580 "value": {Type: cty.String, Optional: true}, 3581 }, 3582 }, 3583 ResourceTypes: map[string]*configschema.Block{ 3584 "aws_instance": { 3585 Attributes: map[string]*configschema.Attribute{ 3586 "foo": {Type: cty.String, Optional: true}, 3587 }, 3588 }, 3589 }, 3590 } 3591 3592 p2 := testProvider("vault") 3593 p2.ApplyFn = testApplyFn 3594 p2.DiffFn = testDiffFn 3595 p2.GetSchemaReturn = &ProviderSchema{ 3596 Provider: &configschema.Block{}, 3597 ResourceTypes: map[string]*configschema.Block{ 3598 "vault_instance": { 3599 Attributes: map[string]*configschema.Attribute{ 3600 "id": {Type: cty.String, Computed: true}, 3601 }, 3602 }, 3603 }, 3604 } 3605 3606 var state *states.State 3607 3608 // First, create the instances 3609 { 3610 ctx := testContext2(t, &ContextOpts{ 3611 Config: m, 3612 ProviderResolver: providers.ResolverFixed( 3613 map[string]providers.Factory{ 3614 "aws": testProviderFuncFixed(p), 3615 "vault": testProviderFuncFixed(p2), 3616 }, 3617 ), 3618 }) 3619 3620 if _, diags := ctx.Plan(); diags.HasErrors() { 3621 t.Fatalf("diags: %s", diags.Err()) 3622 } 3623 3624 s, diags := ctx.Apply() 3625 if diags.HasErrors() { 3626 t.Fatalf("diags: %s", diags.Err()) 3627 } 3628 3629 state = s 3630 } 3631 3632 // Destroy them 3633 { 3634 // Verify that aws_instance.bar is destroyed first 3635 var checked bool 3636 var called int32 3637 var lock sync.Mutex 3638 applyFn := func( 3639 info *InstanceInfo, 3640 is *InstanceState, 3641 id *InstanceDiff) (*InstanceState, error) { 3642 lock.Lock() 3643 defer lock.Unlock() 3644 3645 if info.Type == "aws_instance" { 3646 checked = true 3647 3648 // Sleep to allow parallel execution 3649 time.Sleep(50 * time.Millisecond) 3650 3651 // Verify that called is 0 (dep not called) 3652 if atomic.LoadInt32(&called) != 0 { 3653 return nil, fmt.Errorf("nothing else should be called") 3654 } 3655 } 3656 3657 atomic.AddInt32(&called, 1) 3658 return testApplyFn(info, is, id) 3659 } 3660 3661 // Set the apply functions 3662 p.ApplyFn = applyFn 3663 p2.ApplyFn = applyFn 3664 3665 ctx := testContext2(t, &ContextOpts{ 3666 Destroy: true, 3667 State: state, 3668 Config: m, 3669 ProviderResolver: providers.ResolverFixed( 3670 map[string]providers.Factory{ 3671 "aws": testProviderFuncFixed(p), 3672 "vault": testProviderFuncFixed(p2), 3673 }, 3674 ), 3675 }) 3676 3677 if _, diags := ctx.Plan(); diags.HasErrors() { 3678 t.Fatalf("diags: %s", diags.Err()) 3679 } 3680 3681 s, diags := ctx.Apply() 3682 if diags.HasErrors() { 3683 t.Fatalf("diags: %s", diags.Err()) 3684 } 3685 3686 if !checked { 3687 t.Fatal("should be checked") 3688 } 3689 3690 state = s 3691 } 3692 3693 checkStateString(t, state, ` 3694 <no state> 3695 `) 3696 } 3697 3698 func TestContext2Apply_multiVar(t *testing.T) { 3699 m := testModule(t, "apply-multi-var") 3700 p := testProvider("aws") 3701 p.ApplyFn = testApplyFn 3702 p.DiffFn = testDiffFn 3703 3704 // First, apply with a count of 3 3705 ctx := testContext2(t, &ContextOpts{ 3706 Config: m, 3707 ProviderResolver: providers.ResolverFixed( 3708 map[string]providers.Factory{ 3709 "aws": testProviderFuncFixed(p), 3710 }, 3711 ), 3712 Variables: InputValues{ 3713 "num": &InputValue{ 3714 Value: cty.NumberIntVal(3), 3715 SourceType: ValueFromCaller, 3716 }, 3717 }, 3718 }) 3719 3720 if _, diags := ctx.Plan(); diags.HasErrors() { 3721 t.Fatalf("plan errors: %s", diags.Err()) 3722 } 3723 3724 state, diags := ctx.Apply() 3725 if diags.HasErrors() { 3726 t.Fatalf("diags: %s", diags.Err()) 3727 } 3728 3729 actual := state.RootModule().OutputValues["output"] 3730 expected := cty.StringVal("bar0,bar1,bar2") 3731 if actual == nil || actual.Value != expected { 3732 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3733 } 3734 3735 t.Logf("Initial state: %s", state.String()) 3736 3737 // Apply again, reduce the count to 1 3738 { 3739 ctx := testContext2(t, &ContextOpts{ 3740 Config: m, 3741 State: state, 3742 ProviderResolver: providers.ResolverFixed( 3743 map[string]providers.Factory{ 3744 "aws": testProviderFuncFixed(p), 3745 }, 3746 ), 3747 Variables: InputValues{ 3748 "num": &InputValue{ 3749 Value: cty.NumberIntVal(1), 3750 SourceType: ValueFromCaller, 3751 }, 3752 }, 3753 }) 3754 3755 if _, diags := ctx.Plan(); diags.HasErrors() { 3756 t.Fatalf("diags: %s", diags.Err()) 3757 } 3758 3759 state, diags := ctx.Apply() 3760 if diags.HasErrors() { 3761 t.Fatalf("diags: %s", diags.Err()) 3762 } 3763 3764 t.Logf("End state: %s", state.String()) 3765 3766 actual := state.RootModule().OutputValues["output"] 3767 if actual == nil { 3768 t.Fatal("missing output") 3769 } 3770 3771 expected := cty.StringVal("bar0") 3772 if actual.Value != expected { 3773 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 3774 } 3775 } 3776 } 3777 3778 // This is a holistic test of multi-var (aka "splat variable") handling 3779 // across several different Terraform subsystems. This is here because 3780 // historically there were quirky differences in handling across different 3781 // parts of Terraform and so here we want to assert the expected behavior and 3782 // ensure that it remains consistent in future. 3783 func TestContext2Apply_multiVarComprehensive(t *testing.T) { 3784 m := testModule(t, "apply-multi-var-comprehensive") 3785 p := testProvider("test") 3786 3787 configs := map[string]*ResourceConfig{} 3788 var configsLock sync.Mutex 3789 3790 p.ApplyFn = testApplyFn 3791 3792 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 3793 proposed := req.ProposedNewState 3794 configsLock.Lock() 3795 defer configsLock.Unlock() 3796 key := proposed.GetAttr("key").AsString() 3797 // This test was originally written using the legacy p.DiffFn interface, 3798 // and so the assertions below expect an old-style ResourceConfig, which 3799 // we'll construct via our shim for now to avoid rewriting all of the 3800 // assertions. 3801 configs[key] = NewResourceConfigShimmed(req.Config, p.GetSchemaReturn.ResourceTypes["test_thing"]) 3802 3803 retVals := make(map[string]cty.Value) 3804 for it := proposed.ElementIterator(); it.Next(); { 3805 idxVal, val := it.Element() 3806 idx := idxVal.AsString() 3807 3808 switch idx { 3809 case "id": 3810 retVals[idx] = cty.UnknownVal(cty.String) 3811 case "name": 3812 retVals[idx] = cty.StringVal(key) 3813 default: 3814 retVals[idx] = val 3815 } 3816 } 3817 3818 return providers.PlanResourceChangeResponse{ 3819 PlannedState: cty.ObjectVal(retVals), 3820 } 3821 } 3822 3823 p.GetSchemaReturn = &ProviderSchema{ 3824 ResourceTypes: map[string]*configschema.Block{ 3825 "test_thing": { 3826 Attributes: map[string]*configschema.Attribute{ 3827 "key": {Type: cty.String, Required: true}, 3828 3829 "source_id": {Type: cty.String, Optional: true}, 3830 "source_name": {Type: cty.String, Optional: true}, 3831 "first_source_id": {Type: cty.String, Optional: true}, 3832 "first_source_name": {Type: cty.String, Optional: true}, 3833 "source_ids": {Type: cty.List(cty.String), Optional: true}, 3834 "source_names": {Type: cty.List(cty.String), Optional: true}, 3835 "source_ids_from_func": {Type: cty.List(cty.String), Optional: true}, 3836 "source_names_from_func": {Type: cty.List(cty.String), Optional: true}, 3837 "source_ids_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true}, 3838 "source_names_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true}, 3839 3840 "id": {Type: cty.String, Computed: true}, 3841 "name": {Type: cty.String, Computed: true}, 3842 }, 3843 }, 3844 }, 3845 } 3846 3847 // First, apply with a count of 3 3848 ctx := testContext2(t, &ContextOpts{ 3849 Config: m, 3850 ProviderResolver: providers.ResolverFixed( 3851 map[string]providers.Factory{ 3852 "test": testProviderFuncFixed(p), 3853 }, 3854 ), 3855 Variables: InputValues{ 3856 "num": &InputValue{ 3857 Value: cty.NumberIntVal(3), 3858 SourceType: ValueFromCaller, 3859 }, 3860 }, 3861 }) 3862 3863 if _, diags := ctx.Plan(); diags.HasErrors() { 3864 logDiagnostics(t, diags) 3865 t.Fatalf("errors during plan") 3866 } 3867 3868 checkConfig := func(key string, want map[string]interface{}) { 3869 configsLock.Lock() 3870 defer configsLock.Unlock() 3871 3872 if _, ok := configs[key]; !ok { 3873 t.Errorf("no config recorded for %s; expected a configuration", key) 3874 return 3875 } 3876 got := configs[key].Config 3877 t.Run("config for "+key, func(t *testing.T) { 3878 want["key"] = key // to avoid doing this for every example 3879 for _, problem := range deep.Equal(got, want) { 3880 t.Errorf(problem) 3881 } 3882 }) 3883 } 3884 3885 checkConfig("multi_count_var.0", map[string]interface{}{ 3886 "source_id": hcl2shim.UnknownVariableValue, 3887 "source_name": "source.0", 3888 }) 3889 checkConfig("multi_count_var.2", map[string]interface{}{ 3890 "source_id": hcl2shim.UnknownVariableValue, 3891 "source_name": "source.2", 3892 }) 3893 checkConfig("multi_count_derived.0", map[string]interface{}{ 3894 "source_id": hcl2shim.UnknownVariableValue, 3895 "source_name": "source.0", 3896 }) 3897 checkConfig("multi_count_derived.2", map[string]interface{}{ 3898 "source_id": hcl2shim.UnknownVariableValue, 3899 "source_name": "source.2", 3900 }) 3901 checkConfig("whole_splat", map[string]interface{}{ 3902 "source_ids": []interface{}{ 3903 hcl2shim.UnknownVariableValue, 3904 hcl2shim.UnknownVariableValue, 3905 hcl2shim.UnknownVariableValue, 3906 }, 3907 "source_names": []interface{}{ 3908 "source.0", 3909 "source.1", 3910 "source.2", 3911 }, 3912 "source_ids_from_func": hcl2shim.UnknownVariableValue, 3913 "source_names_from_func": []interface{}{ 3914 "source.0", 3915 "source.1", 3916 "source.2", 3917 }, 3918 3919 "source_ids_wrapped": []interface{}{ 3920 []interface{}{ 3921 hcl2shim.UnknownVariableValue, 3922 hcl2shim.UnknownVariableValue, 3923 hcl2shim.UnknownVariableValue, 3924 }, 3925 }, 3926 "source_names_wrapped": []interface{}{ 3927 []interface{}{ 3928 "source.0", 3929 "source.1", 3930 "source.2", 3931 }, 3932 }, 3933 3934 "first_source_id": hcl2shim.UnknownVariableValue, 3935 "first_source_name": "source.0", 3936 }) 3937 checkConfig("child.whole_splat", map[string]interface{}{ 3938 "source_ids": []interface{}{ 3939 hcl2shim.UnknownVariableValue, 3940 hcl2shim.UnknownVariableValue, 3941 hcl2shim.UnknownVariableValue, 3942 }, 3943 "source_names": []interface{}{ 3944 "source.0", 3945 "source.1", 3946 "source.2", 3947 }, 3948 3949 "source_ids_wrapped": []interface{}{ 3950 []interface{}{ 3951 hcl2shim.UnknownVariableValue, 3952 hcl2shim.UnknownVariableValue, 3953 hcl2shim.UnknownVariableValue, 3954 }, 3955 }, 3956 "source_names_wrapped": []interface{}{ 3957 []interface{}{ 3958 "source.0", 3959 "source.1", 3960 "source.2", 3961 }, 3962 }, 3963 }) 3964 3965 t.Run("apply", func(t *testing.T) { 3966 state, diags := ctx.Apply() 3967 if diags.HasErrors() { 3968 t.Fatalf("error during apply: %s", diags.Err()) 3969 } 3970 3971 want := map[string]interface{}{ 3972 "source_ids": []interface{}{"foo", "foo", "foo"}, 3973 "source_names": []interface{}{ 3974 "source.0", 3975 "source.1", 3976 "source.2", 3977 }, 3978 } 3979 got := map[string]interface{}{} 3980 for k, s := range state.RootModule().OutputValues { 3981 got[k] = hcl2shim.ConfigValueFromHCL2(s.Value) 3982 } 3983 if !reflect.DeepEqual(got, want) { 3984 t.Errorf( 3985 "wrong outputs\ngot: %s\nwant: %s", 3986 spew.Sdump(got), spew.Sdump(want), 3987 ) 3988 } 3989 }) 3990 } 3991 3992 // Test that multi-var (splat) access is ordered by count, not by 3993 // value. 3994 func TestContext2Apply_multiVarOrder(t *testing.T) { 3995 m := testModule(t, "apply-multi-var-order") 3996 p := testProvider("aws") 3997 p.ApplyFn = testApplyFn 3998 p.DiffFn = testDiffFn 3999 4000 // First, apply with a count of 3 4001 ctx := testContext2(t, &ContextOpts{ 4002 Config: m, 4003 ProviderResolver: providers.ResolverFixed( 4004 map[string]providers.Factory{ 4005 "aws": testProviderFuncFixed(p), 4006 }, 4007 ), 4008 }) 4009 4010 if _, diags := ctx.Plan(); diags.HasErrors() { 4011 t.Fatalf("plan errors: %s", diags.Err()) 4012 } 4013 4014 state, diags := ctx.Apply() 4015 if diags.HasErrors() { 4016 t.Fatalf("diags: %s", diags.Err()) 4017 } 4018 4019 t.Logf("State: %s", state.String()) 4020 4021 actual := state.RootModule().OutputValues["should-be-11"] 4022 expected := cty.StringVal("index-11") 4023 if actual == nil || actual.Value != expected { 4024 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 4025 } 4026 } 4027 4028 // Test that multi-var (splat) access is ordered by count, not by 4029 // value, through interpolations. 4030 func TestContext2Apply_multiVarOrderInterp(t *testing.T) { 4031 m := testModule(t, "apply-multi-var-order-interp") 4032 p := testProvider("aws") 4033 p.ApplyFn = testApplyFn 4034 p.DiffFn = testDiffFn 4035 4036 // First, apply with a count of 3 4037 ctx := testContext2(t, &ContextOpts{ 4038 Config: m, 4039 ProviderResolver: providers.ResolverFixed( 4040 map[string]providers.Factory{ 4041 "aws": testProviderFuncFixed(p), 4042 }, 4043 ), 4044 }) 4045 4046 if _, diags := ctx.Plan(); diags.HasErrors() { 4047 t.Fatalf("plan errors: %s", diags.Err()) 4048 } 4049 4050 state, diags := ctx.Apply() 4051 if diags.HasErrors() { 4052 t.Fatalf("diags: %s", diags.Err()) 4053 } 4054 4055 t.Logf("State: %s", state.String()) 4056 4057 actual := state.RootModule().OutputValues["should-be-11"] 4058 expected := cty.StringVal("baz-index-11") 4059 if actual == nil || actual.Value != expected { 4060 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 4061 } 4062 } 4063 4064 // Based on GH-10440 where a graph edge wasn't properly being created 4065 // between a modified resource and a count instance being destroyed. 4066 func TestContext2Apply_multiVarCountDec(t *testing.T) { 4067 var s *states.State 4068 4069 // First create resources. Nothing sneaky here. 4070 { 4071 m := testModule(t, "apply-multi-var-count-dec") 4072 p := testProvider("aws") 4073 p.ApplyFn = testApplyFn 4074 p.DiffFn = testDiffFn 4075 ctx := testContext2(t, &ContextOpts{ 4076 Config: m, 4077 ProviderResolver: providers.ResolverFixed( 4078 map[string]providers.Factory{ 4079 "aws": testProviderFuncFixed(p), 4080 }, 4081 ), 4082 Variables: InputValues{ 4083 "num": &InputValue{ 4084 Value: cty.NumberIntVal(2), 4085 SourceType: ValueFromCaller, 4086 }, 4087 }, 4088 }) 4089 4090 log.Print("\n========\nStep 1 Plan\n========") 4091 if _, diags := ctx.Plan(); diags.HasErrors() { 4092 t.Fatalf("diags: %s", diags.Err()) 4093 } 4094 4095 log.Print("\n========\nStep 1 Apply\n========") 4096 state, diags := ctx.Apply() 4097 if diags.HasErrors() { 4098 t.Fatalf("diags: %s", diags.Err()) 4099 } 4100 4101 t.Logf("Step 1 state:\n%s", state) 4102 4103 s = state 4104 } 4105 4106 // Decrease the count by 1 and verify that everything happens in the 4107 // right order. 4108 { 4109 m := testModule(t, "apply-multi-var-count-dec") 4110 p := testProvider("aws") 4111 p.ApplyFn = testApplyFn 4112 p.DiffFn = testDiffFn 4113 4114 // Verify that aws_instance.bar is modified first and nothing 4115 // else happens at the same time. 4116 var checked bool 4117 var called int32 4118 var lock sync.Mutex 4119 p.ApplyFn = func( 4120 info *InstanceInfo, 4121 is *InstanceState, 4122 id *InstanceDiff) (*InstanceState, error) { 4123 lock.Lock() 4124 defer lock.Unlock() 4125 4126 if id != nil && id.Attributes != nil && id.Attributes["ami"] != nil && id.Attributes["ami"].New == "special" { 4127 checked = true 4128 4129 // Sleep to allow parallel execution 4130 time.Sleep(50 * time.Millisecond) 4131 4132 // Verify that called is 0 (dep not called) 4133 if atomic.LoadInt32(&called) != 1 { 4134 return nil, fmt.Errorf("nothing else should be called") 4135 } 4136 } 4137 4138 atomic.AddInt32(&called, 1) 4139 return testApplyFn(info, is, id) 4140 } 4141 4142 ctx := testContext2(t, &ContextOpts{ 4143 State: s, 4144 Config: m, 4145 ProviderResolver: providers.ResolverFixed( 4146 map[string]providers.Factory{ 4147 "aws": testProviderFuncFixed(p), 4148 }, 4149 ), 4150 Variables: InputValues{ 4151 "num": &InputValue{ 4152 Value: cty.NumberIntVal(1), 4153 SourceType: ValueFromCaller, 4154 }, 4155 }, 4156 }) 4157 4158 log.Print("\n========\nStep 2 Plan\n========") 4159 plan, diags := ctx.Plan() 4160 if diags.HasErrors() { 4161 t.Fatalf("plan errors: %s", diags.Err()) 4162 } 4163 4164 t.Logf("Step 2 plan:\n%s", legacyDiffComparisonString(plan.Changes)) 4165 4166 log.Print("\n========\nStep 2 Apply\n========") 4167 state, diags := ctx.Apply() 4168 if diags.HasErrors() { 4169 t.Fatalf("apply errors: %s", diags.Err()) 4170 } 4171 4172 if !checked { 4173 t.Error("apply never called") 4174 } 4175 4176 t.Logf("Step 2 state:\n%s", state) 4177 4178 s = state 4179 } 4180 } 4181 4182 // Test that we can resolve a multi-var (splat) for the first resource 4183 // created in a non-root module, which happens when the module state doesn't 4184 // exist yet. 4185 // https://github.com/hashicorp/terraform-plugin-sdk/issues/14438 4186 func TestContext2Apply_multiVarMissingState(t *testing.T) { 4187 m := testModule(t, "apply-multi-var-missing-state") 4188 p := testProvider("test") 4189 p.ApplyFn = testApplyFn 4190 p.DiffFn = testDiffFn 4191 p.GetSchemaReturn = &ProviderSchema{ 4192 ResourceTypes: map[string]*configschema.Block{ 4193 "test_thing": { 4194 Attributes: map[string]*configschema.Attribute{ 4195 "a_ids": {Type: cty.String, Optional: true}, 4196 "id": {Type: cty.String, Computed: true}, 4197 }, 4198 }, 4199 }, 4200 } 4201 4202 // First, apply with a count of 3 4203 ctx := testContext2(t, &ContextOpts{ 4204 Config: m, 4205 ProviderResolver: providers.ResolverFixed( 4206 map[string]providers.Factory{ 4207 "test": testProviderFuncFixed(p), 4208 }, 4209 ), 4210 }) 4211 4212 if _, diags := ctx.Plan(); diags.HasErrors() { 4213 t.Fatalf("plan failed: %s", diags.Err()) 4214 } 4215 4216 // Before the relevant bug was fixed, Tdiagsaform would panic during apply. 4217 if _, diags := ctx.Apply(); diags.HasErrors() { 4218 t.Fatalf("apply failed: %s", diags.Err()) 4219 } 4220 4221 // If we get here with no errors or panics then our test was successful. 4222 } 4223 4224 func TestContext2Apply_nilDiff(t *testing.T) { 4225 m := testModule(t, "apply-good") 4226 p := testProvider("aws") 4227 p.ApplyFn = testApplyFn 4228 p.DiffFn = testDiffFn 4229 ctx := testContext2(t, &ContextOpts{ 4230 Config: m, 4231 ProviderResolver: providers.ResolverFixed( 4232 map[string]providers.Factory{ 4233 "aws": testProviderFuncFixed(p), 4234 }, 4235 ), 4236 }) 4237 4238 if _, diags := ctx.Plan(); diags.HasErrors() { 4239 t.Fatalf("plan errors: %s", diags.Err()) 4240 } 4241 4242 p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { 4243 return nil, nil 4244 } 4245 4246 if _, diags := ctx.Apply(); diags == nil { 4247 t.Fatal("should error") 4248 } 4249 } 4250 4251 func TestContext2Apply_outputDependsOn(t *testing.T) { 4252 m := testModule(t, "apply-output-depends-on") 4253 p := testProvider("aws") 4254 p.DiffFn = testDiffFn 4255 4256 { 4257 // Create a custom apply function that sleeps a bit (to allow parallel 4258 // graph execution) and then returns an error to force a partial state 4259 // return. We then verify the output is NOT there. 4260 p.ApplyFn = func( 4261 info *InstanceInfo, 4262 is *InstanceState, 4263 id *InstanceDiff) (*InstanceState, error) { 4264 // Sleep to allow parallel execution 4265 time.Sleep(50 * time.Millisecond) 4266 4267 // Return error to force partial state 4268 return nil, fmt.Errorf("abcd") 4269 } 4270 4271 ctx := testContext2(t, &ContextOpts{ 4272 Config: m, 4273 ProviderResolver: providers.ResolverFixed( 4274 map[string]providers.Factory{ 4275 "aws": testProviderFuncFixed(p), 4276 }, 4277 ), 4278 }) 4279 4280 if _, diags := ctx.Plan(); diags.HasErrors() { 4281 t.Fatalf("diags: %s", diags.Err()) 4282 } 4283 4284 state, diags := ctx.Apply() 4285 if !diags.HasErrors() || !strings.Contains(diags.Err().Error(), "abcd") { 4286 t.Fatalf("err: %s", diags.Err()) 4287 } 4288 4289 checkStateString(t, state, `<no state>`) 4290 } 4291 4292 { 4293 // Create the standard apply function and verify we get the output 4294 p.ApplyFn = testApplyFn 4295 4296 ctx := testContext2(t, &ContextOpts{ 4297 Config: m, 4298 ProviderResolver: providers.ResolverFixed( 4299 map[string]providers.Factory{ 4300 "aws": testProviderFuncFixed(p), 4301 }, 4302 ), 4303 }) 4304 4305 if _, diags := ctx.Plan(); diags.HasErrors() { 4306 t.Fatalf("diags: %s", diags.Err()) 4307 } 4308 4309 state, diags := ctx.Apply() 4310 if diags.HasErrors() { 4311 t.Fatalf("diags: %s", diags.Err()) 4312 } 4313 4314 checkStateString(t, state, ` 4315 aws_instance.foo: 4316 ID = foo 4317 provider = provider.aws 4318 4319 Outputs: 4320 4321 value = result 4322 `) 4323 } 4324 } 4325 4326 func TestContext2Apply_outputOrphan(t *testing.T) { 4327 m := testModule(t, "apply-output-orphan") 4328 p := testProvider("aws") 4329 p.ApplyFn = testApplyFn 4330 p.DiffFn = testDiffFn 4331 4332 state := MustShimLegacyState(&State{ 4333 Modules: []*ModuleState{ 4334 { 4335 Path: rootModulePath, 4336 Outputs: map[string]*OutputState{ 4337 "foo": { 4338 Type: "string", 4339 Sensitive: false, 4340 Value: "bar", 4341 }, 4342 "bar": { 4343 Type: "string", 4344 Sensitive: false, 4345 Value: "baz", 4346 }, 4347 }, 4348 }, 4349 }, 4350 }) 4351 4352 ctx := testContext2(t, &ContextOpts{ 4353 Config: m, 4354 ProviderResolver: providers.ResolverFixed( 4355 map[string]providers.Factory{ 4356 "aws": testProviderFuncFixed(p), 4357 }, 4358 ), 4359 State: state, 4360 }) 4361 4362 if _, diags := ctx.Plan(); diags.HasErrors() { 4363 t.Fatalf("plan errors: %s", diags.Err()) 4364 } 4365 4366 state, diags := ctx.Apply() 4367 if diags.HasErrors() { 4368 t.Fatalf("diags: %s", diags.Err()) 4369 } 4370 4371 actual := strings.TrimSpace(state.String()) 4372 expected := strings.TrimSpace(testTerraformApplyOutputOrphanStr) 4373 if actual != expected { 4374 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4375 } 4376 } 4377 4378 func TestContext2Apply_outputOrphanModule(t *testing.T) { 4379 m := testModule(t, "apply-output-orphan-module") 4380 p := testProvider("aws") 4381 p.ApplyFn = testApplyFn 4382 p.DiffFn = testDiffFn 4383 4384 state := MustShimLegacyState(&State{ 4385 Modules: []*ModuleState{ 4386 { 4387 Path: []string{"root", "child"}, 4388 Outputs: map[string]*OutputState{ 4389 "foo": { 4390 Type: "string", 4391 Value: "bar", 4392 }, 4393 "bar": { 4394 Type: "string", 4395 Value: "baz", 4396 }, 4397 }, 4398 }, 4399 }, 4400 }) 4401 4402 ctx := testContext2(t, &ContextOpts{ 4403 Config: m, 4404 ProviderResolver: providers.ResolverFixed( 4405 map[string]providers.Factory{ 4406 "aws": testProviderFuncFixed(p), 4407 }, 4408 ), 4409 State: state.DeepCopy(), 4410 }) 4411 4412 if _, diags := ctx.Plan(); diags.HasErrors() { 4413 t.Fatalf("plan errors: %s", diags.Err()) 4414 } 4415 4416 state, diags := ctx.Apply() 4417 if diags.HasErrors() { 4418 t.Fatalf("diags: %s", diags.Err()) 4419 } 4420 4421 actual := strings.TrimSpace(state.String()) 4422 expected := strings.TrimSpace(testTerraformApplyOutputOrphanModuleStr) 4423 if actual != expected { 4424 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 4425 } 4426 4427 // now apply with no module in the config, which should remove the 4428 // remaining output 4429 ctx = testContext2(t, &ContextOpts{ 4430 Config: configs.NewEmptyConfig(), 4431 ProviderResolver: providers.ResolverFixed( 4432 map[string]providers.Factory{ 4433 "aws": testProviderFuncFixed(p), 4434 }, 4435 ), 4436 State: state.DeepCopy(), 4437 }) 4438 4439 if _, diags := ctx.Plan(); diags.HasErrors() { 4440 t.Fatalf("plan errors: %s", diags.Err()) 4441 } 4442 4443 state, diags = ctx.Apply() 4444 if diags.HasErrors() { 4445 t.Fatalf("diags: %s", diags.Err()) 4446 } 4447 4448 if !state.Empty() { 4449 t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(state)) 4450 } 4451 } 4452 4453 func TestContext2Apply_providerComputedVar(t *testing.T) { 4454 m := testModule(t, "apply-provider-computed") 4455 p := testProvider("aws") 4456 p.ApplyFn = testApplyFn 4457 p.DiffFn = testDiffFn 4458 4459 pTest := testProvider("test") 4460 pTest.ApplyFn = testApplyFn 4461 pTest.DiffFn = testDiffFn 4462 4463 ctx := testContext2(t, &ContextOpts{ 4464 Config: m, 4465 ProviderResolver: providers.ResolverFixed( 4466 map[string]providers.Factory{ 4467 "aws": testProviderFuncFixed(p), 4468 "test": testProviderFuncFixed(pTest), 4469 }, 4470 ), 4471 }) 4472 4473 p.ConfigureFn = func(c *ResourceConfig) error { 4474 if c.IsComputed("value") { 4475 return fmt.Errorf("value is computed") 4476 } 4477 4478 v, ok := c.Get("value") 4479 if !ok { 4480 return fmt.Errorf("value is not found") 4481 } 4482 if v != "yes" { 4483 return fmt.Errorf("value is not 'yes': %v", v) 4484 } 4485 4486 return nil 4487 } 4488 4489 if _, diags := ctx.Plan(); diags.HasErrors() { 4490 t.Fatalf("plan errors: %s", diags.Err()) 4491 } 4492 4493 if _, diags := ctx.Apply(); diags.HasErrors() { 4494 t.Fatalf("apply errors: %s", diags.Err()) 4495 } 4496 } 4497 4498 func TestContext2Apply_providerConfigureDisabled(t *testing.T) { 4499 m := testModule(t, "apply-provider-configure-disabled") 4500 p := testProvider("aws") 4501 p.ApplyFn = testApplyFn 4502 p.DiffFn = testDiffFn 4503 4504 called := false 4505 p.ConfigureFn = func(c *ResourceConfig) error { 4506 called = true 4507 4508 if _, ok := c.Get("value"); !ok { 4509 return fmt.Errorf("value is not found") 4510 } 4511 4512 return nil 4513 } 4514 4515 ctx := testContext2(t, &ContextOpts{ 4516 Config: m, 4517 ProviderResolver: providers.ResolverFixed( 4518 map[string]providers.Factory{ 4519 "aws": testProviderFuncFixed(p), 4520 }, 4521 ), 4522 }) 4523 4524 if _, diags := ctx.Plan(); diags.HasErrors() { 4525 t.Fatalf("plan errors: %s", diags.Err()) 4526 } 4527 4528 if _, diags := ctx.Apply(); diags.HasErrors() { 4529 t.Fatalf("apply errors: %s", diags.Err()) 4530 } 4531 4532 if !called { 4533 t.Fatal("configure never called") 4534 } 4535 } 4536 4537 func TestContext2Apply_provisionerModule(t *testing.T) { 4538 m := testModule(t, "apply-provisioner-module") 4539 4540 p := testProvider("aws") 4541 p.ApplyFn = testApplyFn 4542 p.DiffFn = testDiffFn 4543 4544 pr := testProvisioner() 4545 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 4546 Provisioner: &configschema.Block{ 4547 Attributes: map[string]*configschema.Attribute{ 4548 "foo": {Type: cty.String, Optional: true}, 4549 }, 4550 }, 4551 } 4552 4553 ctx := testContext2(t, &ContextOpts{ 4554 Config: m, 4555 ProviderResolver: providers.ResolverFixed( 4556 map[string]providers.Factory{ 4557 "aws": testProviderFuncFixed(p), 4558 }, 4559 ), 4560 Provisioners: map[string]ProvisionerFactory{ 4561 "shell": testProvisionerFuncFixed(pr), 4562 }, 4563 }) 4564 4565 if _, diags := ctx.Plan(); diags.HasErrors() { 4566 t.Fatalf("plan errors: %s", diags.Err()) 4567 } 4568 4569 state, diags := ctx.Apply() 4570 if diags.HasErrors() { 4571 t.Fatalf("diags: %s", diags.Err()) 4572 } 4573 4574 actual := strings.TrimSpace(state.String()) 4575 expected := strings.TrimSpace(testTerraformApplyProvisionerModuleStr) 4576 if actual != expected { 4577 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4578 } 4579 4580 // Verify apply was invoked 4581 if !pr.ProvisionResourceCalled { 4582 t.Fatalf("provisioner not invoked") 4583 } 4584 } 4585 4586 func TestContext2Apply_Provisioner_compute(t *testing.T) { 4587 m := testModule(t, "apply-provisioner-compute") 4588 p := testProvider("aws") 4589 pr := testProvisioner() 4590 p.ApplyFn = testApplyFn 4591 p.DiffFn = testDiffFn 4592 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 4593 val, ok := c.Config["command"] 4594 if !ok || val != "computed_value" { 4595 t.Fatalf("bad value for foo: %v %#v", val, c) 4596 } 4597 4598 return nil 4599 } 4600 ctx := testContext2(t, &ContextOpts{ 4601 Config: m, 4602 ProviderResolver: providers.ResolverFixed( 4603 map[string]providers.Factory{ 4604 "aws": testProviderFuncFixed(p), 4605 }, 4606 ), 4607 Provisioners: map[string]ProvisionerFactory{ 4608 "shell": testProvisionerFuncFixed(pr), 4609 }, 4610 Variables: InputValues{ 4611 "value": &InputValue{ 4612 Value: cty.NumberIntVal(1), 4613 SourceType: ValueFromCaller, 4614 }, 4615 }, 4616 }) 4617 4618 if _, diags := ctx.Plan(); diags.HasErrors() { 4619 t.Fatalf("plan errors: %s", diags.Err()) 4620 } 4621 4622 state, diags := ctx.Apply() 4623 if diags.HasErrors() { 4624 t.Fatalf("diags: %s", diags.Err()) 4625 } 4626 4627 actual := strings.TrimSpace(state.String()) 4628 expected := strings.TrimSpace(testTerraformApplyProvisionerStr) 4629 if actual != expected { 4630 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4631 } 4632 4633 // Verify apply was invoked 4634 if !pr.ProvisionResourceCalled { 4635 t.Fatalf("provisioner not invoked") 4636 } 4637 } 4638 4639 func TestContext2Apply_provisionerCreateFail(t *testing.T) { 4640 m := testModule(t, "apply-provisioner-fail-create") 4641 p := testProvider("aws") 4642 pr := testProvisioner() 4643 p.DiffFn = testDiffFn 4644 4645 p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { 4646 is.ID = "foo" 4647 return is, fmt.Errorf("error") 4648 } 4649 4650 ctx := testContext2(t, &ContextOpts{ 4651 Config: m, 4652 ProviderResolver: providers.ResolverFixed( 4653 map[string]providers.Factory{ 4654 "aws": testProviderFuncFixed(p), 4655 }, 4656 ), 4657 Provisioners: map[string]ProvisionerFactory{ 4658 "shell": testProvisionerFuncFixed(pr), 4659 }, 4660 }) 4661 4662 if _, diags := ctx.Plan(); diags.HasErrors() { 4663 t.Fatalf("plan errors: %s", diags.Err()) 4664 } 4665 4666 state, diags := ctx.Apply() 4667 if diags == nil { 4668 t.Fatal("should error") 4669 } 4670 4671 got := strings.TrimSpace(state.String()) 4672 want := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr) 4673 if got != want { 4674 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want) 4675 } 4676 } 4677 4678 func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) { 4679 m := testModule(t, "apply-provisioner-fail-create") 4680 p := testProvider("aws") 4681 pr := testProvisioner() 4682 p.DiffFn = testDiffFn 4683 4684 p.ApplyFn = func( 4685 info *InstanceInfo, 4686 is *InstanceState, 4687 id *InstanceDiff) (*InstanceState, error) { 4688 return nil, fmt.Errorf("error") 4689 } 4690 4691 ctx := testContext2(t, &ContextOpts{ 4692 Config: m, 4693 ProviderResolver: providers.ResolverFixed( 4694 map[string]providers.Factory{ 4695 "aws": testProviderFuncFixed(p), 4696 }, 4697 ), 4698 Provisioners: map[string]ProvisionerFactory{ 4699 "shell": testProvisionerFuncFixed(pr), 4700 }, 4701 }) 4702 4703 if _, diags := ctx.Plan(); diags.HasErrors() { 4704 t.Fatalf("plan errors: %s", diags.Err()) 4705 } 4706 4707 state, diags := ctx.Apply() 4708 if diags == nil { 4709 t.Fatal("should error") 4710 } 4711 4712 actual := strings.TrimSpace(state.String()) 4713 expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr) 4714 if actual != expected { 4715 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4716 } 4717 } 4718 4719 func TestContext2Apply_provisionerFail(t *testing.T) { 4720 m := testModule(t, "apply-provisioner-fail") 4721 p := testProvider("aws") 4722 pr := testProvisioner() 4723 p.ApplyFn = testApplyFn 4724 p.DiffFn = testDiffFn 4725 4726 pr.ApplyFn = func(*InstanceState, *ResourceConfig) error { 4727 return fmt.Errorf("EXPLOSION") 4728 } 4729 4730 ctx := testContext2(t, &ContextOpts{ 4731 Config: m, 4732 ProviderResolver: providers.ResolverFixed( 4733 map[string]providers.Factory{ 4734 "aws": testProviderFuncFixed(p), 4735 }, 4736 ), 4737 Provisioners: map[string]ProvisionerFactory{ 4738 "shell": testProvisionerFuncFixed(pr), 4739 }, 4740 Variables: InputValues{ 4741 "value": &InputValue{ 4742 Value: cty.NumberIntVal(1), 4743 SourceType: ValueFromCaller, 4744 }, 4745 }, 4746 }) 4747 4748 if _, diags := ctx.Plan(); diags.HasErrors() { 4749 t.Fatalf("plan errors: %s", diags.Err()) 4750 } 4751 4752 state, diags := ctx.Apply() 4753 if diags == nil { 4754 t.Fatal("should error") 4755 } 4756 4757 actual := strings.TrimSpace(state.String()) 4758 expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr) 4759 if actual != expected { 4760 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 4761 } 4762 } 4763 4764 func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) { 4765 m := testModule(t, "apply-provisioner-fail-create-before") 4766 p := testProvider("aws") 4767 pr := testProvisioner() 4768 p.ApplyFn = testApplyFn 4769 p.DiffFn = testDiffFn 4770 pr.ApplyFn = func(*InstanceState, *ResourceConfig) error { 4771 return fmt.Errorf("EXPLOSION") 4772 } 4773 4774 state := MustShimLegacyState(&State{ 4775 Modules: []*ModuleState{ 4776 { 4777 Path: rootModulePath, 4778 Resources: map[string]*ResourceState{ 4779 "aws_instance.bar": { 4780 Type: "aws_instance", 4781 Primary: &InstanceState{ 4782 ID: "bar", 4783 Attributes: map[string]string{ 4784 "require_new": "abc", 4785 }, 4786 }, 4787 }, 4788 }, 4789 }, 4790 }, 4791 }) 4792 ctx := testContext2(t, &ContextOpts{ 4793 Config: m, 4794 ProviderResolver: providers.ResolverFixed( 4795 map[string]providers.Factory{ 4796 "aws": testProviderFuncFixed(p), 4797 }, 4798 ), 4799 Provisioners: map[string]ProvisionerFactory{ 4800 "shell": testProvisionerFuncFixed(pr), 4801 }, 4802 State: state, 4803 }) 4804 4805 if _, diags := ctx.Plan(); diags.HasErrors() { 4806 t.Fatalf("plan errors: %s", diags.Err()) 4807 } 4808 4809 state, diags := ctx.Apply() 4810 if diags == nil { 4811 t.Fatal("should error") 4812 } 4813 4814 actual := strings.TrimSpace(state.String()) 4815 expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr) 4816 if actual != expected { 4817 t.Fatalf("expected:\n%s\n:got\n%s", expected, actual) 4818 } 4819 } 4820 4821 func TestContext2Apply_error_createBeforeDestroy(t *testing.T) { 4822 m := testModule(t, "apply-error-create-before") 4823 p := testProvider("aws") 4824 state := MustShimLegacyState(&State{ 4825 Modules: []*ModuleState{ 4826 { 4827 Path: rootModulePath, 4828 Resources: map[string]*ResourceState{ 4829 "aws_instance.bar": { 4830 Type: "aws_instance", 4831 Primary: &InstanceState{ 4832 ID: "bar", 4833 Attributes: map[string]string{ 4834 "require_new": "abc", 4835 }, 4836 }, 4837 }, 4838 }, 4839 }, 4840 }, 4841 }) 4842 ctx := testContext2(t, &ContextOpts{ 4843 Config: m, 4844 ProviderResolver: providers.ResolverFixed( 4845 map[string]providers.Factory{ 4846 "aws": testProviderFuncFixed(p), 4847 }, 4848 ), 4849 State: state, 4850 }) 4851 p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { 4852 return nil, fmt.Errorf("error") 4853 } 4854 p.DiffFn = testDiffFn 4855 4856 if _, diags := ctx.Plan(); diags.HasErrors() { 4857 t.Fatalf("plan errors: %s", diags.Err()) 4858 } 4859 4860 state, diags := ctx.Apply() 4861 if diags == nil { 4862 t.Fatal("should have error") 4863 } 4864 4865 actual := strings.TrimSpace(state.String()) 4866 expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr) 4867 if actual != expected { 4868 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected) 4869 } 4870 } 4871 4872 func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) { 4873 m := testModule(t, "apply-error-create-before") 4874 p := testProvider("aws") 4875 state := MustShimLegacyState(&State{ 4876 Modules: []*ModuleState{ 4877 { 4878 Path: rootModulePath, 4879 Resources: map[string]*ResourceState{ 4880 "aws_instance.bar": { 4881 Type: "aws_instance", 4882 Primary: &InstanceState{ 4883 ID: "bar", 4884 Attributes: map[string]string{ 4885 "require_new": "abc", 4886 }, 4887 }, 4888 }, 4889 }, 4890 }, 4891 }, 4892 }) 4893 ctx := testContext2(t, &ContextOpts{ 4894 Config: m, 4895 ProviderResolver: providers.ResolverFixed( 4896 map[string]providers.Factory{ 4897 "aws": testProviderFuncFixed(p), 4898 }, 4899 ), 4900 State: state, 4901 }) 4902 p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { 4903 // Fail the destroy! 4904 if id.Destroy { 4905 return is, fmt.Errorf("error") 4906 } 4907 4908 // Create should work 4909 is = &InstanceState{ 4910 ID: "foo", 4911 Attributes: map[string]string{ 4912 "type": "aws_instance", 4913 "require_new": "xyz", 4914 }, 4915 } 4916 return is, nil 4917 } 4918 p.DiffFn = testDiffFn 4919 4920 if _, diags := ctx.Plan(); diags.HasErrors() { 4921 t.Fatalf("plan errors: %s", diags.Err()) 4922 } 4923 4924 state, diags := ctx.Apply() 4925 if diags == nil { 4926 t.Fatal("should have error") 4927 } 4928 4929 actual := strings.TrimSpace(state.String()) 4930 expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr) 4931 if actual != expected { 4932 t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected) 4933 } 4934 } 4935 4936 func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) { 4937 m := testModule(t, "apply-multi-depose-create-before-destroy") 4938 p := testProvider("aws") 4939 p.GetSchemaReturn = &ProviderSchema{ 4940 ResourceTypes: map[string]*configschema.Block{ 4941 "aws_instance": { 4942 Attributes: map[string]*configschema.Attribute{ 4943 "require_new": {Type: cty.String, Optional: true}, 4944 "id": {Type: cty.String, Computed: true}, 4945 }, 4946 }, 4947 }, 4948 } 4949 ps := map[string]providers.Factory{"aws": testProviderFuncFixed(p)} 4950 state := MustShimLegacyState(&State{ 4951 Modules: []*ModuleState{ 4952 { 4953 Path: rootModulePath, 4954 Resources: map[string]*ResourceState{ 4955 "aws_instance.web": { 4956 Type: "aws_instance", 4957 Primary: &InstanceState{ID: "foo"}, 4958 }, 4959 }, 4960 }, 4961 }, 4962 }) 4963 4964 p.DiffFn = func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 4965 if rc == nil { 4966 return &InstanceDiff{ 4967 Destroy: true, 4968 }, nil 4969 } 4970 4971 rn, _ := rc.Get("require_new") 4972 return &InstanceDiff{ 4973 Attributes: map[string]*ResourceAttrDiff{ 4974 "id": { 4975 New: hcl2shim.UnknownVariableValue, 4976 NewComputed: true, 4977 RequiresNew: true, 4978 }, 4979 "require_new": { 4980 Old: s.Attributes["require_new"], 4981 New: rn.(string), 4982 RequiresNew: true, 4983 }, 4984 }, 4985 }, nil 4986 } 4987 4988 ctx := testContext2(t, &ContextOpts{ 4989 Config: m, 4990 ProviderResolver: providers.ResolverFixed(ps), 4991 State: state, 4992 }) 4993 createdInstanceId := "bar" 4994 // Create works 4995 createFunc := func(is *InstanceState, id *InstanceDiff) (*InstanceState, error) { 4996 return &InstanceState{ 4997 ID: createdInstanceId, 4998 Attributes: map[string]string{ 4999 "require_new": id.Attributes["require_new"].New, 5000 }, 5001 }, nil 5002 } 5003 // Destroy starts broken 5004 destroyFunc := func(is *InstanceState) (*InstanceState, error) { 5005 return is, fmt.Errorf("destroy failed") 5006 } 5007 p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { 5008 if id.Destroy { 5009 return destroyFunc(is) 5010 } else { 5011 return createFunc(is, id) 5012 } 5013 } 5014 5015 if _, diags := ctx.Plan(); diags.HasErrors() { 5016 t.Fatalf("plan errors: %s", diags.Err()) 5017 } 5018 5019 // Destroy is broken, so even though CBD successfully replaces the instance, 5020 // we'll have to save the Deposed instance to destroy later 5021 state, diags := ctx.Apply() 5022 if diags == nil { 5023 t.Fatal("should have error") 5024 } 5025 5026 checkStateString(t, state, ` 5027 aws_instance.web: (1 deposed) 5028 ID = bar 5029 provider = provider.aws 5030 require_new = yes 5031 Deposed ID 1 = foo 5032 `) 5033 5034 createdInstanceId = "baz" 5035 ctx = testContext2(t, &ContextOpts{ 5036 Config: m, 5037 ProviderResolver: providers.ResolverFixed(ps), 5038 State: state, 5039 }) 5040 5041 if _, diags := ctx.Plan(); diags.HasErrors() { 5042 t.Fatalf("plan errors: %s", diags.Err()) 5043 } 5044 5045 // We're replacing the primary instance once again. Destroy is _still_ 5046 // broken, so the Deposed list gets longer 5047 state, diags = ctx.Apply() 5048 if diags == nil { 5049 t.Fatal("should have error") 5050 } 5051 5052 // For this one we can't rely on checkStateString because its result is 5053 // not deterministic when multiple deposed objects are present. Instead, 5054 // we will probe the state object directly. 5055 { 5056 is := state.RootModule().Resources["aws_instance.web"].Instances[addrs.NoKey] 5057 t.Logf("aws_instance.web is %s", spew.Sdump(is)) 5058 if is.Current == nil { 5059 t.Fatalf("no current object for aws_instance web; should have one") 5060 } 5061 if !bytes.Contains(is.Current.AttrsJSON, []byte("baz")) { 5062 t.Fatalf("incorrect current object attrs %s; want id=baz", is.Current.AttrsJSON) 5063 } 5064 if got, want := len(is.Deposed), 2; got != want { 5065 t.Fatalf("wrong number of deposed instances %d; want %d", got, want) 5066 } 5067 var foos, bars int 5068 for _, obj := range is.Deposed { 5069 if bytes.Contains(obj.AttrsJSON, []byte("foo")) { 5070 foos++ 5071 } 5072 if bytes.Contains(obj.AttrsJSON, []byte("bar")) { 5073 bars++ 5074 } 5075 } 5076 if got, want := foos, 1; got != want { 5077 t.Fatalf("wrong number of deposed instances with id=foo %d; want %d", got, want) 5078 } 5079 if got, want := bars, 1; got != want { 5080 t.Fatalf("wrong number of deposed instances with id=bar %d; want %d", got, want) 5081 } 5082 } 5083 5084 // Destroy partially fixed! 5085 destroyFunc = func(is *InstanceState) (*InstanceState, error) { 5086 if is.ID == "foo" || is.ID == "baz" { 5087 return nil, nil 5088 } else { 5089 return is, fmt.Errorf("destroy partially failed") 5090 } 5091 } 5092 5093 createdInstanceId = "qux" 5094 ctx = testContext2(t, &ContextOpts{ 5095 Config: m, 5096 ProviderResolver: providers.ResolverFixed(ps), 5097 State: state, 5098 }) 5099 if _, diags := ctx.Plan(); diags.HasErrors() { 5100 t.Fatalf("plan errors: %s", diags.Err()) 5101 } 5102 state, diags = ctx.Apply() 5103 // Expect error because 1/2 of Deposed destroys failed 5104 if diags == nil { 5105 t.Fatal("should have error") 5106 } 5107 5108 // foo and baz are now gone, bar sticks around 5109 checkStateString(t, state, ` 5110 aws_instance.web: (1 deposed) 5111 ID = qux 5112 provider = provider.aws 5113 require_new = yes 5114 Deposed ID 1 = bar 5115 `) 5116 5117 // Destroy working fully! 5118 destroyFunc = func(is *InstanceState) (*InstanceState, error) { 5119 return nil, nil 5120 } 5121 5122 createdInstanceId = "quux" 5123 ctx = testContext2(t, &ContextOpts{ 5124 Config: m, 5125 ProviderResolver: providers.ResolverFixed(ps), 5126 State: state, 5127 }) 5128 if _, diags := ctx.Plan(); diags.HasErrors() { 5129 t.Fatalf("plan errors: %s", diags.Err()) 5130 } 5131 state, diags = ctx.Apply() 5132 if diags.HasErrors() { 5133 t.Fatal("should not have error:", diags.Err()) 5134 } 5135 5136 // And finally the state is clean 5137 checkStateString(t, state, ` 5138 aws_instance.web: 5139 ID = quux 5140 provider = provider.aws 5141 require_new = yes 5142 `) 5143 } 5144 5145 // Verify that a normal provisioner with on_failure "continue" set won't 5146 // taint the resource and continues executing. 5147 func TestContext2Apply_provisionerFailContinue(t *testing.T) { 5148 m := testModule(t, "apply-provisioner-fail-continue") 5149 p := testProvider("aws") 5150 pr := testProvisioner() 5151 p.ApplyFn = testApplyFn 5152 p.DiffFn = testDiffFn 5153 5154 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5155 return fmt.Errorf("provisioner error") 5156 } 5157 5158 ctx := testContext2(t, &ContextOpts{ 5159 Config: m, 5160 ProviderResolver: providers.ResolverFixed( 5161 map[string]providers.Factory{ 5162 "aws": testProviderFuncFixed(p), 5163 }, 5164 ), 5165 Provisioners: map[string]ProvisionerFactory{ 5166 "shell": testProvisionerFuncFixed(pr), 5167 }, 5168 }) 5169 5170 if _, diags := ctx.Plan(); diags.HasErrors() { 5171 t.Fatalf("plan errors: %s", diags.Err()) 5172 } 5173 5174 state, diags := ctx.Apply() 5175 if diags.HasErrors() { 5176 t.Fatalf("diags: %s", diags.Err()) 5177 } 5178 5179 checkStateString(t, state, ` 5180 aws_instance.foo: 5181 ID = foo 5182 provider = provider.aws 5183 foo = bar 5184 type = aws_instance 5185 `) 5186 5187 // Verify apply was invoked 5188 if !pr.ProvisionResourceCalled { 5189 t.Fatalf("provisioner not invoked") 5190 } 5191 } 5192 5193 // Verify that a normal provisioner with on_failure "continue" records 5194 // the error with the hook. 5195 func TestContext2Apply_provisionerFailContinueHook(t *testing.T) { 5196 h := new(MockHook) 5197 m := testModule(t, "apply-provisioner-fail-continue") 5198 p := testProvider("aws") 5199 pr := testProvisioner() 5200 p.ApplyFn = testApplyFn 5201 p.DiffFn = testDiffFn 5202 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5203 return fmt.Errorf("provisioner error") 5204 } 5205 5206 ctx := testContext2(t, &ContextOpts{ 5207 Config: m, 5208 Hooks: []Hook{h}, 5209 ProviderResolver: providers.ResolverFixed( 5210 map[string]providers.Factory{ 5211 "aws": testProviderFuncFixed(p), 5212 }, 5213 ), 5214 Provisioners: map[string]ProvisionerFactory{ 5215 "shell": testProvisionerFuncFixed(pr), 5216 }, 5217 }) 5218 5219 if _, diags := ctx.Plan(); diags.HasErrors() { 5220 t.Fatalf("plan errors: %s", diags.Err()) 5221 } 5222 5223 if _, diags := ctx.Apply(); diags.HasErrors() { 5224 t.Fatalf("apply errors: %s", diags.Err()) 5225 } 5226 5227 if !h.PostProvisionInstanceStepCalled { 5228 t.Fatal("PostProvisionInstanceStep not called") 5229 } 5230 if h.PostProvisionInstanceStepErrorArg == nil { 5231 t.Fatal("should have error") 5232 } 5233 } 5234 5235 func TestContext2Apply_provisionerDestroy(t *testing.T) { 5236 m := testModule(t, "apply-provisioner-destroy") 5237 p := testProvider("aws") 5238 pr := testProvisioner() 5239 p.ApplyFn = testApplyFn 5240 p.DiffFn = testDiffFn 5241 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5242 val, ok := c.Config["command"] 5243 if !ok || val != "destroy" { 5244 t.Fatalf("bad value for foo: %v %#v", val, c) 5245 } 5246 5247 return nil 5248 } 5249 5250 state := MustShimLegacyState(&State{ 5251 Modules: []*ModuleState{ 5252 { 5253 Path: rootModulePath, 5254 Resources: map[string]*ResourceState{ 5255 "aws_instance.foo": { 5256 Type: "aws_instance", 5257 Primary: &InstanceState{ 5258 ID: "bar", 5259 }, 5260 }, 5261 }, 5262 }, 5263 }, 5264 }) 5265 5266 ctx := testContext2(t, &ContextOpts{ 5267 Config: m, 5268 State: state, 5269 Destroy: true, 5270 ProviderResolver: providers.ResolverFixed( 5271 map[string]providers.Factory{ 5272 "aws": testProviderFuncFixed(p), 5273 }, 5274 ), 5275 Provisioners: map[string]ProvisionerFactory{ 5276 "shell": testProvisionerFuncFixed(pr), 5277 }, 5278 }) 5279 5280 if _, diags := ctx.Plan(); diags.HasErrors() { 5281 t.Fatalf("plan errors: %s", diags.Err()) 5282 } 5283 5284 state, diags := ctx.Apply() 5285 if diags.HasErrors() { 5286 t.Fatalf("diags: %s", diags.Err()) 5287 } 5288 5289 checkStateString(t, state, `<no state>`) 5290 5291 // Verify apply was invoked 5292 if !pr.ProvisionResourceCalled { 5293 t.Fatalf("provisioner not invoked") 5294 } 5295 } 5296 5297 // Verify that on destroy provisioner failure, nothing happens to the instance 5298 func TestContext2Apply_provisionerDestroyFail(t *testing.T) { 5299 m := testModule(t, "apply-provisioner-destroy") 5300 p := testProvider("aws") 5301 pr := testProvisioner() 5302 p.ApplyFn = testApplyFn 5303 p.DiffFn = testDiffFn 5304 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5305 return fmt.Errorf("provisioner error") 5306 } 5307 5308 state := MustShimLegacyState(&State{ 5309 Modules: []*ModuleState{ 5310 { 5311 Path: rootModulePath, 5312 Resources: map[string]*ResourceState{ 5313 "aws_instance.foo": { 5314 Type: "aws_instance", 5315 Primary: &InstanceState{ 5316 ID: "bar", 5317 }, 5318 }, 5319 }, 5320 }, 5321 }, 5322 }) 5323 5324 ctx := testContext2(t, &ContextOpts{ 5325 Config: m, 5326 State: state, 5327 Destroy: true, 5328 ProviderResolver: providers.ResolverFixed( 5329 map[string]providers.Factory{ 5330 "aws": testProviderFuncFixed(p), 5331 }, 5332 ), 5333 Provisioners: map[string]ProvisionerFactory{ 5334 "shell": testProvisionerFuncFixed(pr), 5335 }, 5336 }) 5337 5338 if _, diags := ctx.Plan(); diags.HasErrors() { 5339 t.Fatalf("plan errors: %s", diags.Err()) 5340 } 5341 5342 state, diags := ctx.Apply() 5343 if diags == nil { 5344 t.Fatal("should error") 5345 } 5346 5347 checkStateString(t, state, ` 5348 aws_instance.foo: 5349 ID = bar 5350 provider = provider.aws 5351 `) 5352 5353 // Verify apply was invoked 5354 if !pr.ProvisionResourceCalled { 5355 t.Fatalf("provisioner not invoked") 5356 } 5357 } 5358 5359 // Verify that on destroy provisioner failure with "continue" that 5360 // we continue to the next provisioner. 5361 func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) { 5362 m := testModule(t, "apply-provisioner-destroy-continue") 5363 p := testProvider("aws") 5364 pr := testProvisioner() 5365 p.ApplyFn = testApplyFn 5366 p.DiffFn = testDiffFn 5367 5368 var l sync.Mutex 5369 var calls []string 5370 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5371 val, ok := c.Config["command"] 5372 if !ok { 5373 t.Fatalf("bad value for foo: %v %#v", val, c) 5374 } 5375 5376 l.Lock() 5377 defer l.Unlock() 5378 calls = append(calls, val.(string)) 5379 return fmt.Errorf("provisioner error") 5380 } 5381 5382 state := MustShimLegacyState(&State{ 5383 Modules: []*ModuleState{ 5384 { 5385 Path: rootModulePath, 5386 Resources: map[string]*ResourceState{ 5387 "aws_instance.foo": { 5388 Type: "aws_instance", 5389 Primary: &InstanceState{ 5390 ID: "bar", 5391 }, 5392 }, 5393 }, 5394 }, 5395 }, 5396 }) 5397 5398 ctx := testContext2(t, &ContextOpts{ 5399 Config: m, 5400 State: state, 5401 Destroy: true, 5402 ProviderResolver: providers.ResolverFixed( 5403 map[string]providers.Factory{ 5404 "aws": testProviderFuncFixed(p), 5405 }, 5406 ), 5407 Provisioners: map[string]ProvisionerFactory{ 5408 "shell": testProvisionerFuncFixed(pr), 5409 }, 5410 }) 5411 5412 if _, diags := ctx.Plan(); diags.HasErrors() { 5413 t.Fatalf("plan errors: %s", diags.Err()) 5414 } 5415 5416 state, diags := ctx.Apply() 5417 if diags.HasErrors() { 5418 t.Fatalf("diags: %s", diags.Err()) 5419 } 5420 5421 checkStateString(t, state, `<no state>`) 5422 5423 // Verify apply was invoked 5424 if !pr.ProvisionResourceCalled { 5425 t.Fatalf("provisioner not invoked") 5426 } 5427 5428 expected := []string{"one", "two"} 5429 if !reflect.DeepEqual(calls, expected) { 5430 t.Fatalf("wrong commands\ngot: %#v\nwant: %#v", calls, expected) 5431 } 5432 } 5433 5434 // Verify that on destroy provisioner failure with "continue" that 5435 // we continue to the next provisioner. But if the next provisioner defines 5436 // to fail, then we fail after running it. 5437 func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) { 5438 m := testModule(t, "apply-provisioner-destroy-fail") 5439 p := testProvider("aws") 5440 pr := testProvisioner() 5441 p.ApplyFn = testApplyFn 5442 p.DiffFn = testDiffFn 5443 5444 var l sync.Mutex 5445 var calls []string 5446 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5447 val, ok := c.Config["command"] 5448 if !ok { 5449 t.Fatalf("bad value for foo: %v %#v", val, c) 5450 } 5451 5452 l.Lock() 5453 defer l.Unlock() 5454 calls = append(calls, val.(string)) 5455 return fmt.Errorf("provisioner error") 5456 } 5457 5458 state := MustShimLegacyState(&State{ 5459 Modules: []*ModuleState{ 5460 { 5461 Path: rootModulePath, 5462 Resources: map[string]*ResourceState{ 5463 "aws_instance.foo": { 5464 Type: "aws_instance", 5465 Primary: &InstanceState{ 5466 ID: "bar", 5467 }, 5468 }, 5469 }, 5470 }, 5471 }, 5472 }) 5473 5474 ctx := testContext2(t, &ContextOpts{ 5475 Config: m, 5476 State: state, 5477 Destroy: true, 5478 ProviderResolver: providers.ResolverFixed( 5479 map[string]providers.Factory{ 5480 "aws": testProviderFuncFixed(p), 5481 }, 5482 ), 5483 Provisioners: map[string]ProvisionerFactory{ 5484 "shell": testProvisionerFuncFixed(pr), 5485 }, 5486 }) 5487 5488 if _, diags := ctx.Plan(); diags.HasErrors() { 5489 t.Fatalf("plan errors: %s", diags.Err()) 5490 } 5491 5492 state, diags := ctx.Apply() 5493 if diags == nil { 5494 t.Fatal("apply succeeded; wanted error from second provisioner") 5495 } 5496 5497 checkStateString(t, state, ` 5498 aws_instance.foo: 5499 ID = bar 5500 provider = provider.aws 5501 `) 5502 5503 // Verify apply was invoked 5504 if !pr.ProvisionResourceCalled { 5505 t.Fatalf("provisioner not invoked") 5506 } 5507 5508 expected := []string{"one", "two"} 5509 if !reflect.DeepEqual(calls, expected) { 5510 t.Fatalf("bad: %#v", calls) 5511 } 5512 } 5513 5514 // Verify destroy provisioners are not run for tainted instances. 5515 func TestContext2Apply_provisionerDestroyTainted(t *testing.T) { 5516 m := testModule(t, "apply-provisioner-destroy") 5517 p := testProvider("aws") 5518 pr := testProvisioner() 5519 p.ApplyFn = testApplyFn 5520 p.DiffFn = testDiffFn 5521 5522 destroyCalled := false 5523 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5524 expected := "create" 5525 if rs.ID == "bar" { 5526 destroyCalled = true 5527 return nil 5528 } 5529 5530 val, ok := c.Config["command"] 5531 if !ok || val != expected { 5532 t.Fatalf("bad value for command: %v %#v", val, c) 5533 } 5534 5535 return nil 5536 } 5537 5538 state := MustShimLegacyState(&State{ 5539 Modules: []*ModuleState{ 5540 { 5541 Path: rootModulePath, 5542 Resources: map[string]*ResourceState{ 5543 "aws_instance.foo": { 5544 Type: "aws_instance", 5545 Primary: &InstanceState{ 5546 ID: "bar", 5547 Tainted: true, 5548 }, 5549 }, 5550 }, 5551 }, 5552 }, 5553 }) 5554 5555 ctx := testContext2(t, &ContextOpts{ 5556 Config: m, 5557 State: state, 5558 ProviderResolver: providers.ResolverFixed( 5559 map[string]providers.Factory{ 5560 "aws": testProviderFuncFixed(p), 5561 }, 5562 ), 5563 Provisioners: map[string]ProvisionerFactory{ 5564 "shell": testProvisionerFuncFixed(pr), 5565 }, 5566 }) 5567 5568 if _, diags := ctx.Plan(); diags.HasErrors() { 5569 t.Fatalf("plan errors: %s", diags.Err()) 5570 } 5571 5572 state, diags := ctx.Apply() 5573 if diags.HasErrors() { 5574 t.Fatalf("diags: %s", diags.Err()) 5575 } 5576 5577 checkStateString(t, state, ` 5578 aws_instance.foo: 5579 ID = foo 5580 provider = provider.aws 5581 foo = bar 5582 type = aws_instance 5583 `) 5584 5585 // Verify apply was invoked 5586 if !pr.ProvisionResourceCalled { 5587 t.Fatalf("provisioner not invoked") 5588 } 5589 5590 if destroyCalled { 5591 t.Fatal("destroy should not be called") 5592 } 5593 } 5594 5595 func TestContext2Apply_provisionerDestroyModule(t *testing.T) { 5596 m := testModule(t, "apply-provisioner-destroy-module") 5597 p := testProvider("aws") 5598 pr := testProvisioner() 5599 p.ApplyFn = testApplyFn 5600 p.DiffFn = testDiffFn 5601 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5602 val, ok := c.Config["command"] 5603 if !ok || val != "value" { 5604 t.Fatalf("bad value for foo: %v %#v", val, c) 5605 } 5606 5607 return nil 5608 } 5609 5610 state := MustShimLegacyState(&State{ 5611 Modules: []*ModuleState{ 5612 { 5613 Path: []string{"root", "child"}, 5614 Resources: map[string]*ResourceState{ 5615 "aws_instance.foo": { 5616 Type: "aws_instance", 5617 Primary: &InstanceState{ 5618 ID: "bar", 5619 }, 5620 }, 5621 }, 5622 }, 5623 }, 5624 }) 5625 5626 ctx := testContext2(t, &ContextOpts{ 5627 Config: m, 5628 State: state, 5629 Destroy: true, 5630 ProviderResolver: providers.ResolverFixed( 5631 map[string]providers.Factory{ 5632 "aws": testProviderFuncFixed(p), 5633 }, 5634 ), 5635 Provisioners: map[string]ProvisionerFactory{ 5636 "shell": testProvisionerFuncFixed(pr), 5637 }, 5638 }) 5639 5640 if _, diags := ctx.Plan(); diags.HasErrors() { 5641 t.Fatalf("plan errors: %s", diags.Err()) 5642 } 5643 5644 state, diags := ctx.Apply() 5645 if diags.HasErrors() { 5646 t.Fatalf("diags: %s", diags.Err()) 5647 } 5648 5649 checkStateString(t, state, `<no state>`) 5650 5651 // Verify apply was invoked 5652 if !pr.ProvisionResourceCalled { 5653 t.Fatalf("provisioner not invoked") 5654 } 5655 } 5656 5657 func TestContext2Apply_provisionerDestroyRef(t *testing.T) { 5658 m := testModule(t, "apply-provisioner-destroy-ref") 5659 p := testProvider("aws") 5660 pr := testProvisioner() 5661 p.ApplyFn = testApplyFn 5662 p.DiffFn = testDiffFn 5663 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5664 val, ok := c.Config["command"] 5665 if !ok || val != "hello" { 5666 return fmt.Errorf("bad value for command: %v %#v", val, c) 5667 } 5668 5669 return nil 5670 } 5671 5672 state := MustShimLegacyState(&State{ 5673 Modules: []*ModuleState{ 5674 { 5675 Path: rootModulePath, 5676 Resources: map[string]*ResourceState{ 5677 "aws_instance.bar": { 5678 Type: "aws_instance", 5679 Primary: &InstanceState{ 5680 ID: "bar", 5681 Attributes: map[string]string{ 5682 "value": "hello", 5683 }, 5684 }, 5685 Provider: "provider.aws", 5686 }, 5687 5688 "aws_instance.foo": { 5689 Type: "aws_instance", 5690 Primary: &InstanceState{ 5691 ID: "bar", 5692 }, 5693 Provider: "provider.aws", 5694 }, 5695 }, 5696 }, 5697 }, 5698 }) 5699 5700 ctx := testContext2(t, &ContextOpts{ 5701 Config: m, 5702 State: state, 5703 Destroy: true, 5704 ProviderResolver: providers.ResolverFixed( 5705 map[string]providers.Factory{ 5706 "aws": testProviderFuncFixed(p), 5707 }, 5708 ), 5709 Provisioners: map[string]ProvisionerFactory{ 5710 "shell": testProvisionerFuncFixed(pr), 5711 }, 5712 }) 5713 5714 if _, diags := ctx.Plan(); diags.HasErrors() { 5715 t.Fatalf("plan errors: %s", diags.Err()) 5716 } 5717 5718 state, diags := ctx.Apply() 5719 if diags.HasErrors() { 5720 t.Fatalf("diags: %s", diags.Err()) 5721 } 5722 5723 checkStateString(t, state, `<no state>`) 5724 5725 // Verify apply was invoked 5726 if !pr.ProvisionResourceCalled { 5727 t.Fatalf("provisioner not invoked") 5728 } 5729 } 5730 5731 // Test that a destroy provisioner referencing an invalid key errors. 5732 func TestContext2Apply_provisionerDestroyRefInvalid(t *testing.T) { 5733 m := testModule(t, "apply-provisioner-destroy-ref-invalid") 5734 p := testProvider("aws") 5735 pr := testProvisioner() 5736 p.ApplyFn = testApplyFn 5737 p.DiffFn = testDiffFn 5738 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5739 return nil 5740 } 5741 5742 state := MustShimLegacyState(&State{ 5743 Modules: []*ModuleState{ 5744 { 5745 Path: rootModulePath, 5746 Resources: map[string]*ResourceState{ 5747 "aws_instance.bar": { 5748 Type: "aws_instance", 5749 Primary: &InstanceState{ 5750 ID: "bar", 5751 }, 5752 }, 5753 5754 "aws_instance.foo": { 5755 Type: "aws_instance", 5756 Primary: &InstanceState{ 5757 ID: "bar", 5758 }, 5759 }, 5760 }, 5761 }, 5762 }, 5763 }) 5764 5765 ctx := testContext2(t, &ContextOpts{ 5766 Config: m, 5767 State: state, 5768 Destroy: true, 5769 ProviderResolver: providers.ResolverFixed( 5770 map[string]providers.Factory{ 5771 "aws": testProviderFuncFixed(p), 5772 }, 5773 ), 5774 Provisioners: map[string]ProvisionerFactory{ 5775 "shell": testProvisionerFuncFixed(pr), 5776 }, 5777 }) 5778 5779 // this was an apply test, but this is now caught in Validation 5780 if diags := ctx.Validate(); !diags.HasErrors() { 5781 t.Fatal("expected error") 5782 } 5783 } 5784 5785 func TestContext2Apply_provisionerResourceRef(t *testing.T) { 5786 m := testModule(t, "apply-provisioner-resource-ref") 5787 p := testProvider("aws") 5788 p.ApplyFn = testApplyFn 5789 p.DiffFn = testDiffFn 5790 5791 pr := testProvisioner() 5792 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5793 val, ok := c.Config["command"] 5794 if !ok || val != "2" { 5795 t.Fatalf("bad value for foo: %v %#v", val, c) 5796 } 5797 5798 return nil 5799 } 5800 5801 ctx := testContext2(t, &ContextOpts{ 5802 Config: m, 5803 ProviderResolver: providers.ResolverFixed( 5804 map[string]providers.Factory{ 5805 "aws": testProviderFuncFixed(p), 5806 }, 5807 ), 5808 Provisioners: map[string]ProvisionerFactory{ 5809 "shell": testProvisionerFuncFixed(pr), 5810 }, 5811 }) 5812 5813 if _, diags := ctx.Plan(); diags.HasErrors() { 5814 t.Fatalf("plan errors: %s", diags.Err()) 5815 } 5816 5817 state, diags := ctx.Apply() 5818 if diags.HasErrors() { 5819 t.Fatalf("diags: %s", diags.Err()) 5820 } 5821 5822 actual := strings.TrimSpace(state.String()) 5823 expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr) 5824 if actual != expected { 5825 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5826 } 5827 5828 // Verify apply was invoked 5829 if !pr.ProvisionResourceCalled { 5830 t.Fatalf("provisioner not invoked") 5831 } 5832 } 5833 5834 func TestContext2Apply_provisionerSelfRef(t *testing.T) { 5835 m := testModule(t, "apply-provisioner-self-ref") 5836 p := testProvider("aws") 5837 pr := testProvisioner() 5838 p.ApplyFn = testApplyFn 5839 p.DiffFn = testDiffFn 5840 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5841 val, ok := c.Config["command"] 5842 if !ok || val != "bar" { 5843 t.Fatalf("bad value for command: %v %#v", val, c) 5844 } 5845 5846 return nil 5847 } 5848 5849 ctx := testContext2(t, &ContextOpts{ 5850 Config: m, 5851 ProviderResolver: providers.ResolverFixed( 5852 map[string]providers.Factory{ 5853 "aws": testProviderFuncFixed(p), 5854 }, 5855 ), 5856 Provisioners: map[string]ProvisionerFactory{ 5857 "shell": testProvisionerFuncFixed(pr), 5858 }, 5859 }) 5860 5861 if _, diags := ctx.Plan(); diags.HasErrors() { 5862 t.Fatalf("plan errors: %s", diags.Err()) 5863 } 5864 5865 state, diags := ctx.Apply() 5866 if diags.HasErrors() { 5867 t.Fatalf("diags: %s", diags.Err()) 5868 } 5869 5870 actual := strings.TrimSpace(state.String()) 5871 expected := strings.TrimSpace(testTerraformApplyProvisionerSelfRefStr) 5872 if actual != expected { 5873 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5874 } 5875 5876 // Verify apply was invoked 5877 if !pr.ProvisionResourceCalled { 5878 t.Fatalf("provisioner not invoked") 5879 } 5880 } 5881 5882 func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { 5883 var lock sync.Mutex 5884 commands := make([]string, 0, 5) 5885 5886 m := testModule(t, "apply-provisioner-multi-self-ref") 5887 p := testProvider("aws") 5888 pr := testProvisioner() 5889 p.ApplyFn = testApplyFn 5890 p.DiffFn = testDiffFn 5891 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5892 lock.Lock() 5893 defer lock.Unlock() 5894 5895 val, ok := c.Config["command"] 5896 if !ok { 5897 t.Fatalf("bad value for command: %v %#v", val, c) 5898 } 5899 5900 commands = append(commands, val.(string)) 5901 return nil 5902 } 5903 5904 ctx := testContext2(t, &ContextOpts{ 5905 Config: m, 5906 ProviderResolver: providers.ResolverFixed( 5907 map[string]providers.Factory{ 5908 "aws": testProviderFuncFixed(p), 5909 }, 5910 ), 5911 Provisioners: map[string]ProvisionerFactory{ 5912 "shell": testProvisionerFuncFixed(pr), 5913 }, 5914 }) 5915 5916 if _, diags := ctx.Plan(); diags.HasErrors() { 5917 t.Fatalf("plan errors: %s", diags.Err()) 5918 } 5919 5920 state, diags := ctx.Apply() 5921 if diags.HasErrors() { 5922 t.Fatalf("diags: %s", diags.Err()) 5923 } 5924 5925 actual := strings.TrimSpace(state.String()) 5926 expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefStr) 5927 if actual != expected { 5928 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5929 } 5930 5931 // Verify apply was invoked 5932 if !pr.ProvisionResourceCalled { 5933 t.Fatalf("provisioner not invoked") 5934 } 5935 5936 // Verify our result 5937 sort.Strings(commands) 5938 expectedCommands := []string{"number 0", "number 1", "number 2"} 5939 if !reflect.DeepEqual(commands, expectedCommands) { 5940 t.Fatalf("bad: %#v", commands) 5941 } 5942 } 5943 5944 func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) { 5945 var lock sync.Mutex 5946 order := make([]string, 0, 5) 5947 5948 m := testModule(t, "apply-provisioner-multi-self-ref-single") 5949 p := testProvider("aws") 5950 pr := testProvisioner() 5951 p.ApplyFn = testApplyFn 5952 p.DiffFn = testDiffFn 5953 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 5954 lock.Lock() 5955 defer lock.Unlock() 5956 5957 val, ok := c.Config["order"] 5958 if !ok { 5959 t.Fatalf("bad value for order: %v %#v", val, c) 5960 } 5961 5962 order = append(order, val.(string)) 5963 return nil 5964 } 5965 5966 ctx := testContext2(t, &ContextOpts{ 5967 Config: m, 5968 ProviderResolver: providers.ResolverFixed( 5969 map[string]providers.Factory{ 5970 "aws": testProviderFuncFixed(p), 5971 }, 5972 ), 5973 Provisioners: map[string]ProvisionerFactory{ 5974 "shell": testProvisionerFuncFixed(pr), 5975 }, 5976 }) 5977 5978 if _, diags := ctx.Plan(); diags.HasErrors() { 5979 t.Fatalf("plan errors: %s", diags.Err()) 5980 } 5981 5982 state, diags := ctx.Apply() 5983 if diags.HasErrors() { 5984 t.Fatalf("diags: %s", diags.Err()) 5985 } 5986 5987 actual := strings.TrimSpace(state.String()) 5988 expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefSingleStr) 5989 if actual != expected { 5990 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 5991 } 5992 5993 // Verify apply was invoked 5994 if !pr.ProvisionResourceCalled { 5995 t.Fatalf("provisioner not invoked") 5996 } 5997 5998 // Verify our result 5999 sort.Strings(order) 6000 expectedOrder := []string{"0", "1", "2"} 6001 if !reflect.DeepEqual(order, expectedOrder) { 6002 t.Fatalf("bad: %#v", order) 6003 } 6004 } 6005 6006 func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) { 6007 m := testModule(t, "apply-provisioner-explicit-self-ref") 6008 p := testProvider("aws") 6009 pr := testProvisioner() 6010 p.ApplyFn = testApplyFn 6011 p.DiffFn = testDiffFn 6012 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 6013 val, ok := c.Config["command"] 6014 if !ok || val != "bar" { 6015 t.Fatalf("bad value for command: %v %#v", val, c) 6016 } 6017 6018 return nil 6019 } 6020 6021 var state *states.State 6022 { 6023 ctx := testContext2(t, &ContextOpts{ 6024 Config: m, 6025 ProviderResolver: providers.ResolverFixed( 6026 map[string]providers.Factory{ 6027 "aws": testProviderFuncFixed(p), 6028 }, 6029 ), 6030 Provisioners: map[string]ProvisionerFactory{ 6031 "shell": testProvisionerFuncFixed(pr), 6032 }, 6033 }) 6034 6035 _, diags := ctx.Plan() 6036 if diags.HasErrors() { 6037 t.Fatalf("diags: %s", diags.Err()) 6038 } 6039 6040 state, diags = ctx.Apply() 6041 if diags.HasErrors() { 6042 t.Fatalf("diags: %s", diags.Err()) 6043 } 6044 6045 // Verify apply was invoked 6046 if !pr.ProvisionResourceCalled { 6047 t.Fatalf("provisioner not invoked") 6048 } 6049 } 6050 6051 { 6052 ctx := testContext2(t, &ContextOpts{ 6053 Config: m, 6054 Destroy: true, 6055 State: state, 6056 ProviderResolver: providers.ResolverFixed( 6057 map[string]providers.Factory{ 6058 "aws": testProviderFuncFixed(p), 6059 }, 6060 ), 6061 Provisioners: map[string]ProvisionerFactory{ 6062 "shell": testProvisionerFuncFixed(pr), 6063 }, 6064 }) 6065 6066 _, diags := ctx.Plan() 6067 if diags.HasErrors() { 6068 t.Fatalf("diags: %s", diags.Err()) 6069 } 6070 6071 state, diags = ctx.Apply() 6072 if diags.HasErrors() { 6073 t.Fatalf("diags: %s", diags.Err()) 6074 } 6075 6076 checkStateString(t, state, `<no state>`) 6077 } 6078 } 6079 6080 // Provisioner should NOT run on a diff, only create 6081 func TestContext2Apply_Provisioner_Diff(t *testing.T) { 6082 m := testModule(t, "apply-provisioner-diff") 6083 p := testProvider("aws") 6084 pr := testProvisioner() 6085 p.ApplyFn = testApplyFn 6086 p.DiffFn = testDiffFn 6087 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 6088 return nil 6089 } 6090 ctx := testContext2(t, &ContextOpts{ 6091 Config: m, 6092 ProviderResolver: providers.ResolverFixed( 6093 map[string]providers.Factory{ 6094 "aws": testProviderFuncFixed(p), 6095 }, 6096 ), 6097 Provisioners: map[string]ProvisionerFactory{ 6098 "shell": testProvisionerFuncFixed(pr), 6099 }, 6100 }) 6101 6102 if _, diags := ctx.Plan(); diags.HasErrors() { 6103 logDiagnostics(t, diags) 6104 t.Fatal("plan failed") 6105 } 6106 6107 state, diags := ctx.Apply() 6108 if diags.HasErrors() { 6109 logDiagnostics(t, diags) 6110 t.Fatal("apply failed") 6111 } 6112 6113 actual := strings.TrimSpace(state.String()) 6114 expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr) 6115 if actual != expected { 6116 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6117 } 6118 6119 // Verify apply was invoked 6120 if !pr.ProvisionResourceCalled { 6121 t.Fatalf("provisioner was not called on first apply") 6122 } 6123 pr.ProvisionResourceCalled = false 6124 6125 // Change the state to force a diff 6126 mod := state.RootModule() 6127 obj := mod.Resources["aws_instance.bar"].Instances[addrs.NoKey].Current 6128 var attrs map[string]interface{} 6129 err := json.Unmarshal(obj.AttrsJSON, &attrs) 6130 if err != nil { 6131 t.Fatal(err) 6132 } 6133 attrs["foo"] = "baz" 6134 obj.AttrsJSON, err = json.Marshal(attrs) 6135 if err != nil { 6136 t.Fatal(err) 6137 } 6138 6139 // Re-create context with state 6140 ctx = testContext2(t, &ContextOpts{ 6141 Config: m, 6142 ProviderResolver: providers.ResolverFixed( 6143 map[string]providers.Factory{ 6144 "aws": testProviderFuncFixed(p), 6145 }, 6146 ), 6147 Provisioners: map[string]ProvisionerFactory{ 6148 "shell": testProvisionerFuncFixed(pr), 6149 }, 6150 State: state, 6151 }) 6152 6153 if _, diags := ctx.Plan(); diags.HasErrors() { 6154 logDiagnostics(t, diags) 6155 t.Fatal("plan failed") 6156 } 6157 6158 state2, diags := ctx.Apply() 6159 if diags.HasErrors() { 6160 logDiagnostics(t, diags) 6161 t.Fatal("apply failed") 6162 } 6163 6164 actual = strings.TrimSpace(state2.String()) 6165 if actual != expected { 6166 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6167 } 6168 6169 // Verify apply was NOT invoked 6170 if pr.ProvisionResourceCalled { 6171 t.Fatalf("provisioner was called on second apply; should not have been") 6172 } 6173 } 6174 6175 func TestContext2Apply_outputDiffVars(t *testing.T) { 6176 m := testModule(t, "apply-good") 6177 p := testProvider("aws") 6178 s := MustShimLegacyState(&State{ 6179 Modules: []*ModuleState{ 6180 { 6181 Path: rootModulePath, 6182 Resources: map[string]*ResourceState{ 6183 "aws_instance.baz": { // This one is not in config, so should be destroyed 6184 Type: "aws_instance", 6185 Primary: &InstanceState{ 6186 ID: "bar", 6187 }, 6188 }, 6189 }, 6190 }, 6191 }, 6192 }) 6193 ctx := testContext2(t, &ContextOpts{ 6194 Config: m, 6195 ProviderResolver: providers.ResolverFixed( 6196 map[string]providers.Factory{ 6197 "aws": testProviderFuncFixed(p), 6198 }, 6199 ), 6200 State: s, 6201 }) 6202 6203 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 6204 if d.Destroy { 6205 return nil, nil 6206 } 6207 6208 result := s.MergeDiff(d) 6209 result.ID = "foo" 6210 return result, nil 6211 } 6212 p.DiffFn = func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 6213 d := &InstanceDiff{ 6214 Attributes: map[string]*ResourceAttrDiff{}, 6215 } 6216 if new, ok := rc.Get("value"); ok { 6217 d.Attributes["value"] = &ResourceAttrDiff{ 6218 New: new.(string), 6219 } 6220 } 6221 if new, ok := rc.Get("foo"); ok { 6222 d.Attributes["foo"] = &ResourceAttrDiff{ 6223 New: new.(string), 6224 } 6225 } else if rc.IsComputed("foo") { 6226 d.Attributes["foo"] = &ResourceAttrDiff{ 6227 NewComputed: true, 6228 Type: DiffAttrOutput, // This doesn't actually really do anything anymore, but this test originally set it. 6229 } 6230 } 6231 if new, ok := rc.Get("num"); ok { 6232 d.Attributes["num"] = &ResourceAttrDiff{ 6233 New: fmt.Sprintf("%#v", new), 6234 } 6235 } 6236 return d, nil 6237 } 6238 6239 if _, diags := ctx.Plan(); diags.HasErrors() { 6240 logDiagnostics(t, diags) 6241 t.Fatal("plan failed") 6242 } 6243 if _, diags := ctx.Apply(); diags.HasErrors() { 6244 logDiagnostics(t, diags) 6245 t.Fatal("apply failed") 6246 } 6247 } 6248 6249 func TestContext2Apply_destroyX(t *testing.T) { 6250 m := testModule(t, "apply-destroy") 6251 h := new(HookRecordApplyOrder) 6252 p := testProvider("aws") 6253 p.ApplyFn = testApplyFn 6254 p.DiffFn = testDiffFn 6255 ctx := testContext2(t, &ContextOpts{ 6256 Config: m, 6257 Hooks: []Hook{h}, 6258 ProviderResolver: providers.ResolverFixed( 6259 map[string]providers.Factory{ 6260 "aws": testProviderFuncFixed(p), 6261 }, 6262 ), 6263 }) 6264 6265 // First plan and apply a create operation 6266 if _, diags := ctx.Plan(); diags.HasErrors() { 6267 t.Fatalf("plan errors: %s", diags.Err()) 6268 } 6269 6270 state, diags := ctx.Apply() 6271 if diags.HasErrors() { 6272 t.Fatalf("diags: %s", diags.Err()) 6273 } 6274 6275 // Next, plan and apply a destroy operation 6276 h.Active = true 6277 ctx = testContext2(t, &ContextOpts{ 6278 Destroy: true, 6279 State: state, 6280 Config: m, 6281 Hooks: []Hook{h}, 6282 ProviderResolver: providers.ResolverFixed( 6283 map[string]providers.Factory{ 6284 "aws": testProviderFuncFixed(p), 6285 }, 6286 ), 6287 }) 6288 6289 if _, diags := ctx.Plan(); diags.HasErrors() { 6290 t.Fatalf("plan errors: %s", diags.Err()) 6291 } 6292 6293 state, diags = ctx.Apply() 6294 if diags.HasErrors() { 6295 t.Fatalf("diags: %s", diags.Err()) 6296 } 6297 6298 // Test that things were destroyed 6299 actual := strings.TrimSpace(state.String()) 6300 expected := strings.TrimSpace(testTerraformApplyDestroyStr) 6301 if actual != expected { 6302 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6303 } 6304 6305 // Test that things were destroyed _in the right order_ 6306 expected2 := []string{"aws_instance.bar", "aws_instance.foo"} 6307 actual2 := h.IDs 6308 if !reflect.DeepEqual(actual2, expected2) { 6309 t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) 6310 } 6311 } 6312 6313 func TestContext2Apply_destroyOrder(t *testing.T) { 6314 m := testModule(t, "apply-destroy") 6315 h := new(HookRecordApplyOrder) 6316 p := testProvider("aws") 6317 p.ApplyFn = testApplyFn 6318 p.DiffFn = testDiffFn 6319 ctx := testContext2(t, &ContextOpts{ 6320 Config: m, 6321 Hooks: []Hook{h}, 6322 ProviderResolver: providers.ResolverFixed( 6323 map[string]providers.Factory{ 6324 "aws": testProviderFuncFixed(p), 6325 }, 6326 ), 6327 }) 6328 6329 // First plan and apply a create operation 6330 if _, diags := ctx.Plan(); diags.HasErrors() { 6331 t.Fatalf("plan errors: %s", diags.Err()) 6332 } 6333 6334 state, diags := ctx.Apply() 6335 if diags.HasErrors() { 6336 t.Fatalf("diags: %s", diags.Err()) 6337 } 6338 6339 t.Logf("State 1: %s", state) 6340 6341 // Next, plan and apply a destroy 6342 h.Active = true 6343 ctx = testContext2(t, &ContextOpts{ 6344 Destroy: true, 6345 State: state, 6346 Config: m, 6347 Hooks: []Hook{h}, 6348 ProviderResolver: providers.ResolverFixed( 6349 map[string]providers.Factory{ 6350 "aws": testProviderFuncFixed(p), 6351 }, 6352 ), 6353 }) 6354 6355 if _, diags := ctx.Plan(); diags.HasErrors() { 6356 t.Fatalf("plan errors: %s", diags.Err()) 6357 } 6358 6359 state, diags = ctx.Apply() 6360 if diags.HasErrors() { 6361 t.Fatalf("diags: %s", diags.Err()) 6362 } 6363 6364 // Test that things were destroyed 6365 actual := strings.TrimSpace(state.String()) 6366 expected := strings.TrimSpace(testTerraformApplyDestroyStr) 6367 if actual != expected { 6368 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 6369 } 6370 6371 // Test that things were destroyed _in the right order_ 6372 expected2 := []string{"aws_instance.bar", "aws_instance.foo"} 6373 actual2 := h.IDs 6374 if !reflect.DeepEqual(actual2, expected2) { 6375 t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) 6376 } 6377 } 6378 6379 // https://github.com/hashicorp/terraform-plugin-sdk/issues/2767 6380 func TestContext2Apply_destroyModulePrefix(t *testing.T) { 6381 m := testModule(t, "apply-destroy-module-resource-prefix") 6382 h := new(MockHook) 6383 p := testProvider("aws") 6384 p.ApplyFn = testApplyFn 6385 p.DiffFn = testDiffFn 6386 ctx := testContext2(t, &ContextOpts{ 6387 Config: m, 6388 Hooks: []Hook{h}, 6389 ProviderResolver: providers.ResolverFixed( 6390 map[string]providers.Factory{ 6391 "aws": testProviderFuncFixed(p), 6392 }, 6393 ), 6394 }) 6395 6396 // First plan and apply a create operation 6397 if _, diags := ctx.Plan(); diags.HasErrors() { 6398 t.Fatalf("plan errors: %s", diags.Err()) 6399 } 6400 6401 state, diags := ctx.Apply() 6402 if diags.HasErrors() { 6403 t.Fatalf("diags: %s", diags.Err()) 6404 } 6405 6406 // Verify that we got the apply info correct 6407 if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" { 6408 t.Fatalf("bad: %s", v) 6409 } 6410 6411 // Next, plan and apply a destroy operation and reset the hook 6412 h = new(MockHook) 6413 ctx = testContext2(t, &ContextOpts{ 6414 Destroy: true, 6415 State: state, 6416 Config: m, 6417 Hooks: []Hook{h}, 6418 ProviderResolver: providers.ResolverFixed( 6419 map[string]providers.Factory{ 6420 "aws": testProviderFuncFixed(p), 6421 }, 6422 ), 6423 }) 6424 6425 if _, diags := ctx.Plan(); diags.HasErrors() { 6426 t.Fatalf("plan errors: %s", diags.Err()) 6427 } 6428 6429 state, diags = ctx.Apply() 6430 if diags.HasErrors() { 6431 t.Fatalf("diags: %s", diags.Err()) 6432 } 6433 6434 // Test that things were destroyed 6435 if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" { 6436 t.Fatalf("bad: %s", v) 6437 } 6438 } 6439 6440 func TestContext2Apply_destroyNestedModule(t *testing.T) { 6441 m := testModule(t, "apply-destroy-nested-module") 6442 p := testProvider("aws") 6443 p.ApplyFn = testApplyFn 6444 p.DiffFn = testDiffFn 6445 6446 s := MustShimLegacyState(&State{ 6447 Modules: []*ModuleState{ 6448 { 6449 Path: []string{"root", "child", "subchild"}, 6450 Resources: map[string]*ResourceState{ 6451 "aws_instance.bar": { 6452 Type: "aws_instance", 6453 Primary: &InstanceState{ 6454 ID: "bar", 6455 }, 6456 Provider: "provider.aws", 6457 }, 6458 }, 6459 }, 6460 }, 6461 }) 6462 6463 ctx := testContext2(t, &ContextOpts{ 6464 Config: m, 6465 ProviderResolver: providers.ResolverFixed( 6466 map[string]providers.Factory{ 6467 "aws": testProviderFuncFixed(p), 6468 }, 6469 ), 6470 State: s, 6471 }) 6472 6473 // First plan and apply a create operation 6474 if _, diags := ctx.Plan(); diags.HasErrors() { 6475 t.Fatalf("plan errors: %s", diags.Err()) 6476 } 6477 6478 state, diags := ctx.Apply() 6479 if diags.HasErrors() { 6480 t.Fatalf("diags: %s", diags.Err()) 6481 } 6482 6483 // Test that things were destroyed 6484 actual := strings.TrimSpace(state.String()) 6485 if actual != "<no state>" { 6486 t.Fatalf("expected no state, got: %s", actual) 6487 } 6488 } 6489 6490 func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) { 6491 m := testModule(t, "apply-destroy-deeply-nested-module") 6492 p := testProvider("aws") 6493 p.ApplyFn = testApplyFn 6494 p.DiffFn = testDiffFn 6495 6496 s := MustShimLegacyState(&State{ 6497 Modules: []*ModuleState{ 6498 { 6499 Path: []string{"root", "child", "subchild", "subsubchild"}, 6500 Resources: map[string]*ResourceState{ 6501 "aws_instance.bar": { 6502 Type: "aws_instance", 6503 Primary: &InstanceState{ 6504 ID: "bar", 6505 }, 6506 Provider: "provider.aws", 6507 }, 6508 }, 6509 }, 6510 }, 6511 }) 6512 6513 ctx := testContext2(t, &ContextOpts{ 6514 Config: m, 6515 ProviderResolver: providers.ResolverFixed( 6516 map[string]providers.Factory{ 6517 "aws": testProviderFuncFixed(p), 6518 }, 6519 ), 6520 State: s, 6521 }) 6522 6523 // First plan and apply a create operation 6524 if _, diags := ctx.Plan(); diags.HasErrors() { 6525 t.Fatalf("plan errors: %s", diags.Err()) 6526 } 6527 6528 state, diags := ctx.Apply() 6529 if diags.HasErrors() { 6530 t.Fatalf("diags: %s", diags.Err()) 6531 } 6532 6533 // Test that things were destroyed 6534 if !state.Empty() { 6535 t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(state)) 6536 } 6537 } 6538 6539 // https://github.com/hashicorp/terraform-plugin-sdk/issues/5440 6540 func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) { 6541 m, snap := testModuleWithSnapshot(t, "apply-destroy-module-with-attrs") 6542 p := testProvider("aws") 6543 p.ApplyFn = testApplyFn 6544 p.DiffFn = testDiffFn 6545 6546 var state *states.State 6547 { 6548 ctx := testContext2(t, &ContextOpts{ 6549 Config: m, 6550 ProviderResolver: providers.ResolverFixed( 6551 map[string]providers.Factory{ 6552 "aws": testProviderFuncFixed(p), 6553 }, 6554 ), 6555 }) 6556 6557 // First plan and apply a create operation 6558 if p, diags := ctx.Plan(); diags.HasErrors() { 6559 t.Fatalf("plan diags: %s", diags.Err()) 6560 } else { 6561 t.Logf("Step 1 plan: %s", legacyDiffComparisonString(p.Changes)) 6562 } 6563 6564 var diags tfdiags.Diagnostics 6565 state, diags = ctx.Apply() 6566 if diags.HasErrors() { 6567 t.Fatalf("apply errs: %s", diags.Err()) 6568 } 6569 6570 t.Logf("Step 1 state: %s", state) 6571 } 6572 6573 h := new(HookRecordApplyOrder) 6574 h.Active = true 6575 6576 { 6577 ctx := testContext2(t, &ContextOpts{ 6578 Destroy: true, 6579 Config: m, 6580 State: state, 6581 Hooks: []Hook{h}, 6582 ProviderResolver: providers.ResolverFixed( 6583 map[string]providers.Factory{ 6584 "aws": testProviderFuncFixed(p), 6585 }, 6586 ), 6587 Variables: InputValues{ 6588 "key_name": &InputValue{ 6589 Value: cty.StringVal("foobarkey"), 6590 SourceType: ValueFromCaller, 6591 }, 6592 }, 6593 }) 6594 6595 // First plan and apply a create operation 6596 plan, diags := ctx.Plan() 6597 if diags.HasErrors() { 6598 t.Fatalf("destroy plan err: %s", diags.Err()) 6599 } 6600 6601 t.Logf("Step 2 plan: %s", legacyDiffComparisonString(plan.Changes)) 6602 6603 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 6604 if err != nil { 6605 t.Fatalf("failed to round-trip through planfile: %s", err) 6606 } 6607 6608 ctxOpts.ProviderResolver = providers.ResolverFixed( 6609 map[string]providers.Factory{ 6610 "aws": testProviderFuncFixed(p), 6611 }, 6612 ) 6613 ctx, diags = NewContext(ctxOpts) 6614 if diags.HasErrors() { 6615 t.Fatalf("err: %s", diags.Err()) 6616 } 6617 6618 state, diags = ctx.Apply() 6619 if diags.HasErrors() { 6620 t.Fatalf("destroy apply err: %s", diags.Err()) 6621 } 6622 6623 t.Logf("Step 2 state: %s", state) 6624 } 6625 6626 //Test that things were destroyed 6627 actual := strings.TrimSpace(state.String()) 6628 expected := strings.TrimSpace(`<no state>`) 6629 if actual != expected { 6630 t.Fatalf("expected:\n\n%s\n\nactual:\n\n%s", expected, actual) 6631 } 6632 } 6633 6634 func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { 6635 m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count") 6636 p := testProvider("aws") 6637 p.ApplyFn = testApplyFn 6638 p.DiffFn = testDiffFn 6639 6640 var state *states.State 6641 var diags tfdiags.Diagnostics 6642 { 6643 ctx := testContext2(t, &ContextOpts{ 6644 Config: m, 6645 ProviderResolver: providers.ResolverFixed( 6646 map[string]providers.Factory{ 6647 "aws": testProviderFuncFixed(p), 6648 }, 6649 ), 6650 }) 6651 6652 // First plan and apply a create operation 6653 if _, diags := ctx.Plan(); diags.HasErrors() { 6654 t.Fatalf("plan err: %s", diags.Err()) 6655 } 6656 6657 state, diags = ctx.Apply() 6658 if diags.HasErrors() { 6659 t.Fatalf("apply err: %s", diags.Err()) 6660 } 6661 } 6662 6663 h := new(HookRecordApplyOrder) 6664 h.Active = true 6665 6666 { 6667 ctx := testContext2(t, &ContextOpts{ 6668 Destroy: true, 6669 Config: m, 6670 State: state, 6671 Hooks: []Hook{h}, 6672 ProviderResolver: providers.ResolverFixed( 6673 map[string]providers.Factory{ 6674 "aws": testProviderFuncFixed(p), 6675 }, 6676 ), 6677 }) 6678 6679 // First plan and apply a create operation 6680 plan, diags := ctx.Plan() 6681 if diags.HasErrors() { 6682 t.Fatalf("destroy plan err: %s", diags.Err()) 6683 } 6684 6685 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 6686 if err != nil { 6687 t.Fatalf("failed to round-trip through planfile: %s", err) 6688 } 6689 6690 ctxOpts.ProviderResolver = providers.ResolverFixed( 6691 map[string]providers.Factory{ 6692 "aws": testProviderFuncFixed(p), 6693 }, 6694 ) 6695 ctx, diags = NewContext(ctxOpts) 6696 if diags.HasErrors() { 6697 t.Fatalf("err: %s", diags.Err()) 6698 } 6699 6700 state, diags = ctx.Apply() 6701 if diags.HasErrors() { 6702 t.Fatalf("destroy apply err: %s", diags.Err()) 6703 } 6704 } 6705 6706 //Test that things were destroyed 6707 actual := strings.TrimSpace(state.String()) 6708 expected := strings.TrimSpace(` 6709 <no state> 6710 module.child: 6711 `) 6712 if actual != expected { 6713 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6714 } 6715 } 6716 6717 func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) { 6718 m := testModule(t, "apply-destroy-mod-var-and-count") 6719 p := testProvider("aws") 6720 p.ApplyFn = testApplyFn 6721 p.DiffFn = testDiffFn 6722 6723 var state *states.State 6724 var diags tfdiags.Diagnostics 6725 { 6726 ctx := testContext2(t, &ContextOpts{ 6727 Config: m, 6728 ProviderResolver: providers.ResolverFixed( 6729 map[string]providers.Factory{ 6730 "aws": testProviderFuncFixed(p), 6731 }, 6732 ), 6733 }) 6734 6735 // First plan and apply a create operation 6736 if _, diags := ctx.Plan(); diags.HasErrors() { 6737 t.Fatalf("plan err: %s", diags.Err()) 6738 } 6739 6740 state, diags = ctx.Apply() 6741 if diags.HasErrors() { 6742 t.Fatalf("apply err: %s", diags.Err()) 6743 } 6744 } 6745 6746 { 6747 ctx := testContext2(t, &ContextOpts{ 6748 Destroy: true, 6749 Config: m, 6750 State: state, 6751 ProviderResolver: providers.ResolverFixed( 6752 map[string]providers.Factory{ 6753 "aws": testProviderFuncFixed(p), 6754 }, 6755 ), 6756 Targets: []addrs.Targetable{ 6757 addrs.RootModuleInstance.Child("child", addrs.NoKey), 6758 }, 6759 }) 6760 6761 _, diags := ctx.Plan() 6762 if diags.HasErrors() { 6763 t.Fatalf("plan err: %s", diags) 6764 } 6765 if len(diags) != 1 { 6766 // Should have one warning that -target is in effect. 6767 t.Fatalf("got %d diagnostics in plan; want 1", len(diags)) 6768 } 6769 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 6770 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 6771 } 6772 if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want { 6773 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 6774 } 6775 6776 // Destroy, targeting the module explicitly 6777 state, diags = ctx.Apply() 6778 if diags.HasErrors() { 6779 t.Fatalf("destroy apply err: %s", diags) 6780 } 6781 if len(diags) != 1 { 6782 t.Fatalf("got %d diagnostics; want 1", len(diags)) 6783 } 6784 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 6785 t.Errorf("wrong diagnostic severity %#v; want %#v", got, want) 6786 } 6787 if got, want := diags[0].Description().Summary, "Applied changes may be incomplete"; got != want { 6788 t.Errorf("wrong diagnostic summary %#v; want %#v", got, want) 6789 } 6790 } 6791 6792 //Test that things were destroyed 6793 actual := strings.TrimSpace(state.String()) 6794 expected := strings.TrimSpace(`<no state>`) 6795 if actual != expected { 6796 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6797 } 6798 } 6799 6800 func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { 6801 m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count-nested") 6802 p := testProvider("aws") 6803 p.ApplyFn = testApplyFn 6804 p.DiffFn = testDiffFn 6805 6806 var state *states.State 6807 var diags tfdiags.Diagnostics 6808 { 6809 ctx := testContext2(t, &ContextOpts{ 6810 Config: m, 6811 ProviderResolver: providers.ResolverFixed( 6812 map[string]providers.Factory{ 6813 "aws": testProviderFuncFixed(p), 6814 }, 6815 ), 6816 }) 6817 6818 // First plan and apply a create operation 6819 if _, diags := ctx.Plan(); diags.HasErrors() { 6820 t.Fatalf("plan err: %s", diags.Err()) 6821 } 6822 6823 state, diags = ctx.Apply() 6824 if diags.HasErrors() { 6825 t.Fatalf("apply err: %s", diags.Err()) 6826 } 6827 } 6828 6829 h := new(HookRecordApplyOrder) 6830 h.Active = true 6831 6832 { 6833 ctx := testContext2(t, &ContextOpts{ 6834 Destroy: true, 6835 Config: m, 6836 State: state, 6837 Hooks: []Hook{h}, 6838 ProviderResolver: providers.ResolverFixed( 6839 map[string]providers.Factory{ 6840 "aws": testProviderFuncFixed(p), 6841 }, 6842 ), 6843 }) 6844 6845 // First plan and apply a create operation 6846 plan, diags := ctx.Plan() 6847 if diags.HasErrors() { 6848 t.Fatalf("destroy plan err: %s", diags.Err()) 6849 } 6850 6851 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 6852 if err != nil { 6853 t.Fatalf("failed to round-trip through planfile: %s", err) 6854 } 6855 6856 ctxOpts.ProviderResolver = providers.ResolverFixed( 6857 map[string]providers.Factory{ 6858 "aws": testProviderFuncFixed(p), 6859 }, 6860 ) 6861 ctx, diags = NewContext(ctxOpts) 6862 if diags.HasErrors() { 6863 t.Fatalf("err: %s", diags.Err()) 6864 } 6865 6866 state, diags = ctx.Apply() 6867 if diags.HasErrors() { 6868 t.Fatalf("destroy apply err: %s", diags.Err()) 6869 } 6870 } 6871 6872 //Test that things were destroyed 6873 actual := strings.TrimSpace(state.String()) 6874 expected := strings.TrimSpace(` 6875 <no state> 6876 module.child.child2: 6877 `) 6878 if actual != expected { 6879 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 6880 } 6881 } 6882 6883 func TestContext2Apply_destroyOutputs(t *testing.T) { 6884 m := testModule(t, "apply-destroy-outputs") 6885 p := testProvider("aws") 6886 p.ApplyFn = testApplyFn 6887 p.DiffFn = testDiffFn 6888 ctx := testContext2(t, &ContextOpts{ 6889 Config: m, 6890 ProviderResolver: providers.ResolverFixed( 6891 map[string]providers.Factory{ 6892 "aws": testProviderFuncFixed(p), 6893 }, 6894 ), 6895 }) 6896 6897 // First plan and apply a create operation 6898 if _, diags := ctx.Plan(); diags.HasErrors() { 6899 t.Fatalf("plan errors: %s", diags.Err()) 6900 } 6901 6902 state, diags := ctx.Apply() 6903 6904 if diags.HasErrors() { 6905 t.Fatalf("diags: %s", diags.Err()) 6906 } 6907 6908 // Next, plan and apply a destroy operation 6909 ctx = testContext2(t, &ContextOpts{ 6910 Destroy: true, 6911 State: state, 6912 Config: m, 6913 ProviderResolver: providers.ResolverFixed( 6914 map[string]providers.Factory{ 6915 "aws": testProviderFuncFixed(p), 6916 }, 6917 ), 6918 }) 6919 6920 if _, diags := ctx.Plan(); diags.HasErrors() { 6921 t.Fatalf("plan errors: %s", diags.Err()) 6922 } 6923 6924 state, diags = ctx.Apply() 6925 if diags.HasErrors() { 6926 t.Fatalf("diags: %s", diags.Err()) 6927 } 6928 6929 mod := state.RootModule() 6930 if len(mod.Resources) > 0 { 6931 t.Fatalf("expected no resources, got: %#v", mod) 6932 } 6933 6934 // destroying again should produce no errors 6935 ctx = testContext2(t, &ContextOpts{ 6936 Destroy: true, 6937 State: state, 6938 Config: m, 6939 ProviderResolver: providers.ResolverFixed( 6940 map[string]providers.Factory{ 6941 "aws": testProviderFuncFixed(p), 6942 }, 6943 ), 6944 }) 6945 if _, diags := ctx.Plan(); diags.HasErrors() { 6946 t.Fatal(diags.Err()) 6947 } 6948 6949 if _, diags := ctx.Apply(); diags.HasErrors() { 6950 t.Fatal(diags.Err()) 6951 } 6952 } 6953 6954 func TestContext2Apply_destroyOrphan(t *testing.T) { 6955 m := testModule(t, "apply-error") 6956 p := testProvider("aws") 6957 s := MustShimLegacyState(&State{ 6958 Modules: []*ModuleState{ 6959 { 6960 Path: rootModulePath, 6961 Resources: map[string]*ResourceState{ 6962 "aws_instance.baz": { 6963 Type: "aws_instance", 6964 Primary: &InstanceState{ 6965 ID: "bar", 6966 }, 6967 }, 6968 }, 6969 }, 6970 }, 6971 }) 6972 ctx := testContext2(t, &ContextOpts{ 6973 Config: m, 6974 ProviderResolver: providers.ResolverFixed( 6975 map[string]providers.Factory{ 6976 "aws": testProviderFuncFixed(p), 6977 }, 6978 ), 6979 State: s, 6980 }) 6981 6982 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 6983 if d.Destroy { 6984 return nil, nil 6985 } 6986 6987 result := s.MergeDiff(d) 6988 result.ID = "foo" 6989 return result, nil 6990 } 6991 p.DiffFn = func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 6992 d := &InstanceDiff{ 6993 Attributes: map[string]*ResourceAttrDiff{}, 6994 } 6995 if new, ok := rc.Get("value"); ok { 6996 d.Attributes["value"] = &ResourceAttrDiff{ 6997 New: new.(string), 6998 } 6999 } 7000 if new, ok := rc.Get("foo"); ok { 7001 d.Attributes["foo"] = &ResourceAttrDiff{ 7002 New: new.(string), 7003 } 7004 } 7005 return d, nil 7006 } 7007 7008 if _, diags := ctx.Plan(); diags.HasErrors() { 7009 t.Fatalf("plan errors: %s", diags.Err()) 7010 } 7011 7012 state, diags := ctx.Apply() 7013 if diags.HasErrors() { 7014 t.Fatalf("diags: %s", diags.Err()) 7015 } 7016 7017 mod := state.RootModule() 7018 if _, ok := mod.Resources["aws_instance.baz"]; ok { 7019 t.Fatalf("bad: %#v", mod.Resources) 7020 } 7021 } 7022 7023 func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) { 7024 m := testModule(t, "apply-destroy-provisioner") 7025 p := testProvider("aws") 7026 pr := testProvisioner() 7027 p.ApplyFn = testApplyFn 7028 p.DiffFn = testDiffFn 7029 7030 called := false 7031 pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { 7032 called = true 7033 return nil 7034 } 7035 7036 s := MustShimLegacyState(&State{ 7037 Modules: []*ModuleState{ 7038 { 7039 Path: rootModulePath, 7040 Resources: map[string]*ResourceState{ 7041 "aws_instance.foo": { 7042 Type: "aws_instance", 7043 Primary: &InstanceState{ 7044 ID: "bar", 7045 Attributes: map[string]string{ 7046 "id": "bar", 7047 }, 7048 Tainted: true, 7049 }, 7050 }, 7051 }, 7052 }, 7053 }, 7054 }) 7055 7056 ctx := testContext2(t, &ContextOpts{ 7057 Config: m, 7058 ProviderResolver: providers.ResolverFixed( 7059 map[string]providers.Factory{ 7060 "aws": testProviderFuncFixed(p), 7061 }, 7062 ), 7063 Provisioners: map[string]ProvisionerFactory{ 7064 "shell": testProvisionerFuncFixed(pr), 7065 }, 7066 State: s, 7067 Destroy: true, 7068 }) 7069 7070 if _, diags := ctx.Plan(); diags.HasErrors() { 7071 t.Fatalf("plan errors: %s", diags.Err()) 7072 } 7073 7074 state, diags := ctx.Apply() 7075 if diags.HasErrors() { 7076 t.Fatalf("diags: %s", diags.Err()) 7077 } 7078 7079 if called { 7080 t.Fatal("provisioner should not be called") 7081 } 7082 7083 actual := strings.TrimSpace(state.String()) 7084 expected := strings.TrimSpace("<no state>") 7085 if actual != expected { 7086 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7087 } 7088 } 7089 7090 func TestContext2Apply_error(t *testing.T) { 7091 errored := false 7092 7093 m := testModule(t, "apply-error") 7094 p := testProvider("aws") 7095 ctx := testContext2(t, &ContextOpts{ 7096 Config: m, 7097 ProviderResolver: providers.ResolverFixed( 7098 map[string]providers.Factory{ 7099 "aws": testProviderFuncFixed(p), 7100 }, 7101 ), 7102 }) 7103 7104 p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { 7105 if errored { 7106 state := &InstanceState{ 7107 ID: "bar", 7108 } 7109 return state, fmt.Errorf("error") 7110 } 7111 errored = true 7112 7113 return &InstanceState{ 7114 ID: "foo", 7115 Attributes: map[string]string{ 7116 "value": "2", 7117 }, 7118 }, nil 7119 } 7120 p.DiffFn = func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 7121 d := &InstanceDiff{ 7122 Attributes: map[string]*ResourceAttrDiff{}, 7123 } 7124 if new, ok := rc.Get("value"); ok { 7125 d.Attributes["value"] = &ResourceAttrDiff{ 7126 New: new.(string), 7127 } 7128 } 7129 if new, ok := rc.Get("foo"); ok { 7130 d.Attributes["foo"] = &ResourceAttrDiff{ 7131 New: new.(string), 7132 } 7133 } 7134 return d, nil 7135 } 7136 7137 if _, diags := ctx.Plan(); diags.HasErrors() { 7138 t.Fatalf("plan errors: %s", diags.Err()) 7139 } 7140 7141 state, diags := ctx.Apply() 7142 if diags == nil { 7143 t.Fatal("should have error") 7144 } 7145 7146 actual := strings.TrimSpace(state.String()) 7147 expected := strings.TrimSpace(testTerraformApplyErrorStr) 7148 if actual != expected { 7149 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 7150 } 7151 } 7152 7153 func TestContext2Apply_errorDestroy(t *testing.T) { 7154 m := testModule(t, "empty") 7155 p := testProvider("test") 7156 7157 p.GetSchemaReturn = &ProviderSchema{ 7158 ResourceTypes: map[string]*configschema.Block{ 7159 "test_thing": { 7160 Attributes: map[string]*configschema.Attribute{ 7161 "id": {Type: cty.String, Optional: true}, 7162 }, 7163 }, 7164 }, 7165 } 7166 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 7167 // Should actually be called for this test, because Terraform Core 7168 // constructs the plan for a destroy operation itself. 7169 return providers.PlanResourceChangeResponse{ 7170 PlannedState: req.ProposedNewState, 7171 } 7172 } 7173 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 7174 // The apply (in this case, a destroy) always fails, so we can verify 7175 // that the object stays in the state after a destroy fails even though 7176 // we aren't returning a new state object here. 7177 return providers.ApplyResourceChangeResponse{ 7178 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("failed")), 7179 } 7180 } 7181 7182 ctx := testContext2(t, &ContextOpts{ 7183 Config: m, 7184 State: states.BuildState(func(ss *states.SyncState) { 7185 ss.SetResourceInstanceCurrent( 7186 addrs.Resource{ 7187 Mode: addrs.ManagedResourceMode, 7188 Type: "test_thing", 7189 Name: "foo", 7190 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 7191 &states.ResourceInstanceObjectSrc{ 7192 Status: states.ObjectReady, 7193 AttrsJSON: []byte(`{"id":"baz"}`), 7194 }, 7195 addrs.ProviderConfig{ 7196 Type: "test", 7197 }.Absolute(addrs.RootModuleInstance), 7198 ) 7199 }), 7200 ProviderResolver: providers.ResolverFixed( 7201 map[string]providers.Factory{ 7202 "test": testProviderFuncFixed(p), 7203 }, 7204 ), 7205 }) 7206 7207 if _, diags := ctx.Plan(); diags.HasErrors() { 7208 t.Fatalf("plan errors: %s", diags.Err()) 7209 } 7210 7211 state, diags := ctx.Apply() 7212 if diags == nil { 7213 t.Fatal("should have error") 7214 } 7215 7216 actual := strings.TrimSpace(state.String()) 7217 expected := strings.TrimSpace(` 7218 test_thing.foo: 7219 ID = baz 7220 provider = provider.test 7221 `) // test_thing.foo is still here, even though provider returned no new state along with its error 7222 if actual != expected { 7223 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 7224 } 7225 } 7226 7227 func TestContext2Apply_errorCreateInvalidNew(t *testing.T) { 7228 m := testModule(t, "apply-error") 7229 7230 p := testProvider("aws") 7231 p.GetSchemaReturn = &ProviderSchema{ 7232 ResourceTypes: map[string]*configschema.Block{ 7233 "aws_instance": { 7234 Attributes: map[string]*configschema.Attribute{ 7235 "value": {Type: cty.String, Optional: true}, 7236 "foo": {Type: cty.String, Optional: true}, 7237 }, 7238 }, 7239 }, 7240 } 7241 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 7242 return providers.PlanResourceChangeResponse{ 7243 PlannedState: req.ProposedNewState, 7244 } 7245 } 7246 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 7247 // We're intentionally returning an inconsistent new state here 7248 // because we want to test that Terraform ignores the inconsistency 7249 // when accompanied by another error. 7250 return providers.ApplyResourceChangeResponse{ 7251 NewState: cty.ObjectVal(map[string]cty.Value{ 7252 "value": cty.StringVal("wrong wrong wrong wrong"), 7253 "foo": cty.StringVal("absolutely brimming over with wrongability"), 7254 }), 7255 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")), 7256 } 7257 } 7258 7259 ctx := testContext2(t, &ContextOpts{ 7260 Config: m, 7261 ProviderResolver: providers.ResolverFixed( 7262 map[string]providers.Factory{ 7263 "aws": testProviderFuncFixed(p), 7264 }, 7265 ), 7266 }) 7267 7268 if _, diags := ctx.Plan(); diags.HasErrors() { 7269 t.Fatalf("plan errors: %s", diags.Err()) 7270 } 7271 7272 state, diags := ctx.Apply() 7273 if diags == nil { 7274 t.Fatal("should have error") 7275 } 7276 if got, want := len(diags), 1; got != want { 7277 // There should be no additional diagnostics generated by Terraform's own eval logic, 7278 // because the provider's own error supersedes them. 7279 t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err()) 7280 } 7281 if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) { 7282 t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err()) 7283 } 7284 if got, want := len(state.RootModule().Resources), 2; got != want { 7285 t.Errorf("%d resources in state before prune; should have %d\n%s", got, want, spew.Sdump(state)) 7286 } 7287 state.PruneResourceHusks() // aws_instance.bar with no instances gets left behind when we bail out, but that's okay 7288 if got, want := len(state.RootModule().Resources), 1; got != want { 7289 t.Errorf("%d resources in state after prune; should have only one (aws_instance.foo, tainted)\n%s", got, spew.Sdump(state)) 7290 } 7291 } 7292 7293 func TestContext2Apply_errorUpdateNullNew(t *testing.T) { 7294 m := testModule(t, "apply-error") 7295 7296 p := testProvider("aws") 7297 p.GetSchemaReturn = &ProviderSchema{ 7298 ResourceTypes: map[string]*configschema.Block{ 7299 "aws_instance": { 7300 Attributes: map[string]*configschema.Attribute{ 7301 "value": {Type: cty.String, Optional: true}, 7302 "foo": {Type: cty.String, Optional: true}, 7303 }, 7304 }, 7305 }, 7306 } 7307 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 7308 return providers.PlanResourceChangeResponse{ 7309 PlannedState: req.ProposedNewState, 7310 } 7311 } 7312 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 7313 // We're intentionally returning no NewState here because we want to 7314 // test that Terraform retains the prior state, rather than treating 7315 // the returned null as "no state" (object deleted). 7316 return providers.ApplyResourceChangeResponse{ 7317 Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")), 7318 } 7319 } 7320 7321 ctx := testContext2(t, &ContextOpts{ 7322 Config: m, 7323 State: states.BuildState(func(ss *states.SyncState) { 7324 ss.SetResourceInstanceCurrent( 7325 addrs.Resource{ 7326 Mode: addrs.ManagedResourceMode, 7327 Type: "aws_instance", 7328 Name: "foo", 7329 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 7330 &states.ResourceInstanceObjectSrc{ 7331 Status: states.ObjectReady, 7332 AttrsJSON: []byte(`{"value":"old"}`), 7333 }, 7334 addrs.ProviderConfig{ 7335 Type: "aws", 7336 }.Absolute(addrs.RootModuleInstance), 7337 ) 7338 }), 7339 ProviderResolver: providers.ResolverFixed( 7340 map[string]providers.Factory{ 7341 "aws": testProviderFuncFixed(p), 7342 }, 7343 ), 7344 }) 7345 7346 if _, diags := ctx.Plan(); diags.HasErrors() { 7347 t.Fatalf("plan errors: %s", diags.Err()) 7348 } 7349 7350 state, diags := ctx.Apply() 7351 if diags == nil { 7352 t.Fatal("should have error") 7353 } 7354 if got, want := len(diags), 1; got != want { 7355 // There should be no additional diagnostics generated by Terraform's own eval logic, 7356 // because the provider's own error supersedes them. 7357 t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err()) 7358 } 7359 if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) { 7360 t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err()) 7361 } 7362 state.PruneResourceHusks() 7363 if got, want := len(state.RootModule().Resources), 1; got != want { 7364 t.Fatalf("%d resources in state; should have only one (aws_instance.foo, unmodified)\n%s", got, spew.Sdump(state)) 7365 } 7366 7367 is := state.ResourceInstance(addrs.Resource{ 7368 Mode: addrs.ManagedResourceMode, 7369 Type: "aws_instance", 7370 Name: "foo", 7371 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) 7372 if is == nil { 7373 t.Fatalf("aws_instance.foo is not in the state after apply") 7374 } 7375 if got, want := is.Current.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) { 7376 t.Fatalf("incorrect attributes for aws_instance.foo\ngot: %s\nwant: JSON containing %s\n\n%s", got, want, spew.Sdump(is)) 7377 } 7378 } 7379 7380 func TestContext2Apply_errorPartial(t *testing.T) { 7381 errored := false 7382 7383 m := testModule(t, "apply-error") 7384 p := testProvider("aws") 7385 s := MustShimLegacyState(&State{ 7386 Modules: []*ModuleState{ 7387 { 7388 Path: rootModulePath, 7389 Resources: map[string]*ResourceState{ 7390 "aws_instance.bar": { 7391 Type: "aws_instance", 7392 Primary: &InstanceState{ 7393 ID: "bar", 7394 }, 7395 }, 7396 }, 7397 }, 7398 }, 7399 }) 7400 ctx := testContext2(t, &ContextOpts{ 7401 Config: m, 7402 ProviderResolver: providers.ResolverFixed( 7403 map[string]providers.Factory{ 7404 "aws": testProviderFuncFixed(p), 7405 }, 7406 ), 7407 State: s, 7408 }) 7409 7410 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 7411 if errored { 7412 return s, fmt.Errorf("error") 7413 } 7414 errored = true 7415 7416 return &InstanceState{ 7417 ID: "foo", 7418 Attributes: map[string]string{ 7419 "value": "2", 7420 }, 7421 }, nil 7422 } 7423 p.DiffFn = func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { 7424 d := &InstanceDiff{ 7425 Attributes: map[string]*ResourceAttrDiff{}, 7426 } 7427 if new, ok := rc.Get("value"); ok { 7428 d.Attributes["value"] = &ResourceAttrDiff{ 7429 New: new.(string), 7430 } 7431 } 7432 if new, ok := rc.Get("foo"); ok { 7433 d.Attributes["foo"] = &ResourceAttrDiff{ 7434 New: new.(string), 7435 } 7436 } 7437 return d, nil 7438 } 7439 7440 if _, diags := ctx.Plan(); diags.HasErrors() { 7441 t.Fatalf("plan errors: %s", diags.Err()) 7442 } 7443 7444 state, diags := ctx.Apply() 7445 if diags == nil { 7446 t.Fatal("should have error") 7447 } 7448 7449 mod := state.RootModule() 7450 if len(mod.Resources) != 2 { 7451 t.Fatalf("bad: %#v", mod.Resources) 7452 } 7453 7454 actual := strings.TrimSpace(state.String()) 7455 expected := strings.TrimSpace(testTerraformApplyErrorPartialStr) 7456 if actual != expected { 7457 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 7458 } 7459 } 7460 7461 func TestContext2Apply_hook(t *testing.T) { 7462 m := testModule(t, "apply-good") 7463 h := new(MockHook) 7464 p := testProvider("aws") 7465 p.ApplyFn = testApplyFn 7466 p.DiffFn = testDiffFn 7467 ctx := testContext2(t, &ContextOpts{ 7468 Config: m, 7469 Hooks: []Hook{h}, 7470 ProviderResolver: providers.ResolverFixed( 7471 map[string]providers.Factory{ 7472 "aws": testProviderFuncFixed(p), 7473 }, 7474 ), 7475 }) 7476 7477 if _, diags := ctx.Plan(); diags.HasErrors() { 7478 t.Fatalf("plan errors: %s", diags.Err()) 7479 } 7480 7481 if _, diags := ctx.Apply(); diags.HasErrors() { 7482 t.Fatalf("apply errors: %s", diags.Err()) 7483 } 7484 7485 if !h.PreApplyCalled { 7486 t.Fatal("should be called") 7487 } 7488 if !h.PostApplyCalled { 7489 t.Fatal("should be called") 7490 } 7491 if !h.PostStateUpdateCalled { 7492 t.Fatalf("should call post state update") 7493 } 7494 } 7495 7496 func TestContext2Apply_hookOrphan(t *testing.T) { 7497 m := testModule(t, "apply-blank") 7498 h := new(MockHook) 7499 p := testProvider("aws") 7500 p.ApplyFn = testApplyFn 7501 p.DiffFn = testDiffFn 7502 7503 state := MustShimLegacyState(&State{ 7504 Modules: []*ModuleState{ 7505 { 7506 Path: rootModulePath, 7507 Resources: map[string]*ResourceState{ 7508 "aws_instance.bar": { 7509 Type: "aws_instance", 7510 Primary: &InstanceState{ 7511 ID: "bar", 7512 }, 7513 Provider: "provider.aws", 7514 }, 7515 }, 7516 }, 7517 }, 7518 }) 7519 7520 ctx := testContext2(t, &ContextOpts{ 7521 Config: m, 7522 State: state, 7523 Hooks: []Hook{h}, 7524 ProviderResolver: providers.ResolverFixed( 7525 map[string]providers.Factory{ 7526 "aws": testProviderFuncFixed(p), 7527 }, 7528 ), 7529 }) 7530 7531 if _, diags := ctx.Plan(); diags.HasErrors() { 7532 t.Fatalf("plan errors: %s", diags.Err()) 7533 } 7534 7535 if _, diags := ctx.Apply(); diags.HasErrors() { 7536 t.Fatalf("apply errors: %s", diags.Err()) 7537 } 7538 7539 if !h.PreApplyCalled { 7540 t.Fatal("should be called") 7541 } 7542 if !h.PostApplyCalled { 7543 t.Fatal("should be called") 7544 } 7545 if !h.PostStateUpdateCalled { 7546 t.Fatalf("should call post state update") 7547 } 7548 } 7549 7550 func TestContext2Apply_idAttr(t *testing.T) { 7551 m := testModule(t, "apply-idattr") 7552 p := testProvider("aws") 7553 ctx := testContext2(t, &ContextOpts{ 7554 Config: m, 7555 ProviderResolver: providers.ResolverFixed( 7556 map[string]providers.Factory{ 7557 "aws": testProviderFuncFixed(p), 7558 }, 7559 ), 7560 }) 7561 7562 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 7563 result := s.MergeDiff(d) 7564 result.ID = "foo" 7565 result.Attributes = map[string]string{ 7566 "id": "bar", 7567 "num": "42", 7568 } 7569 7570 return result, nil 7571 } 7572 p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { 7573 return &InstanceDiff{ 7574 Attributes: map[string]*ResourceAttrDiff{ 7575 "num": { 7576 New: "42", 7577 }, 7578 }, 7579 }, nil 7580 } 7581 7582 if _, diags := ctx.Plan(); diags.HasErrors() { 7583 t.Fatalf("plan errors: %s", diags.Err()) 7584 } 7585 7586 state, diags := ctx.Apply() 7587 if diags.HasErrors() { 7588 t.Fatalf("apply errors: %s", diags.Err()) 7589 } 7590 7591 mod := state.RootModule() 7592 rs, ok := mod.Resources["aws_instance.foo"] 7593 if !ok { 7594 t.Fatal("not in state") 7595 } 7596 var attrs map[string]interface{} 7597 err := json.Unmarshal(rs.Instances[addrs.NoKey].Current.AttrsJSON, &attrs) 7598 if err != nil { 7599 t.Fatal(err) 7600 } 7601 if got, want := attrs["id"], "foo"; got != want { 7602 t.Fatalf("wrong id\ngot: %#v\nwant: %#v", got, want) 7603 } 7604 } 7605 7606 func TestContext2Apply_outputBasic(t *testing.T) { 7607 m := testModule(t, "apply-output") 7608 p := testProvider("aws") 7609 p.ApplyFn = testApplyFn 7610 p.DiffFn = testDiffFn 7611 ctx := testContext2(t, &ContextOpts{ 7612 Config: m, 7613 ProviderResolver: providers.ResolverFixed( 7614 map[string]providers.Factory{ 7615 "aws": testProviderFuncFixed(p), 7616 }, 7617 ), 7618 }) 7619 7620 if _, diags := ctx.Plan(); diags.HasErrors() { 7621 t.Fatalf("plan errors: %s", diags.Err()) 7622 } 7623 7624 state, diags := ctx.Apply() 7625 if diags.HasErrors() { 7626 t.Fatalf("diags: %s", diags.Err()) 7627 } 7628 7629 actual := strings.TrimSpace(state.String()) 7630 expected := strings.TrimSpace(testTerraformApplyOutputStr) 7631 if actual != expected { 7632 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7633 } 7634 } 7635 7636 func TestContext2Apply_outputAdd(t *testing.T) { 7637 m1 := testModule(t, "apply-output-add-before") 7638 p1 := testProvider("aws") 7639 p1.ApplyFn = testApplyFn 7640 p1.DiffFn = testDiffFn 7641 ctx1 := testContext2(t, &ContextOpts{ 7642 Config: m1, 7643 ProviderResolver: providers.ResolverFixed( 7644 map[string]providers.Factory{ 7645 "aws": testProviderFuncFixed(p1), 7646 }, 7647 ), 7648 }) 7649 7650 if _, diags := ctx1.Plan(); diags.HasErrors() { 7651 t.Fatalf("diags: %s", diags.Err()) 7652 } 7653 7654 state1, diags := ctx1.Apply() 7655 if diags.HasErrors() { 7656 t.Fatalf("diags: %s", diags.Err()) 7657 } 7658 7659 m2 := testModule(t, "apply-output-add-after") 7660 p2 := testProvider("aws") 7661 p2.ApplyFn = testApplyFn 7662 p2.DiffFn = testDiffFn 7663 ctx2 := testContext2(t, &ContextOpts{ 7664 Config: m2, 7665 ProviderResolver: providers.ResolverFixed( 7666 map[string]providers.Factory{ 7667 "aws": testProviderFuncFixed(p2), 7668 }, 7669 ), 7670 State: state1, 7671 }) 7672 7673 if _, diags := ctx2.Plan(); diags.HasErrors() { 7674 t.Fatalf("diags: %s", diags.Err()) 7675 } 7676 7677 state2, diags := ctx2.Apply() 7678 if diags.HasErrors() { 7679 t.Fatalf("diags: %s", diags.Err()) 7680 } 7681 7682 actual := strings.TrimSpace(state2.String()) 7683 expected := strings.TrimSpace(testTerraformApplyOutputAddStr) 7684 if actual != expected { 7685 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7686 } 7687 } 7688 7689 func TestContext2Apply_outputList(t *testing.T) { 7690 m := testModule(t, "apply-output-list") 7691 p := testProvider("aws") 7692 p.ApplyFn = testApplyFn 7693 p.DiffFn = testDiffFn 7694 ctx := testContext2(t, &ContextOpts{ 7695 Config: m, 7696 ProviderResolver: providers.ResolverFixed( 7697 map[string]providers.Factory{ 7698 "aws": testProviderFuncFixed(p), 7699 }, 7700 ), 7701 }) 7702 7703 if _, diags := ctx.Plan(); diags.HasErrors() { 7704 t.Fatalf("plan errors: %s", diags.Err()) 7705 } 7706 7707 state, diags := ctx.Apply() 7708 if diags.HasErrors() { 7709 t.Fatalf("diags: %s", diags.Err()) 7710 } 7711 7712 actual := strings.TrimSpace(state.String()) 7713 expected := strings.TrimSpace(testTerraformApplyOutputListStr) 7714 if actual != expected { 7715 t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) 7716 } 7717 } 7718 7719 func TestContext2Apply_outputMulti(t *testing.T) { 7720 m := testModule(t, "apply-output-multi") 7721 p := testProvider("aws") 7722 p.ApplyFn = testApplyFn 7723 p.DiffFn = testDiffFn 7724 ctx := testContext2(t, &ContextOpts{ 7725 Config: m, 7726 ProviderResolver: providers.ResolverFixed( 7727 map[string]providers.Factory{ 7728 "aws": testProviderFuncFixed(p), 7729 }, 7730 ), 7731 }) 7732 7733 if _, diags := ctx.Plan(); diags.HasErrors() { 7734 t.Fatalf("plan errors: %s", diags.Err()) 7735 } 7736 7737 state, diags := ctx.Apply() 7738 if diags.HasErrors() { 7739 t.Fatalf("diags: %s", diags.Err()) 7740 } 7741 7742 actual := strings.TrimSpace(state.String()) 7743 expected := strings.TrimSpace(testTerraformApplyOutputMultiStr) 7744 if actual != expected { 7745 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7746 } 7747 } 7748 7749 func TestContext2Apply_outputMultiIndex(t *testing.T) { 7750 m := testModule(t, "apply-output-multi-index") 7751 p := testProvider("aws") 7752 p.ApplyFn = testApplyFn 7753 p.DiffFn = testDiffFn 7754 ctx := testContext2(t, &ContextOpts{ 7755 Config: m, 7756 ProviderResolver: providers.ResolverFixed( 7757 map[string]providers.Factory{ 7758 "aws": testProviderFuncFixed(p), 7759 }, 7760 ), 7761 }) 7762 7763 if _, diags := ctx.Plan(); diags.HasErrors() { 7764 t.Fatalf("plan errors: %s", diags.Err()) 7765 } 7766 7767 state, diags := ctx.Apply() 7768 if diags.HasErrors() { 7769 t.Fatalf("diags: %s", diags.Err()) 7770 } 7771 7772 actual := strings.TrimSpace(state.String()) 7773 expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr) 7774 if actual != expected { 7775 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 7776 } 7777 } 7778 7779 func TestContext2Apply_taintX(t *testing.T) { 7780 m := testModule(t, "apply-taint") 7781 p := testProvider("aws") 7782 // destroyCount tests against regression of 7783 // https://github.com/hashicorp/terraform-plugin-sdk/issues/1056 7784 var destroyCount = int32(0) 7785 var once sync.Once 7786 simulateProviderDelay := func() { 7787 time.Sleep(10 * time.Millisecond) 7788 } 7789 7790 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 7791 once.Do(simulateProviderDelay) 7792 if d.Destroy { 7793 atomic.AddInt32(&destroyCount, 1) 7794 } 7795 return testApplyFn(info, s, d) 7796 } 7797 p.DiffFn = testDiffFn 7798 s := MustShimLegacyState(&State{ 7799 Modules: []*ModuleState{ 7800 { 7801 Path: rootModulePath, 7802 Resources: map[string]*ResourceState{ 7803 "aws_instance.bar": { 7804 Type: "aws_instance", 7805 Primary: &InstanceState{ 7806 ID: "baz", 7807 Attributes: map[string]string{ 7808 "num": "2", 7809 "type": "aws_instance", 7810 }, 7811 Tainted: true, 7812 }, 7813 }, 7814 }, 7815 }, 7816 }, 7817 }) 7818 ctx := testContext2(t, &ContextOpts{ 7819 Config: m, 7820 ProviderResolver: providers.ResolverFixed( 7821 map[string]providers.Factory{ 7822 "aws": testProviderFuncFixed(p), 7823 }, 7824 ), 7825 State: s, 7826 }) 7827 7828 if p, diags := ctx.Plan(); diags.HasErrors() { 7829 t.Fatalf("diags: %s", diags.Err()) 7830 } else { 7831 t.Logf("plan: %s", legacyDiffComparisonString(p.Changes)) 7832 } 7833 7834 state, diags := ctx.Apply() 7835 if diags.HasErrors() { 7836 t.Fatalf("diags: %s", diags.Err()) 7837 } 7838 7839 actual := strings.TrimSpace(state.String()) 7840 expected := strings.TrimSpace(testTerraformApplyTaintStr) 7841 if actual != expected { 7842 t.Fatalf("bad:\n%s", actual) 7843 } 7844 7845 if destroyCount != 1 { 7846 t.Fatalf("Expected 1 destroy, got %d", destroyCount) 7847 } 7848 } 7849 7850 func TestContext2Apply_taintDep(t *testing.T) { 7851 m := testModule(t, "apply-taint-dep") 7852 p := testProvider("aws") 7853 p.ApplyFn = testApplyFn 7854 p.DiffFn = testDiffFn 7855 s := MustShimLegacyState(&State{ 7856 Modules: []*ModuleState{ 7857 { 7858 Path: rootModulePath, 7859 Resources: map[string]*ResourceState{ 7860 "aws_instance.foo": { 7861 Type: "aws_instance", 7862 Primary: &InstanceState{ 7863 ID: "baz", 7864 Attributes: map[string]string{ 7865 "num": "2", 7866 "type": "aws_instance", 7867 }, 7868 Tainted: true, 7869 }, 7870 }, 7871 "aws_instance.bar": { 7872 Type: "aws_instance", 7873 Primary: &InstanceState{ 7874 ID: "bar", 7875 Attributes: map[string]string{ 7876 "foo": "baz", 7877 "num": "2", 7878 "type": "aws_instance", 7879 }, 7880 }, 7881 }, 7882 }, 7883 }, 7884 }, 7885 }) 7886 ctx := testContext2(t, &ContextOpts{ 7887 Config: m, 7888 ProviderResolver: providers.ResolverFixed( 7889 map[string]providers.Factory{ 7890 "aws": testProviderFuncFixed(p), 7891 }, 7892 ), 7893 State: s, 7894 }) 7895 7896 if p, diags := ctx.Plan(); diags.HasErrors() { 7897 t.Fatalf("diags: %s", diags.Err()) 7898 } else { 7899 t.Logf("plan: %s", legacyDiffComparisonString(p.Changes)) 7900 } 7901 7902 state, diags := ctx.Apply() 7903 if diags.HasErrors() { 7904 t.Fatalf("diags: %s", diags.Err()) 7905 } 7906 7907 actual := strings.TrimSpace(state.String()) 7908 expected := strings.TrimSpace(testTerraformApplyTaintDepStr) 7909 if actual != expected { 7910 t.Fatalf("bad:\n%s", actual) 7911 } 7912 } 7913 7914 func TestContext2Apply_taintDepRequiresNew(t *testing.T) { 7915 m := testModule(t, "apply-taint-dep-requires-new") 7916 p := testProvider("aws") 7917 p.ApplyFn = testApplyFn 7918 p.DiffFn = testDiffFn 7919 s := MustShimLegacyState(&State{ 7920 Modules: []*ModuleState{ 7921 { 7922 Path: rootModulePath, 7923 Resources: map[string]*ResourceState{ 7924 "aws_instance.foo": { 7925 Type: "aws_instance", 7926 Primary: &InstanceState{ 7927 ID: "baz", 7928 Attributes: map[string]string{ 7929 "num": "2", 7930 "type": "aws_instance", 7931 }, 7932 Tainted: true, 7933 }, 7934 }, 7935 "aws_instance.bar": { 7936 Type: "aws_instance", 7937 Primary: &InstanceState{ 7938 ID: "bar", 7939 Attributes: map[string]string{ 7940 "foo": "baz", 7941 "num": "2", 7942 "type": "aws_instance", 7943 }, 7944 }, 7945 }, 7946 }, 7947 }, 7948 }, 7949 }) 7950 ctx := testContext2(t, &ContextOpts{ 7951 Config: m, 7952 ProviderResolver: providers.ResolverFixed( 7953 map[string]providers.Factory{ 7954 "aws": testProviderFuncFixed(p), 7955 }, 7956 ), 7957 State: s, 7958 }) 7959 7960 if p, diags := ctx.Plan(); diags.HasErrors() { 7961 t.Fatalf("diags: %s", diags.Err()) 7962 } else { 7963 t.Logf("plan: %s", legacyDiffComparisonString(p.Changes)) 7964 } 7965 7966 state, diags := ctx.Apply() 7967 if diags.HasErrors() { 7968 t.Fatalf("diags: %s", diags.Err()) 7969 } 7970 7971 actual := strings.TrimSpace(state.String()) 7972 expected := strings.TrimSpace(testTerraformApplyTaintDepRequireNewStr) 7973 if actual != expected { 7974 t.Fatalf("bad:\n%s", actual) 7975 } 7976 } 7977 7978 func TestContext2Apply_targeted(t *testing.T) { 7979 m := testModule(t, "apply-targeted") 7980 p := testProvider("aws") 7981 p.ApplyFn = testApplyFn 7982 p.DiffFn = testDiffFn 7983 ctx := testContext2(t, &ContextOpts{ 7984 Config: m, 7985 ProviderResolver: providers.ResolverFixed( 7986 map[string]providers.Factory{ 7987 "aws": testProviderFuncFixed(p), 7988 }, 7989 ), 7990 Targets: []addrs.Targetable{ 7991 addrs.RootModuleInstance.Resource( 7992 addrs.ManagedResourceMode, "aws_instance", "foo", 7993 ), 7994 }, 7995 }) 7996 7997 if _, diags := ctx.Plan(); diags.HasErrors() { 7998 t.Fatalf("plan errors: %s", diags.Err()) 7999 } 8000 8001 state, diags := ctx.Apply() 8002 if diags.HasErrors() { 8003 t.Fatalf("diags: %s", diags.Err()) 8004 } 8005 8006 mod := state.RootModule() 8007 if len(mod.Resources) != 1 { 8008 t.Fatalf("expected 1 resource, got: %#v", mod.Resources) 8009 } 8010 8011 checkStateString(t, state, ` 8012 aws_instance.foo: 8013 ID = foo 8014 provider = provider.aws 8015 num = 2 8016 type = aws_instance 8017 `) 8018 } 8019 8020 func TestContext2Apply_targetedCount(t *testing.T) { 8021 m := testModule(t, "apply-targeted-count") 8022 p := testProvider("aws") 8023 p.ApplyFn = testApplyFn 8024 p.DiffFn = testDiffFn 8025 ctx := testContext2(t, &ContextOpts{ 8026 Config: m, 8027 ProviderResolver: providers.ResolverFixed( 8028 map[string]providers.Factory{ 8029 "aws": testProviderFuncFixed(p), 8030 }, 8031 ), 8032 Targets: []addrs.Targetable{ 8033 addrs.RootModuleInstance.Resource( 8034 addrs.ManagedResourceMode, "aws_instance", "foo", 8035 ), 8036 }, 8037 }) 8038 8039 if _, diags := ctx.Plan(); diags.HasErrors() { 8040 t.Fatalf("plan errors: %s", diags.Err()) 8041 } 8042 8043 state, diags := ctx.Apply() 8044 if diags.HasErrors() { 8045 t.Fatalf("diags: %s", diags.Err()) 8046 } 8047 8048 checkStateString(t, state, ` 8049 aws_instance.foo.0: 8050 ID = foo 8051 provider = provider.aws 8052 aws_instance.foo.1: 8053 ID = foo 8054 provider = provider.aws 8055 aws_instance.foo.2: 8056 ID = foo 8057 provider = provider.aws 8058 `) 8059 } 8060 8061 func TestContext2Apply_targetedCountIndex(t *testing.T) { 8062 m := testModule(t, "apply-targeted-count") 8063 p := testProvider("aws") 8064 p.ApplyFn = testApplyFn 8065 p.DiffFn = testDiffFn 8066 ctx := testContext2(t, &ContextOpts{ 8067 Config: m, 8068 ProviderResolver: providers.ResolverFixed( 8069 map[string]providers.Factory{ 8070 "aws": testProviderFuncFixed(p), 8071 }, 8072 ), 8073 Targets: []addrs.Targetable{ 8074 addrs.RootModuleInstance.ResourceInstance( 8075 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1), 8076 ), 8077 }, 8078 }) 8079 8080 if _, diags := ctx.Plan(); diags.HasErrors() { 8081 t.Fatalf("plan errors: %s", diags.Err()) 8082 } 8083 8084 state, diags := ctx.Apply() 8085 if diags.HasErrors() { 8086 t.Fatalf("diags: %s", diags.Err()) 8087 } 8088 8089 checkStateString(t, state, ` 8090 aws_instance.foo.1: 8091 ID = foo 8092 provider = provider.aws 8093 `) 8094 } 8095 8096 func TestContext2Apply_targetedDestroy(t *testing.T) { 8097 m := testModule(t, "apply-targeted") 8098 p := testProvider("aws") 8099 p.ApplyFn = testApplyFn 8100 p.DiffFn = testDiffFn 8101 ctx := testContext2(t, &ContextOpts{ 8102 Config: m, 8103 ProviderResolver: providers.ResolverFixed( 8104 map[string]providers.Factory{ 8105 "aws": testProviderFuncFixed(p), 8106 }, 8107 ), 8108 State: MustShimLegacyState(&State{ 8109 Modules: []*ModuleState{ 8110 { 8111 Path: rootModulePath, 8112 Resources: map[string]*ResourceState{ 8113 "aws_instance.foo": resourceState("aws_instance", "i-bcd345"), 8114 "aws_instance.bar": resourceState("aws_instance", "i-abc123"), 8115 }, 8116 }, 8117 }, 8118 }), 8119 Targets: []addrs.Targetable{ 8120 addrs.RootModuleInstance.Resource( 8121 addrs.ManagedResourceMode, "aws_instance", "foo", 8122 ), 8123 }, 8124 Destroy: true, 8125 }) 8126 8127 if _, diags := ctx.Plan(); diags.HasErrors() { 8128 t.Fatalf("plan errors: %s", diags.Err()) 8129 } 8130 8131 state, diags := ctx.Apply() 8132 if diags.HasErrors() { 8133 t.Fatalf("diags: %s", diags.Err()) 8134 } 8135 8136 mod := state.RootModule() 8137 if len(mod.Resources) != 1 { 8138 t.Fatalf("expected 1 resource, got: %#v", mod.Resources) 8139 } 8140 8141 checkStateString(t, state, ` 8142 aws_instance.bar: 8143 ID = i-abc123 8144 provider = provider.aws 8145 `) 8146 } 8147 8148 func TestContext2Apply_destroyProvisionerWithLocals(t *testing.T) { 8149 m := testModule(t, "apply-provisioner-destroy-locals") 8150 p := testProvider("aws") 8151 p.ApplyFn = testApplyFn 8152 p.DiffFn = testDiffFn 8153 8154 pr := testProvisioner() 8155 pr.ApplyFn = func(_ *InstanceState, rc *ResourceConfig) error { 8156 cmd, ok := rc.Get("command") 8157 if !ok || cmd != "local" { 8158 return fmt.Errorf("provisioner got %v:%s", ok, cmd) 8159 } 8160 return nil 8161 } 8162 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 8163 Provisioner: &configschema.Block{ 8164 Attributes: map[string]*configschema.Attribute{ 8165 "command": { 8166 Type: cty.String, 8167 Required: true, 8168 }, 8169 "when": { 8170 Type: cty.String, 8171 Optional: true, 8172 }, 8173 }, 8174 }, 8175 } 8176 8177 ctx := testContext2(t, &ContextOpts{ 8178 Config: m, 8179 ProviderResolver: providers.ResolverFixed( 8180 map[string]providers.Factory{ 8181 "aws": testProviderFuncFixed(p), 8182 }, 8183 ), 8184 Provisioners: map[string]ProvisionerFactory{ 8185 "shell": testProvisionerFuncFixed(pr), 8186 }, 8187 State: MustShimLegacyState(&State{ 8188 Modules: []*ModuleState{ 8189 { 8190 Path: []string{"root"}, 8191 Resources: map[string]*ResourceState{ 8192 "aws_instance.foo": resourceState("aws_instance", "1234"), 8193 }, 8194 }, 8195 }, 8196 }), 8197 Destroy: true, 8198 // the test works without targeting, but this also tests that the local 8199 // node isn't inadvertently pruned because of the wrong evaluation 8200 // order. 8201 Targets: []addrs.Targetable{ 8202 addrs.RootModuleInstance.Resource( 8203 addrs.ManagedResourceMode, "aws_instance", "foo", 8204 ), 8205 }, 8206 }) 8207 8208 if _, diags := ctx.Plan(); diags.HasErrors() { 8209 t.Fatal(diags.Err()) 8210 } 8211 8212 if _, diags := ctx.Apply(); diags.HasErrors() { 8213 t.Fatal(diags.Err()) 8214 } 8215 8216 if !pr.ProvisionResourceCalled { 8217 t.Fatal("provisioner not called") 8218 } 8219 } 8220 8221 // this also tests a local value in the config referencing a resource that 8222 // wasn't in the state during destroy. 8223 func TestContext2Apply_destroyProvisionerWithMultipleLocals(t *testing.T) { 8224 m := testModule(t, "apply-provisioner-destroy-multiple-locals") 8225 p := testProvider("aws") 8226 p.ApplyFn = testApplyFn 8227 p.DiffFn = testDiffFn 8228 8229 pr := testProvisioner() 8230 pr.GetSchemaResponse = provisioners.GetSchemaResponse{ 8231 Provisioner: &configschema.Block{ 8232 Attributes: map[string]*configschema.Attribute{ 8233 "id": { 8234 Type: cty.String, 8235 Required: true, 8236 }, 8237 "command": { 8238 Type: cty.String, 8239 Required: true, 8240 }, 8241 "when": { 8242 Type: cty.String, 8243 Optional: true, 8244 }, 8245 }, 8246 }, 8247 } 8248 8249 pr.ApplyFn = func(is *InstanceState, rc *ResourceConfig) error { 8250 cmd, ok := rc.Get("command") 8251 if !ok { 8252 return errors.New("no command in provisioner") 8253 } 8254 id, ok := rc.Get("id") 8255 if !ok { 8256 return errors.New("no id in provisioner") 8257 } 8258 8259 switch id { 8260 case "1234": 8261 if cmd != "local" { 8262 return fmt.Errorf("provisioner %q got:%q", is.ID, cmd) 8263 } 8264 case "3456": 8265 if cmd != "1234" { 8266 return fmt.Errorf("provisioner %q got:%q", is.ID, cmd) 8267 } 8268 default: 8269 t.Fatal("unknown instance") 8270 } 8271 return nil 8272 } 8273 8274 ctx := testContext2(t, &ContextOpts{ 8275 Config: m, 8276 ProviderResolver: providers.ResolverFixed( 8277 map[string]providers.Factory{ 8278 "aws": testProviderFuncFixed(p), 8279 }, 8280 ), 8281 Provisioners: map[string]ProvisionerFactory{ 8282 "shell": testProvisionerFuncFixed(pr), 8283 }, 8284 State: MustShimLegacyState(&State{ 8285 Modules: []*ModuleState{ 8286 { 8287 Path: []string{"root"}, 8288 Resources: map[string]*ResourceState{ 8289 "aws_instance.foo": resourceState("aws_instance", "1234"), 8290 "aws_instance.bar": resourceState("aws_instance", "3456"), 8291 }, 8292 }, 8293 }, 8294 }), 8295 Destroy: true, 8296 }) 8297 8298 if _, diags := ctx.Plan(); diags.HasErrors() { 8299 t.Fatal(diags.Err()) 8300 } 8301 8302 if _, diags := ctx.Apply(); diags.HasErrors() { 8303 t.Fatal(diags.Err()) 8304 } 8305 8306 if !pr.ProvisionResourceCalled { 8307 t.Fatal("provisioner not called") 8308 } 8309 } 8310 8311 func TestContext2Apply_destroyProvisionerWithOutput(t *testing.T) { 8312 m := testModule(t, "apply-provisioner-destroy-outputs") 8313 p := testProvider("aws") 8314 p.ApplyFn = testApplyFn 8315 p.DiffFn = testDiffFn 8316 8317 pr := testProvisioner() 8318 pr.ApplyFn = func(is *InstanceState, rc *ResourceConfig) error { 8319 cmd, ok := rc.Get("command") 8320 if !ok || cmd != "3" { 8321 return fmt.Errorf("provisioner for %s got %v:%s", is.ID, ok, cmd) 8322 } 8323 return nil 8324 } 8325 ctx := testContext2(t, &ContextOpts{ 8326 Config: m, 8327 ProviderResolver: providers.ResolverFixed( 8328 map[string]providers.Factory{ 8329 "aws": testProviderFuncFixed(p), 8330 }, 8331 ), 8332 Provisioners: map[string]ProvisionerFactory{ 8333 "shell": testProvisionerFuncFixed(pr), 8334 }, 8335 State: MustShimLegacyState(&State{ 8336 Modules: []*ModuleState{ 8337 { 8338 Path: []string{"root"}, 8339 Resources: map[string]*ResourceState{ 8340 "aws_instance.foo": resourceState("aws_instance", "1"), 8341 }, 8342 Outputs: map[string]*OutputState{ 8343 "value": { 8344 Type: "string", 8345 Value: "3", 8346 }, 8347 }, 8348 }, 8349 { 8350 Path: []string{"root", "mod"}, 8351 Resources: map[string]*ResourceState{ 8352 "aws_instance.baz": resourceState("aws_instance", "3"), 8353 }, 8354 // state needs to be properly initialized 8355 Outputs: map[string]*OutputState{}, 8356 }, 8357 { 8358 Path: []string{"root", "mod2"}, 8359 Resources: map[string]*ResourceState{ 8360 "aws_instance.bar": resourceState("aws_instance", "2"), 8361 }, 8362 }, 8363 }, 8364 }), 8365 Destroy: true, 8366 8367 // targeting the source of the value used by all resources should still 8368 // destroy them all. 8369 Targets: []addrs.Targetable{ 8370 addrs.RootModuleInstance.Child("mod", addrs.NoKey).Resource( 8371 addrs.ManagedResourceMode, "aws_instance", "baz", 8372 ), 8373 }, 8374 }) 8375 8376 if _, diags := ctx.Plan(); diags.HasErrors() { 8377 t.Fatal(diags.Err()) 8378 } 8379 8380 state, diags := ctx.Apply() 8381 if diags.HasErrors() { 8382 t.Fatal(diags.Err()) 8383 } 8384 if !pr.ProvisionResourceCalled { 8385 t.Fatal("provisioner not called") 8386 } 8387 8388 // confirm all outputs were removed too 8389 for _, mod := range state.Modules { 8390 if len(mod.OutputValues) > 0 { 8391 t.Fatalf("output left in module state: %#v\n", mod) 8392 } 8393 } 8394 } 8395 8396 func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) { 8397 m := testModule(t, "apply-destroy-targeted-count") 8398 p := testProvider("aws") 8399 p.ApplyFn = testApplyFn 8400 p.DiffFn = testDiffFn 8401 ctx := testContext2(t, &ContextOpts{ 8402 Config: m, 8403 ProviderResolver: providers.ResolverFixed( 8404 map[string]providers.Factory{ 8405 "aws": testProviderFuncFixed(p), 8406 }, 8407 ), 8408 State: MustShimLegacyState(&State{ 8409 Modules: []*ModuleState{ 8410 { 8411 Path: rootModulePath, 8412 Resources: map[string]*ResourceState{ 8413 "aws_instance.foo": resourceState("aws_instance", "i-bcd345"), 8414 "aws_instance.bar": resourceState("aws_instance", "i-abc123"), 8415 }, 8416 }, 8417 }, 8418 }), 8419 Targets: []addrs.Targetable{ 8420 addrs.RootModuleInstance.Resource( 8421 addrs.ManagedResourceMode, "aws_instance", "foo", 8422 ), 8423 }, 8424 Destroy: true, 8425 }) 8426 8427 if _, diags := ctx.Plan(); diags.HasErrors() { 8428 t.Fatalf("plan errors: %s", diags.Err()) 8429 } 8430 8431 state, diags := ctx.Apply() 8432 if diags.HasErrors() { 8433 t.Fatalf("diags: %s", diags.Err()) 8434 } 8435 8436 checkStateString(t, state, `<no state>`) 8437 } 8438 8439 // https://github.com/hashicorp/terraform-plugin-sdk/issues/4462 8440 func TestContext2Apply_targetedDestroyModule(t *testing.T) { 8441 m := testModule(t, "apply-targeted-module") 8442 p := testProvider("aws") 8443 p.ApplyFn = testApplyFn 8444 p.DiffFn = testDiffFn 8445 ctx := testContext2(t, &ContextOpts{ 8446 Config: m, 8447 ProviderResolver: providers.ResolverFixed( 8448 map[string]providers.Factory{ 8449 "aws": testProviderFuncFixed(p), 8450 }, 8451 ), 8452 State: MustShimLegacyState(&State{ 8453 Modules: []*ModuleState{ 8454 { 8455 Path: rootModulePath, 8456 Resources: map[string]*ResourceState{ 8457 "aws_instance.foo": resourceState("aws_instance", "i-bcd345"), 8458 "aws_instance.bar": resourceState("aws_instance", "i-abc123"), 8459 }, 8460 }, 8461 { 8462 Path: []string{"root", "child"}, 8463 Resources: map[string]*ResourceState{ 8464 "aws_instance.foo": resourceState("aws_instance", "i-bcd345"), 8465 "aws_instance.bar": resourceState("aws_instance", "i-abc123"), 8466 }, 8467 }, 8468 }, 8469 }), 8470 Targets: []addrs.Targetable{ 8471 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 8472 addrs.ManagedResourceMode, "aws_instance", "foo", 8473 ), 8474 }, 8475 Destroy: true, 8476 }) 8477 8478 if _, diags := ctx.Plan(); diags.HasErrors() { 8479 t.Fatalf("plan errors: %s", diags.Err()) 8480 } 8481 8482 state, diags := ctx.Apply() 8483 if diags.HasErrors() { 8484 t.Fatalf("diags: %s", diags.Err()) 8485 } 8486 8487 checkStateString(t, state, ` 8488 aws_instance.bar: 8489 ID = i-abc123 8490 provider = provider.aws 8491 aws_instance.foo: 8492 ID = i-bcd345 8493 provider = provider.aws 8494 8495 module.child: 8496 aws_instance.bar: 8497 ID = i-abc123 8498 provider = provider.aws 8499 `) 8500 } 8501 8502 func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) { 8503 m := testModule(t, "apply-targeted-count") 8504 p := testProvider("aws") 8505 p.ApplyFn = testApplyFn 8506 p.DiffFn = testDiffFn 8507 ctx := testContext2(t, &ContextOpts{ 8508 Config: m, 8509 ProviderResolver: providers.ResolverFixed( 8510 map[string]providers.Factory{ 8511 "aws": testProviderFuncFixed(p), 8512 }, 8513 ), 8514 State: MustShimLegacyState(&State{ 8515 Modules: []*ModuleState{ 8516 { 8517 Path: rootModulePath, 8518 Resources: map[string]*ResourceState{ 8519 "aws_instance.foo.0": resourceState("aws_instance", "i-bcd345"), 8520 "aws_instance.foo.1": resourceState("aws_instance", "i-bcd345"), 8521 "aws_instance.foo.2": resourceState("aws_instance", "i-bcd345"), 8522 "aws_instance.bar.0": resourceState("aws_instance", "i-abc123"), 8523 "aws_instance.bar.1": resourceState("aws_instance", "i-abc123"), 8524 "aws_instance.bar.2": resourceState("aws_instance", "i-abc123"), 8525 }, 8526 }, 8527 }, 8528 }), 8529 Targets: []addrs.Targetable{ 8530 addrs.RootModuleInstance.ResourceInstance( 8531 addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(2), 8532 ), 8533 addrs.RootModuleInstance.ResourceInstance( 8534 addrs.ManagedResourceMode, "aws_instance", "bar", addrs.IntKey(1), 8535 ), 8536 }, 8537 Destroy: true, 8538 }) 8539 8540 if _, diags := ctx.Plan(); diags.HasErrors() { 8541 t.Fatalf("plan errors: %s", diags.Err()) 8542 } 8543 8544 state, diags := ctx.Apply() 8545 if diags.HasErrors() { 8546 t.Fatalf("diags: %s", diags.Err()) 8547 } 8548 8549 checkStateString(t, state, ` 8550 aws_instance.bar.0: 8551 ID = i-abc123 8552 provider = provider.aws 8553 aws_instance.bar.2: 8554 ID = i-abc123 8555 provider = provider.aws 8556 aws_instance.foo.0: 8557 ID = i-bcd345 8558 provider = provider.aws 8559 aws_instance.foo.1: 8560 ID = i-bcd345 8561 provider = provider.aws 8562 `) 8563 } 8564 8565 func TestContext2Apply_targetedModule(t *testing.T) { 8566 m := testModule(t, "apply-targeted-module") 8567 p := testProvider("aws") 8568 p.ApplyFn = testApplyFn 8569 p.DiffFn = testDiffFn 8570 ctx := testContext2(t, &ContextOpts{ 8571 Config: m, 8572 ProviderResolver: providers.ResolverFixed( 8573 map[string]providers.Factory{ 8574 "aws": testProviderFuncFixed(p), 8575 }, 8576 ), 8577 Targets: []addrs.Targetable{ 8578 addrs.RootModuleInstance.Child("child", addrs.NoKey), 8579 }, 8580 }) 8581 8582 if _, diags := ctx.Plan(); diags.HasErrors() { 8583 t.Fatalf("plan errors: %s", diags.Err()) 8584 } 8585 8586 state, diags := ctx.Apply() 8587 if diags.HasErrors() { 8588 t.Fatalf("diags: %s", diags.Err()) 8589 } 8590 8591 mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 8592 if mod == nil { 8593 t.Fatalf("no child module found in the state!\n\n%#v", state) 8594 } 8595 if len(mod.Resources) != 2 { 8596 t.Fatalf("expected 2 resources, got: %#v", mod.Resources) 8597 } 8598 8599 checkStateString(t, state, ` 8600 <no state> 8601 module.child: 8602 aws_instance.bar: 8603 ID = foo 8604 provider = provider.aws 8605 num = 2 8606 type = aws_instance 8607 aws_instance.foo: 8608 ID = foo 8609 provider = provider.aws 8610 num = 2 8611 type = aws_instance 8612 `) 8613 } 8614 8615 // GH-1858 8616 func TestContext2Apply_targetedModuleDep(t *testing.T) { 8617 m := testModule(t, "apply-targeted-module-dep") 8618 p := testProvider("aws") 8619 p.ApplyFn = testApplyFn 8620 p.DiffFn = testDiffFn 8621 ctx := testContext2(t, &ContextOpts{ 8622 Config: m, 8623 ProviderResolver: providers.ResolverFixed( 8624 map[string]providers.Factory{ 8625 "aws": testProviderFuncFixed(p), 8626 }, 8627 ), 8628 Targets: []addrs.Targetable{ 8629 addrs.RootModuleInstance.Resource( 8630 addrs.ManagedResourceMode, "aws_instance", "foo", 8631 ), 8632 }, 8633 }) 8634 8635 if p, diags := ctx.Plan(); diags.HasErrors() { 8636 t.Fatalf("diags: %s", diags.Err()) 8637 } else { 8638 t.Logf("Diff: %s", legacyDiffComparisonString(p.Changes)) 8639 } 8640 8641 state, diags := ctx.Apply() 8642 if diags.HasErrors() { 8643 t.Fatalf("diags: %s", diags.Err()) 8644 } 8645 8646 checkStateString(t, state, ` 8647 aws_instance.foo: 8648 ID = foo 8649 provider = provider.aws 8650 foo = foo 8651 type = aws_instance 8652 8653 Dependencies: 8654 module.child 8655 8656 module.child: 8657 aws_instance.mod: 8658 ID = foo 8659 provider = provider.aws 8660 8661 Outputs: 8662 8663 output = foo 8664 `) 8665 } 8666 8667 // GH-10911 untargeted outputs should not be in the graph, and therefore 8668 // not execute. 8669 func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) { 8670 m := testModule(t, "apply-targeted-module-unrelated-outputs") 8671 p := testProvider("aws") 8672 p.ApplyFn = testApplyFn 8673 p.DiffFn = testDiffFn 8674 ctx := testContext2(t, &ContextOpts{ 8675 Config: m, 8676 ProviderResolver: providers.ResolverFixed( 8677 map[string]providers.Factory{ 8678 "aws": testProviderFuncFixed(p), 8679 }, 8680 ), 8681 Targets: []addrs.Targetable{ 8682 addrs.RootModuleInstance.Child("child2", addrs.NoKey), 8683 }, 8684 State: MustShimLegacyState(&State{ 8685 Modules: []*ModuleState{ 8686 { 8687 Path: []string{"root"}, 8688 Outputs: map[string]*OutputState{}, 8689 Resources: map[string]*ResourceState{}, 8690 }, 8691 { 8692 Path: []string{"root", "child1"}, 8693 Outputs: map[string]*OutputState{ 8694 "instance_id": { 8695 Type: "string", 8696 Value: "foo-bar-baz", 8697 }, 8698 }, 8699 Resources: map[string]*ResourceState{}, 8700 }, 8701 { 8702 Path: []string{"root", "child2"}, 8703 Outputs: map[string]*OutputState{}, 8704 Resources: map[string]*ResourceState{}, 8705 }, 8706 }, 8707 }), 8708 }) 8709 8710 if _, diags := ctx.Plan(); diags.HasErrors() { 8711 t.Fatalf("plan errors: %s", diags.Err()) 8712 } 8713 8714 state, diags := ctx.Apply() 8715 if diags.HasErrors() { 8716 t.Fatalf("diags: %s", diags.Err()) 8717 } 8718 8719 // - module.child1's instance_id output is dropped because we don't preserve 8720 // non-root module outputs between runs (they can be recalculated from config) 8721 // - module.child2's instance_id is updated because its dependency is updated 8722 // - child2_id is updated because if its transitive dependency via module.child2 8723 checkStateString(t, state, ` 8724 <no state> 8725 Outputs: 8726 8727 child2_id = foo 8728 8729 module.child2: 8730 aws_instance.foo: 8731 ID = foo 8732 provider = provider.aws 8733 8734 Outputs: 8735 8736 instance_id = foo 8737 `) 8738 } 8739 8740 func TestContext2Apply_targetedModuleResource(t *testing.T) { 8741 m := testModule(t, "apply-targeted-module-resource") 8742 p := testProvider("aws") 8743 p.ApplyFn = testApplyFn 8744 p.DiffFn = testDiffFn 8745 ctx := testContext2(t, &ContextOpts{ 8746 Config: m, 8747 ProviderResolver: providers.ResolverFixed( 8748 map[string]providers.Factory{ 8749 "aws": testProviderFuncFixed(p), 8750 }, 8751 ), 8752 Targets: []addrs.Targetable{ 8753 addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( 8754 addrs.ManagedResourceMode, "aws_instance", "foo", 8755 ), 8756 }, 8757 }) 8758 8759 if _, diags := ctx.Plan(); diags.HasErrors() { 8760 t.Fatalf("plan errors: %s", diags.Err()) 8761 } 8762 8763 state, diags := ctx.Apply() 8764 if diags.HasErrors() { 8765 t.Fatalf("diags: %s", diags.Err()) 8766 } 8767 8768 mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 8769 if mod == nil || len(mod.Resources) != 1 { 8770 t.Fatalf("expected 1 resource, got: %#v", mod) 8771 } 8772 8773 checkStateString(t, state, ` 8774 <no state> 8775 module.child: 8776 aws_instance.foo: 8777 ID = foo 8778 provider = provider.aws 8779 num = 2 8780 type = aws_instance 8781 `) 8782 } 8783 8784 func TestContext2Apply_targetedResourceOrphanModule(t *testing.T) { 8785 m := testModule(t, "apply-targeted-resource-orphan-module") 8786 p := testProvider("aws") 8787 p.ApplyFn = testApplyFn 8788 p.DiffFn = testDiffFn 8789 8790 // Create a state with an orphan module 8791 state := MustShimLegacyState(&State{ 8792 Modules: []*ModuleState{ 8793 { 8794 Path: []string{"root", "child"}, 8795 Resources: map[string]*ResourceState{ 8796 "aws_instance.bar": { 8797 Type: "aws_instance", 8798 Primary: &InstanceState{}, 8799 Provider: "provider.aws", 8800 }, 8801 }, 8802 }, 8803 }, 8804 }) 8805 8806 ctx := testContext2(t, &ContextOpts{ 8807 Config: m, 8808 ProviderResolver: providers.ResolverFixed( 8809 map[string]providers.Factory{ 8810 "aws": testProviderFuncFixed(p), 8811 }, 8812 ), 8813 State: state, 8814 Targets: []addrs.Targetable{ 8815 addrs.RootModuleInstance.Resource( 8816 addrs.ManagedResourceMode, "aws_instance", "foo", 8817 ), 8818 }, 8819 }) 8820 8821 if _, diags := ctx.Plan(); diags.HasErrors() { 8822 t.Fatalf("plan errors: %s", diags.Err()) 8823 } 8824 8825 if _, diags := ctx.Apply(); diags.HasErrors() { 8826 t.Fatalf("apply errors: %s", diags.Err()) 8827 } 8828 } 8829 8830 func TestContext2Apply_unknownAttribute(t *testing.T) { 8831 m := testModule(t, "apply-unknown") 8832 p := testProvider("aws") 8833 p.ApplyFn = testApplyFn 8834 p.DiffFn = testDiffFn 8835 ctx := testContext2(t, &ContextOpts{ 8836 Config: m, 8837 ProviderResolver: providers.ResolverFixed( 8838 map[string]providers.Factory{ 8839 "aws": testProviderFuncFixed(p), 8840 }, 8841 ), 8842 }) 8843 8844 if _, diags := ctx.Plan(); diags.HasErrors() { 8845 t.Fatalf("plan errors: %s", diags.Err()) 8846 } 8847 8848 state, diags := ctx.Apply() 8849 if !diags.HasErrors() { 8850 t.Error("should error, because attribute 'unknown' is still unknown after apply") 8851 } 8852 8853 actual := strings.TrimSpace(state.String()) 8854 expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr) 8855 if actual != expected { 8856 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 8857 } 8858 } 8859 8860 func TestContext2Apply_unknownAttributeInterpolate(t *testing.T) { 8861 m := testModule(t, "apply-unknown-interpolate") 8862 p := testProvider("aws") 8863 p.ApplyFn = testApplyFn 8864 p.DiffFn = testDiffFn 8865 ctx := testContext2(t, &ContextOpts{ 8866 Config: m, 8867 ProviderResolver: providers.ResolverFixed( 8868 map[string]providers.Factory{ 8869 "aws": testProviderFuncFixed(p), 8870 }, 8871 ), 8872 }) 8873 8874 if _, diags := ctx.Plan(); diags == nil { 8875 t.Fatal("should error") 8876 } 8877 } 8878 8879 func TestContext2Apply_vars(t *testing.T) { 8880 fixture := contextFixtureApplyVars(t) 8881 opts := fixture.ContextOpts() 8882 opts.Variables = InputValues{ 8883 "foo": &InputValue{ 8884 Value: cty.StringVal("us-east-1"), 8885 SourceType: ValueFromCaller, 8886 }, 8887 "test_list": &InputValue{ 8888 Value: cty.ListVal([]cty.Value{ 8889 cty.StringVal("Hello"), 8890 cty.StringVal("World"), 8891 }), 8892 SourceType: ValueFromCaller, 8893 }, 8894 "test_map": &InputValue{ 8895 Value: cty.MapVal(map[string]cty.Value{ 8896 "Hello": cty.StringVal("World"), 8897 "Foo": cty.StringVal("Bar"), 8898 "Baz": cty.StringVal("Foo"), 8899 }), 8900 SourceType: ValueFromCaller, 8901 }, 8902 "amis": &InputValue{ 8903 Value: cty.MapVal(map[string]cty.Value{ 8904 "us-east-1": cty.StringVal("override"), 8905 }), 8906 SourceType: ValueFromCaller, 8907 }, 8908 } 8909 ctx := testContext2(t, opts) 8910 8911 diags := ctx.Validate() 8912 if len(diags) != 0 { 8913 t.Fatalf("bad: %s", diags.ErrWithWarnings()) 8914 } 8915 8916 if _, diags := ctx.Plan(); diags.HasErrors() { 8917 t.Fatalf("err: %s", diags.Err()) 8918 } 8919 8920 state, diags := ctx.Apply() 8921 if diags.HasErrors() { 8922 t.Fatalf("err: %s", diags.Err()) 8923 } 8924 8925 got := strings.TrimSpace(state.String()) 8926 want := strings.TrimSpace(testTerraformApplyVarsStr) 8927 if got != want { 8928 t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want) 8929 } 8930 } 8931 8932 func TestContext2Apply_varsEnv(t *testing.T) { 8933 fixture := contextFixtureApplyVarsEnv(t) 8934 opts := fixture.ContextOpts() 8935 opts.Variables = InputValues{ 8936 "string": &InputValue{ 8937 Value: cty.StringVal("baz"), 8938 SourceType: ValueFromEnvVar, 8939 }, 8940 "list": &InputValue{ 8941 Value: cty.ListVal([]cty.Value{ 8942 cty.StringVal("Hello"), 8943 cty.StringVal("World"), 8944 }), 8945 SourceType: ValueFromEnvVar, 8946 }, 8947 "map": &InputValue{ 8948 Value: cty.MapVal(map[string]cty.Value{ 8949 "Hello": cty.StringVal("World"), 8950 "Foo": cty.StringVal("Bar"), 8951 "Baz": cty.StringVal("Foo"), 8952 }), 8953 SourceType: ValueFromEnvVar, 8954 }, 8955 } 8956 ctx := testContext2(t, opts) 8957 8958 diags := ctx.Validate() 8959 if len(diags) != 0 { 8960 t.Fatalf("bad: %s", diags.ErrWithWarnings()) 8961 } 8962 8963 if _, diags := ctx.Plan(); diags.HasErrors() { 8964 t.Fatalf("err: %s", diags.Err()) 8965 } 8966 8967 state, diags := ctx.Apply() 8968 if diags.HasErrors() { 8969 t.Fatalf("err: %s", diags.Err()) 8970 } 8971 8972 actual := strings.TrimSpace(state.String()) 8973 expected := strings.TrimSpace(testTerraformApplyVarsEnvStr) 8974 if actual != expected { 8975 t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 8976 } 8977 } 8978 8979 func TestContext2Apply_createBefore_depends(t *testing.T) { 8980 m := testModule(t, "apply-depends-create-before") 8981 h := new(HookRecordApplyOrder) 8982 p := testProvider("aws") 8983 p.ApplyFn = testApplyFn 8984 p.DiffFn = testDiffFn 8985 state := MustShimLegacyState(&State{ 8986 Modules: []*ModuleState{ 8987 { 8988 Path: rootModulePath, 8989 Resources: map[string]*ResourceState{ 8990 "aws_instance.web": { 8991 Type: "aws_instance", 8992 Primary: &InstanceState{ 8993 ID: "bar", 8994 Attributes: map[string]string{ 8995 "require_new": "ami-old", 8996 }, 8997 }, 8998 }, 8999 "aws_instance.lb": { 9000 Type: "aws_instance", 9001 Primary: &InstanceState{ 9002 ID: "baz", 9003 Attributes: map[string]string{ 9004 "instance": "bar", 9005 }, 9006 }, 9007 }, 9008 }, 9009 }, 9010 }, 9011 }) 9012 ctx := testContext2(t, &ContextOpts{ 9013 Config: m, 9014 Hooks: []Hook{h}, 9015 ProviderResolver: providers.ResolverFixed( 9016 map[string]providers.Factory{ 9017 "aws": testProviderFuncFixed(p), 9018 }, 9019 ), 9020 State: state, 9021 }) 9022 9023 if p, diags := ctx.Plan(); diags.HasErrors() { 9024 logDiagnostics(t, diags) 9025 t.Fatal("plan failed") 9026 } else { 9027 t.Logf("plan:\n%s", legacyDiffComparisonString(p.Changes)) 9028 } 9029 9030 h.Active = true 9031 state, diags := ctx.Apply() 9032 if diags.HasErrors() { 9033 logDiagnostics(t, diags) 9034 t.Fatal("apply failed") 9035 } 9036 9037 mod := state.RootModule() 9038 if len(mod.Resources) < 2 { 9039 t.Logf("state after apply:\n%s", state.String()) 9040 t.Fatalf("only %d resources in root module; want at least 2", len(mod.Resources)) 9041 } 9042 9043 got := strings.TrimSpace(state.String()) 9044 want := strings.TrimSpace(testTerraformApplyDependsCreateBeforeStr) 9045 if got != want { 9046 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", got, want) 9047 } 9048 9049 // Test that things were managed _in the right order_ 9050 order := h.States 9051 diffs := h.Diffs 9052 if !order[0].IsNull() || diffs[0].Action == plans.Delete { 9053 t.Fatalf("should create new instance first: %#v", order) 9054 } 9055 9056 if order[1].GetAttr("id").AsString() != "baz" { 9057 t.Fatalf("update must happen after create: %#v", order) 9058 } 9059 9060 if order[2].GetAttr("id").AsString() != "bar" || diffs[2].Action != plans.Delete { 9061 t.Fatalf("destroy must happen after update: %#v", order) 9062 } 9063 } 9064 9065 func TestContext2Apply_singleDestroy(t *testing.T) { 9066 m := testModule(t, "apply-depends-create-before") 9067 h := new(HookRecordApplyOrder) 9068 p := testProvider("aws") 9069 invokeCount := 0 9070 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 9071 invokeCount++ 9072 switch invokeCount { 9073 case 1: 9074 if d.Destroy { 9075 t.Fatalf("should not destroy") 9076 } 9077 if s.ID != "" { 9078 t.Fatalf("should not have ID") 9079 } 9080 case 2: 9081 if d.Destroy { 9082 t.Fatalf("should not destroy") 9083 } 9084 if s.ID != "baz" { 9085 t.Fatalf("should have id") 9086 } 9087 case 3: 9088 if !d.Destroy { 9089 t.Fatalf("should destroy") 9090 } 9091 if s.ID == "" { 9092 t.Fatalf("should have ID") 9093 } 9094 default: 9095 t.Fatalf("bad invoke count %d", invokeCount) 9096 } 9097 return testApplyFn(info, s, d) 9098 } 9099 p.DiffFn = testDiffFn 9100 state := MustShimLegacyState(&State{ 9101 Modules: []*ModuleState{ 9102 { 9103 Path: rootModulePath, 9104 Resources: map[string]*ResourceState{ 9105 "aws_instance.web": { 9106 Type: "aws_instance", 9107 Primary: &InstanceState{ 9108 ID: "bar", 9109 Attributes: map[string]string{ 9110 "require_new": "ami-old", 9111 }, 9112 }, 9113 }, 9114 "aws_instance.lb": { 9115 Type: "aws_instance", 9116 Primary: &InstanceState{ 9117 ID: "baz", 9118 Attributes: map[string]string{ 9119 "instance": "bar", 9120 }, 9121 }, 9122 }, 9123 }, 9124 }, 9125 }, 9126 }) 9127 ctx := testContext2(t, &ContextOpts{ 9128 Config: m, 9129 Hooks: []Hook{h}, 9130 ProviderResolver: providers.ResolverFixed( 9131 map[string]providers.Factory{ 9132 "aws": testProviderFuncFixed(p), 9133 }, 9134 ), 9135 State: state, 9136 }) 9137 9138 if _, diags := ctx.Plan(); diags.HasErrors() { 9139 t.Fatalf("plan errors: %s", diags.Err()) 9140 } 9141 9142 h.Active = true 9143 state, diags := ctx.Apply() 9144 if diags.HasErrors() { 9145 t.Fatalf("diags: %s", diags.Err()) 9146 } 9147 9148 if invokeCount != 3 { 9149 t.Fatalf("bad: %d", invokeCount) 9150 } 9151 } 9152 9153 // GH-7824 9154 func TestContext2Apply_issue7824(t *testing.T) { 9155 p := testProvider("template") 9156 p.ApplyFn = testApplyFn 9157 p.DiffFn = testDiffFn 9158 p.GetSchemaReturn = &ProviderSchema{ 9159 ResourceTypes: map[string]*configschema.Block{ 9160 "template_file": { 9161 Attributes: map[string]*configschema.Attribute{ 9162 "template": {Type: cty.String, Optional: true}, 9163 "__template_requires_new": {Type: cty.Bool, Optional: true}, 9164 }, 9165 }, 9166 }, 9167 } 9168 9169 m, snap := testModuleWithSnapshot(t, "issue-7824") 9170 9171 // Apply cleanly step 0 9172 ctx := testContext2(t, &ContextOpts{ 9173 Config: m, 9174 ProviderResolver: providers.ResolverFixed( 9175 map[string]providers.Factory{ 9176 "template": testProviderFuncFixed(p), 9177 }, 9178 ), 9179 }) 9180 9181 plan, diags := ctx.Plan() 9182 if diags.HasErrors() { 9183 t.Fatalf("err: %s", diags.Err()) 9184 } 9185 9186 // Write / Read plan to simulate running it through a Plan file 9187 ctxOpts, err := contextOptsForPlanViaFile(snap, ctx.State(), plan) 9188 if err != nil { 9189 t.Fatalf("failed to round-trip through planfile: %s", err) 9190 } 9191 9192 ctxOpts.ProviderResolver = providers.ResolverFixed( 9193 map[string]providers.Factory{ 9194 "template": testProviderFuncFixed(p), 9195 }, 9196 ) 9197 ctx, diags = NewContext(ctxOpts) 9198 if diags.HasErrors() { 9199 t.Fatalf("err: %s", diags.Err()) 9200 } 9201 9202 _, diags = ctx.Apply() 9203 if diags.HasErrors() { 9204 t.Fatalf("err: %s", diags.Err()) 9205 } 9206 } 9207 9208 // This deals with the situation where a splat expression is used referring 9209 // to another resource whose count is non-constant. 9210 func TestContext2Apply_issue5254(t *testing.T) { 9211 // Create a provider. We use "template" here just to match the repro 9212 // we got from the issue itself. 9213 p := testProvider("template") 9214 p.ApplyFn = testApplyFn 9215 p.DiffFn = testDiffFn 9216 p.GetSchemaReturn = &ProviderSchema{ 9217 ResourceTypes: map[string]*configschema.Block{ 9218 "template_file": { 9219 Attributes: map[string]*configschema.Attribute{ 9220 "template": {Type: cty.String, Optional: true}, 9221 "__template_requires_new": {Type: cty.Bool, Optional: true}, 9222 "id": {Type: cty.String, Computed: true}, 9223 "type": {Type: cty.String, Computed: true}, 9224 }, 9225 }, 9226 }, 9227 } 9228 9229 // Apply cleanly step 0 9230 ctx := testContext2(t, &ContextOpts{ 9231 Config: testModule(t, "issue-5254/step-0"), 9232 ProviderResolver: providers.ResolverFixed( 9233 map[string]providers.Factory{ 9234 "template": testProviderFuncFixed(p), 9235 }, 9236 ), 9237 }) 9238 9239 plan, diags := ctx.Plan() 9240 if diags.HasErrors() { 9241 t.Fatalf("err: %s", diags.Err()) 9242 } 9243 9244 state, diags := ctx.Apply() 9245 if diags.HasErrors() { 9246 t.Fatalf("err: %s", diags.Err()) 9247 } 9248 9249 m, snap := testModuleWithSnapshot(t, "issue-5254/step-1") 9250 9251 // Application success. Now make the modification and store a plan 9252 ctx = testContext2(t, &ContextOpts{ 9253 Config: m, 9254 State: state, 9255 ProviderResolver: providers.ResolverFixed( 9256 map[string]providers.Factory{ 9257 "template": testProviderFuncFixed(p), 9258 }, 9259 ), 9260 }) 9261 9262 plan, diags = ctx.Plan() 9263 if diags.HasErrors() { 9264 t.Fatalf("err: %s", diags.Err()) 9265 } 9266 9267 // Write / Read plan to simulate running it through a Plan file 9268 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 9269 if err != nil { 9270 t.Fatalf("failed to round-trip through planfile: %s", err) 9271 } 9272 9273 ctxOpts.ProviderResolver = providers.ResolverFixed( 9274 map[string]providers.Factory{ 9275 "template": testProviderFuncFixed(p), 9276 }, 9277 ) 9278 ctx, diags = NewContext(ctxOpts) 9279 if diags.HasErrors() { 9280 t.Fatalf("err: %s", diags.Err()) 9281 } 9282 9283 state, diags = ctx.Apply() 9284 if diags.HasErrors() { 9285 t.Fatalf("err: %s", diags.Err()) 9286 } 9287 9288 actual := strings.TrimSpace(state.String()) 9289 expected := strings.TrimSpace(` 9290 template_file.child: 9291 ID = foo 9292 provider = provider.template 9293 __template_requires_new = true 9294 template = Hi 9295 type = template_file 9296 9297 Dependencies: 9298 template_file.parent 9299 template_file.parent.0: 9300 ID = foo 9301 provider = provider.template 9302 template = Hi 9303 type = template_file 9304 `) 9305 if actual != expected { 9306 t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected) 9307 } 9308 } 9309 9310 func TestContext2Apply_targetedWithTaintedInState(t *testing.T) { 9311 p := testProvider("aws") 9312 p.DiffFn = testDiffFn 9313 p.ApplyFn = testApplyFn 9314 m, snap := testModuleWithSnapshot(t, "apply-tainted-targets") 9315 ctx := testContext2(t, &ContextOpts{ 9316 Config: m, 9317 ProviderResolver: providers.ResolverFixed( 9318 map[string]providers.Factory{ 9319 "aws": testProviderFuncFixed(p), 9320 }, 9321 ), 9322 Targets: []addrs.Targetable{ 9323 addrs.RootModuleInstance.Resource( 9324 addrs.ManagedResourceMode, "aws_instance", "iambeingadded", 9325 ), 9326 }, 9327 State: MustShimLegacyState(&State{ 9328 Modules: []*ModuleState{ 9329 { 9330 Path: rootModulePath, 9331 Resources: map[string]*ResourceState{ 9332 "aws_instance.ifailedprovisioners": { 9333 Type: "aws_instance", 9334 Primary: &InstanceState{ 9335 ID: "ifailedprovisioners", 9336 Tainted: true, 9337 }, 9338 }, 9339 }, 9340 }, 9341 }, 9342 }), 9343 }) 9344 9345 plan, diags := ctx.Plan() 9346 if diags.HasErrors() { 9347 t.Fatalf("err: %s", diags.Err()) 9348 } 9349 9350 // Write / Read plan to simulate running it through a Plan file 9351 ctxOpts, err := contextOptsForPlanViaFile(snap, ctx.State(), plan) 9352 if err != nil { 9353 t.Fatalf("failed to round-trip through planfile: %s", err) 9354 } 9355 9356 ctxOpts.ProviderResolver = providers.ResolverFixed( 9357 map[string]providers.Factory{ 9358 "aws": testProviderFuncFixed(p), 9359 }, 9360 ) 9361 ctx, diags = NewContext(ctxOpts) 9362 if diags.HasErrors() { 9363 t.Fatalf("err: %s", diags.Err()) 9364 } 9365 9366 state, diags := ctx.Apply() 9367 if diags.HasErrors() { 9368 t.Fatalf("err: %s", diags.Err()) 9369 } 9370 9371 actual := strings.TrimSpace(state.String()) 9372 expected := strings.TrimSpace(` 9373 aws_instance.iambeingadded: 9374 ID = foo 9375 provider = provider.aws 9376 aws_instance.ifailedprovisioners: (tainted) 9377 ID = ifailedprovisioners 9378 provider = provider.aws 9379 `) 9380 if actual != expected { 9381 t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual) 9382 } 9383 } 9384 9385 // Higher level test exposing the bug this covers in 9386 // TestResource_ignoreChangesRequired 9387 func TestContext2Apply_ignoreChangesCreate(t *testing.T) { 9388 m := testModule(t, "apply-ignore-changes-create") 9389 p := testProvider("aws") 9390 p.ApplyFn = testApplyFn 9391 p.DiffFn = testDiffFn 9392 9393 instanceSchema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 9394 instanceSchema.Attributes["required_field"] = &configschema.Attribute{ 9395 Type: cty.String, 9396 Required: true, 9397 } 9398 9399 ctx := testContext2(t, &ContextOpts{ 9400 Config: m, 9401 ProviderResolver: providers.ResolverFixed( 9402 map[string]providers.Factory{ 9403 "aws": testProviderFuncFixed(p), 9404 }, 9405 ), 9406 }) 9407 9408 if p, diags := ctx.Plan(); diags.HasErrors() { 9409 t.Fatalf("diags: %s", diags.Err()) 9410 } else { 9411 t.Logf(legacyDiffComparisonString(p.Changes)) 9412 } 9413 9414 state, diags := ctx.Apply() 9415 if diags.HasErrors() { 9416 t.Fatalf("diags: %s", diags.Err()) 9417 } 9418 9419 mod := state.RootModule() 9420 if len(mod.Resources) != 1 { 9421 t.Fatalf("bad: %s", state) 9422 } 9423 9424 actual := strings.TrimSpace(state.String()) 9425 // Expect no changes from original state 9426 expected := strings.TrimSpace(` 9427 aws_instance.foo: 9428 ID = foo 9429 provider = provider.aws 9430 required_field = set 9431 type = aws_instance 9432 `) 9433 if actual != expected { 9434 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 9435 } 9436 } 9437 9438 func TestContext2Apply_ignoreChangesWithDep(t *testing.T) { 9439 m := testModule(t, "apply-ignore-changes-dep") 9440 p := testProvider("aws") 9441 p.ApplyFn = testApplyFn 9442 9443 p.DiffFn = func(i *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 9444 switch i.Type { 9445 case "aws_instance": 9446 newAmi, _ := c.Get("ami") 9447 return &InstanceDiff{ 9448 Attributes: map[string]*ResourceAttrDiff{ 9449 "ami": { 9450 Old: s.Attributes["ami"], 9451 New: newAmi.(string), 9452 RequiresNew: true, 9453 }, 9454 }, 9455 }, nil 9456 case "aws_eip": 9457 return testDiffFn(i, s, c) 9458 default: 9459 t.Fatalf("Unexpected type: %s", i.Type) 9460 return nil, nil 9461 } 9462 } 9463 s := MustShimLegacyState(&State{ 9464 Modules: []*ModuleState{ 9465 { 9466 Path: rootModulePath, 9467 Resources: map[string]*ResourceState{ 9468 "aws_instance.foo.0": { 9469 Type: "aws_instance", 9470 Primary: &InstanceState{ 9471 ID: "i-abc123", 9472 Attributes: map[string]string{ 9473 "ami": "ami-abcd1234", 9474 "id": "i-abc123", 9475 }, 9476 }, 9477 }, 9478 "aws_instance.foo.1": { 9479 Type: "aws_instance", 9480 Primary: &InstanceState{ 9481 ID: "i-bcd234", 9482 Attributes: map[string]string{ 9483 "ami": "ami-abcd1234", 9484 "id": "i-bcd234", 9485 }, 9486 }, 9487 }, 9488 "aws_eip.foo.0": { 9489 Type: "aws_eip", 9490 Primary: &InstanceState{ 9491 ID: "eip-abc123", 9492 Attributes: map[string]string{ 9493 "id": "eip-abc123", 9494 "instance": "i-abc123", 9495 }, 9496 }, 9497 }, 9498 "aws_eip.foo.1": { 9499 Type: "aws_eip", 9500 Primary: &InstanceState{ 9501 ID: "eip-bcd234", 9502 Attributes: map[string]string{ 9503 "id": "eip-bcd234", 9504 "instance": "i-bcd234", 9505 }, 9506 }, 9507 }, 9508 }, 9509 }, 9510 }, 9511 }) 9512 ctx := testContext2(t, &ContextOpts{ 9513 Config: m, 9514 ProviderResolver: providers.ResolverFixed( 9515 map[string]providers.Factory{ 9516 "aws": testProviderFuncFixed(p), 9517 }, 9518 ), 9519 State: s, 9520 }) 9521 9522 _, diags := ctx.Plan() 9523 assertNoErrors(t, diags) 9524 9525 state, diags := ctx.Apply() 9526 assertNoErrors(t, diags) 9527 9528 actual := strings.TrimSpace(state.String()) 9529 expected := strings.TrimSpace(s.String()) 9530 if actual != expected { 9531 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 9532 } 9533 } 9534 9535 func TestContext2Apply_ignoreChangesWildcard(t *testing.T) { 9536 m := testModule(t, "apply-ignore-changes-wildcard") 9537 p := testProvider("aws") 9538 p.ApplyFn = testApplyFn 9539 p.DiffFn = testDiffFn 9540 9541 instanceSchema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 9542 instanceSchema.Attributes["required_field"] = &configschema.Attribute{ 9543 Type: cty.String, 9544 Required: true, 9545 } 9546 9547 ctx := testContext2(t, &ContextOpts{ 9548 Config: m, 9549 ProviderResolver: providers.ResolverFixed( 9550 map[string]providers.Factory{ 9551 "aws": testProviderFuncFixed(p), 9552 }, 9553 ), 9554 }) 9555 9556 if p, diags := ctx.Plan(); diags.HasErrors() { 9557 logDiagnostics(t, diags) 9558 t.Fatal("plan failed") 9559 } else { 9560 t.Logf(legacyDiffComparisonString(p.Changes)) 9561 } 9562 9563 state, diags := ctx.Apply() 9564 assertNoErrors(t, diags) 9565 9566 mod := state.RootModule() 9567 if len(mod.Resources) != 1 { 9568 t.Fatalf("bad: %s", state) 9569 } 9570 9571 actual := strings.TrimSpace(state.String()) 9572 // Expect no changes from original state 9573 expected := strings.TrimSpace(` 9574 aws_instance.foo: 9575 ID = foo 9576 provider = provider.aws 9577 required_field = set 9578 type = aws_instance 9579 `) 9580 if actual != expected { 9581 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 9582 } 9583 } 9584 9585 // https://github.com/hashicorp/terraform-plugin-sdk/issues/7378 9586 func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testing.T) { 9587 m, snap := testModuleWithSnapshot(t, "apply-destroy-nested-module-with-attrs") 9588 p := testProvider("null") 9589 p.ApplyFn = testApplyFn 9590 p.DiffFn = testDiffFn 9591 9592 var state *states.State 9593 var diags tfdiags.Diagnostics 9594 { 9595 ctx := testContext2(t, &ContextOpts{ 9596 Config: m, 9597 ProviderResolver: providers.ResolverFixed( 9598 map[string]providers.Factory{ 9599 "null": testProviderFuncFixed(p), 9600 }, 9601 ), 9602 }) 9603 9604 // First plan and apply a create operation 9605 if _, diags := ctx.Plan(); diags.HasErrors() { 9606 t.Fatalf("plan err: %s", diags.Err()) 9607 } 9608 9609 state, diags = ctx.Apply() 9610 if diags.HasErrors() { 9611 t.Fatalf("apply err: %s", diags.Err()) 9612 } 9613 } 9614 9615 { 9616 ctx := testContext2(t, &ContextOpts{ 9617 Destroy: true, 9618 Config: m, 9619 State: state, 9620 ProviderResolver: providers.ResolverFixed( 9621 map[string]providers.Factory{ 9622 "null": testProviderFuncFixed(p), 9623 }, 9624 ), 9625 }) 9626 9627 plan, diags := ctx.Plan() 9628 if diags.HasErrors() { 9629 t.Fatalf("destroy plan err: %s", diags.Err()) 9630 } 9631 9632 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 9633 if err != nil { 9634 t.Fatalf("failed to round-trip through planfile: %s", err) 9635 } 9636 9637 ctxOpts.ProviderResolver = providers.ResolverFixed( 9638 map[string]providers.Factory{ 9639 "null": testProviderFuncFixed(p), 9640 }, 9641 ) 9642 ctx, diags = NewContext(ctxOpts) 9643 if diags.HasErrors() { 9644 t.Fatalf("err: %s", diags.Err()) 9645 } 9646 9647 state, diags = ctx.Apply() 9648 if diags.HasErrors() { 9649 t.Fatalf("destroy apply err: %s", diags.Err()) 9650 } 9651 } 9652 9653 if !state.Empty() { 9654 t.Fatalf("state after apply: %s\nwant empty state", spew.Sdump(state)) 9655 } 9656 } 9657 9658 // If a data source explicitly depends on another resource, it's because we need 9659 // that resource to be applied first. 9660 func TestContext2Apply_dataDependsOn(t *testing.T) { 9661 p := testProvider("null") 9662 m := testModule(t, "apply-data-depends-on") 9663 9664 ctx := testContext2(t, &ContextOpts{ 9665 Config: m, 9666 ProviderResolver: providers.ResolverFixed( 9667 map[string]providers.Factory{ 9668 "null": testProviderFuncFixed(p), 9669 }, 9670 ), 9671 }) 9672 9673 // the "provisioner" here writes to this variable, because the intent is to 9674 // create a dependency which can't be viewed through the graph, and depends 9675 // solely on the configuration providing "depends_on" 9676 provisionerOutput := "" 9677 9678 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { 9679 // the side effect of the resource being applied 9680 provisionerOutput = "APPLIED" 9681 return testApplyFn(info, s, d) 9682 } 9683 9684 p.DiffFn = testDiffFn 9685 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 9686 return providers.ReadDataSourceResponse{ 9687 State: cty.ObjectVal(map[string]cty.Value{ 9688 "id": cty.StringVal("boop"), 9689 "foo": cty.StringVal(provisionerOutput), 9690 }), 9691 } 9692 } 9693 9694 _, diags := ctx.Refresh() 9695 assertNoErrors(t, diags) 9696 9697 _, diags = ctx.Plan() 9698 assertNoErrors(t, diags) 9699 9700 state, diags := ctx.Apply() 9701 assertNoErrors(t, diags) 9702 9703 root := state.Module(addrs.RootModuleInstance) 9704 is := root.ResourceInstance(addrs.Resource{ 9705 Mode: addrs.DataResourceMode, 9706 Type: "null_data_source", 9707 Name: "read", 9708 }.Instance(addrs.NoKey)) 9709 if is == nil { 9710 t.Fatal("data resource instance is not present in state; should be") 9711 } 9712 var attrs map[string]interface{} 9713 err := json.Unmarshal(is.Current.AttrsJSON, &attrs) 9714 if err != nil { 9715 t.Fatal(err) 9716 } 9717 actual := attrs["foo"] 9718 expected := "APPLIED" 9719 if actual != expected { 9720 t.Fatalf("bad:\n%s", strings.TrimSpace(state.String())) 9721 } 9722 } 9723 9724 func TestContext2Apply_terraformWorkspace(t *testing.T) { 9725 m := testModule(t, "apply-terraform-workspace") 9726 p := testProvider("aws") 9727 p.ApplyFn = testApplyFn 9728 p.DiffFn = testDiffFn 9729 9730 ctx := testContext2(t, &ContextOpts{ 9731 Meta: &ContextMeta{Env: "foo"}, 9732 Config: m, 9733 ProviderResolver: providers.ResolverFixed( 9734 map[string]providers.Factory{ 9735 "aws": testProviderFuncFixed(p), 9736 }, 9737 ), 9738 }) 9739 9740 if _, diags := ctx.Plan(); diags.HasErrors() { 9741 t.Fatalf("plan errors: %s", diags.Err()) 9742 } 9743 9744 state, diags := ctx.Apply() 9745 if diags.HasErrors() { 9746 t.Fatalf("diags: %s", diags.Err()) 9747 } 9748 9749 actual := state.RootModule().OutputValues["output"] 9750 expected := cty.StringVal("foo") 9751 if actual == nil || actual.Value != expected { 9752 t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) 9753 } 9754 } 9755 9756 // verify that multiple config references only create a single depends_on entry 9757 func TestContext2Apply_multiRef(t *testing.T) { 9758 m := testModule(t, "apply-multi-ref") 9759 p := testProvider("aws") 9760 p.ApplyFn = testApplyFn 9761 p.DiffFn = testDiffFn 9762 ctx := testContext2(t, &ContextOpts{ 9763 Config: m, 9764 ProviderResolver: providers.ResolverFixed( 9765 map[string]providers.Factory{ 9766 "aws": testProviderFuncFixed(p), 9767 }, 9768 ), 9769 }) 9770 9771 if _, diags := ctx.Plan(); diags.HasErrors() { 9772 t.Fatalf("err: %s", diags.Err()) 9773 } 9774 9775 state, diags := ctx.Apply() 9776 if diags.HasErrors() { 9777 t.Fatalf("err: %s", diags.Err()) 9778 } 9779 9780 deps := state.Modules[""].Resources["aws_instance.other"].Instances[addrs.NoKey].Current.Dependencies 9781 if len(deps) != 1 || deps[0].String() != "aws_instance.create" { 9782 t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps) 9783 } 9784 } 9785 9786 func TestContext2Apply_targetedModuleRecursive(t *testing.T) { 9787 m := testModule(t, "apply-targeted-module-recursive") 9788 p := testProvider("aws") 9789 p.ApplyFn = testApplyFn 9790 p.DiffFn = testDiffFn 9791 ctx := testContext2(t, &ContextOpts{ 9792 Config: m, 9793 ProviderResolver: providers.ResolverFixed( 9794 map[string]providers.Factory{ 9795 "aws": testProviderFuncFixed(p), 9796 }, 9797 ), 9798 Targets: []addrs.Targetable{ 9799 addrs.RootModuleInstance.Child("child", addrs.NoKey), 9800 }, 9801 }) 9802 9803 if _, diags := ctx.Plan(); diags.HasErrors() { 9804 t.Fatalf("err: %s", diags.Err()) 9805 } 9806 9807 state, diags := ctx.Apply() 9808 if diags.HasErrors() { 9809 t.Fatalf("err: %s", diags.Err()) 9810 } 9811 9812 mod := state.Module( 9813 addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("subchild", addrs.NoKey), 9814 ) 9815 if mod == nil { 9816 t.Fatalf("no subchild module found in the state!\n\n%#v", state) 9817 } 9818 if len(mod.Resources) != 1 { 9819 t.Fatalf("expected 1 resources, got: %#v", mod.Resources) 9820 } 9821 9822 checkStateString(t, state, ` 9823 <no state> 9824 module.child.subchild: 9825 aws_instance.foo: 9826 ID = foo 9827 provider = provider.aws 9828 num = 2 9829 type = aws_instance 9830 `) 9831 } 9832 9833 func TestContext2Apply_localVal(t *testing.T) { 9834 m := testModule(t, "apply-local-val") 9835 ctx := testContext2(t, &ContextOpts{ 9836 Config: m, 9837 ProviderResolver: providers.ResolverFixed( 9838 map[string]providers.Factory{}, 9839 ), 9840 }) 9841 9842 if _, diags := ctx.Plan(); diags.HasErrors() { 9843 t.Fatalf("error during plan: %s", diags.Err()) 9844 } 9845 9846 state, diags := ctx.Apply() 9847 if diags.HasErrors() { 9848 t.Fatalf("error during apply: %s", diags.Err()) 9849 } 9850 9851 got := strings.TrimSpace(state.String()) 9852 want := strings.TrimSpace(` 9853 <no state> 9854 Outputs: 9855 9856 result_1 = hello 9857 result_3 = hello world 9858 9859 module.child: 9860 <no state> 9861 Outputs: 9862 9863 result = hello 9864 `) 9865 if got != want { 9866 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 9867 } 9868 } 9869 9870 func TestContext2Apply_destroyWithLocals(t *testing.T) { 9871 m := testModule(t, "apply-destroy-with-locals") 9872 p := testProvider("aws") 9873 p.ApplyFn = testApplyFn 9874 p.DiffFn = func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { 9875 d, err := testDiffFn(info, s, c) 9876 return d, err 9877 } 9878 s := MustShimLegacyState(&State{ 9879 Modules: []*ModuleState{ 9880 { 9881 Path: rootModulePath, 9882 Outputs: map[string]*OutputState{ 9883 "name": { 9884 Type: "string", 9885 Value: "test-bar", 9886 }, 9887 }, 9888 Resources: map[string]*ResourceState{ 9889 "aws_instance.foo": { 9890 Type: "aws_instance", 9891 Primary: &InstanceState{ 9892 ID: "foo", 9893 // FIXME: id should only exist in one place 9894 Attributes: map[string]string{ 9895 "id": "foo", 9896 }, 9897 }, 9898 Provider: "provider.aws", 9899 }, 9900 }, 9901 }, 9902 }, 9903 }) 9904 9905 ctx := testContext2(t, &ContextOpts{ 9906 Config: m, 9907 ProviderResolver: providers.ResolverFixed( 9908 map[string]providers.Factory{ 9909 "aws": testProviderFuncFixed(p), 9910 }, 9911 ), 9912 State: s, 9913 Destroy: true, 9914 }) 9915 9916 if _, diags := ctx.Plan(); diags.HasErrors() { 9917 t.Fatalf("err: %s", diags.Err()) 9918 } 9919 9920 state, diags := ctx.Apply() 9921 if diags.HasErrors() { 9922 t.Fatalf("error during apply: %s", diags.Err()) 9923 } 9924 9925 got := strings.TrimSpace(state.String()) 9926 want := strings.TrimSpace(`<no state>`) 9927 if got != want { 9928 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 9929 } 9930 } 9931 9932 func TestContext2Apply_providerWithLocals(t *testing.T) { 9933 m := testModule(t, "provider-with-locals") 9934 p := testProvider("aws") 9935 9936 providerRegion := "" 9937 // this should not be overridden during destroy 9938 p.ConfigureFn = func(c *ResourceConfig) error { 9939 if r, ok := c.Get("region"); ok { 9940 providerRegion = r.(string) 9941 } 9942 return nil 9943 } 9944 p.DiffFn = testDiffFn 9945 p.ApplyFn = testApplyFn 9946 ctx := testContext2(t, &ContextOpts{ 9947 Config: m, 9948 ProviderResolver: providers.ResolverFixed( 9949 map[string]providers.Factory{ 9950 "aws": testProviderFuncFixed(p), 9951 }, 9952 ), 9953 }) 9954 9955 if _, diags := ctx.Plan(); diags.HasErrors() { 9956 t.Fatalf("err: %s", diags.Err()) 9957 } 9958 9959 state, diags := ctx.Apply() 9960 if diags.HasErrors() { 9961 t.Fatalf("err: %s", diags.Err()) 9962 } 9963 9964 ctx = testContext2(t, &ContextOpts{ 9965 Config: m, 9966 ProviderResolver: providers.ResolverFixed( 9967 map[string]providers.Factory{ 9968 "aws": testProviderFuncFixed(p), 9969 }, 9970 ), 9971 State: state, 9972 Destroy: true, 9973 }) 9974 9975 if _, diags := ctx.Plan(); diags.HasErrors() { 9976 t.Fatalf("err: %s", diags.Err()) 9977 } 9978 9979 state, diags = ctx.Apply() 9980 if diags.HasErrors() { 9981 t.Fatalf("err: %s", diags.Err()) 9982 } 9983 9984 if state.HasResources() { 9985 t.Fatal("expected no state, got:", state) 9986 } 9987 9988 if providerRegion != "bar" { 9989 t.Fatalf("expected region %q, got: %q", "bar", providerRegion) 9990 } 9991 } 9992 9993 func TestContext2Apply_destroyWithProviders(t *testing.T) { 9994 m := testModule(t, "destroy-module-with-provider") 9995 p := testProvider("aws") 9996 p.ApplyFn = testApplyFn 9997 p.DiffFn = testDiffFn 9998 9999 s := MustShimLegacyState(&State{ 10000 Modules: []*ModuleState{ 10001 { 10002 Path: rootModulePath, 10003 }, 10004 { 10005 Path: []string{"root", "child"}, 10006 }, 10007 { 10008 Path: []string{"root", "mod", "removed"}, 10009 Resources: map[string]*ResourceState{ 10010 "aws_instance.child": { 10011 Type: "aws_instance", 10012 Primary: &InstanceState{ 10013 ID: "bar", 10014 }, 10015 // this provider doesn't exist 10016 Provider: "provider.aws.baz", 10017 }, 10018 }, 10019 }, 10020 }, 10021 }) 10022 10023 ctx := testContext2(t, &ContextOpts{ 10024 Config: m, 10025 ProviderResolver: providers.ResolverFixed( 10026 map[string]providers.Factory{ 10027 "aws": testProviderFuncFixed(p), 10028 }, 10029 ), 10030 State: s, 10031 Destroy: true, 10032 }) 10033 10034 // test that we can't destroy if the provider is missing 10035 if _, diags := ctx.Plan(); diags == nil { 10036 t.Fatal("expected plan error, provider.aws.baz doesn't exist") 10037 } 10038 10039 // correct the state 10040 s.Modules["module.mod.module.removed"].Resources["aws_instance.child"].ProviderConfig = addrs.ProviderConfig{ 10041 Type: "aws", 10042 Alias: "bar", 10043 }.Absolute(addrs.RootModuleInstance) 10044 10045 if _, diags := ctx.Plan(); diags.HasErrors() { 10046 t.Fatal(diags.Err()) 10047 } 10048 state, diags := ctx.Apply() 10049 if diags.HasErrors() { 10050 t.Fatalf("error during apply: %s", diags.Err()) 10051 } 10052 10053 got := strings.TrimSpace(state.String()) 10054 10055 want := strings.TrimSpace("<no state>") 10056 if got != want { 10057 t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) 10058 } 10059 } 10060 10061 func TestContext2Apply_providersFromState(t *testing.T) { 10062 m := configs.NewEmptyConfig() 10063 p := testProvider("aws") 10064 p.DiffFn = testDiffFn 10065 10066 for _, tc := range []struct { 10067 name string 10068 state *states.State 10069 output string 10070 err bool 10071 }{ 10072 { 10073 name: "add implicit provider", 10074 state: MustShimLegacyState(&State{ 10075 Modules: []*ModuleState{ 10076 { 10077 Path: []string{"root"}, 10078 Resources: map[string]*ResourceState{ 10079 "aws_instance.a": { 10080 Type: "aws_instance", 10081 Primary: &InstanceState{ 10082 ID: "bar", 10083 }, 10084 Provider: "provider.aws", 10085 }, 10086 }, 10087 }, 10088 }, 10089 }), 10090 err: false, 10091 output: "<no state>", 10092 }, 10093 10094 // an aliased provider must be in the config to remove a resource 10095 { 10096 name: "add aliased provider", 10097 state: MustShimLegacyState(&State{ 10098 Modules: []*ModuleState{ 10099 { 10100 Path: []string{"root"}, 10101 Resources: map[string]*ResourceState{ 10102 "aws_instance.a": { 10103 Type: "aws_instance", 10104 Primary: &InstanceState{ 10105 ID: "bar", 10106 }, 10107 Provider: "provider.aws.bar", 10108 }, 10109 }, 10110 }, 10111 }, 10112 }), 10113 err: true, 10114 }, 10115 10116 // a provider in a module implies some sort of config, so this isn't 10117 // allowed even without an alias 10118 { 10119 name: "add unaliased module provider", 10120 state: MustShimLegacyState(&State{ 10121 Modules: []*ModuleState{ 10122 { 10123 Path: []string{"root", "child"}, 10124 Resources: map[string]*ResourceState{ 10125 "aws_instance.a": { 10126 Type: "aws_instance", 10127 Primary: &InstanceState{ 10128 ID: "bar", 10129 }, 10130 Provider: "module.child.provider.aws", 10131 }, 10132 }, 10133 }, 10134 }, 10135 }), 10136 err: true, 10137 }, 10138 } { 10139 10140 t.Run(tc.name, func(t *testing.T) { 10141 ctx := testContext2(t, &ContextOpts{ 10142 Config: m, 10143 ProviderResolver: providers.ResolverFixed( 10144 map[string]providers.Factory{ 10145 "aws": testProviderFuncFixed(p), 10146 }, 10147 ), 10148 State: tc.state, 10149 }) 10150 10151 _, diags := ctx.Plan() 10152 if tc.err { 10153 if diags == nil { 10154 t.Fatal("expected error") 10155 } else { 10156 return 10157 } 10158 } 10159 if !tc.err && diags.HasErrors() { 10160 t.Fatal(diags.Err()) 10161 } 10162 10163 state, diags := ctx.Apply() 10164 if diags.HasErrors() { 10165 t.Fatalf("diags: %s", diags.Err()) 10166 } 10167 10168 checkStateString(t, state, "<no state>") 10169 10170 }) 10171 } 10172 } 10173 10174 func TestContext2Apply_plannedInterpolatedCount(t *testing.T) { 10175 m, snap := testModuleWithSnapshot(t, "apply-interpolated-count") 10176 10177 p := testProvider("aws") 10178 p.ApplyFn = testApplyFn 10179 p.DiffFn = testDiffFn 10180 10181 providerResolver := providers.ResolverFixed( 10182 map[string]providers.Factory{ 10183 "aws": testProviderFuncFixed(p), 10184 }, 10185 ) 10186 10187 s := MustShimLegacyState(&State{ 10188 Modules: []*ModuleState{ 10189 { 10190 Path: rootModulePath, 10191 Resources: map[string]*ResourceState{ 10192 "aws_instance.test": { 10193 Type: "aws_instance", 10194 Primary: &InstanceState{ 10195 ID: "foo", 10196 }, 10197 Provider: "provider.aws", 10198 }, 10199 }, 10200 }, 10201 }, 10202 }) 10203 10204 ctx := testContext2(t, &ContextOpts{ 10205 Config: m, 10206 ProviderResolver: providerResolver, 10207 State: s, 10208 }) 10209 10210 plan, diags := ctx.Plan() 10211 if diags.HasErrors() { 10212 t.Fatalf("plan failed: %s", diags.Err()) 10213 } 10214 10215 // We'll marshal and unmarshal the plan here, to ensure that we have 10216 // a clean new context as would be created if we separately ran 10217 // terraform plan -out=tfplan && terraform apply tfplan 10218 ctxOpts, err := contextOptsForPlanViaFile(snap, ctx.State(), plan) 10219 if err != nil { 10220 t.Fatalf("failed to round-trip through planfile: %s", err) 10221 } 10222 10223 ctxOpts.ProviderResolver = providerResolver 10224 ctx, diags = NewContext(ctxOpts) 10225 if diags.HasErrors() { 10226 t.Fatalf("err: %s", diags.Err()) 10227 } 10228 10229 // Applying the plan should now succeed 10230 _, diags = ctx.Apply() 10231 if diags.HasErrors() { 10232 t.Fatalf("apply failed: %s", diags.Err()) 10233 } 10234 } 10235 10236 func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) { 10237 m, snap := testModuleWithSnapshot(t, "plan-destroy-interpolated-count") 10238 10239 p := testProvider("aws") 10240 p.ApplyFn = testApplyFn 10241 p.DiffFn = testDiffFn 10242 10243 providerResolver := providers.ResolverFixed( 10244 map[string]providers.Factory{ 10245 "aws": testProviderFuncFixed(p), 10246 }, 10247 ) 10248 10249 s := MustShimLegacyState(&State{ 10250 Modules: []*ModuleState{ 10251 { 10252 Path: rootModulePath, 10253 Resources: map[string]*ResourceState{ 10254 "aws_instance.a.0": { 10255 Type: "aws_instance", 10256 Primary: &InstanceState{ 10257 ID: "foo", 10258 }, 10259 Provider: "provider.aws", 10260 }, 10261 "aws_instance.a.1": { 10262 Type: "aws_instance", 10263 Primary: &InstanceState{ 10264 ID: "foo", 10265 }, 10266 Provider: "provider.aws", 10267 }, 10268 }, 10269 Outputs: map[string]*OutputState{ 10270 "out": { 10271 Type: "list", 10272 Value: []string{"foo", "foo"}, 10273 }, 10274 }, 10275 }, 10276 }, 10277 }) 10278 10279 ctx := testContext2(t, &ContextOpts{ 10280 Config: m, 10281 ProviderResolver: providerResolver, 10282 State: s, 10283 Destroy: true, 10284 }) 10285 10286 plan, diags := ctx.Plan() 10287 if diags.HasErrors() { 10288 t.Fatalf("plan failed: %s", diags.Err()) 10289 } 10290 10291 // We'll marshal and unmarshal the plan here, to ensure that we have 10292 // a clean new context as would be created if we separately ran 10293 // terraform plan -out=tfplan && terraform apply tfplan 10294 ctxOpts, err := contextOptsForPlanViaFile(snap, ctx.State(), plan) 10295 if err != nil { 10296 t.Fatalf("failed to round-trip through planfile: %s", err) 10297 } 10298 10299 ctxOpts.ProviderResolver = providerResolver 10300 ctxOpts.Destroy = true 10301 ctx, diags = NewContext(ctxOpts) 10302 if diags.HasErrors() { 10303 t.Fatalf("err: %s", diags.Err()) 10304 } 10305 10306 // Applying the plan should now succeed 10307 _, diags = ctx.Apply() 10308 if diags.HasErrors() { 10309 t.Fatalf("apply failed: %s", diags.Err()) 10310 } 10311 } 10312 10313 func TestContext2Apply_scaleInMultivarRef(t *testing.T) { 10314 m := testModule(t, "apply-resource-scale-in") 10315 10316 p := testProvider("aws") 10317 p.ApplyFn = testApplyFn 10318 p.DiffFn = testDiffFn 10319 10320 providerResolver := providers.ResolverFixed( 10321 map[string]providers.Factory{ 10322 "aws": testProviderFuncFixed(p), 10323 }, 10324 ) 10325 10326 s := MustShimLegacyState(&State{ 10327 Modules: []*ModuleState{ 10328 { 10329 Path: rootModulePath, 10330 Resources: map[string]*ResourceState{ 10331 "aws_instance.one": { 10332 Type: "aws_instance", 10333 Primary: &InstanceState{ 10334 ID: "foo", 10335 }, 10336 Provider: "provider.aws", 10337 }, 10338 "aws_instance.two": { 10339 Type: "aws_instance", 10340 Primary: &InstanceState{ 10341 ID: "foo", 10342 Attributes: map[string]string{ 10343 "value": "foo", 10344 }, 10345 }, 10346 Provider: "provider.aws", 10347 }, 10348 }, 10349 }, 10350 }, 10351 }) 10352 10353 ctx := testContext2(t, &ContextOpts{ 10354 Config: m, 10355 ProviderResolver: providerResolver, 10356 State: s, 10357 Variables: InputValues{ 10358 "instance_count": { 10359 Value: cty.NumberIntVal(0), 10360 SourceType: ValueFromCaller, 10361 }, 10362 }, 10363 }) 10364 10365 _, diags := ctx.Plan() 10366 assertNoErrors(t, diags) 10367 10368 // Applying the plan should now succeed 10369 _, diags = ctx.Apply() 10370 assertNoErrors(t, diags) 10371 } 10372 10373 func TestContext2Apply_inconsistentWithPlan(t *testing.T) { 10374 m := testModule(t, "apply-inconsistent-with-plan") 10375 p := testProvider("test") 10376 p.GetSchemaReturn = &ProviderSchema{ 10377 ResourceTypes: map[string]*configschema.Block{ 10378 "test": { 10379 Attributes: map[string]*configschema.Attribute{ 10380 "id": {Type: cty.String, Computed: true}, 10381 }, 10382 }, 10383 }, 10384 } 10385 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 10386 return providers.PlanResourceChangeResponse{ 10387 PlannedState: cty.ObjectVal(map[string]cty.Value{ 10388 "id": cty.StringVal("before"), 10389 }), 10390 } 10391 } 10392 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 10393 return providers.ApplyResourceChangeResponse{ 10394 NewState: cty.ObjectVal(map[string]cty.Value{ 10395 // This is intentionally incorrect: because id was fixed at "before" 10396 // during plan, it must not change during apply. 10397 "id": cty.StringVal("after"), 10398 }), 10399 } 10400 } 10401 ctx := testContext2(t, &ContextOpts{ 10402 Config: m, 10403 ProviderResolver: providers.ResolverFixed( 10404 map[string]providers.Factory{ 10405 "test": testProviderFuncFixed(p), 10406 }, 10407 ), 10408 }) 10409 10410 if _, diags := ctx.Plan(); diags.HasErrors() { 10411 t.Fatalf("plan errors: %s", diags.Err()) 10412 } 10413 10414 _, diags := ctx.Apply() 10415 if !diags.HasErrors() { 10416 t.Fatalf("apply succeeded; want error") 10417 } 10418 if got, want := diags.Err().Error(), "Provider produced inconsistent result after apply"; !strings.Contains(got, want) { 10419 t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want) 10420 } 10421 } 10422 10423 // Issue 19908 was about retaining an existing object in the state when an 10424 // update to it fails and the provider does not return a partially-updated 10425 // value for it. Previously we were incorrectly removing it from the state 10426 // in that case, but instead it should be retained so the update can be 10427 // retried. 10428 func TestContext2Apply_issue19908(t *testing.T) { 10429 m := testModule(t, "apply-issue19908") 10430 p := testProvider("test") 10431 p.GetSchemaReturn = &ProviderSchema{ 10432 ResourceTypes: map[string]*configschema.Block{ 10433 "test": { 10434 Attributes: map[string]*configschema.Attribute{ 10435 "baz": {Type: cty.String, Required: true}, 10436 }, 10437 }, 10438 }, 10439 } 10440 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 10441 return providers.PlanResourceChangeResponse{ 10442 PlannedState: req.ProposedNewState, 10443 } 10444 } 10445 p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { 10446 var diags tfdiags.Diagnostics 10447 diags = diags.Append(fmt.Errorf("update failed")) 10448 return providers.ApplyResourceChangeResponse{ 10449 Diagnostics: diags, 10450 } 10451 } 10452 ctx := testContext2(t, &ContextOpts{ 10453 Config: m, 10454 State: states.BuildState(func(s *states.SyncState) { 10455 s.SetResourceInstanceCurrent( 10456 addrs.Resource{ 10457 Mode: addrs.ManagedResourceMode, 10458 Type: "test", 10459 Name: "foo", 10460 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 10461 &states.ResourceInstanceObjectSrc{ 10462 AttrsJSON: []byte(`{"baz":"old"}`), 10463 Status: states.ObjectReady, 10464 }, 10465 addrs.ProviderConfig{ 10466 Type: "test", 10467 }.Absolute(addrs.RootModuleInstance), 10468 ) 10469 }), 10470 ProviderResolver: providers.ResolverFixed( 10471 map[string]providers.Factory{ 10472 "test": testProviderFuncFixed(p), 10473 }, 10474 ), 10475 }) 10476 10477 if _, diags := ctx.Plan(); diags.HasErrors() { 10478 t.Fatalf("plan errors: %s", diags.Err()) 10479 } 10480 10481 state, diags := ctx.Apply() 10482 if !diags.HasErrors() { 10483 t.Fatalf("apply succeeded; want error") 10484 } 10485 if got, want := diags.Err().Error(), "update failed"; !strings.Contains(got, want) { 10486 t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want) 10487 } 10488 10489 mod := state.RootModule() 10490 rs := mod.Resources["test.foo"] 10491 if rs == nil { 10492 t.Fatalf("test.foo not in state after apply, but should be") 10493 } 10494 is := rs.Instances[addrs.NoKey] 10495 if is == nil { 10496 t.Fatalf("test.foo not in state after apply, but should be") 10497 } 10498 obj := is.Current 10499 if obj == nil { 10500 t.Fatalf("test.foo has no current object in state after apply, but should do") 10501 } 10502 10503 if got, want := obj.Status, states.ObjectReady; got != want { 10504 t.Errorf("test.foo has wrong status %s after apply; want %s", got, want) 10505 } 10506 if got, want := obj.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) { 10507 t.Errorf("test.foo attributes JSON doesn't contain %s after apply\ngot: %s", want, got) 10508 } 10509 } 10510 10511 func TestContext2Apply_invalidIndexRef(t *testing.T) { 10512 p := testProvider("test") 10513 p.GetSchemaReturn = &ProviderSchema{ 10514 ResourceTypes: map[string]*configschema.Block{ 10515 "test_instance": { 10516 Attributes: map[string]*configschema.Attribute{ 10517 "value": {Type: cty.String, Optional: true, Computed: true}, 10518 }, 10519 }, 10520 }, 10521 } 10522 p.DiffFn = testDiffFn 10523 10524 m := testModule(t, "apply-invalid-index") 10525 c := testContext2(t, &ContextOpts{ 10526 Config: m, 10527 ProviderResolver: providers.ResolverFixed( 10528 map[string]providers.Factory{ 10529 "test": testProviderFuncFixed(p), 10530 }, 10531 ), 10532 }) 10533 10534 diags := c.Validate() 10535 if diags.HasErrors() { 10536 t.Fatalf("unexpected validation failure: %s", diags.Err()) 10537 } 10538 10539 wantErr := `The given key does not identify an element in this collection value` 10540 _, diags = c.Plan() 10541 10542 if !diags.HasErrors() { 10543 t.Fatalf("plan succeeded; want error") 10544 } 10545 gotErr := diags.Err().Error() 10546 10547 if !strings.Contains(gotErr, wantErr) { 10548 t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErr, wantErr) 10549 } 10550 } 10551 10552 func TestContext2Apply_moduleReplaceCycle(t *testing.T) { 10553 for _, mode := range []string{"normal", "cbd"} { 10554 var m *configs.Config 10555 10556 switch mode { 10557 case "normal": 10558 m = testModule(t, "apply-module-replace-cycle") 10559 case "cbd": 10560 m = testModule(t, "apply-module-replace-cycle-cbd") 10561 } 10562 10563 p := testProvider("aws") 10564 p.DiffFn = testDiffFn 10565 p.ApplyFn = testApplyFn 10566 10567 instanceSchema := &configschema.Block{ 10568 Attributes: map[string]*configschema.Attribute{ 10569 "id": {Type: cty.String, Computed: true}, 10570 "require_new": {Type: cty.String, Optional: true}, 10571 }, 10572 } 10573 10574 p.GetSchemaReturn = &ProviderSchema{ 10575 ResourceTypes: map[string]*configschema.Block{ 10576 "aws_instance": instanceSchema, 10577 }, 10578 } 10579 10580 state := states.NewState() 10581 modA := state.EnsureModule(addrs.RootModuleInstance.Child("a", addrs.NoKey)) 10582 modA.SetResourceInstanceCurrent( 10583 addrs.Resource{ 10584 Mode: addrs.ManagedResourceMode, 10585 Type: "aws_instance", 10586 Name: "a", 10587 }.Instance(addrs.NoKey), 10588 &states.ResourceInstanceObjectSrc{ 10589 Status: states.ObjectReady, 10590 AttrsJSON: []byte(`{"id":"a","require_new":"old"}`), 10591 }, 10592 addrs.ProviderConfig{ 10593 Type: "aws", 10594 }.Absolute(addrs.RootModuleInstance), 10595 ) 10596 10597 modB := state.EnsureModule(addrs.RootModuleInstance.Child("b", addrs.NoKey)) 10598 modB.SetResourceInstanceCurrent( 10599 addrs.Resource{ 10600 Mode: addrs.ManagedResourceMode, 10601 Type: "aws_instance", 10602 Name: "b", 10603 }.Instance(addrs.IntKey(0)), 10604 &states.ResourceInstanceObjectSrc{ 10605 Status: states.ObjectReady, 10606 AttrsJSON: []byte(`{"id":"b","require_new":"old"}`), 10607 }, 10608 addrs.ProviderConfig{ 10609 Type: "aws", 10610 }.Absolute(addrs.RootModuleInstance), 10611 ) 10612 10613 aBefore, _ := plans.NewDynamicValue( 10614 cty.ObjectVal(map[string]cty.Value{ 10615 "id": cty.StringVal("a"), 10616 "require_new": cty.StringVal("old"), 10617 }), instanceSchema.ImpliedType()) 10618 aAfter, _ := plans.NewDynamicValue( 10619 cty.ObjectVal(map[string]cty.Value{ 10620 "id": cty.UnknownVal(cty.String), 10621 "require_new": cty.StringVal("new"), 10622 }), instanceSchema.ImpliedType()) 10623 bBefore, _ := plans.NewDynamicValue( 10624 cty.ObjectVal(map[string]cty.Value{ 10625 "id": cty.StringVal("b"), 10626 "require_new": cty.StringVal("old"), 10627 }), instanceSchema.ImpliedType()) 10628 bAfter, _ := plans.NewDynamicValue( 10629 cty.ObjectVal(map[string]cty.Value{ 10630 "id": cty.UnknownVal(cty.String), 10631 "require_new": cty.UnknownVal(cty.String), 10632 }), instanceSchema.ImpliedType()) 10633 10634 var aAction plans.Action 10635 switch mode { 10636 case "normal": 10637 aAction = plans.DeleteThenCreate 10638 case "cbd": 10639 aAction = plans.CreateThenDelete 10640 } 10641 10642 changes := &plans.Changes{ 10643 Resources: []*plans.ResourceInstanceChangeSrc{ 10644 { 10645 Addr: addrs.Resource{ 10646 Mode: addrs.ManagedResourceMode, 10647 Type: "aws_instance", 10648 Name: "a", 10649 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("a", addrs.NoKey)), 10650 ProviderAddr: addrs.ProviderConfig{ 10651 Type: "aws", 10652 }.Absolute(addrs.RootModuleInstance), 10653 ChangeSrc: plans.ChangeSrc{ 10654 Action: aAction, 10655 Before: aBefore, 10656 After: aAfter, 10657 }, 10658 }, 10659 { 10660 Addr: addrs.Resource{ 10661 Mode: addrs.ManagedResourceMode, 10662 Type: "aws_instance", 10663 Name: "b", 10664 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("b", addrs.NoKey)), 10665 ProviderAddr: addrs.ProviderConfig{ 10666 Type: "aws", 10667 }.Absolute(addrs.RootModuleInstance), 10668 ChangeSrc: plans.ChangeSrc{ 10669 Action: plans.DeleteThenCreate, 10670 Before: bBefore, 10671 After: bAfter, 10672 }, 10673 }, 10674 }, 10675 } 10676 10677 ctx := testContext2(t, &ContextOpts{ 10678 Config: m, 10679 ProviderResolver: providers.ResolverFixed( 10680 map[string]providers.Factory{ 10681 "aws": testProviderFuncFixed(p), 10682 }, 10683 ), 10684 State: state, 10685 Changes: changes, 10686 }) 10687 10688 t.Run(mode, func(t *testing.T) { 10689 _, diags := ctx.Apply() 10690 if diags.HasErrors() { 10691 t.Fatal(diags.Err()) 10692 } 10693 }) 10694 } 10695 } 10696 10697 func TestContext2Apply_destroyDataCycle(t *testing.T) { 10698 m, snap := testModuleWithSnapshot(t, "apply-destroy-data-cycle") 10699 p := testProvider("null") 10700 p.ApplyFn = testApplyFn 10701 p.DiffFn = testDiffFn 10702 10703 state := states.NewState() 10704 root := state.EnsureModule(addrs.RootModuleInstance) 10705 root.SetResourceInstanceCurrent( 10706 addrs.Resource{ 10707 Mode: addrs.ManagedResourceMode, 10708 Type: "null_resource", 10709 Name: "a", 10710 }.Instance(addrs.IntKey(0)), 10711 &states.ResourceInstanceObjectSrc{ 10712 Status: states.ObjectReady, 10713 AttrsJSON: []byte(`{"id":"a"}`), 10714 }, 10715 addrs.ProviderConfig{ 10716 Type: "null", 10717 }.Absolute(addrs.RootModuleInstance), 10718 ) 10719 root.SetResourceInstanceCurrent( 10720 addrs.Resource{ 10721 Mode: addrs.DataResourceMode, 10722 Type: "null_data_source", 10723 Name: "d", 10724 }.Instance(addrs.NoKey), 10725 &states.ResourceInstanceObjectSrc{ 10726 Status: states.ObjectReady, 10727 AttrsJSON: []byte(`{"id":"data"}`), 10728 }, 10729 addrs.ProviderConfig{ 10730 Type: "null", 10731 }.Absolute(addrs.RootModuleInstance), 10732 ) 10733 10734 providerResolver := providers.ResolverFixed( 10735 map[string]providers.Factory{ 10736 "null": testProviderFuncFixed(p), 10737 }, 10738 ) 10739 10740 hook := &testHook{} 10741 ctx := testContext2(t, &ContextOpts{ 10742 Config: m, 10743 ProviderResolver: providerResolver, 10744 State: state, 10745 Destroy: true, 10746 Hooks: []Hook{hook}, 10747 }) 10748 10749 plan, diags := ctx.Plan() 10750 diags.HasErrors() 10751 if diags.HasErrors() { 10752 t.Fatalf("diags: %s", diags.Err()) 10753 } 10754 10755 // We'll marshal and unmarshal the plan here, to ensure that we have 10756 // a clean new context as would be created if we separately ran 10757 // terraform plan -out=tfplan && terraform apply tfplan 10758 ctxOpts, err := contextOptsForPlanViaFile(snap, state, plan) 10759 if err != nil { 10760 t.Fatal(err) 10761 } 10762 ctxOpts.ProviderResolver = providerResolver 10763 ctx, diags = NewContext(ctxOpts) 10764 if diags.HasErrors() { 10765 t.Fatalf("failed to create context for plan: %s", diags.Err()) 10766 } 10767 10768 _, diags = ctx.Apply() 10769 if diags.HasErrors() { 10770 t.Fatalf("diags: %s", diags.Err()) 10771 } 10772 }