github.com/federicobaldo/terraform@v0.6.15-0.20160323222747-b20f680cbf05/terraform/state_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "reflect" 7 "strings" 8 "testing" 9 10 "github.com/hashicorp/terraform/config" 11 ) 12 13 func TestStateAddModule(t *testing.T) { 14 cases := []struct { 15 In [][]string 16 Out [][]string 17 }{ 18 { 19 [][]string{ 20 []string{"root"}, 21 []string{"root", "child"}, 22 }, 23 [][]string{ 24 []string{"root"}, 25 []string{"root", "child"}, 26 }, 27 }, 28 29 { 30 [][]string{ 31 []string{"root", "foo", "bar"}, 32 []string{"root", "foo"}, 33 []string{"root"}, 34 []string{"root", "bar"}, 35 }, 36 [][]string{ 37 []string{"root"}, 38 []string{"root", "bar"}, 39 []string{"root", "foo"}, 40 []string{"root", "foo", "bar"}, 41 }, 42 }, 43 // Same last element, different middle element 44 { 45 [][]string{ 46 []string{"root", "foo", "bar"}, // This one should sort after... 47 []string{"root", "foo"}, 48 []string{"root"}, 49 []string{"root", "bar", "bar"}, // ...this one. 50 []string{"root", "bar"}, 51 }, 52 [][]string{ 53 []string{"root"}, 54 []string{"root", "bar"}, 55 []string{"root", "foo"}, 56 []string{"root", "bar", "bar"}, 57 []string{"root", "foo", "bar"}, 58 }, 59 }, 60 } 61 62 for _, tc := range cases { 63 s := new(State) 64 for _, p := range tc.In { 65 s.AddModule(p) 66 } 67 68 actual := make([][]string, 0, len(tc.In)) 69 for _, m := range s.Modules { 70 actual = append(actual, m.Path) 71 } 72 73 if !reflect.DeepEqual(actual, tc.Out) { 74 t.Fatalf("In: %#v\n\nOut: %#v", tc.In, actual) 75 } 76 } 77 } 78 79 func TestStateModuleOrphans(t *testing.T) { 80 state := &State{ 81 Modules: []*ModuleState{ 82 &ModuleState{ 83 Path: RootModulePath, 84 }, 85 &ModuleState{ 86 Path: []string{RootModuleName, "foo"}, 87 }, 88 &ModuleState{ 89 Path: []string{RootModuleName, "bar"}, 90 }, 91 }, 92 } 93 94 config := testModule(t, "state-module-orphans").Config() 95 actual := state.ModuleOrphans(RootModulePath, config) 96 expected := [][]string{ 97 []string{RootModuleName, "foo"}, 98 } 99 100 if !reflect.DeepEqual(actual, expected) { 101 t.Fatalf("bad: %#v", actual) 102 } 103 } 104 105 func TestStateModuleOrphans_nested(t *testing.T) { 106 state := &State{ 107 Modules: []*ModuleState{ 108 &ModuleState{ 109 Path: RootModulePath, 110 }, 111 &ModuleState{ 112 Path: []string{RootModuleName, "foo", "bar"}, 113 }, 114 }, 115 } 116 117 actual := state.ModuleOrphans(RootModulePath, nil) 118 expected := [][]string{ 119 []string{RootModuleName, "foo"}, 120 } 121 122 if !reflect.DeepEqual(actual, expected) { 123 t.Fatalf("bad: %#v", actual) 124 } 125 } 126 127 func TestStateModuleOrphans_nilConfig(t *testing.T) { 128 state := &State{ 129 Modules: []*ModuleState{ 130 &ModuleState{ 131 Path: RootModulePath, 132 }, 133 &ModuleState{ 134 Path: []string{RootModuleName, "foo"}, 135 }, 136 &ModuleState{ 137 Path: []string{RootModuleName, "bar"}, 138 }, 139 }, 140 } 141 142 actual := state.ModuleOrphans(RootModulePath, nil) 143 expected := [][]string{ 144 []string{RootModuleName, "foo"}, 145 []string{RootModuleName, "bar"}, 146 } 147 148 if !reflect.DeepEqual(actual, expected) { 149 t.Fatalf("bad: %#v", actual) 150 } 151 } 152 153 func TestStateModuleOrphans_deepNestedNilConfig(t *testing.T) { 154 state := &State{ 155 Modules: []*ModuleState{ 156 &ModuleState{ 157 Path: RootModulePath, 158 }, 159 &ModuleState{ 160 Path: []string{RootModuleName, "parent", "childfoo"}, 161 }, 162 &ModuleState{ 163 Path: []string{RootModuleName, "parent", "childbar"}, 164 }, 165 }, 166 } 167 168 actual := state.ModuleOrphans(RootModulePath, nil) 169 expected := [][]string{ 170 []string{RootModuleName, "parent"}, 171 } 172 173 if !reflect.DeepEqual(actual, expected) { 174 t.Fatalf("bad: %#v", actual) 175 } 176 } 177 178 func TestStateEqual(t *testing.T) { 179 cases := []struct { 180 Result bool 181 One, Two *State 182 }{ 183 // Nils 184 { 185 false, 186 nil, 187 &State{Version: 2}, 188 }, 189 190 { 191 true, 192 nil, 193 nil, 194 }, 195 196 // Different versions 197 { 198 false, 199 &State{Version: 5}, 200 &State{Version: 2}, 201 }, 202 203 // Different modules 204 { 205 false, 206 &State{ 207 Modules: []*ModuleState{ 208 &ModuleState{ 209 Path: RootModulePath, 210 }, 211 }, 212 }, 213 &State{}, 214 }, 215 216 { 217 true, 218 &State{ 219 Modules: []*ModuleState{ 220 &ModuleState{ 221 Path: RootModulePath, 222 }, 223 }, 224 }, 225 &State{ 226 Modules: []*ModuleState{ 227 &ModuleState{ 228 Path: RootModulePath, 229 }, 230 }, 231 }, 232 }, 233 234 // Meta differs 235 { 236 false, 237 &State{ 238 Modules: []*ModuleState{ 239 &ModuleState{ 240 Path: rootModulePath, 241 Resources: map[string]*ResourceState{ 242 "test_instance.foo": &ResourceState{ 243 Primary: &InstanceState{ 244 Meta: map[string]string{ 245 "schema_version": "1", 246 }, 247 }, 248 }, 249 }, 250 }, 251 }, 252 }, 253 &State{ 254 Modules: []*ModuleState{ 255 &ModuleState{ 256 Path: rootModulePath, 257 Resources: map[string]*ResourceState{ 258 "test_instance.foo": &ResourceState{ 259 Primary: &InstanceState{ 260 Meta: map[string]string{ 261 "schema_version": "2", 262 }, 263 }, 264 }, 265 }, 266 }, 267 }, 268 }, 269 }, 270 } 271 272 for i, tc := range cases { 273 if tc.One.Equal(tc.Two) != tc.Result { 274 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 275 } 276 if tc.Two.Equal(tc.One) != tc.Result { 277 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 278 } 279 } 280 } 281 282 func TestStateIncrementSerialMaybe(t *testing.T) { 283 cases := map[string]struct { 284 S1, S2 *State 285 Serial int64 286 }{ 287 "S2 is nil": { 288 &State{}, 289 nil, 290 0, 291 }, 292 "S2 is identical": { 293 &State{}, 294 &State{}, 295 0, 296 }, 297 "S2 is different": { 298 &State{}, 299 &State{ 300 Modules: []*ModuleState{ 301 &ModuleState{Path: rootModulePath}, 302 }, 303 }, 304 1, 305 }, 306 "S2 is different, but only via Instance Metadata": { 307 &State{ 308 Serial: 3, 309 Modules: []*ModuleState{ 310 &ModuleState{ 311 Path: rootModulePath, 312 Resources: map[string]*ResourceState{ 313 "test_instance.foo": &ResourceState{ 314 Primary: &InstanceState{ 315 Meta: map[string]string{}, 316 }, 317 }, 318 }, 319 }, 320 }, 321 }, 322 &State{ 323 Serial: 3, 324 Modules: []*ModuleState{ 325 &ModuleState{ 326 Path: rootModulePath, 327 Resources: map[string]*ResourceState{ 328 "test_instance.foo": &ResourceState{ 329 Primary: &InstanceState{ 330 Meta: map[string]string{ 331 "schema_version": "1", 332 }, 333 }, 334 }, 335 }, 336 }, 337 }, 338 }, 339 4, 340 }, 341 "S1 serial is higher": { 342 &State{Serial: 5}, 343 &State{ 344 Serial: 3, 345 Modules: []*ModuleState{ 346 &ModuleState{Path: rootModulePath}, 347 }, 348 }, 349 5, 350 }, 351 } 352 353 for name, tc := range cases { 354 tc.S1.IncrementSerialMaybe(tc.S2) 355 if tc.S1.Serial != tc.Serial { 356 t.Fatalf("Bad: %s\nGot: %d", name, tc.S1.Serial) 357 } 358 } 359 } 360 361 func TestResourceStateEqual(t *testing.T) { 362 cases := []struct { 363 Result bool 364 One, Two *ResourceState 365 }{ 366 // Different types 367 { 368 false, 369 &ResourceState{Type: "foo"}, 370 &ResourceState{Type: "bar"}, 371 }, 372 373 // Different dependencies 374 { 375 false, 376 &ResourceState{Dependencies: []string{"foo"}}, 377 &ResourceState{Dependencies: []string{"bar"}}, 378 }, 379 380 { 381 false, 382 &ResourceState{Dependencies: []string{"foo", "bar"}}, 383 &ResourceState{Dependencies: []string{"foo"}}, 384 }, 385 386 { 387 true, 388 &ResourceState{Dependencies: []string{"bar", "foo"}}, 389 &ResourceState{Dependencies: []string{"foo", "bar"}}, 390 }, 391 392 // Different primaries 393 { 394 false, 395 &ResourceState{Primary: nil}, 396 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 397 }, 398 399 { 400 true, 401 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 402 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 403 }, 404 405 // Different tainted 406 { 407 false, 408 &ResourceState{ 409 Tainted: nil, 410 }, 411 &ResourceState{ 412 Tainted: []*InstanceState{ 413 &InstanceState{ID: "foo"}, 414 }, 415 }, 416 }, 417 418 { 419 true, 420 &ResourceState{ 421 Tainted: []*InstanceState{ 422 &InstanceState{ID: "foo"}, 423 }, 424 }, 425 &ResourceState{ 426 Tainted: []*InstanceState{ 427 &InstanceState{ID: "foo"}, 428 }, 429 }, 430 }, 431 432 { 433 true, 434 &ResourceState{ 435 Tainted: []*InstanceState{ 436 &InstanceState{ID: "foo"}, 437 nil, 438 }, 439 }, 440 &ResourceState{ 441 Tainted: []*InstanceState{ 442 &InstanceState{ID: "foo"}, 443 }, 444 }, 445 }, 446 } 447 448 for i, tc := range cases { 449 if tc.One.Equal(tc.Two) != tc.Result { 450 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 451 } 452 if tc.Two.Equal(tc.One) != tc.Result { 453 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 454 } 455 } 456 } 457 458 func TestResourceStateTaint(t *testing.T) { 459 cases := map[string]struct { 460 Input *ResourceState 461 Output *ResourceState 462 }{ 463 "no primary": { 464 &ResourceState{}, 465 &ResourceState{}, 466 }, 467 468 "primary, no tainted": { 469 &ResourceState{ 470 Primary: &InstanceState{ID: "foo"}, 471 }, 472 &ResourceState{ 473 Tainted: []*InstanceState{ 474 &InstanceState{ID: "foo"}, 475 }, 476 }, 477 }, 478 479 "primary, with tainted": { 480 &ResourceState{ 481 Primary: &InstanceState{ID: "foo"}, 482 Tainted: []*InstanceState{ 483 &InstanceState{ID: "bar"}, 484 }, 485 }, 486 &ResourceState{ 487 Tainted: []*InstanceState{ 488 &InstanceState{ID: "bar"}, 489 &InstanceState{ID: "foo"}, 490 }, 491 }, 492 }, 493 } 494 495 for k, tc := range cases { 496 tc.Input.Taint() 497 if !reflect.DeepEqual(tc.Input, tc.Output) { 498 t.Fatalf( 499 "Failure: %s\n\nExpected: %#v\n\nGot: %#v", 500 k, tc.Output, tc.Input) 501 } 502 } 503 } 504 505 func TestResourceStateUntaint(t *testing.T) { 506 cases := map[string]struct { 507 Input *ResourceState 508 Index func() int 509 ExpectedOutput *ResourceState 510 ExpectedErrMsg string 511 }{ 512 "no primary, no tainted, err": { 513 Input: &ResourceState{}, 514 ExpectedOutput: &ResourceState{}, 515 ExpectedErrMsg: "Nothing to untaint", 516 }, 517 518 "one tainted, no primary": { 519 Input: &ResourceState{ 520 Tainted: []*InstanceState{ 521 &InstanceState{ID: "foo"}, 522 }, 523 }, 524 ExpectedOutput: &ResourceState{ 525 Primary: &InstanceState{ID: "foo"}, 526 Tainted: []*InstanceState{}, 527 }, 528 }, 529 530 "one tainted, existing primary error": { 531 Input: &ResourceState{ 532 Primary: &InstanceState{ID: "foo"}, 533 Tainted: []*InstanceState{ 534 &InstanceState{ID: "foo"}, 535 }, 536 }, 537 ExpectedErrMsg: "Resource has a primary", 538 }, 539 540 "multiple tainted, no index": { 541 Input: &ResourceState{ 542 Tainted: []*InstanceState{ 543 &InstanceState{ID: "bar"}, 544 &InstanceState{ID: "foo"}, 545 }, 546 }, 547 ExpectedErrMsg: "please specify an index", 548 }, 549 550 "multiple tainted, with index": { 551 Input: &ResourceState{ 552 Tainted: []*InstanceState{ 553 &InstanceState{ID: "bar"}, 554 &InstanceState{ID: "foo"}, 555 }, 556 }, 557 Index: func() int { return 1 }, 558 ExpectedOutput: &ResourceState{ 559 Primary: &InstanceState{ID: "foo"}, 560 Tainted: []*InstanceState{ 561 &InstanceState{ID: "bar"}, 562 }, 563 }, 564 }, 565 566 "index out of bounds error": { 567 Input: &ResourceState{ 568 Tainted: []*InstanceState{ 569 &InstanceState{ID: "bar"}, 570 &InstanceState{ID: "foo"}, 571 }, 572 }, 573 Index: func() int { return 2 }, 574 ExpectedErrMsg: "out of range", 575 }, 576 } 577 578 for k, tc := range cases { 579 index := -1 580 if tc.Index != nil { 581 index = tc.Index() 582 } 583 err := tc.Input.Untaint(index) 584 if tc.ExpectedErrMsg == "" && err != nil { 585 t.Fatalf("[%s] unexpected err: %s", k, err) 586 } 587 if tc.ExpectedErrMsg != "" { 588 if strings.Contains(err.Error(), tc.ExpectedErrMsg) { 589 continue 590 } 591 t.Fatalf("[%s] expected err: %s to contain: %s", 592 k, err, tc.ExpectedErrMsg) 593 } 594 if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) { 595 t.Fatalf( 596 "Failure: %s\n\nExpected: %#v\n\nGot: %#v", 597 k, tc.ExpectedOutput, tc.Input) 598 } 599 } 600 } 601 602 func TestInstanceStateEmpty(t *testing.T) { 603 cases := map[string]struct { 604 In *InstanceState 605 Result bool 606 }{ 607 "nil is empty": { 608 nil, 609 true, 610 }, 611 "non-nil but without ID is empty": { 612 &InstanceState{}, 613 true, 614 }, 615 "with ID is not empty": { 616 &InstanceState{ 617 ID: "i-abc123", 618 }, 619 false, 620 }, 621 } 622 623 for tn, tc := range cases { 624 if tc.In.Empty() != tc.Result { 625 t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result) 626 } 627 } 628 } 629 630 func TestInstanceStateEqual(t *testing.T) { 631 cases := []struct { 632 Result bool 633 One, Two *InstanceState 634 }{ 635 // Nils 636 { 637 false, 638 nil, 639 &InstanceState{}, 640 }, 641 642 { 643 false, 644 &InstanceState{}, 645 nil, 646 }, 647 648 // Different IDs 649 { 650 false, 651 &InstanceState{ID: "foo"}, 652 &InstanceState{ID: "bar"}, 653 }, 654 655 // Different Attributes 656 { 657 false, 658 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 659 &InstanceState{Attributes: map[string]string{"foo": "baz"}}, 660 }, 661 662 // Different Attribute keys 663 { 664 false, 665 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 666 &InstanceState{Attributes: map[string]string{"bar": "baz"}}, 667 }, 668 669 { 670 false, 671 &InstanceState{Attributes: map[string]string{"bar": "baz"}}, 672 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 673 }, 674 } 675 676 for i, tc := range cases { 677 if tc.One.Equal(tc.Two) != tc.Result { 678 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 679 } 680 } 681 } 682 683 func TestStateEmpty(t *testing.T) { 684 cases := []struct { 685 In *State 686 Result bool 687 }{ 688 { 689 nil, 690 true, 691 }, 692 { 693 &State{}, 694 true, 695 }, 696 { 697 &State{ 698 Remote: &RemoteState{Type: "foo"}, 699 }, 700 true, 701 }, 702 { 703 &State{ 704 Modules: []*ModuleState{ 705 &ModuleState{}, 706 }, 707 }, 708 false, 709 }, 710 } 711 712 for i, tc := range cases { 713 if tc.In.Empty() != tc.Result { 714 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 715 } 716 } 717 } 718 719 func TestStateIsRemote(t *testing.T) { 720 cases := []struct { 721 In *State 722 Result bool 723 }{ 724 { 725 nil, 726 false, 727 }, 728 { 729 &State{}, 730 false, 731 }, 732 { 733 &State{ 734 Remote: &RemoteState{Type: "foo"}, 735 }, 736 true, 737 }, 738 } 739 740 for i, tc := range cases { 741 if tc.In.IsRemote() != tc.Result { 742 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 743 } 744 } 745 } 746 747 func TestInstanceState_MergeDiff(t *testing.T) { 748 is := InstanceState{ 749 ID: "foo", 750 Attributes: map[string]string{ 751 "foo": "bar", 752 "port": "8000", 753 }, 754 } 755 756 diff := &InstanceDiff{ 757 Attributes: map[string]*ResourceAttrDiff{ 758 "foo": &ResourceAttrDiff{ 759 Old: "bar", 760 New: "baz", 761 }, 762 "bar": &ResourceAttrDiff{ 763 Old: "", 764 New: "foo", 765 }, 766 "baz": &ResourceAttrDiff{ 767 Old: "", 768 New: "foo", 769 NewComputed: true, 770 }, 771 "port": &ResourceAttrDiff{ 772 NewRemoved: true, 773 }, 774 }, 775 } 776 777 is2 := is.MergeDiff(diff) 778 779 expected := map[string]string{ 780 "foo": "baz", 781 "bar": "foo", 782 "baz": config.UnknownVariableValue, 783 } 784 785 if !reflect.DeepEqual(expected, is2.Attributes) { 786 t.Fatalf("bad: %#v", is2.Attributes) 787 } 788 } 789 790 func TestInstanceState_MergeDiff_nil(t *testing.T) { 791 var is *InstanceState = nil 792 793 diff := &InstanceDiff{ 794 Attributes: map[string]*ResourceAttrDiff{ 795 "foo": &ResourceAttrDiff{ 796 Old: "", 797 New: "baz", 798 }, 799 }, 800 } 801 802 is2 := is.MergeDiff(diff) 803 804 expected := map[string]string{ 805 "foo": "baz", 806 } 807 808 if !reflect.DeepEqual(expected, is2.Attributes) { 809 t.Fatalf("bad: %#v", is2.Attributes) 810 } 811 } 812 813 func TestInstanceState_MergeDiff_nilDiff(t *testing.T) { 814 is := InstanceState{ 815 ID: "foo", 816 Attributes: map[string]string{ 817 "foo": "bar", 818 }, 819 } 820 821 is2 := is.MergeDiff(nil) 822 823 expected := map[string]string{ 824 "foo": "bar", 825 } 826 827 if !reflect.DeepEqual(expected, is2.Attributes) { 828 t.Fatalf("bad: %#v", is2.Attributes) 829 } 830 } 831 832 func TestReadUpgradeState(t *testing.T) { 833 state := &StateV1{ 834 Resources: map[string]*ResourceStateV1{ 835 "foo": &ResourceStateV1{ 836 ID: "bar", 837 }, 838 }, 839 } 840 buf := new(bytes.Buffer) 841 if err := testWriteStateV1(state, buf); err != nil { 842 t.Fatalf("err: %s", err) 843 } 844 845 // ReadState should transparently detect the old 846 // version and upgrade up so the latest. 847 actual, err := ReadState(buf) 848 if err != nil { 849 t.Fatalf("err: %s", err) 850 } 851 852 upgraded, err := upgradeV1State(state) 853 if err != nil { 854 t.Fatalf("err: %s", err) 855 } 856 857 if !reflect.DeepEqual(actual, upgraded) { 858 t.Fatalf("bad: %#v", actual) 859 } 860 } 861 862 func TestReadWriteState(t *testing.T) { 863 state := &State{ 864 Serial: 9, 865 Remote: &RemoteState{ 866 Type: "http", 867 Config: map[string]string{ 868 "url": "http://my-cool-server.com/", 869 }, 870 }, 871 Modules: []*ModuleState{ 872 &ModuleState{ 873 Path: rootModulePath, 874 Dependencies: []string{ 875 "aws_instance.bar", 876 }, 877 Resources: map[string]*ResourceState{ 878 "foo": &ResourceState{ 879 Primary: &InstanceState{ 880 ID: "bar", 881 Ephemeral: EphemeralState{ 882 ConnInfo: map[string]string{ 883 "type": "ssh", 884 "user": "root", 885 "password": "supersecret", 886 }, 887 }, 888 }, 889 }, 890 }, 891 }, 892 }, 893 } 894 895 buf := new(bytes.Buffer) 896 if err := WriteState(state, buf); err != nil { 897 t.Fatalf("err: %s", err) 898 } 899 900 // Verify that the version and serial are set 901 if state.Version != StateVersion { 902 t.Fatalf("bad version number: %d", state.Version) 903 } 904 905 actual, err := ReadState(buf) 906 if err != nil { 907 t.Fatalf("err: %s", err) 908 } 909 910 // ReadState should not restore sensitive information! 911 mod := state.RootModule() 912 mod.Resources["foo"].Primary.Ephemeral = EphemeralState{} 913 914 if !reflect.DeepEqual(actual, state) { 915 t.Fatalf("bad: %#v", actual) 916 } 917 } 918 919 func TestReadStateNewVersion(t *testing.T) { 920 type out struct { 921 Version int 922 } 923 924 buf, err := json.Marshal(&out{StateVersion + 1}) 925 if err != nil { 926 t.Fatalf("err: %v", err) 927 } 928 929 s, err := ReadState(bytes.NewReader(buf)) 930 if s != nil { 931 t.Fatalf("unexpected: %#v", s) 932 } 933 if !strings.Contains(err.Error(), "not supported") { 934 t.Fatalf("err: %v", err) 935 } 936 } 937 938 func TestUpgradeV1State(t *testing.T) { 939 old := &StateV1{ 940 Outputs: map[string]string{ 941 "ip": "127.0.0.1", 942 }, 943 Resources: map[string]*ResourceStateV1{ 944 "foo": &ResourceStateV1{ 945 Type: "test_resource", 946 ID: "bar", 947 Attributes: map[string]string{ 948 "key": "val", 949 }, 950 }, 951 "bar": &ResourceStateV1{ 952 Type: "test_resource", 953 ID: "1234", 954 Attributes: map[string]string{ 955 "a": "b", 956 }, 957 }, 958 }, 959 Tainted: map[string]struct{}{ 960 "bar": struct{}{}, 961 }, 962 } 963 state, err := upgradeV1State(old) 964 if err != nil { 965 t.Fatalf("err: %v", err) 966 } 967 968 if len(state.Modules) != 1 { 969 t.Fatalf("should only have root module: %#v", state.Modules) 970 } 971 root := state.RootModule() 972 973 if len(root.Outputs) != 1 { 974 t.Fatalf("bad outputs: %v", root.Outputs) 975 } 976 if root.Outputs["ip"] != "127.0.0.1" { 977 t.Fatalf("bad outputs: %v", root.Outputs) 978 } 979 980 if len(root.Resources) != 2 { 981 t.Fatalf("bad resources: %v", root.Resources) 982 } 983 984 foo := root.Resources["foo"] 985 if foo.Type != "test_resource" { 986 t.Fatalf("bad: %#v", foo) 987 } 988 if foo.Primary == nil || foo.Primary.ID != "bar" || 989 foo.Primary.Attributes["key"] != "val" { 990 t.Fatalf("bad: %#v", foo) 991 } 992 if len(foo.Tainted) > 0 { 993 t.Fatalf("bad: %#v", foo) 994 } 995 996 bar := root.Resources["bar"] 997 if bar.Type != "test_resource" { 998 t.Fatalf("bad: %#v", bar) 999 } 1000 if bar.Primary != nil { 1001 t.Fatalf("bad: %#v", bar) 1002 } 1003 if len(bar.Tainted) != 1 { 1004 t.Fatalf("bad: %#v", bar) 1005 } 1006 bt := bar.Tainted[0] 1007 if bt.ID != "1234" || bt.Attributes["a"] != "b" { 1008 t.Fatalf("bad: %#v", bt) 1009 } 1010 } 1011 1012 func TestParseResourceStateKey(t *testing.T) { 1013 cases := []struct { 1014 Input string 1015 Expected *ResourceStateKey 1016 ExpectedErr bool 1017 }{ 1018 { 1019 Input: "aws_instance.foo.3", 1020 Expected: &ResourceStateKey{ 1021 Type: "aws_instance", 1022 Name: "foo", 1023 Index: 3, 1024 }, 1025 }, 1026 { 1027 Input: "aws_instance.foo.0", 1028 Expected: &ResourceStateKey{ 1029 Type: "aws_instance", 1030 Name: "foo", 1031 Index: 0, 1032 }, 1033 }, 1034 { 1035 Input: "aws_instance.foo", 1036 Expected: &ResourceStateKey{ 1037 Type: "aws_instance", 1038 Name: "foo", 1039 Index: -1, 1040 }, 1041 }, 1042 { 1043 Input: "aws_instance.foo.malformed", 1044 ExpectedErr: true, 1045 }, 1046 { 1047 Input: "aws_instance.foo.malformedwithnumber.123", 1048 ExpectedErr: true, 1049 }, 1050 { 1051 Input: "malformed", 1052 ExpectedErr: true, 1053 }, 1054 } 1055 for _, tc := range cases { 1056 rsk, err := ParseResourceStateKey(tc.Input) 1057 if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) { 1058 t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk) 1059 } 1060 if (err != nil) != tc.ExpectedErr { 1061 t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err) 1062 } 1063 } 1064 }