github.com/ggriffiths/terraform@v0.9.0-beta1.0.20170222213024-79c4935604cb/terraform/context_refresh_test.go (about) 1 package terraform 2 3 import ( 4 "reflect" 5 "sort" 6 "strings" 7 "sync" 8 "testing" 9 ) 10 11 func TestContext2Refresh(t *testing.T) { 12 p := testProvider("aws") 13 m := testModule(t, "refresh-basic") 14 ctx := testContext2(t, &ContextOpts{ 15 Module: m, 16 Providers: map[string]ResourceProviderFactory{ 17 "aws": testProviderFuncFixed(p), 18 }, 19 State: &State{ 20 Modules: []*ModuleState{ 21 &ModuleState{ 22 Path: rootModulePath, 23 Resources: map[string]*ResourceState{ 24 "aws_instance.web": &ResourceState{ 25 Type: "aws_instance", 26 Primary: &InstanceState{ 27 ID: "foo", 28 }, 29 }, 30 }, 31 }, 32 }, 33 }, 34 }) 35 36 p.RefreshFn = nil 37 p.RefreshReturn = &InstanceState{ 38 ID: "foo", 39 } 40 41 s, err := ctx.Refresh() 42 mod := s.RootModule() 43 if err != nil { 44 t.Fatalf("err: %s", err) 45 } 46 if !p.RefreshCalled { 47 t.Fatal("refresh should be called") 48 } 49 if p.RefreshState.ID != "foo" { 50 t.Fatalf("bad: %#v", p.RefreshState) 51 } 52 if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { 53 t.Fatalf("bad: %#v %#v", mod.Resources["aws_instance.web"], p.RefreshReturn) 54 } 55 56 for _, r := range mod.Resources { 57 if r.Type == "" { 58 t.Fatalf("no type: %#v", r) 59 } 60 } 61 } 62 63 func TestContext2Refresh_targeted(t *testing.T) { 64 p := testProvider("aws") 65 m := testModule(t, "refresh-targeted") 66 ctx := testContext2(t, &ContextOpts{ 67 Module: m, 68 Providers: map[string]ResourceProviderFactory{ 69 "aws": testProviderFuncFixed(p), 70 }, 71 State: &State{ 72 Modules: []*ModuleState{ 73 &ModuleState{ 74 Path: rootModulePath, 75 Resources: map[string]*ResourceState{ 76 "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), 77 "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), 78 "aws_instance.me": resourceState("aws_instance", "i-abc123"), 79 "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), 80 }, 81 }, 82 }, 83 }, 84 Targets: []string{"aws_instance.me"}, 85 }) 86 87 refreshedResources := make([]string, 0, 2) 88 p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) { 89 refreshedResources = append(refreshedResources, i.Id) 90 return is, nil 91 } 92 93 _, err := ctx.Refresh() 94 if err != nil { 95 t.Fatalf("err: %s", err) 96 } 97 98 expected := []string{"aws_vpc.metoo", "aws_instance.me"} 99 if !reflect.DeepEqual(refreshedResources, expected) { 100 t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) 101 } 102 } 103 104 func TestContext2Refresh_targetedCount(t *testing.T) { 105 p := testProvider("aws") 106 m := testModule(t, "refresh-targeted-count") 107 ctx := testContext2(t, &ContextOpts{ 108 Module: m, 109 Providers: map[string]ResourceProviderFactory{ 110 "aws": testProviderFuncFixed(p), 111 }, 112 State: &State{ 113 Modules: []*ModuleState{ 114 &ModuleState{ 115 Path: rootModulePath, 116 Resources: map[string]*ResourceState{ 117 "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), 118 "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), 119 "aws_instance.me.0": resourceState("aws_instance", "i-abc123"), 120 "aws_instance.me.1": resourceState("aws_instance", "i-cde567"), 121 "aws_instance.me.2": resourceState("aws_instance", "i-cde789"), 122 "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), 123 }, 124 }, 125 }, 126 }, 127 Targets: []string{"aws_instance.me"}, 128 }) 129 130 refreshedResources := make([]string, 0, 2) 131 p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) { 132 refreshedResources = append(refreshedResources, i.Id) 133 return is, nil 134 } 135 136 _, err := ctx.Refresh() 137 if err != nil { 138 t.Fatalf("err: %s", err) 139 } 140 141 // Target didn't specify index, so we should get all our instances 142 expected := []string{ 143 "aws_vpc.metoo", 144 "aws_instance.me.0", 145 "aws_instance.me.1", 146 "aws_instance.me.2", 147 } 148 sort.Strings(expected) 149 sort.Strings(refreshedResources) 150 if !reflect.DeepEqual(refreshedResources, expected) { 151 t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) 152 } 153 } 154 155 func TestContext2Refresh_targetedCountIndex(t *testing.T) { 156 p := testProvider("aws") 157 m := testModule(t, "refresh-targeted-count") 158 ctx := testContext2(t, &ContextOpts{ 159 Module: m, 160 Providers: map[string]ResourceProviderFactory{ 161 "aws": testProviderFuncFixed(p), 162 }, 163 State: &State{ 164 Modules: []*ModuleState{ 165 &ModuleState{ 166 Path: rootModulePath, 167 Resources: map[string]*ResourceState{ 168 "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), 169 "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), 170 "aws_instance.me.0": resourceState("aws_instance", "i-abc123"), 171 "aws_instance.me.1": resourceState("aws_instance", "i-cde567"), 172 "aws_instance.me.2": resourceState("aws_instance", "i-cde789"), 173 "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), 174 }, 175 }, 176 }, 177 }, 178 Targets: []string{"aws_instance.me[0]"}, 179 }) 180 181 refreshedResources := make([]string, 0, 2) 182 p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) { 183 refreshedResources = append(refreshedResources, i.Id) 184 return is, nil 185 } 186 187 _, err := ctx.Refresh() 188 if err != nil { 189 t.Fatalf("err: %s", err) 190 } 191 192 expected := []string{"aws_vpc.metoo", "aws_instance.me.0"} 193 if !reflect.DeepEqual(refreshedResources, expected) { 194 t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) 195 } 196 } 197 198 func TestContext2Refresh_moduleComputedVar(t *testing.T) { 199 p := testProvider("aws") 200 m := testModule(t, "refresh-module-computed-var") 201 ctx := testContext2(t, &ContextOpts{ 202 Module: m, 203 Providers: map[string]ResourceProviderFactory{ 204 "aws": testProviderFuncFixed(p), 205 }, 206 }) 207 208 // This was failing (see GH-2188) at some point, so this test just 209 // verifies that the failure goes away. 210 if _, err := ctx.Refresh(); err != nil { 211 t.Fatalf("err: %s", err) 212 } 213 } 214 215 func TestContext2Refresh_delete(t *testing.T) { 216 p := testProvider("aws") 217 m := testModule(t, "refresh-basic") 218 ctx := testContext2(t, &ContextOpts{ 219 Module: m, 220 Providers: map[string]ResourceProviderFactory{ 221 "aws": testProviderFuncFixed(p), 222 }, 223 State: &State{ 224 Modules: []*ModuleState{ 225 &ModuleState{ 226 Path: rootModulePath, 227 Resources: map[string]*ResourceState{ 228 "aws_instance.web": &ResourceState{ 229 Type: "aws_instance", 230 Primary: &InstanceState{ 231 ID: "foo", 232 }, 233 }, 234 }, 235 }, 236 }, 237 }, 238 }) 239 240 p.RefreshFn = nil 241 p.RefreshReturn = nil 242 243 s, err := ctx.Refresh() 244 if err != nil { 245 t.Fatalf("err: %s", err) 246 } 247 248 mod := s.RootModule() 249 if len(mod.Resources) > 0 { 250 t.Fatal("resources should be empty") 251 } 252 } 253 254 func TestContext2Refresh_ignoreUncreated(t *testing.T) { 255 p := testProvider("aws") 256 m := testModule(t, "refresh-basic") 257 ctx := testContext2(t, &ContextOpts{ 258 Module: m, 259 Providers: map[string]ResourceProviderFactory{ 260 "aws": testProviderFuncFixed(p), 261 }, 262 State: nil, 263 }) 264 265 p.RefreshFn = nil 266 p.RefreshReturn = &InstanceState{ 267 ID: "foo", 268 } 269 270 _, err := ctx.Refresh() 271 if err != nil { 272 t.Fatalf("err: %s", err) 273 } 274 if p.RefreshCalled { 275 t.Fatal("refresh should not be called") 276 } 277 } 278 279 func TestContext2Refresh_hook(t *testing.T) { 280 h := new(MockHook) 281 p := testProvider("aws") 282 m := testModule(t, "refresh-basic") 283 ctx := testContext2(t, &ContextOpts{ 284 Module: m, 285 Hooks: []Hook{h}, 286 Providers: map[string]ResourceProviderFactory{ 287 "aws": testProviderFuncFixed(p), 288 }, 289 State: &State{ 290 Modules: []*ModuleState{ 291 &ModuleState{ 292 Path: rootModulePath, 293 Resources: map[string]*ResourceState{ 294 "aws_instance.web": &ResourceState{ 295 Type: "aws_instance", 296 Primary: &InstanceState{ 297 ID: "foo", 298 }, 299 }, 300 }, 301 }, 302 }, 303 }, 304 }) 305 306 if _, err := ctx.Refresh(); err != nil { 307 t.Fatalf("err: %s", err) 308 } 309 if !h.PreRefreshCalled { 310 t.Fatal("should be called") 311 } 312 if !h.PostRefreshCalled { 313 t.Fatal("should be called") 314 } 315 } 316 317 func TestContext2Refresh_modules(t *testing.T) { 318 p := testProvider("aws") 319 m := testModule(t, "refresh-modules") 320 state := &State{ 321 Modules: []*ModuleState{ 322 &ModuleState{ 323 Path: rootModulePath, 324 Resources: map[string]*ResourceState{ 325 "aws_instance.web": &ResourceState{ 326 Type: "aws_instance", 327 Primary: &InstanceState{ 328 ID: "bar", 329 Tainted: true, 330 }, 331 }, 332 }, 333 }, 334 335 &ModuleState{ 336 Path: []string{"root", "child"}, 337 Resources: map[string]*ResourceState{ 338 "aws_instance.web": &ResourceState{ 339 Type: "aws_instance", 340 Primary: &InstanceState{ 341 ID: "baz", 342 }, 343 }, 344 }, 345 }, 346 }, 347 } 348 ctx := testContext2(t, &ContextOpts{ 349 Module: m, 350 Providers: map[string]ResourceProviderFactory{ 351 "aws": testProviderFuncFixed(p), 352 }, 353 State: state, 354 }) 355 356 p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { 357 if s.ID != "baz" { 358 return s, nil 359 } 360 361 s.ID = "new" 362 return s, nil 363 } 364 365 s, err := ctx.Refresh() 366 if err != nil { 367 t.Fatalf("err: %s", err) 368 } 369 370 actual := strings.TrimSpace(s.String()) 371 expected := strings.TrimSpace(testContextRefreshModuleStr) 372 if actual != expected { 373 t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) 374 } 375 } 376 377 func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { 378 m := testModule(t, "refresh-module-input-computed-output") 379 p := testProvider("aws") 380 p.DiffFn = testDiffFn 381 ctx := testContext2(t, &ContextOpts{ 382 Module: m, 383 Providers: map[string]ResourceProviderFactory{ 384 "aws": testProviderFuncFixed(p), 385 }, 386 }) 387 388 if _, err := ctx.Refresh(); err != nil { 389 t.Fatalf("err: %s", err) 390 } 391 } 392 393 func TestContext2Refresh_moduleVarModule(t *testing.T) { 394 m := testModule(t, "refresh-module-var-module") 395 p := testProvider("aws") 396 p.DiffFn = testDiffFn 397 ctx := testContext2(t, &ContextOpts{ 398 Module: m, 399 Providers: map[string]ResourceProviderFactory{ 400 "aws": testProviderFuncFixed(p), 401 }, 402 }) 403 404 if _, err := ctx.Refresh(); err != nil { 405 t.Fatalf("err: %s", err) 406 } 407 } 408 409 // GH-70 410 func TestContext2Refresh_noState(t *testing.T) { 411 p := testProvider("aws") 412 m := testModule(t, "refresh-no-state") 413 ctx := testContext2(t, &ContextOpts{ 414 Module: m, 415 Providers: map[string]ResourceProviderFactory{ 416 "aws": testProviderFuncFixed(p), 417 }, 418 }) 419 420 p.RefreshFn = nil 421 p.RefreshReturn = &InstanceState{ 422 ID: "foo", 423 } 424 425 if _, err := ctx.Refresh(); err != nil { 426 t.Fatalf("err: %s", err) 427 } 428 } 429 430 func TestContext2Refresh_output(t *testing.T) { 431 p := testProvider("aws") 432 m := testModule(t, "refresh-output") 433 ctx := testContext2(t, &ContextOpts{ 434 Module: m, 435 Providers: map[string]ResourceProviderFactory{ 436 "aws": testProviderFuncFixed(p), 437 }, 438 State: &State{ 439 Modules: []*ModuleState{ 440 &ModuleState{ 441 Path: rootModulePath, 442 Resources: map[string]*ResourceState{ 443 "aws_instance.web": &ResourceState{ 444 Type: "aws_instance", 445 Primary: &InstanceState{ 446 ID: "foo", 447 Attributes: map[string]string{ 448 "foo": "bar", 449 }, 450 }, 451 }, 452 }, 453 454 Outputs: map[string]*OutputState{ 455 "foo": &OutputState{ 456 Type: "string", 457 Sensitive: false, 458 Value: "foo", 459 }, 460 }, 461 }, 462 }, 463 }, 464 }) 465 466 p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { 467 return s, nil 468 } 469 470 s, err := ctx.Refresh() 471 if err != nil { 472 t.Fatalf("err: %s", err) 473 } 474 475 actual := strings.TrimSpace(s.String()) 476 expected := strings.TrimSpace(testContextRefreshOutputStr) 477 if actual != expected { 478 t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) 479 } 480 } 481 482 func TestContext2Refresh_outputPartial(t *testing.T) { 483 p := testProvider("aws") 484 m := testModule(t, "refresh-output-partial") 485 ctx := testContext2(t, &ContextOpts{ 486 Module: m, 487 Providers: map[string]ResourceProviderFactory{ 488 "aws": testProviderFuncFixed(p), 489 }, 490 State: &State{ 491 Modules: []*ModuleState{ 492 &ModuleState{ 493 Path: rootModulePath, 494 Resources: map[string]*ResourceState{ 495 "aws_instance.foo": &ResourceState{ 496 Type: "aws_instance", 497 Primary: &InstanceState{ 498 ID: "foo", 499 }, 500 }, 501 }, 502 Outputs: map[string]*OutputState{}, 503 }, 504 }, 505 }, 506 }) 507 508 p.RefreshFn = nil 509 p.RefreshReturn = nil 510 511 s, err := ctx.Refresh() 512 if err != nil { 513 t.Fatalf("err: %s", err) 514 } 515 516 actual := strings.TrimSpace(s.String()) 517 expected := strings.TrimSpace(testContextRefreshOutputPartialStr) 518 if actual != expected { 519 t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) 520 } 521 } 522 523 func TestContext2Refresh_stateBasic(t *testing.T) { 524 p := testProvider("aws") 525 m := testModule(t, "refresh-basic") 526 state := &State{ 527 Modules: []*ModuleState{ 528 &ModuleState{ 529 Path: rootModulePath, 530 Resources: map[string]*ResourceState{ 531 "aws_instance.web": &ResourceState{ 532 Type: "aws_instance", 533 Primary: &InstanceState{ 534 ID: "bar", 535 }, 536 }, 537 }, 538 }, 539 }, 540 } 541 ctx := testContext2(t, &ContextOpts{ 542 Module: m, 543 Providers: map[string]ResourceProviderFactory{ 544 "aws": testProviderFuncFixed(p), 545 }, 546 State: state, 547 }) 548 549 p.RefreshFn = nil 550 p.RefreshReturn = &InstanceState{ 551 ID: "foo", 552 } 553 554 s, err := ctx.Refresh() 555 if err != nil { 556 t.Fatalf("err: %s", err) 557 } 558 originalMod := state.RootModule() 559 mod := s.RootModule() 560 if !p.RefreshCalled { 561 t.Fatal("refresh should be called") 562 } 563 if !reflect.DeepEqual(p.RefreshState, originalMod.Resources["aws_instance.web"].Primary) { 564 t.Fatalf( 565 "bad:\n\n%#v\n\n%#v", 566 p.RefreshState, 567 originalMod.Resources["aws_instance.web"].Primary) 568 } 569 if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { 570 t.Fatalf("bad: %#v", mod.Resources) 571 } 572 } 573 574 func TestContext2Refresh_dataOrphan(t *testing.T) { 575 p := testProvider("null") 576 state := &State{ 577 Modules: []*ModuleState{ 578 &ModuleState{ 579 Path: rootModulePath, 580 Resources: map[string]*ResourceState{ 581 "data.null_data_source.bar": &ResourceState{ 582 Type: "foo", 583 Primary: &InstanceState{ 584 ID: "foo", 585 }, 586 }, 587 }, 588 }, 589 }, 590 } 591 ctx := testContext2(t, &ContextOpts{ 592 Providers: map[string]ResourceProviderFactory{ 593 "null": testProviderFuncFixed(p), 594 }, 595 State: state, 596 }) 597 598 s, err := ctx.Refresh() 599 if err != nil { 600 t.Fatalf("err: %s", err) 601 } 602 603 checkStateString(t, s, `<no state>`) 604 } 605 606 func TestContext2Refresh_dataState(t *testing.T) { 607 p := testProvider("null") 608 m := testModule(t, "refresh-data-resource-basic") 609 state := &State{ 610 Modules: []*ModuleState{ 611 &ModuleState{ 612 Path: rootModulePath, 613 // Intentionally no resources since data resources are 614 // supposed to refresh themselves even if they didn't 615 // already exist. 616 Resources: map[string]*ResourceState{}, 617 }, 618 }, 619 } 620 ctx := testContext2(t, &ContextOpts{ 621 Module: m, 622 Providers: map[string]ResourceProviderFactory{ 623 "null": testProviderFuncFixed(p), 624 }, 625 State: state, 626 }) 627 628 p.ReadDataDiffFn = nil 629 p.ReadDataDiffReturn = &InstanceDiff{ 630 Attributes: map[string]*ResourceAttrDiff{ 631 "inputs.#": { 632 Old: "0", 633 New: "1", 634 Type: DiffAttrInput, 635 }, 636 "inputs.test": { 637 Old: "", 638 New: "yes", 639 Type: DiffAttrInput, 640 }, 641 "outputs.#": { 642 Old: "", 643 New: "", 644 NewComputed: true, 645 Type: DiffAttrOutput, 646 }, 647 }, 648 } 649 650 p.ReadDataApplyFn = nil 651 p.ReadDataApplyReturn = &InstanceState{ 652 ID: "-", 653 } 654 655 s, err := ctx.Refresh() 656 if err != nil { 657 t.Fatalf("err: %s", err) 658 } 659 660 if !p.ReadDataDiffCalled { 661 t.Fatal("ReadDataDiff should have been called") 662 } 663 if !p.ReadDataApplyCalled { 664 t.Fatal("ReadDataApply should have been called") 665 } 666 667 mod := s.RootModule() 668 if got := mod.Resources["data.null_data_source.testing"].Primary.ID; got != "-" { 669 t.Fatalf("resource id is %q; want %s", got, "-") 670 } 671 if !reflect.DeepEqual(mod.Resources["data.null_data_source.testing"].Primary, p.ReadDataApplyReturn) { 672 t.Fatalf("bad: %#v", mod.Resources) 673 } 674 } 675 676 func TestContext2Refresh_dataStateRefData(t *testing.T) { 677 p := testProvider("null") 678 m := testModule(t, "refresh-data-ref-data") 679 state := &State{ 680 Modules: []*ModuleState{ 681 &ModuleState{ 682 Path: rootModulePath, 683 // Intentionally no resources since data resources are 684 // supposed to refresh themselves even if they didn't 685 // already exist. 686 Resources: map[string]*ResourceState{}, 687 }, 688 }, 689 } 690 ctx := testContext2(t, &ContextOpts{ 691 Module: m, 692 Providers: map[string]ResourceProviderFactory{ 693 "null": testProviderFuncFixed(p), 694 }, 695 State: state, 696 }) 697 698 p.ReadDataDiffFn = testDataDiffFn 699 p.ReadDataApplyFn = testDataApplyFn 700 701 s, err := ctx.Refresh() 702 if err != nil { 703 t.Fatalf("err: %s", err) 704 } 705 706 actual := strings.TrimSpace(s.String()) 707 expected := strings.TrimSpace(testTerraformRefreshDataRefDataStr) 708 if actual != expected { 709 t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) 710 } 711 } 712 713 func TestContext2Refresh_tainted(t *testing.T) { 714 p := testProvider("aws") 715 m := testModule(t, "refresh-basic") 716 state := &State{ 717 Modules: []*ModuleState{ 718 &ModuleState{ 719 Path: rootModulePath, 720 Resources: map[string]*ResourceState{ 721 "aws_instance.web": &ResourceState{ 722 Type: "aws_instance", 723 Primary: &InstanceState{ 724 ID: "bar", 725 Tainted: true, 726 }, 727 }, 728 }, 729 }, 730 }, 731 } 732 ctx := testContext2(t, &ContextOpts{ 733 Module: m, 734 Providers: map[string]ResourceProviderFactory{ 735 "aws": testProviderFuncFixed(p), 736 }, 737 State: state, 738 }) 739 740 p.RefreshFn = nil 741 p.RefreshReturn = &InstanceState{ 742 ID: "foo", 743 Tainted: true, 744 } 745 746 s, err := ctx.Refresh() 747 if err != nil { 748 t.Fatalf("err: %s", err) 749 } 750 if !p.RefreshCalled { 751 t.Fatal("refresh should be called") 752 } 753 754 actual := strings.TrimSpace(s.String()) 755 expected := strings.TrimSpace(testContextRefreshTaintedStr) 756 if actual != expected { 757 t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) 758 } 759 } 760 761 // Doing a Refresh (or any operation really, but Refresh usually 762 // happens first) with a config with an unknown provider should result in 763 // an error. The key bug this found was that this wasn't happening if 764 // Providers was _empty_. 765 func TestContext2Refresh_unknownProvider(t *testing.T) { 766 m := testModule(t, "refresh-unknown-provider") 767 p := testProvider("aws") 768 p.ApplyFn = testApplyFn 769 p.DiffFn = testDiffFn 770 ctx := testContext2(t, &ContextOpts{ 771 Module: m, 772 Providers: map[string]ResourceProviderFactory{}, 773 State: &State{ 774 Modules: []*ModuleState{ 775 &ModuleState{ 776 Path: rootModulePath, 777 Resources: map[string]*ResourceState{ 778 "aws_instance.web": &ResourceState{ 779 Type: "aws_instance", 780 Primary: &InstanceState{ 781 ID: "foo", 782 }, 783 }, 784 }, 785 }, 786 }, 787 }, 788 }) 789 790 if _, err := ctx.Refresh(); err == nil { 791 t.Fatal("should error") 792 } 793 } 794 795 func TestContext2Refresh_vars(t *testing.T) { 796 p := testProvider("aws") 797 m := testModule(t, "refresh-vars") 798 ctx := testContext2(t, &ContextOpts{ 799 Module: m, 800 Providers: map[string]ResourceProviderFactory{ 801 "aws": testProviderFuncFixed(p), 802 }, 803 State: &State{ 804 805 Modules: []*ModuleState{ 806 &ModuleState{ 807 Path: rootModulePath, 808 Resources: map[string]*ResourceState{ 809 "aws_instance.web": &ResourceState{ 810 Type: "aws_instance", 811 Primary: &InstanceState{ 812 ID: "foo", 813 }, 814 }, 815 }, 816 }, 817 }, 818 }, 819 }) 820 821 p.RefreshFn = nil 822 p.RefreshReturn = &InstanceState{ 823 ID: "foo", 824 } 825 826 s, err := ctx.Refresh() 827 if err != nil { 828 t.Fatalf("err: %s", err) 829 } 830 mod := s.RootModule() 831 if !p.RefreshCalled { 832 t.Fatal("refresh should be called") 833 } 834 if p.RefreshState.ID != "foo" { 835 t.Fatalf("bad: %#v", p.RefreshState) 836 } 837 if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { 838 t.Fatalf("bad: %#v", mod.Resources["aws_instance.web"]) 839 } 840 841 for _, r := range mod.Resources { 842 if r.Type == "" { 843 t.Fatalf("no type: %#v", r) 844 } 845 } 846 } 847 848 func TestContext2Refresh_orphanModule(t *testing.T) { 849 p := testProvider("aws") 850 m := testModule(t, "refresh-module-orphan") 851 852 // Create a custom refresh function to track the order they were visited 853 var order []string 854 var orderLock sync.Mutex 855 p.RefreshFn = func( 856 info *InstanceInfo, 857 is *InstanceState) (*InstanceState, error) { 858 orderLock.Lock() 859 defer orderLock.Unlock() 860 861 order = append(order, is.ID) 862 return is, nil 863 } 864 865 state := &State{ 866 Modules: []*ModuleState{ 867 &ModuleState{ 868 Path: rootModulePath, 869 Resources: map[string]*ResourceState{ 870 "aws_instance.foo": &ResourceState{ 871 Primary: &InstanceState{ 872 ID: "i-abc123", 873 Attributes: map[string]string{ 874 "childid": "i-bcd234", 875 "grandchildid": "i-cde345", 876 }, 877 }, 878 Dependencies: []string{ 879 "module.child", 880 "module.child", 881 }, 882 }, 883 }, 884 }, 885 &ModuleState{ 886 Path: append(rootModulePath, "child"), 887 Resources: map[string]*ResourceState{ 888 "aws_instance.bar": &ResourceState{ 889 Primary: &InstanceState{ 890 ID: "i-bcd234", 891 Attributes: map[string]string{ 892 "grandchildid": "i-cde345", 893 }, 894 }, 895 Dependencies: []string{ 896 "module.grandchild", 897 }, 898 }, 899 }, 900 Outputs: map[string]*OutputState{ 901 "id": &OutputState{ 902 Value: "i-bcd234", 903 Type: "string", 904 }, 905 "grandchild_id": &OutputState{ 906 Value: "i-cde345", 907 Type: "string", 908 }, 909 }, 910 }, 911 &ModuleState{ 912 Path: append(rootModulePath, "child", "grandchild"), 913 Resources: map[string]*ResourceState{ 914 "aws_instance.baz": &ResourceState{ 915 Primary: &InstanceState{ 916 ID: "i-cde345", 917 }, 918 }, 919 }, 920 Outputs: map[string]*OutputState{ 921 "id": &OutputState{ 922 Value: "i-cde345", 923 Type: "string", 924 }, 925 }, 926 }, 927 }, 928 } 929 ctx := testContext2(t, &ContextOpts{ 930 Module: m, 931 Providers: map[string]ResourceProviderFactory{ 932 "aws": testProviderFuncFixed(p), 933 }, 934 State: state, 935 }) 936 937 testCheckDeadlock(t, func() { 938 _, err := ctx.Refresh() 939 if err != nil { 940 t.Fatalf("err: %s", err) 941 } 942 943 // TODO: handle order properly for orphaned modules / resources 944 // expected := []string{"i-abc123", "i-bcd234", "i-cde345"} 945 // if !reflect.DeepEqual(order, expected) { 946 // t.Fatalf("expected: %#v, got: %#v", expected, order) 947 // } 948 }) 949 } 950 951 func TestContext2Validate(t *testing.T) { 952 p := testProvider("aws") 953 m := testModule(t, "validate-good") 954 c := testContext2(t, &ContextOpts{ 955 Module: m, 956 Providers: map[string]ResourceProviderFactory{ 957 "aws": testProviderFuncFixed(p), 958 }, 959 }) 960 961 w, e := c.Validate() 962 if len(w) > 0 { 963 t.Fatalf("bad: %#v", w) 964 } 965 if len(e) > 0 { 966 t.Fatalf("bad: %s", e) 967 } 968 }