github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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 TestStateModuleOrphans(t *testing.T) { 80 state := &State{ 81 Modules: []*ModuleState{ 82 &ModuleState{ 83 Path: RootModulePath, 84 }, 85 &ModuleState{ 86 Path: []string{RootModuleName, "foo"}, 87 }, 88 &ModuleState{ 89 Path: []string{RootModuleName, "bar"}, 90 }, 91 }, 92 } 93 94 config := testModule(t, "state-module-orphans").Config() 95 actual := state.ModuleOrphans(RootModulePath, config) 96 expected := [][]string{ 97 []string{RootModuleName, "foo"}, 98 } 99 100 if !reflect.DeepEqual(actual, expected) { 101 t.Fatalf("bad: %#v", actual) 102 } 103 } 104 105 func TestStateModuleOrphans_nested(t *testing.T) { 106 state := &State{ 107 Modules: []*ModuleState{ 108 &ModuleState{ 109 Path: RootModulePath, 110 }, 111 &ModuleState{ 112 Path: []string{RootModuleName, "foo", "bar"}, 113 }, 114 }, 115 } 116 117 actual := state.ModuleOrphans(RootModulePath, nil) 118 expected := [][]string{ 119 []string{RootModuleName, "foo"}, 120 } 121 122 if !reflect.DeepEqual(actual, expected) { 123 t.Fatalf("bad: %#v", actual) 124 } 125 } 126 127 func TestStateModuleOrphans_nilConfig(t *testing.T) { 128 state := &State{ 129 Modules: []*ModuleState{ 130 &ModuleState{ 131 Path: RootModulePath, 132 }, 133 &ModuleState{ 134 Path: []string{RootModuleName, "foo"}, 135 }, 136 &ModuleState{ 137 Path: []string{RootModuleName, "bar"}, 138 }, 139 }, 140 } 141 142 actual := state.ModuleOrphans(RootModulePath, nil) 143 expected := [][]string{ 144 []string{RootModuleName, "foo"}, 145 []string{RootModuleName, "bar"}, 146 } 147 148 if !reflect.DeepEqual(actual, expected) { 149 t.Fatalf("bad: %#v", actual) 150 } 151 } 152 153 func TestStateEqual(t *testing.T) { 154 cases := []struct { 155 Result bool 156 One, Two *State 157 }{ 158 // Nils 159 { 160 false, 161 nil, 162 &State{Version: 2}, 163 }, 164 165 { 166 true, 167 nil, 168 nil, 169 }, 170 171 // Different versions 172 { 173 false, 174 &State{Version: 5}, 175 &State{Version: 2}, 176 }, 177 178 // Different modules 179 { 180 false, 181 &State{ 182 Modules: []*ModuleState{ 183 &ModuleState{ 184 Path: RootModulePath, 185 }, 186 }, 187 }, 188 &State{}, 189 }, 190 191 { 192 true, 193 &State{ 194 Modules: []*ModuleState{ 195 &ModuleState{ 196 Path: RootModulePath, 197 }, 198 }, 199 }, 200 &State{ 201 Modules: []*ModuleState{ 202 &ModuleState{ 203 Path: RootModulePath, 204 }, 205 }, 206 }, 207 }, 208 209 // Meta differs 210 { 211 false, 212 &State{ 213 Modules: []*ModuleState{ 214 &ModuleState{ 215 Path: rootModulePath, 216 Resources: map[string]*ResourceState{ 217 "test_instance.foo": &ResourceState{ 218 Primary: &InstanceState{ 219 Meta: map[string]string{ 220 "schema_version": "1", 221 }, 222 }, 223 }, 224 }, 225 }, 226 }, 227 }, 228 &State{ 229 Modules: []*ModuleState{ 230 &ModuleState{ 231 Path: rootModulePath, 232 Resources: map[string]*ResourceState{ 233 "test_instance.foo": &ResourceState{ 234 Primary: &InstanceState{ 235 Meta: map[string]string{ 236 "schema_version": "2", 237 }, 238 }, 239 }, 240 }, 241 }, 242 }, 243 }, 244 }, 245 } 246 247 for i, tc := range cases { 248 if tc.One.Equal(tc.Two) != tc.Result { 249 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 250 } 251 if tc.Two.Equal(tc.One) != tc.Result { 252 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 253 } 254 } 255 } 256 257 func TestStateIncrementSerialMaybe(t *testing.T) { 258 cases := map[string]struct { 259 S1, S2 *State 260 Serial int64 261 }{ 262 "S2 is nil": { 263 &State{}, 264 nil, 265 0, 266 }, 267 "S2 is identical": { 268 &State{}, 269 &State{}, 270 0, 271 }, 272 "S2 is different": { 273 &State{}, 274 &State{ 275 Modules: []*ModuleState{ 276 &ModuleState{Path: rootModulePath}, 277 }, 278 }, 279 1, 280 }, 281 "S2 is different, but only via Instance Metadata": { 282 &State{ 283 Serial: 3, 284 Modules: []*ModuleState{ 285 &ModuleState{ 286 Path: rootModulePath, 287 Resources: map[string]*ResourceState{ 288 "test_instance.foo": &ResourceState{ 289 Primary: &InstanceState{ 290 Meta: map[string]string{}, 291 }, 292 }, 293 }, 294 }, 295 }, 296 }, 297 &State{ 298 Serial: 3, 299 Modules: []*ModuleState{ 300 &ModuleState{ 301 Path: rootModulePath, 302 Resources: map[string]*ResourceState{ 303 "test_instance.foo": &ResourceState{ 304 Primary: &InstanceState{ 305 Meta: map[string]string{ 306 "schema_version": "1", 307 }, 308 }, 309 }, 310 }, 311 }, 312 }, 313 }, 314 4, 315 }, 316 "S1 serial is higher": { 317 &State{Serial: 5}, 318 &State{ 319 Serial: 3, 320 Modules: []*ModuleState{ 321 &ModuleState{Path: rootModulePath}, 322 }, 323 }, 324 5, 325 }, 326 } 327 328 for name, tc := range cases { 329 tc.S1.IncrementSerialMaybe(tc.S2) 330 if tc.S1.Serial != tc.Serial { 331 t.Fatalf("Bad: %s\nGot: %d", name, tc.S1.Serial) 332 } 333 } 334 } 335 336 func TestResourceStateEqual(t *testing.T) { 337 cases := []struct { 338 Result bool 339 One, Two *ResourceState 340 }{ 341 // Different types 342 { 343 false, 344 &ResourceState{Type: "foo"}, 345 &ResourceState{Type: "bar"}, 346 }, 347 348 // Different dependencies 349 { 350 false, 351 &ResourceState{Dependencies: []string{"foo"}}, 352 &ResourceState{Dependencies: []string{"bar"}}, 353 }, 354 355 { 356 false, 357 &ResourceState{Dependencies: []string{"foo", "bar"}}, 358 &ResourceState{Dependencies: []string{"foo"}}, 359 }, 360 361 { 362 true, 363 &ResourceState{Dependencies: []string{"bar", "foo"}}, 364 &ResourceState{Dependencies: []string{"foo", "bar"}}, 365 }, 366 367 // Different primaries 368 { 369 false, 370 &ResourceState{Primary: nil}, 371 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 372 }, 373 374 { 375 true, 376 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 377 &ResourceState{Primary: &InstanceState{ID: "foo"}}, 378 }, 379 380 // Different tainted 381 { 382 false, 383 &ResourceState{ 384 Tainted: nil, 385 }, 386 &ResourceState{ 387 Tainted: []*InstanceState{ 388 &InstanceState{ID: "foo"}, 389 }, 390 }, 391 }, 392 393 { 394 true, 395 &ResourceState{ 396 Tainted: []*InstanceState{ 397 &InstanceState{ID: "foo"}, 398 }, 399 }, 400 &ResourceState{ 401 Tainted: []*InstanceState{ 402 &InstanceState{ID: "foo"}, 403 }, 404 }, 405 }, 406 407 { 408 true, 409 &ResourceState{ 410 Tainted: []*InstanceState{ 411 &InstanceState{ID: "foo"}, 412 nil, 413 }, 414 }, 415 &ResourceState{ 416 Tainted: []*InstanceState{ 417 &InstanceState{ID: "foo"}, 418 }, 419 }, 420 }, 421 } 422 423 for i, tc := range cases { 424 if tc.One.Equal(tc.Two) != tc.Result { 425 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 426 } 427 if tc.Two.Equal(tc.One) != tc.Result { 428 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 429 } 430 } 431 } 432 433 func TestResourceStateTaint(t *testing.T) { 434 cases := map[string]struct { 435 Input *ResourceState 436 Output *ResourceState 437 }{ 438 "no primary": { 439 &ResourceState{}, 440 &ResourceState{}, 441 }, 442 443 "primary, no tainted": { 444 &ResourceState{ 445 Primary: &InstanceState{ID: "foo"}, 446 }, 447 &ResourceState{ 448 Tainted: []*InstanceState{ 449 &InstanceState{ID: "foo"}, 450 }, 451 }, 452 }, 453 454 "primary, with tainted": { 455 &ResourceState{ 456 Primary: &InstanceState{ID: "foo"}, 457 Tainted: []*InstanceState{ 458 &InstanceState{ID: "bar"}, 459 }, 460 }, 461 &ResourceState{ 462 Tainted: []*InstanceState{ 463 &InstanceState{ID: "bar"}, 464 &InstanceState{ID: "foo"}, 465 }, 466 }, 467 }, 468 } 469 470 for k, tc := range cases { 471 tc.Input.Taint() 472 if !reflect.DeepEqual(tc.Input, tc.Output) { 473 t.Fatalf( 474 "Failure: %s\n\nExpected: %#v\n\nGot: %#v", 475 k, tc.Output, tc.Input) 476 } 477 } 478 } 479 480 func TestInstanceStateEmpty(t *testing.T) { 481 cases := map[string]struct { 482 In *InstanceState 483 Result bool 484 }{ 485 "nil is empty": { 486 nil, 487 true, 488 }, 489 "non-nil but without ID is empty": { 490 &InstanceState{}, 491 true, 492 }, 493 "with ID is not empty": { 494 &InstanceState{ 495 ID: "i-abc123", 496 }, 497 false, 498 }, 499 } 500 501 for tn, tc := range cases { 502 if tc.In.Empty() != tc.Result { 503 t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result) 504 } 505 } 506 } 507 508 func TestInstanceStateEqual(t *testing.T) { 509 cases := []struct { 510 Result bool 511 One, Two *InstanceState 512 }{ 513 // Nils 514 { 515 false, 516 nil, 517 &InstanceState{}, 518 }, 519 520 { 521 false, 522 &InstanceState{}, 523 nil, 524 }, 525 526 // Different IDs 527 { 528 false, 529 &InstanceState{ID: "foo"}, 530 &InstanceState{ID: "bar"}, 531 }, 532 533 // Different Attributes 534 { 535 false, 536 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 537 &InstanceState{Attributes: map[string]string{"foo": "baz"}}, 538 }, 539 540 // Different Attribute keys 541 { 542 false, 543 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 544 &InstanceState{Attributes: map[string]string{"bar": "baz"}}, 545 }, 546 547 { 548 false, 549 &InstanceState{Attributes: map[string]string{"bar": "baz"}}, 550 &InstanceState{Attributes: map[string]string{"foo": "bar"}}, 551 }, 552 } 553 554 for i, tc := range cases { 555 if tc.One.Equal(tc.Two) != tc.Result { 556 t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) 557 } 558 } 559 } 560 561 func TestStateEmpty(t *testing.T) { 562 cases := []struct { 563 In *State 564 Result bool 565 }{ 566 { 567 nil, 568 true, 569 }, 570 { 571 &State{}, 572 true, 573 }, 574 { 575 &State{ 576 Remote: &RemoteState{Type: "foo"}, 577 }, 578 true, 579 }, 580 { 581 &State{ 582 Modules: []*ModuleState{ 583 &ModuleState{}, 584 }, 585 }, 586 false, 587 }, 588 } 589 590 for i, tc := range cases { 591 if tc.In.Empty() != tc.Result { 592 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 593 } 594 } 595 } 596 597 func TestStateIsRemote(t *testing.T) { 598 cases := []struct { 599 In *State 600 Result bool 601 }{ 602 { 603 nil, 604 false, 605 }, 606 { 607 &State{}, 608 false, 609 }, 610 { 611 &State{ 612 Remote: &RemoteState{Type: "foo"}, 613 }, 614 true, 615 }, 616 } 617 618 for i, tc := range cases { 619 if tc.In.IsRemote() != tc.Result { 620 t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) 621 } 622 } 623 } 624 625 func TestInstanceState_MergeDiff(t *testing.T) { 626 is := InstanceState{ 627 ID: "foo", 628 Attributes: map[string]string{ 629 "foo": "bar", 630 "port": "8000", 631 }, 632 } 633 634 diff := &InstanceDiff{ 635 Attributes: map[string]*ResourceAttrDiff{ 636 "foo": &ResourceAttrDiff{ 637 Old: "bar", 638 New: "baz", 639 }, 640 "bar": &ResourceAttrDiff{ 641 Old: "", 642 New: "foo", 643 }, 644 "baz": &ResourceAttrDiff{ 645 Old: "", 646 New: "foo", 647 NewComputed: true, 648 }, 649 "port": &ResourceAttrDiff{ 650 NewRemoved: true, 651 }, 652 }, 653 } 654 655 is2 := is.MergeDiff(diff) 656 657 expected := map[string]string{ 658 "foo": "baz", 659 "bar": "foo", 660 "baz": config.UnknownVariableValue, 661 } 662 663 if !reflect.DeepEqual(expected, is2.Attributes) { 664 t.Fatalf("bad: %#v", is2.Attributes) 665 } 666 } 667 668 func TestInstanceState_MergeDiff_nil(t *testing.T) { 669 var is *InstanceState = nil 670 671 diff := &InstanceDiff{ 672 Attributes: map[string]*ResourceAttrDiff{ 673 "foo": &ResourceAttrDiff{ 674 Old: "", 675 New: "baz", 676 }, 677 }, 678 } 679 680 is2 := is.MergeDiff(diff) 681 682 expected := map[string]string{ 683 "foo": "baz", 684 } 685 686 if !reflect.DeepEqual(expected, is2.Attributes) { 687 t.Fatalf("bad: %#v", is2.Attributes) 688 } 689 } 690 691 func TestInstanceState_MergeDiff_nilDiff(t *testing.T) { 692 is := InstanceState{ 693 ID: "foo", 694 Attributes: map[string]string{ 695 "foo": "bar", 696 }, 697 } 698 699 is2 := is.MergeDiff(nil) 700 701 expected := map[string]string{ 702 "foo": "bar", 703 } 704 705 if !reflect.DeepEqual(expected, is2.Attributes) { 706 t.Fatalf("bad: %#v", is2.Attributes) 707 } 708 } 709 710 func TestReadUpgradeState(t *testing.T) { 711 state := &StateV1{ 712 Resources: map[string]*ResourceStateV1{ 713 "foo": &ResourceStateV1{ 714 ID: "bar", 715 }, 716 }, 717 } 718 buf := new(bytes.Buffer) 719 if err := testWriteStateV1(state, buf); err != nil { 720 t.Fatalf("err: %s", err) 721 } 722 723 // ReadState should transparently detect the old 724 // version and upgrade up so the latest. 725 actual, err := ReadState(buf) 726 if err != nil { 727 t.Fatalf("err: %s", err) 728 } 729 730 upgraded, err := upgradeV1State(state) 731 if err != nil { 732 t.Fatalf("err: %s", err) 733 } 734 735 if !reflect.DeepEqual(actual, upgraded) { 736 t.Fatalf("bad: %#v", actual) 737 } 738 } 739 740 func TestReadWriteState(t *testing.T) { 741 state := &State{ 742 Serial: 9, 743 Remote: &RemoteState{ 744 Type: "http", 745 Config: map[string]string{ 746 "url": "http://my-cool-server.com/", 747 }, 748 }, 749 Modules: []*ModuleState{ 750 &ModuleState{ 751 Path: rootModulePath, 752 Dependencies: []string{ 753 "aws_instance.bar", 754 }, 755 Resources: map[string]*ResourceState{ 756 "foo": &ResourceState{ 757 Primary: &InstanceState{ 758 ID: "bar", 759 Ephemeral: EphemeralState{ 760 ConnInfo: map[string]string{ 761 "type": "ssh", 762 "user": "root", 763 "password": "supersecret", 764 }, 765 }, 766 }, 767 }, 768 }, 769 }, 770 }, 771 } 772 773 // Checksum before the write 774 chksum := checksumStruct(t, state) 775 776 buf := new(bytes.Buffer) 777 if err := WriteState(state, buf); err != nil { 778 t.Fatalf("err: %s", err) 779 } 780 781 // Verify that the version and serial are set 782 if state.Version != StateVersion { 783 t.Fatalf("bad version number: %d", state.Version) 784 } 785 786 // Checksum after the write 787 chksumAfter := checksumStruct(t, state) 788 if chksumAfter != chksum { 789 t.Fatalf("structure changed during serialization!") 790 } 791 792 actual, err := ReadState(buf) 793 if err != nil { 794 t.Fatalf("err: %s", err) 795 } 796 797 // ReadState should not restore sensitive information! 798 mod := state.RootModule() 799 mod.Resources["foo"].Primary.Ephemeral = EphemeralState{} 800 801 if !reflect.DeepEqual(actual, state) { 802 t.Fatalf("bad: %#v", actual) 803 } 804 } 805 806 func TestReadStateNewVersion(t *testing.T) { 807 type out struct { 808 Version int 809 } 810 811 buf, err := json.Marshal(&out{StateVersion + 1}) 812 if err != nil { 813 t.Fatalf("err: %v", err) 814 } 815 816 s, err := ReadState(bytes.NewReader(buf)) 817 if s != nil { 818 t.Fatalf("unexpected: %#v", s) 819 } 820 if !strings.Contains(err.Error(), "not supported") { 821 t.Fatalf("err: %v", err) 822 } 823 } 824 825 func TestUpgradeV1State(t *testing.T) { 826 old := &StateV1{ 827 Outputs: map[string]string{ 828 "ip": "127.0.0.1", 829 }, 830 Resources: map[string]*ResourceStateV1{ 831 "foo": &ResourceStateV1{ 832 Type: "test_resource", 833 ID: "bar", 834 Attributes: map[string]string{ 835 "key": "val", 836 }, 837 }, 838 "bar": &ResourceStateV1{ 839 Type: "test_resource", 840 ID: "1234", 841 Attributes: map[string]string{ 842 "a": "b", 843 }, 844 }, 845 }, 846 Tainted: map[string]struct{}{ 847 "bar": struct{}{}, 848 }, 849 } 850 state, err := upgradeV1State(old) 851 if err != nil { 852 t.Fatalf("err: %v", err) 853 } 854 855 if len(state.Modules) != 1 { 856 t.Fatalf("should only have root module: %#v", state.Modules) 857 } 858 root := state.RootModule() 859 860 if len(root.Outputs) != 1 { 861 t.Fatalf("bad outputs: %v", root.Outputs) 862 } 863 if root.Outputs["ip"] != "127.0.0.1" { 864 t.Fatalf("bad outputs: %v", root.Outputs) 865 } 866 867 if len(root.Resources) != 2 { 868 t.Fatalf("bad resources: %v", root.Resources) 869 } 870 871 foo := root.Resources["foo"] 872 if foo.Type != "test_resource" { 873 t.Fatalf("bad: %#v", foo) 874 } 875 if foo.Primary == nil || foo.Primary.ID != "bar" || 876 foo.Primary.Attributes["key"] != "val" { 877 t.Fatalf("bad: %#v", foo) 878 } 879 if len(foo.Tainted) > 0 { 880 t.Fatalf("bad: %#v", foo) 881 } 882 883 bar := root.Resources["bar"] 884 if bar.Type != "test_resource" { 885 t.Fatalf("bad: %#v", bar) 886 } 887 if bar.Primary != nil { 888 t.Fatalf("bad: %#v", bar) 889 } 890 if len(bar.Tainted) != 1 { 891 t.Fatalf("bad: %#v", bar) 892 } 893 bt := bar.Tainted[0] 894 if bt.ID != "1234" || bt.Attributes["a"] != "b" { 895 t.Fatalf("bad: %#v", bt) 896 } 897 }