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