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