github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/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/internal/addrs" 15 "github.com/hashicorp/terraform/internal/configs/configschema" 16 "github.com/hashicorp/terraform/internal/configs/hcl2shim" 17 "github.com/hashicorp/terraform/internal/plans" 18 "github.com/hashicorp/terraform/internal/providers" 19 "github.com/hashicorp/terraform/internal/states" 20 ) 21 22 func TestContext2Refresh(t *testing.T) { 23 p := testProvider("aws") 24 m := testModule(t, "refresh-basic") 25 26 state := states.NewState() 27 root := state.EnsureModule(addrs.RootModuleInstance) 28 root.SetResourceInstanceCurrent( 29 mustResourceInstanceAddr("aws_instance.web").Resource, 30 &states.ResourceInstanceObjectSrc{ 31 Status: states.ObjectReady, 32 AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`), 33 }, 34 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 35 ) 36 37 ctx := testContext2(t, &ContextOpts{ 38 Providers: map[addrs.Provider]providers.Factory{ 39 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 40 }, 41 }) 42 43 schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block 44 ty := schema.ImpliedType() 45 readState, err := hcl2shim.HCL2ValueFromFlatmap(map[string]string{"id": "foo", "foo": "baz"}, ty) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 p.ReadResourceResponse = &providers.ReadResourceResponse{ 51 NewState: readState, 52 } 53 54 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 55 if diags.HasErrors() { 56 t.Fatal(diags.Err()) 57 } 58 59 if !p.ReadResourceCalled { 60 t.Fatal("ReadResource should be called") 61 } 62 63 mod := s.RootModule() 64 fromState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(ty) 65 if err != nil { 66 t.Fatal(err) 67 } 68 69 newState, err := schema.CoerceValue(fromState.Value) 70 if err != nil { 71 t.Fatal(err) 72 } 73 74 if !cmp.Equal(readState, newState, valueComparer) { 75 t.Fatal(cmp.Diff(readState, newState, valueComparer, equateEmpty)) 76 } 77 } 78 79 func TestContext2Refresh_dynamicAttr(t *testing.T) { 80 m := testModule(t, "refresh-dynamic") 81 82 startingState := states.BuildState(func(ss *states.SyncState) { 83 ss.SetResourceInstanceCurrent( 84 addrs.Resource{ 85 Mode: addrs.ManagedResourceMode, 86 Type: "test_instance", 87 Name: "foo", 88 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 89 &states.ResourceInstanceObjectSrc{ 90 Status: states.ObjectReady, 91 AttrsJSON: []byte(`{"dynamic":{"type":"string","value":"hello"}}`), 92 }, 93 addrs.AbsProviderConfig{ 94 Provider: addrs.NewDefaultProvider("test"), 95 Module: addrs.RootModule, 96 }, 97 ) 98 }) 99 100 readStateVal := cty.ObjectVal(map[string]cty.Value{ 101 "dynamic": cty.EmptyTupleVal, 102 }) 103 104 p := testProvider("test") 105 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 106 ResourceTypes: map[string]*configschema.Block{ 107 "test_instance": { 108 Attributes: map[string]*configschema.Attribute{ 109 "dynamic": {Type: cty.DynamicPseudoType, Optional: true}, 110 }, 111 }, 112 }, 113 }) 114 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 115 return providers.ReadResourceResponse{ 116 NewState: readStateVal, 117 } 118 } 119 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 120 resp.PlannedState = req.ProposedNewState 121 return resp 122 } 123 124 ctx := testContext2(t, &ContextOpts{ 125 Providers: map[addrs.Provider]providers.Factory{ 126 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 127 }, 128 }) 129 130 schema := p.GetProviderSchemaResponse.ResourceTypes["test_instance"].Block 131 ty := schema.ImpliedType() 132 133 s, diags := ctx.Refresh(m, startingState, &PlanOpts{Mode: plans.NormalMode}) 134 if diags.HasErrors() { 135 t.Fatal(diags.Err()) 136 } 137 138 if !p.ReadResourceCalled { 139 t.Fatal("ReadResource should be called") 140 } 141 142 mod := s.RootModule() 143 newState, err := mod.Resources["test_instance.foo"].Instances[addrs.NoKey].Current.Decode(ty) 144 if err != nil { 145 t.Fatal(err) 146 } 147 148 if !cmp.Equal(readStateVal, newState.Value, valueComparer) { 149 t.Error(cmp.Diff(newState.Value, readStateVal, valueComparer, equateEmpty)) 150 } 151 } 152 153 func TestContext2Refresh_dataComputedModuleVar(t *testing.T) { 154 p := testProvider("aws") 155 m := testModule(t, "refresh-data-module-var") 156 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 157 obj := req.ProposedNewState.AsValueMap() 158 obj["id"] = cty.UnknownVal(cty.String) 159 resp.PlannedState = cty.ObjectVal(obj) 160 return resp 161 } 162 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { 163 resp.State = req.Config 164 return resp 165 } 166 167 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 168 Provider: &configschema.Block{}, 169 ResourceTypes: map[string]*configschema.Block{ 170 "aws_instance": { 171 Attributes: map[string]*configschema.Attribute{ 172 "foo": { 173 Type: cty.String, 174 Optional: true, 175 }, 176 "id": { 177 Type: cty.String, 178 Computed: true, 179 }, 180 }, 181 }, 182 }, 183 DataSources: map[string]*configschema.Block{ 184 "aws_data_source": { 185 Attributes: map[string]*configschema.Attribute{ 186 "id": { 187 Type: cty.String, 188 Optional: true, 189 }, 190 "output": { 191 Type: cty.String, 192 Computed: true, 193 }, 194 }, 195 }, 196 }, 197 }) 198 199 ctx := testContext2(t, &ContextOpts{ 200 Providers: map[addrs.Provider]providers.Factory{ 201 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 202 }, 203 }) 204 205 s, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}) 206 if diags.HasErrors() { 207 t.Fatalf("refresh errors: %s", diags.Err()) 208 } 209 210 checkStateString(t, s, ` 211 <no state> 212 `) 213 } 214 215 func TestContext2Refresh_targeted(t *testing.T) { 216 p := testProvider("aws") 217 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 218 Provider: &configschema.Block{}, 219 ResourceTypes: map[string]*configschema.Block{ 220 "aws_elb": { 221 Attributes: map[string]*configschema.Attribute{ 222 "instances": { 223 Type: cty.Set(cty.String), 224 Optional: true, 225 }, 226 }, 227 }, 228 "aws_instance": { 229 Attributes: map[string]*configschema.Attribute{ 230 "id": { 231 Type: cty.String, 232 Computed: true, 233 }, 234 "vpc_id": { 235 Type: cty.String, 236 Optional: true, 237 }, 238 }, 239 }, 240 "aws_vpc": { 241 Attributes: map[string]*configschema.Attribute{ 242 "id": { 243 Type: cty.String, 244 Computed: true, 245 }, 246 }, 247 }, 248 }, 249 }) 250 251 state := states.NewState() 252 root := state.EnsureModule(addrs.RootModuleInstance) 253 testSetResourceInstanceCurrent(root, "aws_vpc.metoo", `{"id":"vpc-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 254 testSetResourceInstanceCurrent(root, "aws_instance.notme", `{"id":"i-bcd345"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 255 testSetResourceInstanceCurrent(root, "aws_instance.me", `{"id":"i-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 256 testSetResourceInstanceCurrent(root, "aws_elb.meneither", `{"id":"lb-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 257 258 m := testModule(t, "refresh-targeted") 259 ctx := testContext2(t, &ContextOpts{ 260 Providers: map[addrs.Provider]providers.Factory{ 261 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 262 }, 263 }) 264 265 refreshedResources := make([]string, 0, 2) 266 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 267 refreshedResources = append(refreshedResources, req.PriorState.GetAttr("id").AsString()) 268 return providers.ReadResourceResponse{ 269 NewState: req.PriorState, 270 } 271 } 272 273 _, diags := ctx.Refresh(m, state, &PlanOpts{ 274 Mode: plans.NormalMode, 275 Targets: []addrs.Targetable{ 276 addrs.RootModuleInstance.Resource( 277 addrs.ManagedResourceMode, "aws_instance", "me", 278 ), 279 }, 280 }) 281 if diags.HasErrors() { 282 t.Fatalf("refresh errors: %s", diags.Err()) 283 } 284 285 expected := []string{"vpc-abc123", "i-abc123"} 286 if !reflect.DeepEqual(refreshedResources, expected) { 287 t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) 288 } 289 } 290 291 func TestContext2Refresh_targetedCount(t *testing.T) { 292 p := testProvider("aws") 293 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 294 Provider: &configschema.Block{}, 295 ResourceTypes: map[string]*configschema.Block{ 296 "aws_elb": { 297 Attributes: map[string]*configschema.Attribute{ 298 "instances": { 299 Type: cty.Set(cty.String), 300 Optional: true, 301 }, 302 }, 303 }, 304 "aws_instance": { 305 Attributes: map[string]*configschema.Attribute{ 306 "id": { 307 Type: cty.String, 308 Computed: true, 309 }, 310 "vpc_id": { 311 Type: cty.String, 312 Optional: true, 313 }, 314 }, 315 }, 316 "aws_vpc": { 317 Attributes: map[string]*configschema.Attribute{ 318 "id": { 319 Type: cty.String, 320 Computed: true, 321 }, 322 }, 323 }, 324 }, 325 }) 326 327 state := states.NewState() 328 root := state.EnsureModule(addrs.RootModuleInstance) 329 testSetResourceInstanceCurrent(root, "aws_vpc.metoo", `{"id":"vpc-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 330 testSetResourceInstanceCurrent(root, "aws_instance.notme", `{"id":"i-bcd345"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 331 testSetResourceInstanceCurrent(root, "aws_instance.me[0]", `{"id":"i-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 332 testSetResourceInstanceCurrent(root, "aws_instance.me[1]", `{"id":"i-cde567"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 333 testSetResourceInstanceCurrent(root, "aws_instance.me[2]", `{"id":"i-cde789"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 334 testSetResourceInstanceCurrent(root, "aws_elb.meneither", `{"id":"lb-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 335 336 m := testModule(t, "refresh-targeted-count") 337 ctx := testContext2(t, &ContextOpts{ 338 Providers: map[addrs.Provider]providers.Factory{ 339 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 340 }, 341 }) 342 343 refreshedResources := make([]string, 0, 2) 344 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 345 refreshedResources = append(refreshedResources, req.PriorState.GetAttr("id").AsString()) 346 return providers.ReadResourceResponse{ 347 NewState: req.PriorState, 348 } 349 } 350 351 _, diags := ctx.Refresh(m, state, &PlanOpts{ 352 Mode: plans.NormalMode, 353 Targets: []addrs.Targetable{ 354 addrs.RootModuleInstance.Resource( 355 addrs.ManagedResourceMode, "aws_instance", "me", 356 ), 357 }, 358 }) 359 if diags.HasErrors() { 360 t.Fatalf("refresh errors: %s", diags.Err()) 361 } 362 363 // Target didn't specify index, so we should get all our instances 364 expected := []string{ 365 "vpc-abc123", 366 "i-abc123", 367 "i-cde567", 368 "i-cde789", 369 } 370 sort.Strings(expected) 371 sort.Strings(refreshedResources) 372 if !reflect.DeepEqual(refreshedResources, expected) { 373 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", refreshedResources, expected) 374 } 375 } 376 377 func TestContext2Refresh_targetedCountIndex(t *testing.T) { 378 p := testProvider("aws") 379 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 380 Provider: &configschema.Block{}, 381 ResourceTypes: map[string]*configschema.Block{ 382 "aws_elb": { 383 Attributes: map[string]*configschema.Attribute{ 384 "instances": { 385 Type: cty.Set(cty.String), 386 Optional: true, 387 }, 388 }, 389 }, 390 "aws_instance": { 391 Attributes: map[string]*configschema.Attribute{ 392 "id": { 393 Type: cty.String, 394 Computed: true, 395 }, 396 "vpc_id": { 397 Type: cty.String, 398 Optional: true, 399 }, 400 }, 401 }, 402 "aws_vpc": { 403 Attributes: map[string]*configschema.Attribute{ 404 "id": { 405 Type: cty.String, 406 Computed: true, 407 }, 408 }, 409 }, 410 }, 411 }) 412 413 state := states.NewState() 414 root := state.EnsureModule(addrs.RootModuleInstance) 415 testSetResourceInstanceCurrent(root, "aws_vpc.metoo", `{"id":"vpc-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 416 testSetResourceInstanceCurrent(root, "aws_instance.notme", `{"id":"i-bcd345"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 417 testSetResourceInstanceCurrent(root, "aws_instance.me[0]", `{"id":"i-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 418 testSetResourceInstanceCurrent(root, "aws_instance.me[1]", `{"id":"i-cde567"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 419 testSetResourceInstanceCurrent(root, "aws_instance.me[2]", `{"id":"i-cde789"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 420 testSetResourceInstanceCurrent(root, "aws_elb.meneither", `{"id":"lb-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 421 422 m := testModule(t, "refresh-targeted-count") 423 ctx := testContext2(t, &ContextOpts{ 424 Providers: map[addrs.Provider]providers.Factory{ 425 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 426 }, 427 }) 428 429 refreshedResources := make([]string, 0, 2) 430 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 431 refreshedResources = append(refreshedResources, req.PriorState.GetAttr("id").AsString()) 432 return providers.ReadResourceResponse{ 433 NewState: req.PriorState, 434 } 435 } 436 437 _, diags := ctx.Refresh(m, state, &PlanOpts{ 438 Mode: plans.NormalMode, 439 Targets: []addrs.Targetable{ 440 addrs.RootModuleInstance.ResourceInstance( 441 addrs.ManagedResourceMode, "aws_instance", "me", addrs.IntKey(0), 442 ), 443 }, 444 }) 445 if diags.HasErrors() { 446 t.Fatalf("refresh errors: %s", diags.Err()) 447 } 448 449 expected := []string{"vpc-abc123", "i-abc123"} 450 if !reflect.DeepEqual(refreshedResources, expected) { 451 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", refreshedResources, expected) 452 } 453 } 454 455 func TestContext2Refresh_moduleComputedVar(t *testing.T) { 456 p := testProvider("aws") 457 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 458 Provider: &configschema.Block{}, 459 ResourceTypes: map[string]*configschema.Block{ 460 "aws_instance": { 461 Attributes: map[string]*configschema.Attribute{ 462 "id": { 463 Type: cty.String, 464 Computed: true, 465 }, 466 "value": { 467 Type: cty.String, 468 Optional: true, 469 }, 470 }, 471 }, 472 }, 473 }) 474 475 m := testModule(t, "refresh-module-computed-var") 476 ctx := testContext2(t, &ContextOpts{ 477 Providers: map[addrs.Provider]providers.Factory{ 478 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 479 }, 480 }) 481 482 // This was failing (see GH-2188) at some point, so this test just 483 // verifies that the failure goes away. 484 if _, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}); diags.HasErrors() { 485 t.Fatalf("refresh errs: %s", diags.Err()) 486 } 487 } 488 489 func TestContext2Refresh_delete(t *testing.T) { 490 p := testProvider("aws") 491 m := testModule(t, "refresh-basic") 492 493 state := states.NewState() 494 root := state.EnsureModule(addrs.RootModuleInstance) 495 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 496 497 ctx := testContext2(t, &ContextOpts{ 498 Providers: map[addrs.Provider]providers.Factory{ 499 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 500 }, 501 }) 502 503 p.ReadResourceResponse = &providers.ReadResourceResponse{ 504 NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block.ImpliedType()), 505 } 506 507 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 508 if diags.HasErrors() { 509 t.Fatalf("refresh errors: %s", diags.Err()) 510 } 511 512 mod := s.RootModule() 513 if len(mod.Resources) > 0 { 514 t.Fatal("resources should be empty") 515 } 516 } 517 518 func TestContext2Refresh_ignoreUncreated(t *testing.T) { 519 p := testProvider("aws") 520 m := testModule(t, "refresh-basic") 521 ctx := testContext2(t, &ContextOpts{ 522 Providers: map[addrs.Provider]providers.Factory{ 523 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 524 }, 525 }) 526 527 p.ReadResourceResponse = &providers.ReadResourceResponse{ 528 NewState: cty.ObjectVal(map[string]cty.Value{ 529 "id": cty.StringVal("foo"), 530 }), 531 } 532 533 _, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}) 534 if diags.HasErrors() { 535 t.Fatalf("refresh errors: %s", diags.Err()) 536 } 537 if p.ReadResourceCalled { 538 t.Fatal("refresh should not be called") 539 } 540 } 541 542 func TestContext2Refresh_hook(t *testing.T) { 543 h := new(MockHook) 544 p := testProvider("aws") 545 m := testModule(t, "refresh-basic") 546 547 state := states.NewState() 548 root := state.EnsureModule(addrs.RootModuleInstance) 549 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 550 551 ctx := testContext2(t, &ContextOpts{ 552 Hooks: []Hook{h}, 553 Providers: map[addrs.Provider]providers.Factory{ 554 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 555 }, 556 }) 557 558 if _, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}); diags.HasErrors() { 559 t.Fatalf("refresh errs: %s", diags.Err()) 560 } 561 if !h.PreRefreshCalled { 562 t.Fatal("should be called") 563 } 564 if !h.PostRefreshCalled { 565 t.Fatal("should be called") 566 } 567 } 568 569 func TestContext2Refresh_modules(t *testing.T) { 570 p := testProvider("aws") 571 m := testModule(t, "refresh-modules") 572 573 state := states.NewState() 574 root := state.EnsureModule(addrs.RootModuleInstance) 575 testSetResourceInstanceTainted(root, "aws_instance.web", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 576 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 577 testSetResourceInstanceCurrent(child, "aws_instance.web", `{"id":"baz"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 578 579 ctx := testContext2(t, &ContextOpts{ 580 Providers: map[addrs.Provider]providers.Factory{ 581 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 582 }, 583 }) 584 585 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 586 if !req.PriorState.GetAttr("id").RawEquals(cty.StringVal("baz")) { 587 return providers.ReadResourceResponse{ 588 NewState: req.PriorState, 589 } 590 } 591 592 new, _ := cty.Transform(req.PriorState, func(path cty.Path, v cty.Value) (cty.Value, error) { 593 if len(path) == 1 && path[0].(cty.GetAttrStep).Name == "id" { 594 return cty.StringVal("new"), nil 595 } 596 return v, nil 597 }) 598 return providers.ReadResourceResponse{ 599 NewState: new, 600 } 601 } 602 603 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 604 if diags.HasErrors() { 605 t.Fatalf("refresh errors: %s", diags.Err()) 606 } 607 608 actual := strings.TrimSpace(s.String()) 609 expected := strings.TrimSpace(testContextRefreshModuleStr) 610 if actual != expected { 611 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 612 } 613 } 614 615 func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { 616 m := testModule(t, "refresh-module-input-computed-output") 617 p := testProvider("aws") 618 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 619 Provider: &configschema.Block{}, 620 ResourceTypes: map[string]*configschema.Block{ 621 "aws_instance": { 622 Attributes: map[string]*configschema.Attribute{ 623 "foo": { 624 Type: cty.String, 625 Optional: true, 626 Computed: true, 627 }, 628 "compute": { 629 Type: cty.String, 630 Optional: true, 631 }, 632 }, 633 }, 634 }, 635 }) 636 637 ctx := testContext2(t, &ContextOpts{ 638 Providers: map[addrs.Provider]providers.Factory{ 639 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 640 }, 641 }) 642 643 if _, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}); diags.HasErrors() { 644 t.Fatalf("refresh errs: %s", diags.Err()) 645 } 646 } 647 648 func TestContext2Refresh_moduleVarModule(t *testing.T) { 649 m := testModule(t, "refresh-module-var-module") 650 p := testProvider("aws") 651 ctx := testContext2(t, &ContextOpts{ 652 Providers: map[addrs.Provider]providers.Factory{ 653 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 654 }, 655 }) 656 657 if _, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}); diags.HasErrors() { 658 t.Fatalf("refresh errs: %s", diags.Err()) 659 } 660 } 661 662 // GH-70 663 func TestContext2Refresh_noState(t *testing.T) { 664 p := testProvider("aws") 665 m := testModule(t, "refresh-no-state") 666 ctx := testContext2(t, &ContextOpts{ 667 Providers: map[addrs.Provider]providers.Factory{ 668 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 669 }, 670 }) 671 672 p.ReadResourceResponse = &providers.ReadResourceResponse{ 673 NewState: cty.ObjectVal(map[string]cty.Value{ 674 "id": cty.StringVal("foo"), 675 }), 676 } 677 678 if _, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}); diags.HasErrors() { 679 t.Fatalf("refresh errs: %s", diags.Err()) 680 } 681 } 682 683 func TestContext2Refresh_output(t *testing.T) { 684 p := testProvider("aws") 685 p.PlanResourceChangeFn = testDiffFn 686 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 687 Provider: &configschema.Block{}, 688 ResourceTypes: map[string]*configschema.Block{ 689 "aws_instance": { 690 Attributes: map[string]*configschema.Attribute{ 691 "id": { 692 Type: cty.String, 693 Computed: true, 694 }, 695 "foo": { 696 Type: cty.String, 697 Optional: true, 698 Computed: true, 699 }, 700 }, 701 }, 702 }, 703 }) 704 705 m := testModule(t, "refresh-output") 706 707 state := states.NewState() 708 root := state.EnsureModule(addrs.RootModuleInstance) 709 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"foo","foo":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 710 root.SetOutputValue("foo", cty.StringVal("foo"), false) 711 712 ctx := testContext2(t, &ContextOpts{ 713 Providers: map[addrs.Provider]providers.Factory{ 714 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 715 }, 716 }) 717 718 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 719 if diags.HasErrors() { 720 t.Fatalf("refresh errors: %s", diags.Err()) 721 } 722 723 actual := strings.TrimSpace(s.String()) 724 expected := strings.TrimSpace(testContextRefreshOutputStr) 725 if actual != expected { 726 t.Fatalf("wrong result\n\ngot:\n%q\n\nwant:\n%q", actual, expected) 727 } 728 } 729 730 func TestContext2Refresh_outputPartial(t *testing.T) { 731 p := testProvider("aws") 732 m := testModule(t, "refresh-output-partial") 733 734 // Refresh creates a partial plan for any instances that don't have 735 // remote objects yet, to get stub values for interpolation. Therefore 736 // we need to make DiffFn available to let that complete. 737 738 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 739 Provider: &configschema.Block{}, 740 ResourceTypes: map[string]*configschema.Block{ 741 "aws_instance": { 742 Attributes: map[string]*configschema.Attribute{ 743 "foo": { 744 Type: cty.String, 745 Computed: true, 746 }, 747 }, 748 }, 749 }, 750 }) 751 752 p.ReadResourceResponse = &providers.ReadResourceResponse{ 753 NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block.ImpliedType()), 754 } 755 756 state := states.NewState() 757 root := state.EnsureModule(addrs.RootModuleInstance) 758 testSetResourceInstanceCurrent(root, "aws_instance.foo", `{}`, `provider["registry.terraform.io/hashicorp/aws"]`) 759 760 ctx := testContext2(t, &ContextOpts{ 761 Providers: map[addrs.Provider]providers.Factory{ 762 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 763 }, 764 }) 765 766 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 767 if diags.HasErrors() { 768 t.Fatalf("refresh errors: %s", diags.Err()) 769 } 770 771 actual := strings.TrimSpace(s.String()) 772 expected := strings.TrimSpace(testContextRefreshOutputPartialStr) 773 if actual != expected { 774 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 775 } 776 } 777 778 func TestContext2Refresh_stateBasic(t *testing.T) { 779 p := testProvider("aws") 780 m := testModule(t, "refresh-basic") 781 782 state := states.NewState() 783 root := state.EnsureModule(addrs.RootModuleInstance) 784 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 785 786 ctx := testContext2(t, &ContextOpts{ 787 Providers: map[addrs.Provider]providers.Factory{ 788 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 789 }, 790 }) 791 792 schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block 793 ty := schema.ImpliedType() 794 795 readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 796 "id": cty.StringVal("foo"), 797 })) 798 if err != nil { 799 t.Fatal(err) 800 } 801 802 p.ReadResourceResponse = &providers.ReadResourceResponse{ 803 NewState: readStateVal, 804 } 805 806 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 807 if diags.HasErrors() { 808 t.Fatalf("refresh errors: %s", diags.Err()) 809 } 810 811 if !p.ReadResourceCalled { 812 t.Fatal("read resource should be called") 813 } 814 815 mod := s.RootModule() 816 newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(ty) 817 if err != nil { 818 t.Fatal(err) 819 } 820 821 if !cmp.Equal(readStateVal, newState.Value, valueComparer, equateEmpty) { 822 t.Fatal(cmp.Diff(readStateVal, newState.Value, valueComparer, equateEmpty)) 823 } 824 } 825 826 func TestContext2Refresh_dataCount(t *testing.T) { 827 p := testProvider("test") 828 m := testModule(t, "refresh-data-count") 829 830 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 831 m := req.ProposedNewState.AsValueMap() 832 m["things"] = cty.ListVal([]cty.Value{cty.StringVal("foo")}) 833 resp.PlannedState = cty.ObjectVal(m) 834 return resp 835 } 836 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 837 ResourceTypes: map[string]*configschema.Block{ 838 "test": { 839 Attributes: map[string]*configschema.Attribute{ 840 "id": {Type: cty.String, Computed: true}, 841 "things": {Type: cty.List(cty.String), Computed: true}, 842 }, 843 }, 844 }, 845 DataSources: map[string]*configschema.Block{ 846 "test": {}, 847 }, 848 }) 849 850 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 851 return providers.ReadDataSourceResponse{ 852 State: req.Config, 853 } 854 } 855 856 ctx := testContext2(t, &ContextOpts{ 857 Providers: map[addrs.Provider]providers.Factory{ 858 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 859 }, 860 }) 861 862 s, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}) 863 864 if diags.HasErrors() { 865 t.Fatalf("refresh errors: %s", diags.Err()) 866 } 867 868 checkStateString(t, s, `<no state>`) 869 } 870 871 func TestContext2Refresh_dataState(t *testing.T) { 872 m := testModule(t, "refresh-data-resource-basic") 873 state := states.NewState() 874 schema := &configschema.Block{ 875 Attributes: map[string]*configschema.Attribute{ 876 "inputs": { 877 Type: cty.Map(cty.String), 878 Optional: true, 879 }, 880 }, 881 } 882 883 p := testProvider("null") 884 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 885 Provider: &configschema.Block{}, 886 DataSources: map[string]*configschema.Block{ 887 "null_data_source": schema, 888 }, 889 }) 890 891 ctx := testContext2(t, &ContextOpts{ 892 Providers: map[addrs.Provider]providers.Factory{ 893 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 894 }, 895 }) 896 897 var readStateVal cty.Value 898 899 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 900 m := req.Config.AsValueMap() 901 readStateVal = cty.ObjectVal(m) 902 903 return providers.ReadDataSourceResponse{ 904 State: readStateVal, 905 } 906 } 907 908 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 909 if diags.HasErrors() { 910 t.Fatalf("refresh errors: %s", diags.Err()) 911 } 912 913 if !p.ReadDataSourceCalled { 914 t.Fatal("ReadDataSource should have been called") 915 } 916 917 mod := s.RootModule() 918 919 newState, err := mod.Resources["data.null_data_source.testing"].Instances[addrs.NoKey].Current.Decode(schema.ImpliedType()) 920 if err != nil { 921 t.Fatal(err) 922 } 923 924 if !cmp.Equal(readStateVal, newState.Value, valueComparer, equateEmpty) { 925 t.Fatal(cmp.Diff(readStateVal, newState.Value, valueComparer, equateEmpty)) 926 } 927 } 928 929 func TestContext2Refresh_dataStateRefData(t *testing.T) { 930 p := testProvider("null") 931 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 932 Provider: &configschema.Block{}, 933 DataSources: map[string]*configschema.Block{ 934 "null_data_source": { 935 Attributes: map[string]*configschema.Attribute{ 936 "id": { 937 Type: cty.String, 938 Computed: true, 939 }, 940 "foo": { 941 Type: cty.String, 942 Optional: true, 943 }, 944 "bar": { 945 Type: cty.String, 946 Optional: true, 947 }, 948 }, 949 }, 950 }, 951 }) 952 953 m := testModule(t, "refresh-data-ref-data") 954 state := states.NewState() 955 ctx := testContext2(t, &ContextOpts{ 956 Providers: map[addrs.Provider]providers.Factory{ 957 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 958 }, 959 }) 960 961 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 962 // add the required id 963 m := req.Config.AsValueMap() 964 m["id"] = cty.StringVal("foo") 965 966 return providers.ReadDataSourceResponse{ 967 State: cty.ObjectVal(m), 968 } 969 } 970 971 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 972 if diags.HasErrors() { 973 t.Fatalf("refresh errors: %s", diags.Err()) 974 } 975 976 actual := strings.TrimSpace(s.String()) 977 expected := strings.TrimSpace(testTerraformRefreshDataRefDataStr) 978 if actual != expected { 979 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 980 } 981 } 982 983 func TestContext2Refresh_tainted(t *testing.T) { 984 p := testProvider("aws") 985 m := testModule(t, "refresh-basic") 986 987 state := states.NewState() 988 root := state.EnsureModule(addrs.RootModuleInstance) 989 testSetResourceInstanceTainted(root, "aws_instance.web", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 990 991 ctx := testContext2(t, &ContextOpts{ 992 Providers: map[addrs.Provider]providers.Factory{ 993 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 994 }, 995 }) 996 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 997 // add the required id 998 m := req.PriorState.AsValueMap() 999 m["id"] = cty.StringVal("foo") 1000 1001 return providers.ReadResourceResponse{ 1002 NewState: cty.ObjectVal(m), 1003 } 1004 } 1005 1006 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1007 if diags.HasErrors() { 1008 t.Fatalf("refresh errors: %s", diags.Err()) 1009 } 1010 if !p.ReadResourceCalled { 1011 t.Fatal("ReadResource was not called; should have been") 1012 } 1013 1014 actual := strings.TrimSpace(s.String()) 1015 expected := strings.TrimSpace(testContextRefreshTaintedStr) 1016 if actual != expected { 1017 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 1018 } 1019 } 1020 1021 // Doing a Refresh (or any operation really, but Refresh usually 1022 // happens first) with a config with an unknown provider should result in 1023 // an error. The key bug this found was that this wasn't happening if 1024 // Providers was _empty_. 1025 func TestContext2Refresh_unknownProvider(t *testing.T) { 1026 m := testModule(t, "refresh-unknown-provider") 1027 1028 state := states.NewState() 1029 root := state.EnsureModule(addrs.RootModuleInstance) 1030 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 1031 1032 c, diags := NewContext(&ContextOpts{ 1033 Providers: map[addrs.Provider]providers.Factory{}, 1034 }) 1035 assertNoDiagnostics(t, diags) 1036 1037 _, diags = c.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}) 1038 if !diags.HasErrors() { 1039 t.Fatal("successfully refreshed; want error") 1040 } 1041 1042 if !regexp.MustCompile(`failed to instantiate provider ".+"`).MatchString(diags.Err().Error()) { 1043 t.Fatalf("wrong error: %s", diags.Err()) 1044 } 1045 } 1046 1047 func TestContext2Refresh_vars(t *testing.T) { 1048 p := testProvider("aws") 1049 1050 schema := &configschema.Block{ 1051 Attributes: map[string]*configschema.Attribute{ 1052 "ami": { 1053 Type: cty.String, 1054 Optional: true, 1055 }, 1056 "id": { 1057 Type: cty.String, 1058 Computed: true, 1059 }, 1060 }, 1061 } 1062 1063 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1064 Provider: &configschema.Block{}, 1065 ResourceTypes: map[string]*configschema.Block{"aws_instance": schema}, 1066 }) 1067 1068 m := testModule(t, "refresh-vars") 1069 state := states.NewState() 1070 root := state.EnsureModule(addrs.RootModuleInstance) 1071 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 1072 1073 ctx := testContext2(t, &ContextOpts{ 1074 Providers: map[addrs.Provider]providers.Factory{ 1075 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1076 }, 1077 }) 1078 1079 readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 1080 "id": cty.StringVal("foo"), 1081 })) 1082 if err != nil { 1083 t.Fatal(err) 1084 } 1085 1086 p.ReadResourceResponse = &providers.ReadResourceResponse{ 1087 NewState: readStateVal, 1088 } 1089 1090 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 1091 return providers.PlanResourceChangeResponse{ 1092 PlannedState: req.ProposedNewState, 1093 } 1094 } 1095 1096 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1097 if diags.HasErrors() { 1098 t.Fatalf("refresh errors: %s", diags.Err()) 1099 } 1100 1101 if !p.ReadResourceCalled { 1102 t.Fatal("read resource should be called") 1103 } 1104 1105 mod := s.RootModule() 1106 1107 newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema.ImpliedType()) 1108 if err != nil { 1109 t.Fatal(err) 1110 } 1111 1112 if !cmp.Equal(readStateVal, newState.Value, valueComparer, equateEmpty) { 1113 t.Fatal(cmp.Diff(readStateVal, newState.Value, valueComparer, equateEmpty)) 1114 } 1115 1116 for _, r := range mod.Resources { 1117 if r.Addr.Resource.Type == "" { 1118 t.Fatalf("no type: %#v", r) 1119 } 1120 } 1121 } 1122 1123 func TestContext2Refresh_orphanModule(t *testing.T) { 1124 p := testProvider("aws") 1125 m := testModule(t, "refresh-module-orphan") 1126 1127 // Create a custom refresh function to track the order they were visited 1128 var order []string 1129 var orderLock sync.Mutex 1130 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 1131 orderLock.Lock() 1132 defer orderLock.Unlock() 1133 1134 order = append(order, req.PriorState.GetAttr("id").AsString()) 1135 return providers.ReadResourceResponse{ 1136 NewState: req.PriorState, 1137 } 1138 } 1139 1140 state := states.NewState() 1141 root := state.EnsureModule(addrs.RootModuleInstance) 1142 root.SetResourceInstanceCurrent( 1143 mustResourceInstanceAddr("aws_instance.foo").Resource, 1144 &states.ResourceInstanceObjectSrc{ 1145 Status: states.ObjectReady, 1146 AttrsJSON: []byte(`{"id":"i-abc123"}`), 1147 Dependencies: []addrs.ConfigResource{ 1148 {Module: addrs.Module{"module.child"}}, 1149 {Module: addrs.Module{"module.child"}}, 1150 }, 1151 }, 1152 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1153 ) 1154 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 1155 child.SetResourceInstanceCurrent( 1156 mustResourceInstanceAddr("aws_instance.bar").Resource, 1157 &states.ResourceInstanceObjectSrc{ 1158 Status: states.ObjectReady, 1159 AttrsJSON: []byte(`{"id":"i-bcd23"}`), 1160 Dependencies: []addrs.ConfigResource{{Module: addrs.Module{"module.grandchild"}}}, 1161 }, 1162 mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), 1163 ) 1164 grandchild := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("grandchild", addrs.NoKey)) 1165 testSetResourceInstanceCurrent(grandchild, "aws_instance.baz", `{"id":"i-cde345"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 1166 1167 ctx := testContext2(t, &ContextOpts{ 1168 Providers: map[addrs.Provider]providers.Factory{ 1169 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1170 }, 1171 }) 1172 1173 testCheckDeadlock(t, func() { 1174 _, err := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1175 if err != nil { 1176 t.Fatalf("err: %s", err.Err()) 1177 } 1178 1179 // TODO: handle order properly for orphaned modules / resources 1180 // expected := []string{"i-abc123", "i-bcd234", "i-cde345"} 1181 // if !reflect.DeepEqual(order, expected) { 1182 // t.Fatalf("expected: %#v, got: %#v", expected, order) 1183 // } 1184 }) 1185 } 1186 1187 func TestContext2Validate(t *testing.T) { 1188 p := testProvider("aws") 1189 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1190 Provider: &configschema.Block{}, 1191 ResourceTypes: map[string]*configschema.Block{ 1192 "aws_instance": { 1193 Attributes: map[string]*configschema.Attribute{ 1194 "foo": { 1195 Type: cty.String, 1196 Optional: true, 1197 }, 1198 "num": { 1199 Type: cty.String, 1200 Optional: true, 1201 }, 1202 }, 1203 }, 1204 }, 1205 }) 1206 1207 m := testModule(t, "validate-good") 1208 c := testContext2(t, &ContextOpts{ 1209 Providers: map[addrs.Provider]providers.Factory{ 1210 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1211 }, 1212 }) 1213 1214 diags := c.Validate(m) 1215 if len(diags) != 0 { 1216 t.Fatalf("unexpected error: %#v", diags.ErrWithWarnings()) 1217 } 1218 } 1219 1220 func TestContext2Refresh_updateProviderInState(t *testing.T) { 1221 m := testModule(t, "update-resource-provider") 1222 p := testProvider("aws") 1223 1224 state := states.NewState() 1225 root := state.EnsureModule(addrs.RootModuleInstance) 1226 testSetResourceInstanceCurrent(root, "aws_instance.bar", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/aws"].baz`) 1227 1228 ctx := testContext2(t, &ContextOpts{ 1229 Providers: map[addrs.Provider]providers.Factory{ 1230 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1231 }, 1232 }) 1233 1234 expected := strings.TrimSpace(` 1235 aws_instance.bar: 1236 ID = foo 1237 provider = provider["registry.terraform.io/hashicorp/aws"].foo`) 1238 1239 s, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1240 if diags.HasErrors() { 1241 t.Fatal(diags.Err()) 1242 } 1243 1244 actual := s.String() 1245 if actual != expected { 1246 t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) 1247 } 1248 } 1249 1250 func TestContext2Refresh_schemaUpgradeFlatmap(t *testing.T) { 1251 m := testModule(t, "refresh-schema-upgrade") 1252 p := testProvider("test") 1253 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1254 ResourceTypes: map[string]*configschema.Block{ 1255 "test_thing": { 1256 Attributes: map[string]*configschema.Attribute{ 1257 "name": { // imagining we renamed this from "id" 1258 Type: cty.String, 1259 Optional: true, 1260 }, 1261 }, 1262 }, 1263 }, 1264 ResourceTypeSchemaVersions: map[string]uint64{ 1265 "test_thing": 5, 1266 }, 1267 }) 1268 p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ 1269 UpgradedState: cty.ObjectVal(map[string]cty.Value{ 1270 "name": cty.StringVal("foo"), 1271 }), 1272 } 1273 1274 s := states.BuildState(func(s *states.SyncState) { 1275 s.SetResourceInstanceCurrent( 1276 addrs.Resource{ 1277 Mode: addrs.ManagedResourceMode, 1278 Type: "test_thing", 1279 Name: "bar", 1280 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1281 &states.ResourceInstanceObjectSrc{ 1282 Status: states.ObjectReady, 1283 SchemaVersion: 3, 1284 AttrsFlat: map[string]string{ 1285 "id": "foo", 1286 }, 1287 }, 1288 addrs.AbsProviderConfig{ 1289 Provider: addrs.NewDefaultProvider("test"), 1290 Module: addrs.RootModule, 1291 }, 1292 ) 1293 }) 1294 1295 ctx := testContext2(t, &ContextOpts{ 1296 Providers: map[addrs.Provider]providers.Factory{ 1297 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1298 }, 1299 }) 1300 1301 state, diags := ctx.Refresh(m, s, &PlanOpts{Mode: plans.NormalMode}) 1302 if diags.HasErrors() { 1303 t.Fatal(diags.Err()) 1304 } 1305 1306 { 1307 got := p.UpgradeResourceStateRequest 1308 want := providers.UpgradeResourceStateRequest{ 1309 TypeName: "test_thing", 1310 Version: 3, 1311 RawStateFlatmap: map[string]string{ 1312 "id": "foo", 1313 }, 1314 } 1315 if !cmp.Equal(got, want) { 1316 t.Errorf("wrong upgrade request\n%s", cmp.Diff(want, got)) 1317 } 1318 } 1319 1320 { 1321 got := state.String() 1322 want := strings.TrimSpace(` 1323 test_thing.bar: 1324 ID = 1325 provider = provider["registry.terraform.io/hashicorp/test"] 1326 name = foo 1327 `) 1328 if got != want { 1329 t.Fatalf("wrong result state\ngot:\n%s\n\nwant:\n%s", got, want) 1330 } 1331 } 1332 } 1333 1334 func TestContext2Refresh_schemaUpgradeJSON(t *testing.T) { 1335 m := testModule(t, "refresh-schema-upgrade") 1336 p := testProvider("test") 1337 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1338 ResourceTypes: map[string]*configschema.Block{ 1339 "test_thing": { 1340 Attributes: map[string]*configschema.Attribute{ 1341 "name": { // imagining we renamed this from "id" 1342 Type: cty.String, 1343 Optional: true, 1344 }, 1345 }, 1346 }, 1347 }, 1348 ResourceTypeSchemaVersions: map[string]uint64{ 1349 "test_thing": 5, 1350 }, 1351 }) 1352 p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ 1353 UpgradedState: cty.ObjectVal(map[string]cty.Value{ 1354 "name": cty.StringVal("foo"), 1355 }), 1356 } 1357 1358 s := states.BuildState(func(s *states.SyncState) { 1359 s.SetResourceInstanceCurrent( 1360 addrs.Resource{ 1361 Mode: addrs.ManagedResourceMode, 1362 Type: "test_thing", 1363 Name: "bar", 1364 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1365 &states.ResourceInstanceObjectSrc{ 1366 Status: states.ObjectReady, 1367 SchemaVersion: 3, 1368 AttrsJSON: []byte(`{"id":"foo"}`), 1369 }, 1370 addrs.AbsProviderConfig{ 1371 Provider: addrs.NewDefaultProvider("test"), 1372 Module: addrs.RootModule, 1373 }, 1374 ) 1375 }) 1376 1377 ctx := testContext2(t, &ContextOpts{ 1378 Providers: map[addrs.Provider]providers.Factory{ 1379 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1380 }, 1381 }) 1382 1383 state, diags := ctx.Refresh(m, s, &PlanOpts{Mode: plans.NormalMode}) 1384 if diags.HasErrors() { 1385 t.Fatal(diags.Err()) 1386 } 1387 1388 { 1389 got := p.UpgradeResourceStateRequest 1390 want := providers.UpgradeResourceStateRequest{ 1391 TypeName: "test_thing", 1392 Version: 3, 1393 RawStateJSON: []byte(`{"id":"foo"}`), 1394 } 1395 if !cmp.Equal(got, want) { 1396 t.Errorf("wrong upgrade request\n%s", cmp.Diff(want, got)) 1397 } 1398 } 1399 1400 { 1401 got := state.String() 1402 want := strings.TrimSpace(` 1403 test_thing.bar: 1404 ID = 1405 provider = provider["registry.terraform.io/hashicorp/test"] 1406 name = foo 1407 `) 1408 if got != want { 1409 t.Fatalf("wrong result state\ngot:\n%s\n\nwant:\n%s", got, want) 1410 } 1411 } 1412 } 1413 1414 func TestContext2Refresh_dataValidation(t *testing.T) { 1415 m := testModuleInline(t, map[string]string{ 1416 "main.tf": ` 1417 data "aws_data_source" "foo" { 1418 foo = "bar" 1419 } 1420 `, 1421 }) 1422 1423 p := testProvider("aws") 1424 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 1425 resp.PlannedState = req.ProposedNewState 1426 return 1427 } 1428 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { 1429 resp.State = req.Config 1430 return 1431 } 1432 1433 ctx := testContext2(t, &ContextOpts{ 1434 Providers: map[addrs.Provider]providers.Factory{ 1435 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1436 }, 1437 }) 1438 1439 _, diags := ctx.Refresh(m, states.NewState(), &PlanOpts{Mode: plans.NormalMode}) 1440 if diags.HasErrors() { 1441 // Should get this error: 1442 // Unsupported attribute: This object does not have an attribute named "missing" 1443 t.Fatal(diags.Err()) 1444 } 1445 1446 if !p.ValidateDataResourceConfigCalled { 1447 t.Fatal("ValidateDataSourceConfig not called during plan") 1448 } 1449 } 1450 1451 func TestContext2Refresh_dataResourceDependsOn(t *testing.T) { 1452 m := testModule(t, "plan-data-depends-on") 1453 p := testProvider("test") 1454 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 1455 ResourceTypes: map[string]*configschema.Block{ 1456 "test_resource": { 1457 Attributes: map[string]*configschema.Attribute{ 1458 "id": {Type: cty.String, Computed: true}, 1459 "foo": {Type: cty.String, Optional: true}, 1460 }, 1461 }, 1462 }, 1463 DataSources: map[string]*configschema.Block{ 1464 "test_data": { 1465 Attributes: map[string]*configschema.Attribute{ 1466 "compute": {Type: cty.String, Computed: true}, 1467 }, 1468 }, 1469 }, 1470 }) 1471 p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ 1472 State: cty.ObjectVal(map[string]cty.Value{ 1473 "compute": cty.StringVal("value"), 1474 }), 1475 } 1476 1477 state := states.NewState() 1478 root := state.EnsureModule(addrs.RootModuleInstance) 1479 testSetResourceInstanceCurrent(root, "test_resource.a", `{"id":"a"}`, `provider["registry.terraform.io/hashicorp/test"]`) 1480 1481 ctx := testContext2(t, &ContextOpts{ 1482 Providers: map[addrs.Provider]providers.Factory{ 1483 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1484 }, 1485 }) 1486 1487 _, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1488 if diags.HasErrors() { 1489 t.Fatalf("unexpected errors: %s", diags.Err()) 1490 } 1491 } 1492 1493 // verify that create_before_destroy is updated in the state during refresh 1494 func TestRefresh_updateLifecycle(t *testing.T) { 1495 state := states.NewState() 1496 root := state.EnsureModule(addrs.RootModuleInstance) 1497 root.SetResourceInstanceCurrent( 1498 addrs.Resource{ 1499 Mode: addrs.ManagedResourceMode, 1500 Type: "aws_instance", 1501 Name: "bar", 1502 }.Instance(addrs.NoKey), 1503 &states.ResourceInstanceObjectSrc{ 1504 Status: states.ObjectReady, 1505 AttrsJSON: []byte(`{"id":"bar"}`), 1506 }, 1507 addrs.AbsProviderConfig{ 1508 Provider: addrs.NewDefaultProvider("aws"), 1509 Module: addrs.RootModule, 1510 }, 1511 ) 1512 1513 m := testModuleInline(t, map[string]string{ 1514 "main.tf": ` 1515 resource "aws_instance" "bar" { 1516 lifecycle { 1517 create_before_destroy = true 1518 } 1519 } 1520 `, 1521 }) 1522 1523 p := testProvider("aws") 1524 1525 ctx := testContext2(t, &ContextOpts{ 1526 Providers: map[addrs.Provider]providers.Factory{ 1527 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1528 }, 1529 }) 1530 1531 state, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1532 if diags.HasErrors() { 1533 t.Fatalf("plan errors: %s", diags.Err()) 1534 } 1535 1536 r := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.bar")) 1537 if !r.Current.CreateBeforeDestroy { 1538 t.Fatal("create_before_destroy not updated in instance state") 1539 } 1540 } 1541 1542 func TestContext2Refresh_dataSourceOrphan(t *testing.T) { 1543 m := testModuleInline(t, map[string]string{ 1544 "main.tf": ``, 1545 }) 1546 1547 state := states.NewState() 1548 root := state.EnsureModule(addrs.RootModuleInstance) 1549 root.SetResourceInstanceCurrent( 1550 addrs.Resource{ 1551 Mode: addrs.DataResourceMode, 1552 Type: "test_data_source", 1553 Name: "foo", 1554 }.Instance(addrs.NoKey), 1555 &states.ResourceInstanceObjectSrc{ 1556 Status: states.ObjectReady, 1557 AttrsJSON: []byte(`{"id":"foo"}`), 1558 Dependencies: []addrs.ConfigResource{}, 1559 }, 1560 addrs.AbsProviderConfig{ 1561 Provider: addrs.NewDefaultProvider("test"), 1562 Module: addrs.RootModule, 1563 }, 1564 ) 1565 p := testProvider("test") 1566 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { 1567 resp.State = cty.NullVal(req.Config.Type()) 1568 return 1569 } 1570 1571 ctx := testContext2(t, &ContextOpts{ 1572 Providers: map[addrs.Provider]providers.Factory{ 1573 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1574 }, 1575 }) 1576 1577 _, diags := ctx.Refresh(m, state, &PlanOpts{Mode: plans.NormalMode}) 1578 if diags.HasErrors() { 1579 t.Fatal(diags.Err()) 1580 } 1581 1582 if p.ReadResourceCalled { 1583 t.Fatal("there are no managed resources to read") 1584 } 1585 1586 if p.ReadDataSourceCalled { 1587 t.Fatal("orphaned data source instance should not be read") 1588 } 1589 }