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