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