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