github.com/wikibal01/hashicorp-terraform@v0.11.12-beta1/terraform/state_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "os" 8 "reflect" 9 "sort" 10 "strings" 11 "testing" 12 13 "github.com/hashicorp/terraform/config" 14 ) 15 16 func TestStateValidate(t *testing.T) { 17 cases := map[string]struct { 18 In *State 19 Err bool 20 }{ 21 "empty state": { 22 &State{}, 23 false, 24 }, 25 26 "multiple modules": { 27 &State{ 28 Modules: []*ModuleState{ 29 &ModuleState{ 30 Path: []string{"root", "foo"}, 31 }, 32 &ModuleState{ 33 Path: []string{"root", "foo"}, 34 }, 35 }, 36 }, 37 true, 38 }, 39 } 40 41 for name, tc := range cases { 42 // Init the state 43 tc.In.init() 44 45 err := tc.In.Validate() 46 if (err != nil) != tc.Err { 47 t.Fatalf("%s: err: %s", name, err) 48 } 49 } 50 } 51 52 func TestStateAddModule(t *testing.T) { 53 cases := []struct { 54 In [][]string 55 Out [][]string 56 }{ 57 { 58 [][]string{ 59 []string{"root"}, 60 []string{"root", "child"}, 61 }, 62 [][]string{ 63 []string{"root"}, 64 []string{"root", "child"}, 65 }, 66 }, 67 68 { 69 [][]string{ 70 []string{"root", "foo", "bar"}, 71 []string{"root", "foo"}, 72 []string{"root"}, 73 []string{"root", "bar"}, 74 }, 75 [][]string{ 76 []string{"root"}, 77 []string{"root", "bar"}, 78 []string{"root", "foo"}, 79 []string{"root", "foo", "bar"}, 80 }, 81 }, 82 // Same last element, different middle element 83 { 84 [][]string{ 85 []string{"root", "foo", "bar"}, // This one should sort after... 86 []string{"root", "foo"}, 87 []string{"root"}, 88 []string{"root", "bar", "bar"}, // ...this one. 89 []string{"root", "bar"}, 90 }, 91 [][]string{ 92 []string{"root"}, 93 []string{"root", "bar"}, 94 []string{"root", "foo"}, 95 []string{"root", "bar", "bar"}, 96 []string{"root", "foo", "bar"}, 97 }, 98 }, 99 } 100 101 for _, tc := range cases { 102 s := new(State) 103 for _, p := range tc.In { 104 s.AddModule(p) 105 } 106 107 actual := make([][]string, 0, len(tc.In)) 108 for _, m := range s.Modules { 109 actual = append(actual, m.Path) 110 } 111 112 if !reflect.DeepEqual(actual, tc.Out) { 113 t.Fatalf("In: %#v\n\nOut: %#v", tc.In, actual) 114 } 115 } 116 } 117 118 func TestStateOutputTypeRoundTrip(t *testing.T) { 119 state := &State{ 120 Modules: []*ModuleState{ 121 &ModuleState{ 122 Path: RootModulePath, 123 Outputs: map[string]*OutputState{ 124 "string_output": &OutputState{ 125 Value: "String Value", 126 Type: "string", 127 }, 128 }, 129 }, 130 }, 131 } 132 state.init() 133 134 buf := new(bytes.Buffer) 135 if err := WriteState(state, buf); err != nil { 136 t.Fatalf("err: %s", err) 137 } 138 139 roundTripped, err := ReadState(buf) 140 if err != nil { 141 t.Fatalf("err: %s", err) 142 } 143 144 if !reflect.DeepEqual(state, roundTripped) { 145 t.Logf("expected:\n%#v", state) 146 t.Fatalf("got:\n%#v", roundTripped) 147 } 148 } 149 150 func TestStateModuleOrphans(t *testing.T) { 151 state := &State{ 152 Modules: []*ModuleState{ 153 &ModuleState{ 154 Path: RootModulePath, 155 }, 156 &ModuleState{ 157 Path: []string{RootModuleName, "foo"}, 158 }, 159 &ModuleState{ 160 Path: []string{RootModuleName, "bar"}, 161 }, 162 }, 163 } 164 165 state.init() 166 167 config := testModule(t, "state-module-orphans").Config() 168 actual := state.ModuleOrphans(RootModulePath, config) 169 expected := [][]string{ 170 []string{RootModuleName, "foo"}, 171 } 172 173 if !reflect.DeepEqual(actual, expected) { 174 t.Fatalf("bad: %#v", actual) 175 } 176 } 177 178 func TestStateModuleOrphans_nested(t *testing.T) { 179 state := &State{ 180 Modules: []*ModuleState{ 181 &ModuleState{ 182 Path: RootModulePath, 183 }, 184 &ModuleState{ 185 Path: []string{RootModuleName, "foo", "bar"}, 186 }, 187 }, 188 } 189 190 state.init() 191 192 actual := state.ModuleOrphans(RootModulePath, nil) 193 expected := [][]string{ 194 []string{RootModuleName, "foo"}, 195 } 196 197 if !reflect.DeepEqual(actual, expected) { 198 t.Fatalf("bad: %#v", actual) 199 } 200 } 201 202 func TestStateModuleOrphans_nilConfig(t *testing.T) { 203 state := &State{ 204 Modules: []*ModuleState{ 205 &ModuleState{ 206 Path: RootModulePath, 207 }, 208 &ModuleState{ 209 Path: []string{RootModuleName, "foo"}, 210 }, 211 &ModuleState{ 212 Path: []string{RootModuleName, "bar"}, 213 }, 214 }, 215 } 216 217 state.init() 218 219 actual := state.ModuleOrphans(RootModulePath, nil) 220 expected := [][]string{ 221 []string{RootModuleName, "foo"}, 222 []string{RootModuleName, "bar"}, 223 } 224 225 if !reflect.DeepEqual(actual, expected) { 226 t.Fatalf("bad: %#v", actual) 227 } 228 } 229 230 func TestStateModuleOrphans_deepNestedNilConfig(t *testing.T) { 231 state := &State{ 232 Modules: []*ModuleState{ 233 &ModuleState{ 234 Path: RootModulePath, 235 }, 236 &ModuleState{ 237 Path: []string{RootModuleName, "parent", "childfoo"}, 238 }, 239 &ModuleState{ 240 Path: []string{RootModuleName, "parent", "childbar"}, 241 }, 242 }, 243 } 244 245 state.init() 246 247 actual := state.ModuleOrphans(RootModulePath, nil) 248 expected := [][]string{ 249 []string{RootModuleName, "parent"}, 250 } 251 252 if !reflect.DeepEqual(actual, expected) { 253 t.Fatalf("bad: %#v", actual) 254 } 255 } 256 257 func TestStateDeepCopy(t *testing.T) { 258 cases := []struct { 259 State *State 260 }{ 261 // Nil 262 {nil}, 263 264 // Version 265 { 266 &State{Version: 5}, 267 }, 268 // TFVersion 269 { 270 &State{TFVersion: "5"}, 271 }, 272 // Modules 273 { 274 &State{ 275 Version: 6, 276 Modules: []*ModuleState{ 277 &ModuleState{ 278 Path: rootModulePath, 279 Resources: map[string]*ResourceState{ 280 "test_instance.foo": &ResourceState{ 281 Primary: &InstanceState{ 282 Meta: map[string]interface{}{}, 283 }, 284 }, 285 }, 286 }, 287 }, 288 }, 289 }, 290 // Deposed 291 // The nil values shouldn't be there if the State was properly init'ed, 292 // but the Copy should still work anyway. 293 { 294 &State{ 295 Version: 6, 296 Modules: []*ModuleState{ 297 &ModuleState{ 298 Path: rootModulePath, 299 Resources: map[string]*ResourceState{ 300 "test_instance.foo": &ResourceState{ 301 Primary: &InstanceState{ 302 Meta: map[string]interface{}{}, 303 }, 304 Deposed: []*InstanceState{ 305 {ID: "test"}, 306 nil, 307 }, 308 }, 309 }, 310 }, 311 }, 312 }, 313 }, 314 } 315 316 for i, tc := range cases { 317 t.Run(fmt.Sprintf("copy-%d", i), func(t *testing.T) { 318 actual := tc.State.DeepCopy() 319 expected := tc.State 320 if !reflect.DeepEqual(actual, expected) { 321 t.Fatalf("Expected: %#v\nRecevied: %#v\n", expected, actual) 322 } 323 }) 324 } 325 } 326 327 func TestStateEqual(t *testing.T) { 328 cases := []struct { 329 Name string 330 Result bool 331 One, Two *State 332 }{ 333 // Nils 334 { 335 "one nil", 336 false, 337 nil, 338 &State{Version: 2}, 339 }, 340 341 { 342 "both nil", 343 true, 344 nil, 345 nil, 346 }, 347 348 // Different versions 349 { 350 "different state versions", 351 false, 352 &State{Version: 5}, 353 &State{Version: 2}, 354 }, 355 356 // Different modules 357 { 358 "different module states", 359 false, 360 &State{ 361 Modules: []*ModuleState{ 362 &ModuleState{ 363 Path: RootModulePath, 364 }, 365 }, 366 }, 367 &State{}, 368 }, 369 370 { 371 "same module states", 372 true, 373 &State{ 374 Modules: []*ModuleState{ 375 &ModuleState{ 376 Path: RootModulePath, 377 }, 378 }, 379 }, 380 &State{ 381 Modules: []*ModuleState{ 382 &ModuleState{ 383 Path: RootModulePath, 384 }, 385 }, 386 }, 387 }, 388 389 // Meta differs 390 { 391 "differing meta values with primitives", 392 false, 393 &State{ 394 Modules: []*ModuleState{ 395 &ModuleState{ 396 Path: rootModulePath, 397 Resources: map[string]*ResourceState{ 398 "test_instance.foo": &ResourceState{ 399 Primary: &InstanceState{ 400 Meta: map[string]interface{}{ 401 "schema_version": "1", 402 }, 403 }, 404 }, 405 }, 406 }, 407 }, 408 }, 409 &State{ 410 Modules: []*ModuleState{ 411 &ModuleState{ 412 Path: rootModulePath, 413 Resources: map[string]*ResourceState{ 414 "test_instance.foo": &ResourceState{ 415 Primary: &InstanceState{ 416 Meta: map[string]interface{}{ 417 "schema_version": "2", 418 }, 419 }, 420 }, 421 }, 422 }, 423 }, 424 }, 425 }, 426 427 // Meta with complex types 428 { 429 "same meta with complex types", 430 true, 431 &State{ 432 Modules: []*ModuleState{ 433 &ModuleState{ 434 Path: rootModulePath, 435 Resources: map[string]*ResourceState{ 436 "test_instance.foo": &ResourceState{ 437 Primary: &InstanceState{ 438 Meta: map[string]interface{}{ 439 "timeouts": map[string]interface{}{ 440 "create": 42, 441 "read": "27", 442 }, 443 }, 444 }, 445 }, 446 }, 447 }, 448 }, 449 }, 450 &State{ 451 Modules: []*ModuleState{ 452 &ModuleState{ 453 Path: rootModulePath, 454 Resources: map[string]*ResourceState{ 455 "test_instance.foo": &ResourceState{ 456 Primary: &InstanceState{ 457 Meta: map[string]interface{}{ 458 "timeouts": map[string]interface{}{ 459 "create": 42, 460 "read": "27", 461 }, 462 }, 463 }, 464 }, 465 }, 466 }, 467 }, 468 }, 469 }, 470 471 // Meta with complex types that have been altered during serialization 472 { 473 "same meta with complex types that have been json-ified", 474 true, 475 &State{ 476 Modules: []*ModuleState{ 477 &ModuleState{ 478 Path: rootModulePath, 479 Resources: map[string]*ResourceState{ 480 "test_instance.foo": &ResourceState{ 481 Primary: &InstanceState{ 482 Meta: map[string]interface{}{ 483 "timeouts": map[string]interface{}{ 484 "create": int(42), 485 "read": "27", 486 }, 487 }, 488 }, 489 }, 490 }, 491 }, 492 }, 493 }, 494 &State{ 495 Modules: []*ModuleState{ 496 &ModuleState{ 497 Path: rootModulePath, 498 Resources: map[string]*ResourceState{ 499 "test_instance.foo": &ResourceState{ 500 Primary: &InstanceState{ 501 Meta: map[string]interface{}{ 502 "timeouts": map[string]interface{}{ 503 "create": float64(42), 504 "read": "27", 505 }, 506 }, 507 }, 508 }, 509 }, 510 }, 511 }, 512 }, 513 }, 514 } 515 516 for i, tc := range cases { 517 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 518 if tc.One.Equal(tc.Two) != tc.Result { 519 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 520 } 521 if tc.Two.Equal(tc.One) != tc.Result { 522 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 523 } 524 }) 525 } 526 } 527 528 func TestStateCompareAges(t *testing.T) { 529 cases := []struct { 530 Result StateAgeComparison 531 Err bool 532 One, Two *State 533 }{ 534 { 535 StateAgeEqual, false, 536 &State{ 537 Lineage: "1", 538 Serial: 2, 539 }, 540 &State{ 541 Lineage: "1", 542 Serial: 2, 543 }, 544 }, 545 { 546 StateAgeReceiverOlder, false, 547 &State{ 548 Lineage: "1", 549 Serial: 2, 550 }, 551 &State{ 552 Lineage: "1", 553 Serial: 3, 554 }, 555 }, 556 { 557 StateAgeReceiverNewer, false, 558 &State{ 559 Lineage: "1", 560 Serial: 3, 561 }, 562 &State{ 563 Lineage: "1", 564 Serial: 2, 565 }, 566 }, 567 { 568 StateAgeEqual, true, 569 &State{ 570 Lineage: "1", 571 Serial: 2, 572 }, 573 &State{ 574 Lineage: "2", 575 Serial: 2, 576 }, 577 }, 578 { 579 StateAgeEqual, true, 580 &State{ 581 Lineage: "1", 582 Serial: 3, 583 }, 584 &State{ 585 Lineage: "2", 586 Serial: 2, 587 }, 588 }, 589 } 590 591 for i, tc := range cases { 592 result, err := tc.One.CompareAges(tc.Two) 593 594 if err != nil && !tc.Err { 595 t.Errorf( 596 "%d: got error, but want success\n\n%s\n\n%s", 597 i, tc.One, tc.Two, 598 ) 599 continue 600 } 601 602 if err == nil && tc.Err { 603 t.Errorf( 604 "%d: got success, but want error\n\n%s\n\n%s", 605 i, tc.One, tc.Two, 606 ) 607 continue 608 } 609 610 if result != tc.Result { 611 t.Errorf( 612 "%d: got result %d, but want %d\n\n%s\n\n%s", 613 i, result, tc.Result, tc.One, tc.Two, 614 ) 615 continue 616 } 617 } 618 } 619 620 func TestStateSameLineage(t *testing.T) { 621 cases := []struct { 622 Result bool 623 One, Two *State 624 }{ 625 { 626 true, 627 &State{ 628 Lineage: "1", 629 }, 630 &State{ 631 Lineage: "1", 632 }, 633 }, 634 { 635 // Empty lineage is compatible with all 636 true, 637 &State{ 638 Lineage: "", 639 }, 640 &State{ 641 Lineage: "1", 642 }, 643 }, 644 { 645 // Empty lineage is compatible with all 646 true, 647 &State{ 648 Lineage: "1", 649 }, 650 &State{ 651 Lineage: "", 652 }, 653 }, 654 { 655 false, 656 &State{ 657 Lineage: "1", 658 }, 659 &State{ 660 Lineage: "2", 661 }, 662 }, 663 } 664 665 for i, tc := range cases { 666 result := tc.One.SameLineage(tc.Two) 667 668 if result != tc.Result { 669 t.Errorf( 670 "%d: got %v, but want %v\n\n%s\n\n%s", 671 i, result, tc.Result, tc.One, tc.Two, 672 ) 673 continue 674 } 675 } 676 } 677 678 func TestStateMarshalEqual(t *testing.T) { 679 tests := map[string]struct { 680 S1, S2 *State 681 Want bool 682 }{ 683 "both nil": { 684 nil, 685 nil, 686 true, 687 }, 688 "first zero, second nil": { 689 &State{}, 690 nil, 691 false, 692 }, 693 "first nil, second zero": { 694 nil, 695 &State{}, 696 false, 697 }, 698 "both zero": { 699 // These are not equal because they both implicitly init with 700 // different lineage. 701 &State{}, 702 &State{}, 703 false, 704 }, 705 "both set, same lineage": { 706 &State{ 707 Lineage: "abc123", 708 }, 709 &State{ 710 Lineage: "abc123", 711 }, 712 true, 713 }, 714 "both set, same lineage, different serial": { 715 &State{ 716 Lineage: "abc123", 717 Serial: 1, 718 }, 719 &State{ 720 Lineage: "abc123", 721 Serial: 2, 722 }, 723 false, 724 }, 725 "both set, same lineage, same serial, same resources": { 726 &State{ 727 Lineage: "abc123", 728 Serial: 1, 729 Modules: []*ModuleState{ 730 { 731 Path: []string{"root"}, 732 Resources: map[string]*ResourceState{ 733 "foo_bar.baz": {}, 734 }, 735 }, 736 }, 737 }, 738 &State{ 739 Lineage: "abc123", 740 Serial: 1, 741 Modules: []*ModuleState{ 742 { 743 Path: []string{"root"}, 744 Resources: map[string]*ResourceState{ 745 "foo_bar.baz": {}, 746 }, 747 }, 748 }, 749 }, 750 true, 751 }, 752 "both set, same lineage, same serial, different resources": { 753 &State{ 754 Lineage: "abc123", 755 Serial: 1, 756 Modules: []*ModuleState{ 757 { 758 Path: []string{"root"}, 759 Resources: map[string]*ResourceState{ 760 "foo_bar.baz": {}, 761 }, 762 }, 763 }, 764 }, 765 &State{ 766 Lineage: "abc123", 767 Serial: 1, 768 Modules: []*ModuleState{ 769 { 770 Path: []string{"root"}, 771 Resources: map[string]*ResourceState{ 772 "pizza_crust.tasty": {}, 773 }, 774 }, 775 }, 776 }, 777 false, 778 }, 779 } 780 781 for name, test := range tests { 782 t.Run(name, func(t *testing.T) { 783 got := test.S1.MarshalEqual(test.S2) 784 if got != test.Want { 785 t.Errorf("wrong result %#v; want %#v", got, test.Want) 786 s1Buf := &bytes.Buffer{} 787 s2Buf := &bytes.Buffer{} 788 _ = WriteState(test.S1, s1Buf) 789 _ = WriteState(test.S2, s2Buf) 790 t.Logf("\nState 1: %s\nState 2: %s", s1Buf.Bytes(), s2Buf.Bytes()) 791 } 792 }) 793 } 794 } 795 796 func TestStateRemove(t *testing.T) { 797 cases := map[string]struct { 798 Address string 799 One, Two *State 800 }{ 801 "simple resource": { 802 "test_instance.foo", 803 &State{ 804 Modules: []*ModuleState{ 805 &ModuleState{ 806 Path: rootModulePath, 807 Resources: map[string]*ResourceState{ 808 "test_instance.foo": &ResourceState{ 809 Type: "test_instance", 810 Primary: &InstanceState{ 811 ID: "foo", 812 }, 813 }, 814 815 "test_instance.bar": &ResourceState{ 816 Type: "test_instance", 817 Primary: &InstanceState{ 818 ID: "foo", 819 }, 820 }, 821 }, 822 }, 823 }, 824 }, 825 &State{ 826 Modules: []*ModuleState{ 827 &ModuleState{ 828 Path: rootModulePath, 829 Resources: map[string]*ResourceState{ 830 "test_instance.bar": &ResourceState{ 831 Type: "test_instance", 832 Primary: &InstanceState{ 833 ID: "foo", 834 }, 835 }, 836 }, 837 }, 838 }, 839 }, 840 }, 841 842 "single instance": { 843 "test_instance.foo.primary", 844 &State{ 845 Modules: []*ModuleState{ 846 &ModuleState{ 847 Path: rootModulePath, 848 Resources: map[string]*ResourceState{ 849 "test_instance.foo": &ResourceState{ 850 Type: "test_instance", 851 Primary: &InstanceState{ 852 ID: "foo", 853 }, 854 }, 855 }, 856 }, 857 }, 858 }, 859 &State{ 860 Modules: []*ModuleState{ 861 &ModuleState{ 862 Path: rootModulePath, 863 Resources: map[string]*ResourceState{}, 864 }, 865 }, 866 }, 867 }, 868 869 "single instance in multi-count": { 870 "test_instance.foo[0]", 871 &State{ 872 Modules: []*ModuleState{ 873 &ModuleState{ 874 Path: rootModulePath, 875 Resources: map[string]*ResourceState{ 876 "test_instance.foo.0": &ResourceState{ 877 Type: "test_instance", 878 Primary: &InstanceState{ 879 ID: "foo", 880 }, 881 }, 882 883 "test_instance.foo.1": &ResourceState{ 884 Type: "test_instance", 885 Primary: &InstanceState{ 886 ID: "foo", 887 }, 888 }, 889 }, 890 }, 891 }, 892 }, 893 &State{ 894 Modules: []*ModuleState{ 895 &ModuleState{ 896 Path: rootModulePath, 897 Resources: map[string]*ResourceState{ 898 "test_instance.foo.1": &ResourceState{ 899 Type: "test_instance", 900 Primary: &InstanceState{ 901 ID: "foo", 902 }, 903 }, 904 }, 905 }, 906 }, 907 }, 908 }, 909 910 "single resource, multi-count": { 911 "test_instance.foo", 912 &State{ 913 Modules: []*ModuleState{ 914 &ModuleState{ 915 Path: rootModulePath, 916 Resources: map[string]*ResourceState{ 917 "test_instance.foo.0": &ResourceState{ 918 Type: "test_instance", 919 Primary: &InstanceState{ 920 ID: "foo", 921 }, 922 }, 923 924 "test_instance.foo.1": &ResourceState{ 925 Type: "test_instance", 926 Primary: &InstanceState{ 927 ID: "foo", 928 }, 929 }, 930 }, 931 }, 932 }, 933 }, 934 &State{ 935 Modules: []*ModuleState{ 936 &ModuleState{ 937 Path: rootModulePath, 938 Resources: map[string]*ResourceState{}, 939 }, 940 }, 941 }, 942 }, 943 944 "full module": { 945 "module.foo", 946 &State{ 947 Modules: []*ModuleState{ 948 &ModuleState{ 949 Path: rootModulePath, 950 Resources: map[string]*ResourceState{ 951 "test_instance.foo": &ResourceState{ 952 Type: "test_instance", 953 Primary: &InstanceState{ 954 ID: "foo", 955 }, 956 }, 957 }, 958 }, 959 960 &ModuleState{ 961 Path: []string{"root", "foo"}, 962 Resources: map[string]*ResourceState{ 963 "test_instance.foo": &ResourceState{ 964 Type: "test_instance", 965 Primary: &InstanceState{ 966 ID: "foo", 967 }, 968 }, 969 970 "test_instance.bar": &ResourceState{ 971 Type: "test_instance", 972 Primary: &InstanceState{ 973 ID: "foo", 974 }, 975 }, 976 }, 977 }, 978 }, 979 }, 980 &State{ 981 Modules: []*ModuleState{ 982 &ModuleState{ 983 Path: rootModulePath, 984 Resources: map[string]*ResourceState{ 985 "test_instance.foo": &ResourceState{ 986 Type: "test_instance", 987 Primary: &InstanceState{ 988 ID: "foo", 989 }, 990 }, 991 }, 992 }, 993 }, 994 }, 995 }, 996 997 "module and children": { 998 "module.foo", 999 &State{ 1000 Modules: []*ModuleState{ 1001 &ModuleState{ 1002 Path: rootModulePath, 1003 Resources: map[string]*ResourceState{ 1004 "test_instance.foo": &ResourceState{ 1005 Type: "test_instance", 1006 Primary: &InstanceState{ 1007 ID: "foo", 1008 }, 1009 }, 1010 }, 1011 }, 1012 1013 &ModuleState{ 1014 Path: []string{"root", "foo"}, 1015 Resources: map[string]*ResourceState{ 1016 "test_instance.foo": &ResourceState{ 1017 Type: "test_instance", 1018 Primary: &InstanceState{ 1019 ID: "foo", 1020 }, 1021 }, 1022 1023 "test_instance.bar": &ResourceState{ 1024 Type: "test_instance", 1025 Primary: &InstanceState{ 1026 ID: "foo", 1027 }, 1028 }, 1029 }, 1030 }, 1031 1032 &ModuleState{ 1033 Path: []string{"root", "foo", "bar"}, 1034 Resources: map[string]*ResourceState{ 1035 "test_instance.foo": &ResourceState{ 1036 Type: "test_instance", 1037 Primary: &InstanceState{ 1038 ID: "foo", 1039 }, 1040 }, 1041 1042 "test_instance.bar": &ResourceState{ 1043 Type: "test_instance", 1044 Primary: &InstanceState{ 1045 ID: "foo", 1046 }, 1047 }, 1048 }, 1049 }, 1050 }, 1051 }, 1052 &State{ 1053 Modules: []*ModuleState{ 1054 &ModuleState{ 1055 Path: rootModulePath, 1056 Resources: map[string]*ResourceState{ 1057 "test_instance.foo": &ResourceState{ 1058 Type: "test_instance", 1059 Primary: &InstanceState{ 1060 ID: "foo", 1061 }, 1062 }, 1063 }, 1064 }, 1065 }, 1066 }, 1067 }, 1068 } 1069 1070 for k, tc := range cases { 1071 if err := tc.One.Remove(tc.Address); err != nil { 1072 t.Fatalf("bad: %s\n\n%s", k, err) 1073 } 1074 1075 if !tc.One.Equal(tc.Two) { 1076 t.Fatalf("Bad: %s\n\n%s\n\n%s", k, tc.One.String(), tc.Two.String()) 1077 } 1078 } 1079 } 1080 1081 func TestResourceStateEqual(t *testing.T) { 1082 cases := []struct { 1083 Result bool 1084 One, Two *ResourceState 1085 }{ 1086 // Different types 1087 { 1088 false, 1089 &ResourceState{Type: "foo"}, 1090 &ResourceState{Type: "bar"}, 1091 }, 1092 1093 // Different dependencies 1094 { 1095 false, 1096 &ResourceState{Dependencies: []string{"foo"}}, 1097 &ResourceState{Dependencies: []string{"bar"}}, 1098 }, 1099 1100 { 1101 false, 1102 &ResourceState{Dependencies: []string{"foo", "bar"}}, 1103 &ResourceState{Dependencies: []string{"foo"}}, 1104 }, 1105 1106 { 1107 true, 1108 &ResourceState{Dependencies: []string{"bar", "foo"}}, 1109 &ResourceState{Dependencies: []string{"foo", "bar"}}, 1110 }, 1111 1112 // Different primaries 1113 { 1114 false, 1115 &ResourceState{Primary: nil}, 1116 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 1117 }, 1118 1119 { 1120 true, 1121 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 1122 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 1123 }, 1124 1125 // Different tainted 1126 { 1127 false, 1128 &ResourceState{ 1129 Primary: &InstanceState{ 1130 ID: "foo", 1131 }, 1132 }, 1133 &ResourceState{ 1134 Primary: &InstanceState{ 1135 ID: "foo", 1136 Tainted: true, 1137 }, 1138 }, 1139 }, 1140 1141 { 1142 true, 1143 &ResourceState{ 1144 Primary: &InstanceState{ 1145 ID: "foo", 1146 Tainted: true, 1147 }, 1148 }, 1149 &ResourceState{ 1150 Primary: &InstanceState{ 1151 ID: "foo", 1152 Tainted: true, 1153 }, 1154 }, 1155 }, 1156 } 1157 1158 for i, tc := range cases { 1159 if tc.One.Equal(tc.Two) != tc.Result { 1160 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 1161 } 1162 if tc.Two.Equal(tc.One) != tc.Result { 1163 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 1164 } 1165 } 1166 } 1167 1168 func TestResourceStateTaint(t *testing.T) { 1169 cases := map[string]struct { 1170 Input *ResourceState 1171 Output *ResourceState 1172 }{ 1173 "no primary": { 1174 &ResourceState{}, 1175 &ResourceState{}, 1176 }, 1177 1178 "primary, not tainted": { 1179 &ResourceState{ 1180 Primary: &InstanceState{ID: "foo"}, 1181 }, 1182 &ResourceState{ 1183 Primary: &InstanceState{ 1184 ID: "foo", 1185 Tainted: true, 1186 }, 1187 }, 1188 }, 1189 1190 "primary, tainted": { 1191 &ResourceState{ 1192 Primary: &InstanceState{ 1193 ID: "foo", 1194 Tainted: true, 1195 }, 1196 }, 1197 &ResourceState{ 1198 Primary: &InstanceState{ 1199 ID: "foo", 1200 Tainted: true, 1201 }, 1202 }, 1203 }, 1204 } 1205 1206 for k, tc := range cases { 1207 tc.Input.Taint() 1208 if !reflect.DeepEqual(tc.Input, tc.Output) { 1209 t.Fatalf( 1210 "Failure: %s\n\nExpected: %#v\n\nGot: %#v", 1211 k, tc.Output, tc.Input) 1212 } 1213 } 1214 } 1215 1216 func TestResourceStateUntaint(t *testing.T) { 1217 cases := map[string]struct { 1218 Input *ResourceState 1219 ExpectedOutput *ResourceState 1220 }{ 1221 "no primary, err": { 1222 Input: &ResourceState{}, 1223 ExpectedOutput: &ResourceState{}, 1224 }, 1225 1226 "primary, not tainted": { 1227 Input: &ResourceState{ 1228 Primary: &InstanceState{ID: "foo"}, 1229 }, 1230 ExpectedOutput: &ResourceState{ 1231 Primary: &InstanceState{ID: "foo"}, 1232 }, 1233 }, 1234 "primary, tainted": { 1235 Input: &ResourceState{ 1236 Primary: &InstanceState{ 1237 ID: "foo", 1238 Tainted: true, 1239 }, 1240 }, 1241 ExpectedOutput: &ResourceState{ 1242 Primary: &InstanceState{ID: "foo"}, 1243 }, 1244 }, 1245 } 1246 1247 for k, tc := range cases { 1248 tc.Input.Untaint() 1249 if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) { 1250 t.Fatalf( 1251 "Failure: %s\n\nExpected: %#v\n\nGot: %#v", 1252 k, tc.ExpectedOutput, tc.Input) 1253 } 1254 } 1255 } 1256 1257 func TestInstanceStateEmpty(t *testing.T) { 1258 cases := map[string]struct { 1259 In *InstanceState 1260 Result bool 1261 }{ 1262 "nil is empty": { 1263 nil, 1264 true, 1265 }, 1266 "non-nil but without ID is empty": { 1267 &InstanceState{}, 1268 true, 1269 }, 1270 "with ID is not empty": { 1271 &InstanceState{ 1272 ID: "i-abc123", 1273 }, 1274 false, 1275 }, 1276 } 1277 1278 for tn, tc := range cases { 1279 if tc.In.Empty() != tc.Result { 1280 t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result) 1281 } 1282 } 1283 } 1284 1285 func TestInstanceStateEqual(t *testing.T) { 1286 cases := []struct { 1287 Result bool 1288 One, Two *InstanceState 1289 }{ 1290 // Nils 1291 { 1292 false, 1293 nil, 1294 &InstanceState{}, 1295 }, 1296 1297 { 1298 false, 1299 &InstanceState{}, 1300 nil, 1301 }, 1302 1303 // Different IDs 1304 { 1305 false, 1306 &InstanceState{ID: "foo"}, 1307 &InstanceState{ID: "bar"}, 1308 }, 1309 1310 // Different Attributes 1311 { 1312 false, 1313 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 1314 &InstanceState{Attributes: map[string]string{"foo": "baz"}}, 1315 }, 1316 1317 // Different Attribute keys 1318 { 1319 false, 1320 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 1321 &InstanceState{Attributes: map[string]string{"bar": "baz"}}, 1322 }, 1323 1324 { 1325 false, 1326 &InstanceState{Attributes: map[string]string{"bar": "baz"}}, 1327 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 1328 }, 1329 } 1330 1331 for i, tc := range cases { 1332 if tc.One.Equal(tc.Two) != tc.Result { 1333 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 1334 } 1335 } 1336 } 1337 1338 func TestStateEmpty(t *testing.T) { 1339 cases := []struct { 1340 In *State 1341 Result bool 1342 }{ 1343 { 1344 nil, 1345 true, 1346 }, 1347 { 1348 &State{}, 1349 true, 1350 }, 1351 { 1352 &State{ 1353 Remote: &RemoteState{Type: "foo"}, 1354 }, 1355 true, 1356 }, 1357 { 1358 &State{ 1359 Modules: []*ModuleState{ 1360 &ModuleState{}, 1361 }, 1362 }, 1363 false, 1364 }, 1365 } 1366 1367 for i, tc := range cases { 1368 if tc.In.Empty() != tc.Result { 1369 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 1370 } 1371 } 1372 } 1373 1374 func TestStateHasResources(t *testing.T) { 1375 cases := []struct { 1376 In *State 1377 Result bool 1378 }{ 1379 { 1380 nil, 1381 false, 1382 }, 1383 { 1384 &State{}, 1385 false, 1386 }, 1387 { 1388 &State{ 1389 Remote: &RemoteState{Type: "foo"}, 1390 }, 1391 false, 1392 }, 1393 { 1394 &State{ 1395 Modules: []*ModuleState{ 1396 &ModuleState{}, 1397 }, 1398 }, 1399 false, 1400 }, 1401 { 1402 &State{ 1403 Modules: []*ModuleState{ 1404 &ModuleState{}, 1405 &ModuleState{}, 1406 }, 1407 }, 1408 false, 1409 }, 1410 { 1411 &State{ 1412 Modules: []*ModuleState{ 1413 &ModuleState{}, 1414 &ModuleState{ 1415 Resources: map[string]*ResourceState{ 1416 "foo.foo": &ResourceState{}, 1417 }, 1418 }, 1419 }, 1420 }, 1421 true, 1422 }, 1423 } 1424 1425 for i, tc := range cases { 1426 if tc.In.HasResources() != tc.Result { 1427 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 1428 } 1429 } 1430 } 1431 1432 func TestStateFromFutureTerraform(t *testing.T) { 1433 cases := []struct { 1434 In string 1435 Result bool 1436 }{ 1437 { 1438 "", 1439 false, 1440 }, 1441 { 1442 "0.1", 1443 false, 1444 }, 1445 { 1446 "999.15.1", 1447 true, 1448 }, 1449 } 1450 1451 for _, tc := range cases { 1452 state := &State{TFVersion: tc.In} 1453 actual := state.FromFutureTerraform() 1454 if actual != tc.Result { 1455 t.Fatalf("%s: bad: %v", tc.In, actual) 1456 } 1457 } 1458 } 1459 1460 func TestStateIsRemote(t *testing.T) { 1461 cases := []struct { 1462 In *State 1463 Result bool 1464 }{ 1465 { 1466 nil, 1467 false, 1468 }, 1469 { 1470 &State{}, 1471 false, 1472 }, 1473 { 1474 &State{ 1475 Remote: &RemoteState{Type: "foo"}, 1476 }, 1477 true, 1478 }, 1479 } 1480 1481 for i, tc := range cases { 1482 if tc.In.IsRemote() != tc.Result { 1483 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 1484 } 1485 } 1486 } 1487 1488 func TestInstanceState_MergeDiff(t *testing.T) { 1489 is := InstanceState{ 1490 ID: "foo", 1491 Attributes: map[string]string{ 1492 "foo": "bar", 1493 "port": "8000", 1494 }, 1495 } 1496 1497 diff := &InstanceDiff{ 1498 Attributes: map[string]*ResourceAttrDiff{ 1499 "foo": &ResourceAttrDiff{ 1500 Old: "bar", 1501 New: "baz", 1502 }, 1503 "bar": &ResourceAttrDiff{ 1504 Old: "", 1505 New: "foo", 1506 }, 1507 "baz": &ResourceAttrDiff{ 1508 Old: "", 1509 New: "foo", 1510 NewComputed: true, 1511 }, 1512 "port": &ResourceAttrDiff{ 1513 NewRemoved: true, 1514 }, 1515 }, 1516 } 1517 1518 is2 := is.MergeDiff(diff) 1519 1520 expected := map[string]string{ 1521 "foo": "baz", 1522 "bar": "foo", 1523 "baz": config.UnknownVariableValue, 1524 } 1525 1526 if !reflect.DeepEqual(expected, is2.Attributes) { 1527 t.Fatalf("bad: %#v", is2.Attributes) 1528 } 1529 } 1530 1531 // GH-12183. This tests that a list with a computed set generates the 1532 // right partial state. This never failed but is put here for completion 1533 // of the test case for GH-12183. 1534 func TestInstanceState_MergeDiff_computedSet(t *testing.T) { 1535 is := InstanceState{} 1536 1537 diff := &InstanceDiff{ 1538 Attributes: map[string]*ResourceAttrDiff{ 1539 "config.#": &ResourceAttrDiff{ 1540 Old: "0", 1541 New: "1", 1542 RequiresNew: true, 1543 }, 1544 1545 "config.0.name": &ResourceAttrDiff{ 1546 Old: "", 1547 New: "hello", 1548 }, 1549 1550 "config.0.rules.#": &ResourceAttrDiff{ 1551 Old: "", 1552 NewComputed: true, 1553 }, 1554 }, 1555 } 1556 1557 is2 := is.MergeDiff(diff) 1558 1559 expected := map[string]string{ 1560 "config.#": "1", 1561 "config.0.name": "hello", 1562 "config.0.rules.#": config.UnknownVariableValue, 1563 } 1564 1565 if !reflect.DeepEqual(expected, is2.Attributes) { 1566 t.Fatalf("bad: %#v", is2.Attributes) 1567 } 1568 } 1569 1570 func TestInstanceState_MergeDiff_nil(t *testing.T) { 1571 var is *InstanceState 1572 1573 diff := &InstanceDiff{ 1574 Attributes: map[string]*ResourceAttrDiff{ 1575 "foo": &ResourceAttrDiff{ 1576 Old: "", 1577 New: "baz", 1578 }, 1579 }, 1580 } 1581 1582 is2 := is.MergeDiff(diff) 1583 1584 expected := map[string]string{ 1585 "foo": "baz", 1586 } 1587 1588 if !reflect.DeepEqual(expected, is2.Attributes) { 1589 t.Fatalf("bad: %#v", is2.Attributes) 1590 } 1591 } 1592 1593 func TestInstanceState_MergeDiff_nilDiff(t *testing.T) { 1594 is := InstanceState{ 1595 ID: "foo", 1596 Attributes: map[string]string{ 1597 "foo": "bar", 1598 }, 1599 } 1600 1601 is2 := is.MergeDiff(nil) 1602 1603 expected := map[string]string{ 1604 "foo": "bar", 1605 } 1606 1607 if !reflect.DeepEqual(expected, is2.Attributes) { 1608 t.Fatalf("bad: %#v", is2.Attributes) 1609 } 1610 } 1611 1612 func TestReadWriteState(t *testing.T) { 1613 state := &State{ 1614 Serial: 9, 1615 Lineage: "5d1ad1a1-4027-4665-a908-dbe6adff11d8", 1616 Remote: &RemoteState{ 1617 Type: "http", 1618 Config: map[string]string{ 1619 "url": "http://my-cool-server.com/", 1620 }, 1621 }, 1622 Modules: []*ModuleState{ 1623 &ModuleState{ 1624 Path: rootModulePath, 1625 Dependencies: []string{ 1626 "aws_instance.bar", 1627 }, 1628 Resources: map[string]*ResourceState{ 1629 "foo": &ResourceState{ 1630 Primary: &InstanceState{ 1631 ID: "bar", 1632 Ephemeral: EphemeralState{ 1633 ConnInfo: map[string]string{ 1634 "type": "ssh", 1635 "user": "root", 1636 "password": "supersecret", 1637 }, 1638 }, 1639 }, 1640 }, 1641 }, 1642 }, 1643 }, 1644 } 1645 state.init() 1646 1647 buf := new(bytes.Buffer) 1648 if err := WriteState(state, buf); err != nil { 1649 t.Fatalf("err: %s", err) 1650 } 1651 1652 // Verify that the version and serial are set 1653 if state.Version != StateVersion { 1654 t.Fatalf("bad version number: %d", state.Version) 1655 } 1656 1657 actual, err := ReadState(buf) 1658 if err != nil { 1659 t.Fatalf("err: %s", err) 1660 } 1661 1662 // ReadState should not restore sensitive information! 1663 mod := state.RootModule() 1664 mod.Resources["foo"].Primary.Ephemeral = EphemeralState{} 1665 mod.Resources["foo"].Primary.Ephemeral.init() 1666 1667 if !reflect.DeepEqual(actual, state) { 1668 t.Logf("expected:\n%#v", state) 1669 t.Fatalf("got:\n%#v", actual) 1670 } 1671 } 1672 1673 func TestReadStateNewVersion(t *testing.T) { 1674 type out struct { 1675 Version int 1676 } 1677 1678 buf, err := json.Marshal(&out{StateVersion + 1}) 1679 if err != nil { 1680 t.Fatalf("err: %v", err) 1681 } 1682 1683 s, err := ReadState(bytes.NewReader(buf)) 1684 if s != nil { 1685 t.Fatalf("unexpected: %#v", s) 1686 } 1687 if !strings.Contains(err.Error(), "does not support state version") { 1688 t.Fatalf("err: %v", err) 1689 } 1690 } 1691 1692 func TestReadStateEmptyOrNilFile(t *testing.T) { 1693 var emptyState bytes.Buffer 1694 _, err := ReadState(&emptyState) 1695 if err != ErrNoState { 1696 t.Fatal("expected ErrNostate, got", err) 1697 } 1698 1699 var nilFile *os.File 1700 _, err = ReadState(nilFile) 1701 if err != ErrNoState { 1702 t.Fatal("expected ErrNostate, got", err) 1703 } 1704 } 1705 1706 func TestReadStateTFVersion(t *testing.T) { 1707 type tfVersion struct { 1708 Version int `json:"version"` 1709 TFVersion string `json:"terraform_version"` 1710 } 1711 1712 cases := []struct { 1713 Written string 1714 Read string 1715 Err bool 1716 }{ 1717 { 1718 "0.0.0", 1719 "0.0.0", 1720 false, 1721 }, 1722 { 1723 "", 1724 "", 1725 false, 1726 }, 1727 { 1728 "bad", 1729 "", 1730 true, 1731 }, 1732 } 1733 1734 for _, tc := range cases { 1735 buf, err := json.Marshal(&tfVersion{ 1736 Version: 2, 1737 TFVersion: tc.Written, 1738 }) 1739 if err != nil { 1740 t.Fatalf("err: %v", err) 1741 } 1742 1743 s, err := ReadState(bytes.NewReader(buf)) 1744 if (err != nil) != tc.Err { 1745 t.Fatalf("%s: err: %s", tc.Written, err) 1746 } 1747 if err != nil { 1748 continue 1749 } 1750 1751 if s.TFVersion != tc.Read { 1752 t.Fatalf("%s: bad: %s", tc.Written, s.TFVersion) 1753 } 1754 } 1755 } 1756 1757 func TestWriteStateTFVersion(t *testing.T) { 1758 cases := []struct { 1759 Write string 1760 Read string 1761 Err bool 1762 }{ 1763 { 1764 "0.0.0", 1765 "0.0.0", 1766 false, 1767 }, 1768 { 1769 "", 1770 "", 1771 false, 1772 }, 1773 { 1774 "bad", 1775 "", 1776 true, 1777 }, 1778 } 1779 1780 for _, tc := range cases { 1781 var buf bytes.Buffer 1782 err := WriteState(&State{TFVersion: tc.Write}, &buf) 1783 if (err != nil) != tc.Err { 1784 t.Fatalf("%s: err: %s", tc.Write, err) 1785 } 1786 if err != nil { 1787 continue 1788 } 1789 1790 s, err := ReadState(&buf) 1791 if err != nil { 1792 t.Fatalf("%s: err: %s", tc.Write, err) 1793 } 1794 1795 if s.TFVersion != tc.Read { 1796 t.Fatalf("%s: bad: %s", tc.Write, s.TFVersion) 1797 } 1798 } 1799 } 1800 1801 func TestParseResourceStateKey(t *testing.T) { 1802 cases := []struct { 1803 Input string 1804 Expected *ResourceStateKey 1805 ExpectedErr bool 1806 }{ 1807 { 1808 Input: "aws_instance.foo.3", 1809 Expected: &ResourceStateKey{ 1810 Mode: config.ManagedResourceMode, 1811 Type: "aws_instance", 1812 Name: "foo", 1813 Index: 3, 1814 }, 1815 }, 1816 { 1817 Input: "aws_instance.foo.0", 1818 Expected: &ResourceStateKey{ 1819 Mode: config.ManagedResourceMode, 1820 Type: "aws_instance", 1821 Name: "foo", 1822 Index: 0, 1823 }, 1824 }, 1825 { 1826 Input: "aws_instance.foo", 1827 Expected: &ResourceStateKey{ 1828 Mode: config.ManagedResourceMode, 1829 Type: "aws_instance", 1830 Name: "foo", 1831 Index: -1, 1832 }, 1833 }, 1834 { 1835 Input: "data.aws_ami.foo", 1836 Expected: &ResourceStateKey{ 1837 Mode: config.DataResourceMode, 1838 Type: "aws_ami", 1839 Name: "foo", 1840 Index: -1, 1841 }, 1842 }, 1843 { 1844 Input: "aws_instance.foo.malformed", 1845 ExpectedErr: true, 1846 }, 1847 { 1848 Input: "aws_instance.foo.malformedwithnumber.123", 1849 ExpectedErr: true, 1850 }, 1851 { 1852 Input: "malformed", 1853 ExpectedErr: true, 1854 }, 1855 } 1856 for _, tc := range cases { 1857 rsk, err := ParseResourceStateKey(tc.Input) 1858 if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) { 1859 t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk) 1860 } 1861 if (err != nil) != tc.ExpectedErr { 1862 t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err) 1863 } 1864 } 1865 } 1866 1867 func TestStateModuleOrphans_empty(t *testing.T) { 1868 state := &State{ 1869 Modules: []*ModuleState{ 1870 &ModuleState{ 1871 Path: RootModulePath, 1872 }, 1873 &ModuleState{ 1874 Path: []string{RootModuleName, "foo", "bar"}, 1875 }, 1876 &ModuleState{ 1877 Path: []string{}, 1878 }, 1879 nil, 1880 }, 1881 } 1882 1883 state.init() 1884 1885 // just calling this to check for panic 1886 state.ModuleOrphans(RootModulePath, nil) 1887 } 1888 1889 func TestReadState_prune(t *testing.T) { 1890 state := &State{ 1891 Modules: []*ModuleState{ 1892 &ModuleState{Path: rootModulePath}, 1893 nil, 1894 }, 1895 } 1896 state.init() 1897 1898 buf := new(bytes.Buffer) 1899 if err := WriteState(state, buf); err != nil { 1900 t.Fatalf("err: %s", err) 1901 } 1902 1903 actual, err := ReadState(buf) 1904 if err != nil { 1905 t.Fatalf("err: %s", err) 1906 } 1907 1908 expected := &State{ 1909 Version: state.Version, 1910 Lineage: state.Lineage, 1911 } 1912 expected.init() 1913 1914 if !reflect.DeepEqual(actual, expected) { 1915 t.Fatalf("got:\n%#v", actual) 1916 } 1917 } 1918 1919 func TestReadState_pruneDependencies(t *testing.T) { 1920 state := &State{ 1921 Serial: 9, 1922 Lineage: "5d1ad1a1-4027-4665-a908-dbe6adff11d8", 1923 Remote: &RemoteState{ 1924 Type: "http", 1925 Config: map[string]string{ 1926 "url": "http://my-cool-server.com/", 1927 }, 1928 }, 1929 Modules: []*ModuleState{ 1930 &ModuleState{ 1931 Path: rootModulePath, 1932 Dependencies: []string{ 1933 "aws_instance.bar", 1934 "aws_instance.bar", 1935 }, 1936 Resources: map[string]*ResourceState{ 1937 "foo": &ResourceState{ 1938 Dependencies: []string{ 1939 "aws_instance.baz", 1940 "aws_instance.baz", 1941 }, 1942 Primary: &InstanceState{ 1943 ID: "bar", 1944 }, 1945 }, 1946 }, 1947 }, 1948 }, 1949 } 1950 state.init() 1951 1952 buf := new(bytes.Buffer) 1953 if err := WriteState(state, buf); err != nil { 1954 t.Fatalf("err: %s", err) 1955 } 1956 1957 actual, err := ReadState(buf) 1958 if err != nil { 1959 t.Fatalf("err: %s", err) 1960 } 1961 1962 // make sure the duplicate Dependencies are filtered 1963 modDeps := actual.Modules[0].Dependencies 1964 resourceDeps := actual.Modules[0].Resources["foo"].Dependencies 1965 1966 if len(modDeps) > 1 || modDeps[0] != "aws_instance.bar" { 1967 t.Fatalf("expected 1 module depends_on entry, got %q", modDeps) 1968 } 1969 1970 if len(resourceDeps) > 1 || resourceDeps[0] != "aws_instance.baz" { 1971 t.Fatalf("expected 1 resource depends_on entry, got %q", resourceDeps) 1972 } 1973 } 1974 1975 func TestResourceNameSort(t *testing.T) { 1976 names := []string{ 1977 "a", 1978 "b", 1979 "a.0", 1980 "a.c", 1981 "a.d", 1982 "c", 1983 "a.b.0", 1984 "a.b.1", 1985 "a.b.10", 1986 "a.b.2", 1987 } 1988 1989 sort.Sort(resourceNameSort(names)) 1990 1991 expected := []string{ 1992 "a", 1993 "a.0", 1994 "a.b.0", 1995 "a.b.1", 1996 "a.b.2", 1997 "a.b.10", 1998 "a.c", 1999 "a.d", 2000 "b", 2001 "c", 2002 } 2003 2004 if !reflect.DeepEqual(names, expected) { 2005 t.Fatalf("got: %q\nexpected: %q\n", names, expected) 2006 } 2007 }