github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/context_refresh_test.go (about) 1 package terraform 2 3 import ( 4 "reflect" 5 "regexp" 6 "sort" 7 "strings" 8 "sync" 9 "testing" 10 11 "github.com/google/go-cmp/cmp" 12 "github.com/zclconf/go-cty/cty" 13 14 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 15 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 16 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" 17 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 18 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 19 ) 20 21 func TestContext2Refresh(t *testing.T) { 22 p := testProvider("aws") 23 m := testModule(t, "refresh-basic") 24 25 startingState := MustShimLegacyState(&State{ 26 Modules: []*ModuleState{ 27 { 28 Path: rootModulePath, 29 Resources: map[string]*ResourceState{ 30 "aws_instance.web": { 31 Type: "aws_instance", 32 Primary: &InstanceState{ 33 ID: "foo", 34 Attributes: map[string]string{ 35 "id": "foo", 36 "foo": "bar", 37 }, 38 }, 39 }, 40 }, 41 }, 42 }, 43 }) 44 45 ctx := testContext2(t, &ContextOpts{ 46 Config: m, 47 ProviderResolver: providers.ResolverFixed( 48 map[string]providers.Factory{ 49 "aws": testProviderFuncFixed(p), 50 }, 51 ), 52 State: startingState, 53 }) 54 55 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 56 ty := schema.ImpliedType() 57 readState, err := hcl2shim.HCL2ValueFromFlatmap(map[string]string{"id": "foo", "foo": "baz"}, ty) 58 if err != nil { 59 t.Fatal(err) 60 } 61 62 p.ReadResourceFn = nil 63 p.ReadResourceResponse = providers.ReadResourceResponse{ 64 NewState: readState, 65 } 66 67 s, diags := ctx.Refresh() 68 if diags.HasErrors() { 69 t.Fatal(diags.Err()) 70 } 71 72 if !p.ReadResourceCalled { 73 t.Fatal("ReadResource should be called") 74 } 75 76 mod := s.RootModule() 77 fromState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(ty) 78 if err != nil { 79 t.Fatal(err) 80 } 81 82 newState, err := schema.CoerceValue(fromState.Value) 83 if err != nil { 84 t.Fatal(err) 85 } 86 87 if !cmp.Equal(readState, newState, valueComparer) { 88 t.Fatal(cmp.Diff(readState, newState, valueComparer, equateEmpty)) 89 } 90 } 91 92 func TestContext2Refresh_dynamicAttr(t *testing.T) { 93 m := testModule(t, "refresh-dynamic") 94 95 startingState := states.BuildState(func(ss *states.SyncState) { 96 ss.SetResourceInstanceCurrent( 97 addrs.Resource{ 98 Mode: addrs.ManagedResourceMode, 99 Type: "test_instance", 100 Name: "foo", 101 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 102 &states.ResourceInstanceObjectSrc{ 103 Status: states.ObjectReady, 104 AttrsJSON: []byte(`{"dynamic":{"type":"string","value":"hello"}}`), 105 }, 106 addrs.ProviderConfig{ 107 Type: "test", 108 }.Absolute(addrs.RootModuleInstance), 109 ) 110 }) 111 112 readStateVal := cty.ObjectVal(map[string]cty.Value{ 113 "dynamic": cty.EmptyTupleVal, 114 }) 115 116 p := testProvider("test") 117 p.GetSchemaReturn = &ProviderSchema{ 118 ResourceTypes: map[string]*configschema.Block{ 119 "test_instance": { 120 Attributes: map[string]*configschema.Attribute{ 121 "dynamic": {Type: cty.DynamicPseudoType, Optional: true}, 122 }, 123 }, 124 }, 125 } 126 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 127 return providers.ReadResourceResponse{ 128 NewState: readStateVal, 129 } 130 } 131 132 ctx := testContext2(t, &ContextOpts{ 133 Config: m, 134 ProviderResolver: providers.ResolverFixed( 135 map[string]providers.Factory{ 136 "test": testProviderFuncFixed(p), 137 }, 138 ), 139 State: startingState, 140 }) 141 142 schema := p.GetSchemaReturn.ResourceTypes["test_instance"] 143 ty := schema.ImpliedType() 144 145 s, diags := ctx.Refresh() 146 if diags.HasErrors() { 147 t.Fatal(diags.Err()) 148 } 149 150 if !p.ReadResourceCalled { 151 t.Fatal("ReadResource should be called") 152 } 153 154 mod := s.RootModule() 155 newState, err := mod.Resources["test_instance.foo"].Instances[addrs.NoKey].Current.Decode(ty) 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 if !cmp.Equal(readStateVal, newState.Value, valueComparer) { 161 t.Error(cmp.Diff(newState.Value, readStateVal, valueComparer, equateEmpty)) 162 } 163 } 164 165 func TestContext2Refresh_dataComputedModuleVar(t *testing.T) { 166 p := testProvider("aws") 167 m := testModule(t, "refresh-data-module-var") 168 ctx := testContext2(t, &ContextOpts{ 169 Config: m, 170 ProviderResolver: providers.ResolverFixed( 171 map[string]providers.Factory{ 172 "aws": testProviderFuncFixed(p), 173 }, 174 ), 175 }) 176 177 p.ReadResourceFn = nil 178 p.ReadResourceResponse = providers.ReadResourceResponse{ 179 NewState: cty.ObjectVal(map[string]cty.Value{ 180 "id": cty.StringVal("foo"), 181 }), 182 } 183 184 p.GetSchemaReturn = &ProviderSchema{ 185 Provider: &configschema.Block{}, 186 ResourceTypes: map[string]*configschema.Block{ 187 "aws_instance": { 188 Attributes: map[string]*configschema.Attribute{ 189 "foo": { 190 Type: cty.String, 191 Optional: true, 192 }, 193 "id": { 194 Type: cty.String, 195 Computed: true, 196 }, 197 }, 198 }, 199 }, 200 DataSources: map[string]*configschema.Block{ 201 "aws_data_source": { 202 Attributes: map[string]*configschema.Attribute{ 203 "id": { 204 Type: cty.String, 205 Optional: true, 206 }, 207 }, 208 }, 209 }, 210 } 211 212 s, diags := ctx.Refresh() 213 if diags.HasErrors() { 214 t.Fatalf("refresh errors: %s", diags.Err()) 215 } 216 217 checkStateString(t, s, ` 218 <no state> 219 `) 220 } 221 222 func TestContext2Refresh_targeted(t *testing.T) { 223 p := testProvider("aws") 224 p.GetSchemaReturn = &ProviderSchema{ 225 Provider: &configschema.Block{}, 226 ResourceTypes: map[string]*configschema.Block{ 227 "aws_elb": { 228 Attributes: map[string]*configschema.Attribute{ 229 "instances": { 230 Type: cty.Set(cty.String), 231 Optional: true, 232 }, 233 }, 234 }, 235 "aws_instance": { 236 Attributes: map[string]*configschema.Attribute{ 237 "id": { 238 Type: cty.String, 239 Computed: true, 240 }, 241 "vpc_id": { 242 Type: cty.String, 243 Optional: true, 244 }, 245 }, 246 }, 247 "aws_vpc": { 248 Attributes: map[string]*configschema.Attribute{ 249 "id": { 250 Type: cty.String, 251 Computed: true, 252 }, 253 }, 254 }, 255 }, 256 } 257 258 m := testModule(t, "refresh-targeted") 259 ctx := testContext2(t, &ContextOpts{ 260 Config: m, 261 ProviderResolver: providers.ResolverFixed( 262 map[string]providers.Factory{ 263 "aws": testProviderFuncFixed(p), 264 }, 265 ), 266 State: MustShimLegacyState(&State{ 267 Modules: []*ModuleState{ 268 { 269 Path: rootModulePath, 270 Resources: map[string]*ResourceState{ 271 "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), 272 "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), 273 "aws_instance.me": resourceState("aws_instance", "i-abc123"), 274 "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), 275 }, 276 }, 277 }, 278 }), 279 Targets: []addrs.Targetable{ 280 addrs.RootModuleInstance.Resource( 281 addrs.ManagedResourceMode, "aws_instance", "me", 282 ), 283 }, 284 }) 285 286 refreshedResources := make([]string, 0, 2) 287 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 288 refreshedResources = append(refreshedResources, req.PriorState.GetAttr("id").AsString()) 289 return providers.ReadResourceResponse{ 290 NewState: req.PriorState, 291 } 292 } 293 294 _, diags := ctx.Refresh() 295 if diags.HasErrors() { 296 t.Fatalf("refresh errors: %s", diags.Err()) 297 } 298 299 expected := []string{"vpc-abc123", "i-abc123"} 300 if !reflect.DeepEqual(refreshedResources, expected) { 301 t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) 302 } 303 } 304 305 func TestContext2Refresh_targetedCount(t *testing.T) { 306 p := testProvider("aws") 307 p.GetSchemaReturn = &ProviderSchema{ 308 Provider: &configschema.Block{}, 309 ResourceTypes: map[string]*configschema.Block{ 310 "aws_elb": { 311 Attributes: map[string]*configschema.Attribute{ 312 "instances": { 313 Type: cty.Set(cty.String), 314 Optional: true, 315 }, 316 }, 317 }, 318 "aws_instance": { 319 Attributes: map[string]*configschema.Attribute{ 320 "id": { 321 Type: cty.String, 322 Computed: true, 323 }, 324 "vpc_id": { 325 Type: cty.String, 326 Optional: true, 327 }, 328 }, 329 }, 330 "aws_vpc": { 331 Attributes: map[string]*configschema.Attribute{ 332 "id": { 333 Type: cty.String, 334 Computed: true, 335 }, 336 }, 337 }, 338 }, 339 } 340 341 m := testModule(t, "refresh-targeted-count") 342 ctx := testContext2(t, &ContextOpts{ 343 Config: m, 344 ProviderResolver: providers.ResolverFixed( 345 map[string]providers.Factory{ 346 "aws": testProviderFuncFixed(p), 347 }, 348 ), 349 State: MustShimLegacyState(&State{ 350 Modules: []*ModuleState{ 351 { 352 Path: rootModulePath, 353 Resources: map[string]*ResourceState{ 354 "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), 355 "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), 356 "aws_instance.me.0": resourceState("aws_instance", "i-abc123"), 357 "aws_instance.me.1": resourceState("aws_instance", "i-cde567"), 358 "aws_instance.me.2": resourceState("aws_instance", "i-cde789"), 359 "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), 360 }, 361 }, 362 }, 363 }), 364 Targets: []addrs.Targetable{ 365 addrs.RootModuleInstance.Resource( 366 addrs.ManagedResourceMode, "aws_instance", "me", 367 ), 368 }, 369 }) 370 371 refreshedResources := make([]string, 0, 2) 372 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 373 refreshedResources = append(refreshedResources, req.PriorState.GetAttr("id").AsString()) 374 return providers.ReadResourceResponse{ 375 NewState: req.PriorState, 376 } 377 } 378 379 _, diags := ctx.Refresh() 380 if diags.HasErrors() { 381 t.Fatalf("refresh errors: %s", diags.Err()) 382 } 383 384 // Target didn't specify index, so we should get all our instances 385 expected := []string{ 386 "vpc-abc123", 387 "i-abc123", 388 "i-cde567", 389 "i-cde789", 390 } 391 sort.Strings(expected) 392 sort.Strings(refreshedResources) 393 if !reflect.DeepEqual(refreshedResources, expected) { 394 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", refreshedResources, expected) 395 } 396 } 397 398 func TestContext2Refresh_targetedCountIndex(t *testing.T) { 399 p := testProvider("aws") 400 p.GetSchemaReturn = &ProviderSchema{ 401 Provider: &configschema.Block{}, 402 ResourceTypes: map[string]*configschema.Block{ 403 "aws_elb": { 404 Attributes: map[string]*configschema.Attribute{ 405 "instances": { 406 Type: cty.Set(cty.String), 407 Optional: true, 408 }, 409 }, 410 }, 411 "aws_instance": { 412 Attributes: map[string]*configschema.Attribute{ 413 "id": { 414 Type: cty.String, 415 Computed: true, 416 }, 417 "vpc_id": { 418 Type: cty.String, 419 Optional: true, 420 }, 421 }, 422 }, 423 "aws_vpc": { 424 Attributes: map[string]*configschema.Attribute{ 425 "id": { 426 Type: cty.String, 427 Computed: true, 428 }, 429 }, 430 }, 431 }, 432 } 433 434 m := testModule(t, "refresh-targeted-count") 435 ctx := testContext2(t, &ContextOpts{ 436 Config: m, 437 ProviderResolver: providers.ResolverFixed( 438 map[string]providers.Factory{ 439 "aws": testProviderFuncFixed(p), 440 }, 441 ), 442 State: MustShimLegacyState(&State{ 443 Modules: []*ModuleState{ 444 { 445 Path: rootModulePath, 446 Resources: map[string]*ResourceState{ 447 "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), 448 "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), 449 "aws_instance.me.0": resourceState("aws_instance", "i-abc123"), 450 "aws_instance.me.1": resourceState("aws_instance", "i-cde567"), 451 "aws_instance.me.2": resourceState("aws_instance", "i-cde789"), 452 "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), 453 }, 454 }, 455 }, 456 }), 457 Targets: []addrs.Targetable{ 458 addrs.RootModuleInstance.ResourceInstance( 459 addrs.ManagedResourceMode, "aws_instance", "me", addrs.IntKey(0), 460 ), 461 }, 462 }) 463 464 refreshedResources := make([]string, 0, 2) 465 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 466 refreshedResources = append(refreshedResources, req.PriorState.GetAttr("id").AsString()) 467 return providers.ReadResourceResponse{ 468 NewState: req.PriorState, 469 } 470 } 471 472 _, diags := ctx.Refresh() 473 if diags.HasErrors() { 474 t.Fatalf("refresh errors: %s", diags.Err()) 475 } 476 477 expected := []string{"vpc-abc123", "i-abc123"} 478 if !reflect.DeepEqual(refreshedResources, expected) { 479 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", refreshedResources, expected) 480 } 481 } 482 483 func TestContext2Refresh_moduleComputedVar(t *testing.T) { 484 p := testProvider("aws") 485 p.GetSchemaReturn = &ProviderSchema{ 486 Provider: &configschema.Block{}, 487 ResourceTypes: map[string]*configschema.Block{ 488 "aws_instance": { 489 Attributes: map[string]*configschema.Attribute{ 490 "id": { 491 Type: cty.String, 492 Computed: true, 493 }, 494 "value": { 495 Type: cty.String, 496 Optional: true, 497 }, 498 }, 499 }, 500 }, 501 } 502 503 m := testModule(t, "refresh-module-computed-var") 504 ctx := testContext2(t, &ContextOpts{ 505 Config: m, 506 ProviderResolver: providers.ResolverFixed( 507 map[string]providers.Factory{ 508 "aws": testProviderFuncFixed(p), 509 }, 510 ), 511 }) 512 513 // This was failing (see GH-2188) at some point, so this test just 514 // verifies that the failure goes away. 515 if _, diags := ctx.Refresh(); diags.HasErrors() { 516 t.Fatalf("refresh errs: %s", diags.Err()) 517 } 518 } 519 520 func TestContext2Refresh_delete(t *testing.T) { 521 p := testProvider("aws") 522 m := testModule(t, "refresh-basic") 523 ctx := testContext2(t, &ContextOpts{ 524 Config: m, 525 ProviderResolver: providers.ResolverFixed( 526 map[string]providers.Factory{ 527 "aws": testProviderFuncFixed(p), 528 }, 529 ), 530 State: MustShimLegacyState(&State{ 531 Modules: []*ModuleState{ 532 { 533 Path: rootModulePath, 534 Resources: map[string]*ResourceState{ 535 "aws_instance.web": { 536 Type: "aws_instance", 537 Primary: &InstanceState{ 538 ID: "foo", 539 }, 540 }, 541 }, 542 }, 543 }, 544 }), 545 }) 546 547 p.ReadResourceFn = nil 548 p.ReadResourceResponse = providers.ReadResourceResponse{ 549 NewState: cty.NullVal(p.GetSchemaReturn.ResourceTypes["aws_instance"].ImpliedType()), 550 } 551 552 s, diags := ctx.Refresh() 553 if diags.HasErrors() { 554 t.Fatalf("refresh errors: %s", diags.Err()) 555 } 556 557 mod := s.RootModule() 558 if len(mod.Resources) > 0 { 559 t.Fatal("resources should be empty") 560 } 561 } 562 563 func TestContext2Refresh_ignoreUncreated(t *testing.T) { 564 p := testProvider("aws") 565 m := testModule(t, "refresh-basic") 566 ctx := testContext2(t, &ContextOpts{ 567 Config: m, 568 ProviderResolver: providers.ResolverFixed( 569 map[string]providers.Factory{ 570 "aws": testProviderFuncFixed(p), 571 }, 572 ), 573 State: nil, 574 }) 575 576 p.ReadResourceFn = nil 577 p.ReadResourceResponse = providers.ReadResourceResponse{ 578 NewState: cty.ObjectVal(map[string]cty.Value{ 579 "id": cty.StringVal("foo"), 580 }), 581 } 582 583 _, diags := ctx.Refresh() 584 if diags.HasErrors() { 585 t.Fatalf("refresh errors: %s", diags.Err()) 586 } 587 if p.ReadResourceCalled { 588 t.Fatal("refresh should not be called") 589 } 590 } 591 592 func TestContext2Refresh_hook(t *testing.T) { 593 h := new(MockHook) 594 p := testProvider("aws") 595 m := testModule(t, "refresh-basic") 596 ctx := testContext2(t, &ContextOpts{ 597 Config: m, 598 Hooks: []Hook{h}, 599 ProviderResolver: providers.ResolverFixed( 600 map[string]providers.Factory{ 601 "aws": testProviderFuncFixed(p), 602 }, 603 ), 604 State: MustShimLegacyState(&State{ 605 Modules: []*ModuleState{ 606 { 607 Path: rootModulePath, 608 Resources: map[string]*ResourceState{ 609 "aws_instance.web": { 610 Type: "aws_instance", 611 Primary: &InstanceState{ 612 ID: "foo", 613 }, 614 }, 615 }, 616 }, 617 }, 618 }), 619 }) 620 621 if _, diags := ctx.Refresh(); diags.HasErrors() { 622 t.Fatalf("refresh errs: %s", diags.Err()) 623 } 624 if !h.PreRefreshCalled { 625 t.Fatal("should be called") 626 } 627 if !h.PostRefreshCalled { 628 t.Fatal("should be called") 629 } 630 } 631 632 func TestContext2Refresh_modules(t *testing.T) { 633 p := testProvider("aws") 634 m := testModule(t, "refresh-modules") 635 state := MustShimLegacyState(&State{ 636 Modules: []*ModuleState{ 637 { 638 Path: rootModulePath, 639 Resources: map[string]*ResourceState{ 640 "aws_instance.web": { 641 Type: "aws_instance", 642 Primary: &InstanceState{ 643 ID: "bar", 644 Tainted: true, 645 }, 646 }, 647 }, 648 }, 649 650 { 651 Path: []string{"root", "child"}, 652 Resources: map[string]*ResourceState{ 653 "aws_instance.web": { 654 Type: "aws_instance", 655 Primary: &InstanceState{ 656 ID: "baz", 657 }, 658 }, 659 }, 660 }, 661 }, 662 }) 663 ctx := testContext2(t, &ContextOpts{ 664 Config: m, 665 ProviderResolver: providers.ResolverFixed( 666 map[string]providers.Factory{ 667 "aws": testProviderFuncFixed(p), 668 }, 669 ), 670 State: state, 671 }) 672 673 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 674 if !req.PriorState.GetAttr("id").RawEquals(cty.StringVal("baz")) { 675 return providers.ReadResourceResponse{ 676 NewState: req.PriorState, 677 } 678 } 679 680 new, _ := cty.Transform(req.PriorState, func(path cty.Path, v cty.Value) (cty.Value, error) { 681 if len(path) == 1 && path[0].(cty.GetAttrStep).Name == "id" { 682 return cty.StringVal("new"), nil 683 } 684 return v, nil 685 }) 686 return providers.ReadResourceResponse{ 687 NewState: new, 688 } 689 } 690 691 s, diags := ctx.Refresh() 692 if diags.HasErrors() { 693 t.Fatalf("refresh errors: %s", diags.Err()) 694 } 695 696 actual := strings.TrimSpace(s.String()) 697 expected := strings.TrimSpace(testContextRefreshModuleStr) 698 if actual != expected { 699 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 700 } 701 } 702 703 func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { 704 m := testModule(t, "refresh-module-input-computed-output") 705 p := testProvider("aws") 706 p.DiffFn = testDiffFn 707 p.GetSchemaReturn = &ProviderSchema{ 708 Provider: &configschema.Block{}, 709 ResourceTypes: map[string]*configschema.Block{ 710 "aws_instance": { 711 Attributes: map[string]*configschema.Attribute{ 712 "foo": { 713 Type: cty.String, 714 Optional: true, 715 }, 716 "compute": { 717 Type: cty.String, 718 Optional: true, 719 }, 720 }, 721 }, 722 }, 723 } 724 725 ctx := testContext2(t, &ContextOpts{ 726 Config: m, 727 ProviderResolver: providers.ResolverFixed( 728 map[string]providers.Factory{ 729 "aws": testProviderFuncFixed(p), 730 }, 731 ), 732 }) 733 734 if _, diags := ctx.Refresh(); diags.HasErrors() { 735 t.Fatalf("refresh errs: %s", diags.Err()) 736 } 737 } 738 739 func TestContext2Refresh_moduleVarModule(t *testing.T) { 740 m := testModule(t, "refresh-module-var-module") 741 p := testProvider("aws") 742 p.DiffFn = testDiffFn 743 ctx := testContext2(t, &ContextOpts{ 744 Config: m, 745 ProviderResolver: providers.ResolverFixed( 746 map[string]providers.Factory{ 747 "aws": testProviderFuncFixed(p), 748 }, 749 ), 750 }) 751 752 if _, diags := ctx.Refresh(); diags.HasErrors() { 753 t.Fatalf("refresh errs: %s", diags.Err()) 754 } 755 } 756 757 // GH-70 758 func TestContext2Refresh_noState(t *testing.T) { 759 p := testProvider("aws") 760 m := testModule(t, "refresh-no-state") 761 ctx := testContext2(t, &ContextOpts{ 762 Config: m, 763 ProviderResolver: providers.ResolverFixed( 764 map[string]providers.Factory{ 765 "aws": testProviderFuncFixed(p), 766 }, 767 ), 768 }) 769 770 p.ReadResourceFn = nil 771 p.ReadResourceResponse = providers.ReadResourceResponse{ 772 NewState: cty.ObjectVal(map[string]cty.Value{ 773 "id": cty.StringVal("foo"), 774 }), 775 } 776 777 if _, diags := ctx.Refresh(); diags.HasErrors() { 778 t.Fatalf("refresh errs: %s", diags.Err()) 779 } 780 } 781 782 func TestContext2Refresh_output(t *testing.T) { 783 p := testProvider("aws") 784 p.GetSchemaReturn = &ProviderSchema{ 785 Provider: &configschema.Block{}, 786 ResourceTypes: map[string]*configschema.Block{ 787 "aws_instance": { 788 Attributes: map[string]*configschema.Attribute{ 789 "id": { 790 Type: cty.String, 791 Computed: true, 792 }, 793 "foo": { 794 Type: cty.String, 795 Computed: true, 796 }, 797 }, 798 }, 799 }, 800 } 801 802 m := testModule(t, "refresh-output") 803 ctx := testContext2(t, &ContextOpts{ 804 Config: m, 805 ProviderResolver: providers.ResolverFixed( 806 map[string]providers.Factory{ 807 "aws": testProviderFuncFixed(p), 808 }, 809 ), 810 State: MustShimLegacyState(&State{ 811 Modules: []*ModuleState{ 812 { 813 Path: rootModulePath, 814 Resources: map[string]*ResourceState{ 815 "aws_instance.web": { 816 Type: "aws_instance", 817 Primary: &InstanceState{ 818 ID: "foo", 819 Attributes: map[string]string{ 820 "id": "foo", 821 "foo": "bar", 822 }, 823 }, 824 }, 825 }, 826 827 Outputs: map[string]*OutputState{ 828 "foo": { 829 Type: "string", 830 Sensitive: false, 831 Value: "foo", 832 }, 833 }, 834 }, 835 }, 836 }), 837 }) 838 839 s, diags := ctx.Refresh() 840 if diags.HasErrors() { 841 t.Fatalf("refresh errors: %s", diags.Err()) 842 } 843 844 actual := strings.TrimSpace(s.String()) 845 expected := strings.TrimSpace(testContextRefreshOutputStr) 846 if actual != expected { 847 t.Fatalf("wrong result\n\ngot:\n%q\n\nwant:\n%q", actual, expected) 848 } 849 } 850 851 func TestContext2Refresh_outputPartial(t *testing.T) { 852 p := testProvider("aws") 853 m := testModule(t, "refresh-output-partial") 854 855 // Refresh creates a partial plan for any instances that don't have 856 // remote objects yet, to get stub values for interpolation. Therefore 857 // we need to make DiffFn available to let that complete. 858 p.DiffFn = testDiffFn 859 860 p.GetSchemaReturn = &ProviderSchema{ 861 Provider: &configschema.Block{}, 862 ResourceTypes: map[string]*configschema.Block{ 863 "aws_instance": { 864 Attributes: map[string]*configschema.Attribute{ 865 "foo": { 866 Type: cty.String, 867 Computed: true, 868 }, 869 }, 870 }, 871 }, 872 } 873 874 p.ReadResourceFn = nil 875 p.ReadResourceResponse = providers.ReadResourceResponse{ 876 NewState: cty.NullVal(p.GetSchemaReturn.ResourceTypes["aws_instance"].ImpliedType()), 877 } 878 879 ctx := testContext2(t, &ContextOpts{ 880 Config: m, 881 ProviderResolver: providers.ResolverFixed( 882 map[string]providers.Factory{ 883 "aws": testProviderFuncFixed(p), 884 }, 885 ), 886 State: MustShimLegacyState(&State{ 887 Modules: []*ModuleState{ 888 { 889 Path: rootModulePath, 890 Resources: map[string]*ResourceState{ 891 "aws_instance.foo": { 892 Type: "aws_instance", 893 Primary: &InstanceState{ 894 ID: "foo", 895 }, 896 }, 897 }, 898 Outputs: map[string]*OutputState{}, 899 }, 900 }, 901 }), 902 }) 903 904 s, diags := ctx.Refresh() 905 if diags.HasErrors() { 906 t.Fatalf("refresh errors: %s", diags.Err()) 907 } 908 909 actual := strings.TrimSpace(s.String()) 910 expected := strings.TrimSpace(testContextRefreshOutputPartialStr) 911 if actual != expected { 912 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 913 } 914 } 915 916 func TestContext2Refresh_stateBasic(t *testing.T) { 917 p := testProvider("aws") 918 m := testModule(t, "refresh-basic") 919 state := MustShimLegacyState(&State{ 920 Modules: []*ModuleState{ 921 { 922 Path: rootModulePath, 923 Resources: map[string]*ResourceState{ 924 "aws_instance.web": { 925 Type: "aws_instance", 926 Primary: &InstanceState{ 927 ID: "bar", 928 }, 929 }, 930 }, 931 }, 932 }, 933 }) 934 ctx := testContext2(t, &ContextOpts{ 935 Config: m, 936 ProviderResolver: providers.ResolverFixed( 937 map[string]providers.Factory{ 938 "aws": testProviderFuncFixed(p), 939 }, 940 ), 941 State: state, 942 }) 943 944 schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] 945 ty := schema.ImpliedType() 946 947 readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 948 "id": cty.StringVal("foo"), 949 })) 950 if err != nil { 951 t.Fatal(err) 952 } 953 954 p.ReadResourceFn = nil 955 p.ReadResourceResponse = providers.ReadResourceResponse{ 956 NewState: readStateVal, 957 } 958 959 s, diags := ctx.Refresh() 960 if diags.HasErrors() { 961 t.Fatalf("refresh errors: %s", diags.Err()) 962 } 963 964 if !p.ReadResourceCalled { 965 t.Fatal("read resource should be called") 966 } 967 968 mod := s.RootModule() 969 newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(ty) 970 if err != nil { 971 t.Fatal(err) 972 } 973 974 if !cmp.Equal(readStateVal, newState.Value, valueComparer, equateEmpty) { 975 t.Fatal(cmp.Diff(readStateVal, newState.Value, valueComparer, equateEmpty)) 976 } 977 } 978 979 func TestContext2Refresh_dataCount(t *testing.T) { 980 p := testProvider("test") 981 m := testModule(t, "refresh-data-count") 982 983 // This test is verifying that a data resource count can refer to a 984 // resource attribute that can't be known yet during refresh (because 985 // the resource in question isn't in the state at all). In that case, 986 // we skip the data resource during refresh and process it during the 987 // subsequent plan step instead. 988 // 989 // Normally it's an error for "count" to be computed, but during the 990 // refresh step we allow it because we _expect_ to be working with an 991 // incomplete picture of the world sometimes, particularly when we're 992 // creating object for the first time against an empty state. 993 // 994 // For more information, see: 995 // https://github.com/hashicorp/terraform-plugin-sdk/issues/21047 996 997 p.GetSchemaReturn = &ProviderSchema{ 998 ResourceTypes: map[string]*configschema.Block{ 999 "test": { 1000 Attributes: map[string]*configschema.Attribute{ 1001 "things": {Type: cty.List(cty.String), Optional: true}, 1002 }, 1003 }, 1004 }, 1005 DataSources: map[string]*configschema.Block{ 1006 "test": {}, 1007 }, 1008 } 1009 1010 ctx := testContext2(t, &ContextOpts{ 1011 ProviderResolver: providers.ResolverFixed( 1012 map[string]providers.Factory{ 1013 "test": testProviderFuncFixed(p), 1014 }, 1015 ), 1016 Config: m, 1017 }) 1018 1019 s, diags := ctx.Refresh() 1020 if p.ReadResourceCalled { 1021 // The managed resource doesn't exist in the state yet, so there's 1022 // nothing to refresh. 1023 t.Errorf("ReadResource was called, but should not have been") 1024 } 1025 if p.ReadDataSourceCalled { 1026 // The data resource should've been skipped because its count cannot 1027 // be determined yet. 1028 t.Errorf("ReadDataSource was called, but should not have been") 1029 } 1030 1031 if diags.HasErrors() { 1032 t.Fatalf("refresh errors: %s", diags.Err()) 1033 } 1034 1035 checkStateString(t, s, `<no state>`) 1036 } 1037 1038 func TestContext2Refresh_dataOrphan(t *testing.T) { 1039 p := testProvider("null") 1040 state := MustShimLegacyState(&State{ 1041 Modules: []*ModuleState{ 1042 { 1043 Path: rootModulePath, 1044 Resources: map[string]*ResourceState{ 1045 "data.null_data_source.bar": { 1046 Type: "null_data_source", 1047 Primary: &InstanceState{ 1048 ID: "foo", 1049 }, 1050 Provider: "provider.null", 1051 }, 1052 }, 1053 }, 1054 }, 1055 }) 1056 ctx := testContext2(t, &ContextOpts{ 1057 ProviderResolver: providers.ResolverFixed( 1058 map[string]providers.Factory{ 1059 "null": testProviderFuncFixed(p), 1060 }, 1061 ), 1062 State: state, 1063 }) 1064 1065 s, diags := ctx.Refresh() 1066 if diags.HasErrors() { 1067 t.Fatalf("refresh errors: %s", diags.Err()) 1068 } 1069 1070 checkStateString(t, s, `<no state>`) 1071 } 1072 1073 func TestContext2Refresh_dataState(t *testing.T) { 1074 m := testModule(t, "refresh-data-resource-basic") 1075 1076 state := MustShimLegacyState(&State{ 1077 Modules: []*ModuleState{ 1078 { 1079 Path: rootModulePath, 1080 // Intentionally no resources since data resources are 1081 // supposed to refresh themselves even if they didn't 1082 // already exist. 1083 Resources: map[string]*ResourceState{}, 1084 }, 1085 }, 1086 }) 1087 1088 schema := &configschema.Block{ 1089 Attributes: map[string]*configschema.Attribute{ 1090 "inputs": { 1091 Type: cty.Map(cty.String), 1092 Optional: true, 1093 }, 1094 }, 1095 } 1096 1097 p := testProvider("null") 1098 p.GetSchemaReturn = &ProviderSchema{ 1099 Provider: &configschema.Block{}, 1100 DataSources: map[string]*configschema.Block{ 1101 "null_data_source": schema, 1102 }, 1103 } 1104 1105 ctx := testContext2(t, &ContextOpts{ 1106 Config: m, 1107 ProviderResolver: providers.ResolverFixed( 1108 map[string]providers.Factory{ 1109 "null": testProviderFuncFixed(p), 1110 }, 1111 ), 1112 State: state, 1113 }) 1114 1115 var readStateVal cty.Value 1116 1117 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 1118 m := req.Config.AsValueMap() 1119 m["inputs"] = cty.MapVal(map[string]cty.Value{"test": cty.StringVal("yes")}) 1120 readStateVal = cty.ObjectVal(m) 1121 1122 return providers.ReadDataSourceResponse{ 1123 State: readStateVal, 1124 } 1125 1126 // FIXME: should the "outputs" value here be added to the reutnred state? 1127 // Attributes: map[string]*ResourceAttrDiff{ 1128 // "inputs.#": { 1129 // Old: "0", 1130 // New: "1", 1131 // Type: DiffAttrInput, 1132 // }, 1133 // "inputs.test": { 1134 // Old: "", 1135 // New: "yes", 1136 // Type: DiffAttrInput, 1137 // }, 1138 // "outputs.#": { 1139 // Old: "", 1140 // New: "", 1141 // NewComputed: true, 1142 // Type: DiffAttrOutput, 1143 // }, 1144 // }, 1145 } 1146 1147 s, diags := ctx.Refresh() 1148 if diags.HasErrors() { 1149 t.Fatalf("refresh errors: %s", diags.Err()) 1150 } 1151 1152 if !p.ReadDataSourceCalled { 1153 t.Fatal("ReadDataSource should have been called") 1154 } 1155 1156 // mod := s.RootModule() 1157 // if got := mod.Resources["data.null_data_source.testing"].Primary.ID; got != "-" { 1158 // t.Fatalf("resource id is %q; want %s", got, "-") 1159 // } 1160 // if !reflect.DeepEqual(mod.Resources["data.null_data_source.testing"].Primary, p.ReadDataApplyReturn) { 1161 // t.Fatalf("bad: %#v", mod.Resources) 1162 // } 1163 1164 mod := s.RootModule() 1165 1166 newState, err := mod.Resources["data.null_data_source.testing"].Instances[addrs.NoKey].Current.Decode(schema.ImpliedType()) 1167 if err != nil { 1168 t.Fatal(err) 1169 } 1170 1171 if !cmp.Equal(readStateVal, newState.Value, valueComparer, equateEmpty) { 1172 t.Fatal(cmp.Diff(readStateVal, newState.Value, valueComparer, equateEmpty)) 1173 } 1174 } 1175 1176 func TestContext2Refresh_dataStateRefData(t *testing.T) { 1177 p := testProvider("null") 1178 p.GetSchemaReturn = &ProviderSchema{ 1179 Provider: &configschema.Block{}, 1180 DataSources: map[string]*configschema.Block{ 1181 "null_data_source": { 1182 Attributes: map[string]*configschema.Attribute{ 1183 "id": { 1184 Type: cty.String, 1185 Computed: true, 1186 }, 1187 "foo": { 1188 Type: cty.String, 1189 Optional: true, 1190 }, 1191 "bar": { 1192 Type: cty.String, 1193 Optional: true, 1194 }, 1195 }, 1196 }, 1197 }, 1198 } 1199 1200 m := testModule(t, "refresh-data-ref-data") 1201 state := MustShimLegacyState(&State{ 1202 Modules: []*ModuleState{ 1203 { 1204 Path: rootModulePath, 1205 // Intentionally no resources since data resources are 1206 // supposed to refresh themselves even if they didn't 1207 // already exist. 1208 Resources: map[string]*ResourceState{}, 1209 }, 1210 }, 1211 }) 1212 ctx := testContext2(t, &ContextOpts{ 1213 Config: m, 1214 ProviderResolver: providers.ResolverFixed( 1215 map[string]providers.Factory{ 1216 "null": testProviderFuncFixed(p), 1217 }, 1218 ), 1219 State: state, 1220 }) 1221 1222 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 1223 // add the required id 1224 m := req.Config.AsValueMap() 1225 m["id"] = cty.StringVal("foo") 1226 1227 return providers.ReadDataSourceResponse{ 1228 State: cty.ObjectVal(m), 1229 } 1230 } 1231 1232 s, diags := ctx.Refresh() 1233 if diags.HasErrors() { 1234 t.Fatalf("refresh errors: %s", diags.Err()) 1235 } 1236 1237 actual := strings.TrimSpace(s.String()) 1238 expected := strings.TrimSpace(testTerraformRefreshDataRefDataStr) 1239 if actual != expected { 1240 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1241 } 1242 } 1243 1244 func TestContext2Refresh_tainted(t *testing.T) { 1245 p := testProvider("aws") 1246 m := testModule(t, "refresh-basic") 1247 state := MustShimLegacyState(&State{ 1248 Modules: []*ModuleState{ 1249 { 1250 Path: rootModulePath, 1251 Resources: map[string]*ResourceState{ 1252 "aws_instance.web": { 1253 Type: "aws_instance", 1254 Primary: &InstanceState{ 1255 ID: "bar", 1256 Tainted: true, 1257 }, 1258 }, 1259 }, 1260 }, 1261 }, 1262 }) 1263 ctx := testContext2(t, &ContextOpts{ 1264 Config: m, 1265 ProviderResolver: providers.ResolverFixed( 1266 map[string]providers.Factory{ 1267 "aws": testProviderFuncFixed(p), 1268 }, 1269 ), 1270 State: state, 1271 }) 1272 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 1273 // add the required id 1274 m := req.PriorState.AsValueMap() 1275 m["id"] = cty.StringVal("foo") 1276 1277 return providers.ReadResourceResponse{ 1278 NewState: cty.ObjectVal(m), 1279 } 1280 } 1281 1282 s, diags := ctx.Refresh() 1283 if diags.HasErrors() { 1284 t.Fatalf("refresh errors: %s", diags.Err()) 1285 } 1286 if !p.ReadResourceCalled { 1287 t.Fatal("ReadResource was not called; should have been") 1288 } 1289 1290 actual := strings.TrimSpace(s.String()) 1291 expected := strings.TrimSpace(testContextRefreshTaintedStr) 1292 if actual != expected { 1293 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1294 } 1295 } 1296 1297 // Doing a Refresh (or any operation really, but Refresh usually 1298 // happens first) with a config with an unknown provider should result in 1299 // an error. The key bug this found was that this wasn't happening if 1300 // Providers was _empty_. 1301 func TestContext2Refresh_unknownProvider(t *testing.T) { 1302 m := testModule(t, "refresh-unknown-provider") 1303 p := testProvider("aws") 1304 p.ApplyFn = testApplyFn 1305 p.DiffFn = testDiffFn 1306 1307 _, diags := NewContext(&ContextOpts{ 1308 Config: m, 1309 ProviderResolver: providers.ResolverFixed( 1310 map[string]providers.Factory{}, 1311 ), 1312 State: MustShimLegacyState(&State{ 1313 Modules: []*ModuleState{ 1314 { 1315 Path: rootModulePath, 1316 Resources: map[string]*ResourceState{ 1317 "aws_instance.web": { 1318 Type: "aws_instance", 1319 Primary: &InstanceState{ 1320 ID: "foo", 1321 }, 1322 }, 1323 }, 1324 }, 1325 }, 1326 }), 1327 }) 1328 1329 if !diags.HasErrors() { 1330 t.Fatal("successfully created context; want error") 1331 } 1332 1333 if !regexp.MustCompile(`provider ".+" is not available`).MatchString(diags.Err().Error()) { 1334 t.Fatalf("wrong error: %s", diags.Err()) 1335 } 1336 } 1337 1338 func TestContext2Refresh_vars(t *testing.T) { 1339 p := testProvider("aws") 1340 1341 schema := &configschema.Block{ 1342 Attributes: map[string]*configschema.Attribute{ 1343 "ami": { 1344 Type: cty.String, 1345 Optional: true, 1346 }, 1347 "id": { 1348 Type: cty.String, 1349 Computed: true, 1350 }, 1351 }, 1352 } 1353 1354 p.GetSchemaReturn = &ProviderSchema{ 1355 Provider: &configschema.Block{}, 1356 ResourceTypes: map[string]*configschema.Block{"aws_instance": schema}, 1357 } 1358 1359 m := testModule(t, "refresh-vars") 1360 ctx := testContext2(t, &ContextOpts{ 1361 Config: m, 1362 ProviderResolver: providers.ResolverFixed( 1363 map[string]providers.Factory{ 1364 "aws": testProviderFuncFixed(p), 1365 }, 1366 ), 1367 State: MustShimLegacyState(&State{ 1368 1369 Modules: []*ModuleState{ 1370 { 1371 Path: rootModulePath, 1372 Resources: map[string]*ResourceState{ 1373 "aws_instance.web": { 1374 Type: "aws_instance", 1375 Primary: &InstanceState{ 1376 ID: "foo", 1377 }, 1378 }, 1379 }, 1380 }, 1381 }, 1382 }), 1383 }) 1384 1385 readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 1386 "id": cty.StringVal("foo"), 1387 })) 1388 if err != nil { 1389 t.Fatal(err) 1390 } 1391 1392 p.ReadResourceFn = nil 1393 p.ReadResourceResponse = providers.ReadResourceResponse{ 1394 NewState: readStateVal, 1395 } 1396 1397 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 1398 return providers.PlanResourceChangeResponse{ 1399 PlannedState: req.ProposedNewState, 1400 } 1401 } 1402 1403 s, diags := ctx.Refresh() 1404 if diags.HasErrors() { 1405 t.Fatalf("refresh errors: %s", diags.Err()) 1406 } 1407 1408 if !p.ReadResourceCalled { 1409 t.Fatal("read resource should be called") 1410 } 1411 1412 mod := s.RootModule() 1413 1414 newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema.ImpliedType()) 1415 if err != nil { 1416 t.Fatal(err) 1417 } 1418 1419 if !cmp.Equal(readStateVal, newState.Value, valueComparer, equateEmpty) { 1420 t.Fatal(cmp.Diff(readStateVal, newState.Value, valueComparer, equateEmpty)) 1421 } 1422 1423 for _, r := range mod.Resources { 1424 if r.Addr.Type == "" { 1425 t.Fatalf("no type: %#v", r) 1426 } 1427 } 1428 } 1429 1430 func TestContext2Refresh_orphanModule(t *testing.T) { 1431 p := testProvider("aws") 1432 m := testModule(t, "refresh-module-orphan") 1433 1434 // Create a custom refresh function to track the order they were visited 1435 var order []string 1436 var orderLock sync.Mutex 1437 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 1438 orderLock.Lock() 1439 defer orderLock.Unlock() 1440 1441 order = append(order, req.PriorState.GetAttr("id").AsString()) 1442 return providers.ReadResourceResponse{ 1443 NewState: req.PriorState, 1444 } 1445 } 1446 1447 state := MustShimLegacyState(&State{ 1448 Modules: []*ModuleState{ 1449 { 1450 Path: rootModulePath, 1451 Resources: map[string]*ResourceState{ 1452 "aws_instance.foo": { 1453 Type: "aws_instance", 1454 Primary: &InstanceState{ 1455 ID: "i-abc123", 1456 Attributes: map[string]string{ 1457 "id": "i-abc123", 1458 "childid": "i-bcd234", 1459 "grandchildid": "i-cde345", 1460 }, 1461 }, 1462 Dependencies: []string{ 1463 "module.child", 1464 "module.child", 1465 }, 1466 Provider: "provider.aws", 1467 }, 1468 }, 1469 }, 1470 { 1471 Path: append(rootModulePath, "child"), 1472 Resources: map[string]*ResourceState{ 1473 "aws_instance.bar": { 1474 Type: "aws_instance", 1475 Primary: &InstanceState{ 1476 ID: "i-bcd234", 1477 Attributes: map[string]string{ 1478 "id": "i-bcd234", 1479 "grandchildid": "i-cde345", 1480 }, 1481 }, 1482 Dependencies: []string{ 1483 "module.grandchild", 1484 }, 1485 Provider: "provider.aws", 1486 }, 1487 }, 1488 Outputs: map[string]*OutputState{ 1489 "id": { 1490 Value: "i-bcd234", 1491 Type: "string", 1492 }, 1493 "grandchild_id": { 1494 Value: "i-cde345", 1495 Type: "string", 1496 }, 1497 }, 1498 }, 1499 { 1500 Path: append(rootModulePath, "child", "grandchild"), 1501 Resources: map[string]*ResourceState{ 1502 "aws_instance.baz": { 1503 Type: "aws_instance", 1504 Primary: &InstanceState{ 1505 ID: "i-cde345", 1506 Attributes: map[string]string{ 1507 "id": "i-cde345", 1508 }, 1509 }, 1510 Provider: "provider.aws", 1511 }, 1512 }, 1513 Outputs: map[string]*OutputState{ 1514 "id": { 1515 Value: "i-cde345", 1516 Type: "string", 1517 }, 1518 }, 1519 }, 1520 }, 1521 }) 1522 ctx := testContext2(t, &ContextOpts{ 1523 Config: m, 1524 ProviderResolver: providers.ResolverFixed( 1525 map[string]providers.Factory{ 1526 "aws": testProviderFuncFixed(p), 1527 }, 1528 ), 1529 State: state, 1530 }) 1531 1532 testCheckDeadlock(t, func() { 1533 _, err := ctx.Refresh() 1534 if err != nil { 1535 t.Fatalf("err: %s", err) 1536 } 1537 1538 // TODO: handle order properly for orphaned modules / resources 1539 // expected := []string{"i-abc123", "i-bcd234", "i-cde345"} 1540 // if !reflect.DeepEqual(order, expected) { 1541 // t.Fatalf("expected: %#v, got: %#v", expected, order) 1542 // } 1543 }) 1544 } 1545 1546 func TestContext2Validate(t *testing.T) { 1547 p := testProvider("aws") 1548 p.GetSchemaReturn = &ProviderSchema{ 1549 Provider: &configschema.Block{}, 1550 ResourceTypes: map[string]*configschema.Block{ 1551 "aws_instance": { 1552 Attributes: map[string]*configschema.Attribute{ 1553 "foo": { 1554 Type: cty.String, 1555 Optional: true, 1556 }, 1557 "num": { 1558 Type: cty.String, 1559 Optional: true, 1560 }, 1561 }, 1562 }, 1563 }, 1564 } 1565 1566 m := testModule(t, "validate-good") 1567 c := testContext2(t, &ContextOpts{ 1568 Config: m, 1569 ProviderResolver: providers.ResolverFixed( 1570 map[string]providers.Factory{ 1571 "aws": testProviderFuncFixed(p), 1572 }, 1573 ), 1574 }) 1575 1576 diags := c.Validate() 1577 if len(diags) != 0 { 1578 t.Fatalf("unexpected error: %#v", diags.ErrWithWarnings()) 1579 } 1580 } 1581 1582 // TestContext2Refresh_noDiffHookOnScaleOut tests to make sure that 1583 // pre/post-diff hooks are not called when running EvalDiff on scale-out nodes 1584 // (nodes with no state). The effect here is to make sure that the diffs - 1585 // which only exist for interpolation of parallel resources or data sources - 1586 // do not end up being counted in the UI. 1587 func TestContext2Refresh_noDiffHookOnScaleOut(t *testing.T) { 1588 h := new(MockHook) 1589 p := testProvider("aws") 1590 m := testModule(t, "refresh-resource-scale-inout") 1591 1592 // Refresh creates a partial plan for any instances that don't have 1593 // remote objects yet, to get stub values for interpolation. Therefore 1594 // we need to make DiffFn available to let that complete. 1595 p.DiffFn = testDiffFn 1596 1597 state := MustShimLegacyState(&State{ 1598 Modules: []*ModuleState{ 1599 { 1600 Path: rootModulePath, 1601 Resources: map[string]*ResourceState{ 1602 "aws_instance.foo.0": { 1603 Type: "aws_instance", 1604 Deposed: []*InstanceState{ 1605 { 1606 ID: "foo", 1607 Attributes: map[string]string{ 1608 "id": "foo", 1609 }, 1610 }, 1611 }, 1612 }, 1613 "aws_instance.foo.1": { 1614 Type: "aws_instance", 1615 Deposed: []*InstanceState{ 1616 { 1617 ID: "bar", 1618 Attributes: map[string]string{ 1619 "id": "foo", 1620 }, 1621 }, 1622 }, 1623 }, 1624 }, 1625 }, 1626 }, 1627 }) 1628 1629 ctx := testContext2(t, &ContextOpts{ 1630 Config: m, 1631 Hooks: []Hook{h}, 1632 ProviderResolver: providers.ResolverFixed( 1633 map[string]providers.Factory{ 1634 "aws": testProviderFuncFixed(p), 1635 }, 1636 ), 1637 State: state, 1638 }) 1639 1640 _, diags := ctx.Refresh() 1641 if diags.HasErrors() { 1642 t.Fatalf("refresh errors: %s", diags.Err()) 1643 } 1644 if h.PreDiffCalled { 1645 t.Fatal("PreDiff should not have been called") 1646 } 1647 if h.PostDiffCalled { 1648 t.Fatal("PostDiff should not have been called") 1649 } 1650 } 1651 1652 func TestContext2Refresh_updateProviderInState(t *testing.T) { 1653 m := testModule(t, "update-resource-provider") 1654 p := testProvider("aws") 1655 p.DiffFn = testDiffFn 1656 p.ApplyFn = testApplyFn 1657 1658 s := MustShimLegacyState(&State{ 1659 Modules: []*ModuleState{ 1660 { 1661 Path: rootModulePath, 1662 Resources: map[string]*ResourceState{ 1663 "aws_instance.bar": { 1664 Type: "aws_instance", 1665 Primary: &InstanceState{ 1666 ID: "foo", 1667 Attributes: map[string]string{ 1668 "id": "foo", 1669 }, 1670 }, 1671 Provider: "provider.aws.baz", 1672 }, 1673 }, 1674 }, 1675 }, 1676 }) 1677 1678 ctx := testContext2(t, &ContextOpts{ 1679 Config: m, 1680 ProviderResolver: providers.ResolverFixed( 1681 map[string]providers.Factory{ 1682 "aws": testProviderFuncFixed(p), 1683 }, 1684 ), 1685 State: s, 1686 }) 1687 1688 expected := strings.TrimSpace(` 1689 aws_instance.bar: 1690 ID = foo 1691 provider = provider.aws.foo`) 1692 1693 state, diags := ctx.Refresh() 1694 if diags.HasErrors() { 1695 t.Fatal(diags.Err()) 1696 } 1697 1698 actual := state.String() 1699 if actual != expected { 1700 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 1701 } 1702 } 1703 1704 func TestContext2Refresh_schemaUpgradeFlatmap(t *testing.T) { 1705 m := testModule(t, "empty") 1706 p := testProvider("test") 1707 p.GetSchemaReturn = &ProviderSchema{ 1708 ResourceTypes: map[string]*configschema.Block{ 1709 "test_thing": { 1710 Attributes: map[string]*configschema.Attribute{ 1711 "name": { // imagining we renamed this from "id" 1712 Type: cty.String, 1713 Optional: true, 1714 }, 1715 }, 1716 }, 1717 }, 1718 ResourceTypeSchemaVersions: map[string]uint64{ 1719 "test_thing": 5, 1720 }, 1721 } 1722 p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{ 1723 UpgradedState: cty.ObjectVal(map[string]cty.Value{ 1724 "name": cty.StringVal("foo"), 1725 }), 1726 } 1727 1728 s := states.BuildState(func(s *states.SyncState) { 1729 s.SetResourceInstanceCurrent( 1730 addrs.Resource{ 1731 Mode: addrs.ManagedResourceMode, 1732 Type: "test_thing", 1733 Name: "bar", 1734 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1735 &states.ResourceInstanceObjectSrc{ 1736 Status: states.ObjectReady, 1737 SchemaVersion: 3, 1738 AttrsFlat: map[string]string{ 1739 "id": "foo", 1740 }, 1741 }, 1742 addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance), 1743 ) 1744 }) 1745 1746 ctx := testContext2(t, &ContextOpts{ 1747 Config: m, 1748 ProviderResolver: providers.ResolverFixed( 1749 map[string]providers.Factory{ 1750 "test": testProviderFuncFixed(p), 1751 }, 1752 ), 1753 State: s, 1754 }) 1755 1756 state, diags := ctx.Refresh() 1757 if diags.HasErrors() { 1758 t.Fatal(diags.Err()) 1759 } 1760 1761 { 1762 got := p.UpgradeResourceStateRequest 1763 want := providers.UpgradeResourceStateRequest{ 1764 TypeName: "test_thing", 1765 Version: 3, 1766 RawStateFlatmap: map[string]string{ 1767 "id": "foo", 1768 }, 1769 } 1770 if !cmp.Equal(got, want) { 1771 t.Errorf("wrong upgrade request\n%s", cmp.Diff(want, got)) 1772 } 1773 } 1774 1775 { 1776 got := state.String() 1777 want := strings.TrimSpace(` 1778 test_thing.bar: 1779 ID = 1780 provider = provider.test 1781 name = foo 1782 `) 1783 if got != want { 1784 t.Fatalf("wrong result state\ngot:\n%s\n\nwant:\n%s", got, want) 1785 } 1786 } 1787 } 1788 1789 func TestContext2Refresh_schemaUpgradeJSON(t *testing.T) { 1790 m := testModule(t, "empty") 1791 p := testProvider("test") 1792 p.GetSchemaReturn = &ProviderSchema{ 1793 ResourceTypes: map[string]*configschema.Block{ 1794 "test_thing": { 1795 Attributes: map[string]*configschema.Attribute{ 1796 "name": { // imagining we renamed this from "id" 1797 Type: cty.String, 1798 Optional: true, 1799 }, 1800 }, 1801 }, 1802 }, 1803 ResourceTypeSchemaVersions: map[string]uint64{ 1804 "test_thing": 5, 1805 }, 1806 } 1807 p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{ 1808 UpgradedState: cty.ObjectVal(map[string]cty.Value{ 1809 "name": cty.StringVal("foo"), 1810 }), 1811 } 1812 1813 s := states.BuildState(func(s *states.SyncState) { 1814 s.SetResourceInstanceCurrent( 1815 addrs.Resource{ 1816 Mode: addrs.ManagedResourceMode, 1817 Type: "test_thing", 1818 Name: "bar", 1819 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1820 &states.ResourceInstanceObjectSrc{ 1821 Status: states.ObjectReady, 1822 SchemaVersion: 3, 1823 AttrsJSON: []byte(`{"id":"foo"}`), 1824 }, 1825 addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance), 1826 ) 1827 }) 1828 1829 ctx := testContext2(t, &ContextOpts{ 1830 Config: m, 1831 ProviderResolver: providers.ResolverFixed( 1832 map[string]providers.Factory{ 1833 "test": testProviderFuncFixed(p), 1834 }, 1835 ), 1836 State: s, 1837 }) 1838 1839 state, diags := ctx.Refresh() 1840 if diags.HasErrors() { 1841 t.Fatal(diags.Err()) 1842 } 1843 1844 { 1845 got := p.UpgradeResourceStateRequest 1846 want := providers.UpgradeResourceStateRequest{ 1847 TypeName: "test_thing", 1848 Version: 3, 1849 RawStateJSON: []byte(`{"id":"foo"}`), 1850 } 1851 if !cmp.Equal(got, want) { 1852 t.Errorf("wrong upgrade request\n%s", cmp.Diff(want, got)) 1853 } 1854 } 1855 1856 { 1857 got := state.String() 1858 want := strings.TrimSpace(` 1859 test_thing.bar: 1860 ID = 1861 provider = provider.test 1862 name = foo 1863 `) 1864 if got != want { 1865 t.Fatalf("wrong result state\ngot:\n%s\n\nwant:\n%s", got, want) 1866 } 1867 } 1868 } 1869 1870 func TestContext2Refresh_dataValidation(t *testing.T) { 1871 m := testModuleInline(t, map[string]string{ 1872 "main.tf": ` 1873 data "aws_data_source" "foo" { 1874 foo = "bar" 1875 } 1876 `, 1877 }) 1878 1879 p := testProvider("aws") 1880 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 1881 resp.PlannedState = req.ProposedNewState 1882 return 1883 } 1884 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { 1885 resp.State = req.Config 1886 return 1887 } 1888 1889 ctx := testContext2(t, &ContextOpts{ 1890 Config: m, 1891 ProviderResolver: providers.ResolverFixed( 1892 map[string]providers.Factory{ 1893 "aws": testProviderFuncFixed(p), 1894 }, 1895 ), 1896 }) 1897 1898 _, diags := ctx.Refresh() 1899 if diags.HasErrors() { 1900 // Should get this error: 1901 // Unsupported attribute: This object does not have an attribute named "missing" 1902 t.Fatal(diags.Err()) 1903 } 1904 1905 if !p.ValidateDataSourceConfigCalled { 1906 t.Fatal("ValidateDataSourceConfig not called during plan") 1907 } 1908 } 1909 1910 func TestContext2Refresh_dataResourceDependsOn(t *testing.T) { 1911 m := testModule(t, "plan-data-depends-on") 1912 p := testProvider("test") 1913 p.GetSchemaReturn = &ProviderSchema{ 1914 ResourceTypes: map[string]*configschema.Block{ 1915 "test_resource": { 1916 Attributes: map[string]*configschema.Attribute{ 1917 "id": {Type: cty.String, Computed: true}, 1918 "foo": {Type: cty.String, Optional: true}, 1919 }, 1920 }, 1921 }, 1922 DataSources: map[string]*configschema.Block{ 1923 "test_data": { 1924 Attributes: map[string]*configschema.Attribute{ 1925 "compute": {Type: cty.String, Computed: true}, 1926 }, 1927 }, 1928 }, 1929 } 1930 p.DiffFn = testDiffFn 1931 1932 s := MustShimLegacyState(&State{ 1933 Modules: []*ModuleState{ 1934 { 1935 Path: rootModulePath, 1936 Resources: map[string]*ResourceState{ 1937 "test_resource.a": { 1938 Type: "test_resource", 1939 Provider: "provider.test", 1940 Primary: &InstanceState{ 1941 ID: "a", 1942 Attributes: map[string]string{ 1943 "id": "a", 1944 }, 1945 }, 1946 }, 1947 }, 1948 }, 1949 }, 1950 }) 1951 1952 ctx := testContext2(t, &ContextOpts{ 1953 Config: m, 1954 ProviderResolver: providers.ResolverFixed( 1955 map[string]providers.Factory{ 1956 "test": testProviderFuncFixed(p), 1957 }, 1958 ), 1959 State: s, 1960 }) 1961 1962 _, diags := ctx.Refresh() 1963 if diags.HasErrors() { 1964 t.Fatalf("unexpected errors: %s", diags.Err()) 1965 } 1966 }