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