github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/terraform/resource_address_test.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/config/module" 10 ) 11 12 func TestParseResourceAddressInternal(t *testing.T) { 13 cases := map[string]struct { 14 Input string 15 Expected *ResourceAddress 16 Output string 17 }{ 18 "basic resource": { 19 "aws_instance.foo", 20 &ResourceAddress{ 21 Mode: config.ManagedResourceMode, 22 Type: "aws_instance", 23 Name: "foo", 24 InstanceType: TypePrimary, 25 Index: -1, 26 }, 27 "aws_instance.foo", 28 }, 29 30 "basic resource with count": { 31 "aws_instance.foo.1", 32 &ResourceAddress{ 33 Mode: config.ManagedResourceMode, 34 Type: "aws_instance", 35 Name: "foo", 36 InstanceType: TypePrimary, 37 Index: 1, 38 }, 39 "aws_instance.foo[1]", 40 }, 41 42 "data resource": { 43 "data.aws_ami.foo", 44 &ResourceAddress{ 45 Mode: config.DataResourceMode, 46 Type: "aws_ami", 47 Name: "foo", 48 InstanceType: TypePrimary, 49 Index: -1, 50 }, 51 "data.aws_ami.foo", 52 }, 53 54 "data resource with count": { 55 "data.aws_ami.foo.1", 56 &ResourceAddress{ 57 Mode: config.DataResourceMode, 58 Type: "aws_ami", 59 Name: "foo", 60 InstanceType: TypePrimary, 61 Index: 1, 62 }, 63 "data.aws_ami.foo[1]", 64 }, 65 66 "non-data resource with 4 elements": { 67 "aws_instance.foo.bar.1", 68 nil, 69 "", 70 }, 71 } 72 73 for tn, tc := range cases { 74 t.Run(tc.Input, func(t *testing.T) { 75 out, err := parseResourceAddressInternal(tc.Input) 76 if (err != nil) != (tc.Expected == nil) { 77 t.Fatalf("%s: unexpected err: %#v", tn, err) 78 } 79 if err != nil { 80 return 81 } 82 83 if !reflect.DeepEqual(out, tc.Expected) { 84 t.Fatalf("bad: %q\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Expected, out) 85 } 86 87 // Compare outputs if those exist 88 expected := tc.Input 89 if tc.Output != "" { 90 expected = tc.Output 91 } 92 if out.String() != expected { 93 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, expected, out) 94 } 95 96 // Compare equality because the internal parse is used 97 // to compare equality to equal inputs. 98 if !out.Equals(tc.Expected) { 99 t.Fatalf("expected equality:\n\n%#v\n\n%#v", out, tc.Expected) 100 } 101 }) 102 } 103 } 104 105 func TestParseResourceAddress(t *testing.T) { 106 cases := map[string]struct { 107 Input string 108 Expected *ResourceAddress 109 Output string 110 Err bool 111 }{ 112 "implicit primary managed instance, no specific index": { 113 "aws_instance.foo", 114 &ResourceAddress{ 115 Mode: config.ManagedResourceMode, 116 Type: "aws_instance", 117 Name: "foo", 118 InstanceType: TypePrimary, 119 Index: -1, 120 }, 121 "", 122 false, 123 }, 124 "implicit primary data instance, no specific index": { 125 "data.aws_instance.foo", 126 &ResourceAddress{ 127 Mode: config.DataResourceMode, 128 Type: "aws_instance", 129 Name: "foo", 130 InstanceType: TypePrimary, 131 Index: -1, 132 }, 133 "", 134 false, 135 }, 136 "implicit primary, explicit index": { 137 "aws_instance.foo[2]", 138 &ResourceAddress{ 139 Mode: config.ManagedResourceMode, 140 Type: "aws_instance", 141 Name: "foo", 142 InstanceType: TypePrimary, 143 Index: 2, 144 }, 145 "", 146 false, 147 }, 148 "implicit primary, explicit index over ten": { 149 "aws_instance.foo[12]", 150 &ResourceAddress{ 151 Mode: config.ManagedResourceMode, 152 Type: "aws_instance", 153 Name: "foo", 154 InstanceType: TypePrimary, 155 Index: 12, 156 }, 157 "", 158 false, 159 }, 160 "explicit primary, explicit index": { 161 "aws_instance.foo.primary[2]", 162 &ResourceAddress{ 163 Mode: config.ManagedResourceMode, 164 Type: "aws_instance", 165 Name: "foo", 166 InstanceType: TypePrimary, 167 InstanceTypeSet: true, 168 Index: 2, 169 }, 170 "", 171 false, 172 }, 173 "tainted": { 174 "aws_instance.foo.tainted", 175 &ResourceAddress{ 176 Mode: config.ManagedResourceMode, 177 Type: "aws_instance", 178 Name: "foo", 179 InstanceType: TypeTainted, 180 InstanceTypeSet: true, 181 Index: -1, 182 }, 183 "", 184 false, 185 }, 186 "deposed": { 187 "aws_instance.foo.deposed", 188 &ResourceAddress{ 189 Mode: config.ManagedResourceMode, 190 Type: "aws_instance", 191 Name: "foo", 192 InstanceType: TypeDeposed, 193 InstanceTypeSet: true, 194 Index: -1, 195 }, 196 "", 197 false, 198 }, 199 "with a hyphen": { 200 "aws_instance.foo-bar", 201 &ResourceAddress{ 202 Mode: config.ManagedResourceMode, 203 Type: "aws_instance", 204 Name: "foo-bar", 205 InstanceType: TypePrimary, 206 Index: -1, 207 }, 208 "", 209 false, 210 }, 211 "managed in a module": { 212 "module.child.aws_instance.foo", 213 &ResourceAddress{ 214 Path: []string{"child"}, 215 Mode: config.ManagedResourceMode, 216 Type: "aws_instance", 217 Name: "foo", 218 InstanceType: TypePrimary, 219 Index: -1, 220 }, 221 "", 222 false, 223 }, 224 "data in a module": { 225 "module.child.data.aws_instance.foo", 226 &ResourceAddress{ 227 Path: []string{"child"}, 228 Mode: config.DataResourceMode, 229 Type: "aws_instance", 230 Name: "foo", 231 InstanceType: TypePrimary, 232 Index: -1, 233 }, 234 "", 235 false, 236 }, 237 "nested modules": { 238 "module.a.module.b.module.forever.aws_instance.foo", 239 &ResourceAddress{ 240 Path: []string{"a", "b", "forever"}, 241 Mode: config.ManagedResourceMode, 242 Type: "aws_instance", 243 Name: "foo", 244 InstanceType: TypePrimary, 245 Index: -1, 246 }, 247 "", 248 false, 249 }, 250 "just a module": { 251 "module.a", 252 &ResourceAddress{ 253 Path: []string{"a"}, 254 Type: "", 255 Name: "", 256 InstanceType: TypePrimary, 257 Index: -1, 258 }, 259 "", 260 false, 261 }, 262 "just a nested module": { 263 "module.a.module.b", 264 &ResourceAddress{ 265 Path: []string{"a", "b"}, 266 Type: "", 267 Name: "", 268 InstanceType: TypePrimary, 269 Index: -1, 270 }, 271 "", 272 false, 273 }, 274 "module missing resource type": { 275 "module.name.foo", 276 nil, 277 "", 278 true, 279 }, 280 } 281 282 for tn, tc := range cases { 283 t.Run(tn, func(t *testing.T) { 284 out, err := ParseResourceAddress(tc.Input) 285 if (err != nil) != tc.Err { 286 t.Fatalf("%s: unexpected err: %#v", tn, err) 287 } 288 if tc.Err { 289 return 290 } 291 292 if !reflect.DeepEqual(out, tc.Expected) { 293 t.Fatalf("bad: %q\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Expected, out) 294 } 295 296 expected := tc.Input 297 if tc.Output != "" { 298 expected = tc.Output 299 } 300 if out.String() != expected { 301 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, expected, out) 302 } 303 }) 304 } 305 } 306 307 func TestResourceAddressEquals(t *testing.T) { 308 cases := map[string]struct { 309 Address *ResourceAddress 310 Other interface{} 311 Expect bool 312 }{ 313 "basic match": { 314 Address: &ResourceAddress{ 315 Mode: config.ManagedResourceMode, 316 Type: "aws_instance", 317 Name: "foo", 318 InstanceType: TypePrimary, 319 Index: 0, 320 }, 321 Other: &ResourceAddress{ 322 Mode: config.ManagedResourceMode, 323 Type: "aws_instance", 324 Name: "foo", 325 InstanceType: TypePrimary, 326 Index: 0, 327 }, 328 Expect: true, 329 }, 330 "address does not set index": { 331 Address: &ResourceAddress{ 332 Mode: config.ManagedResourceMode, 333 Type: "aws_instance", 334 Name: "foo", 335 InstanceType: TypePrimary, 336 Index: -1, 337 }, 338 Other: &ResourceAddress{ 339 Mode: config.ManagedResourceMode, 340 Type: "aws_instance", 341 Name: "foo", 342 InstanceType: TypePrimary, 343 Index: 3, 344 }, 345 Expect: true, 346 }, 347 "other does not set index": { 348 Address: &ResourceAddress{ 349 Mode: config.ManagedResourceMode, 350 Type: "aws_instance", 351 Name: "foo", 352 InstanceType: TypePrimary, 353 Index: 3, 354 }, 355 Other: &ResourceAddress{ 356 Mode: config.ManagedResourceMode, 357 Type: "aws_instance", 358 Name: "foo", 359 InstanceType: TypePrimary, 360 Index: -1, 361 }, 362 Expect: true, 363 }, 364 "neither sets index": { 365 Address: &ResourceAddress{ 366 Mode: config.ManagedResourceMode, 367 Type: "aws_instance", 368 Name: "foo", 369 InstanceType: TypePrimary, 370 Index: -1, 371 }, 372 Other: &ResourceAddress{ 373 Mode: config.ManagedResourceMode, 374 Type: "aws_instance", 375 Name: "foo", 376 InstanceType: TypePrimary, 377 Index: -1, 378 }, 379 Expect: true, 380 }, 381 "index over ten": { 382 Address: &ResourceAddress{ 383 Mode: config.ManagedResourceMode, 384 Type: "aws_instance", 385 Name: "foo", 386 InstanceType: TypePrimary, 387 Index: 1, 388 }, 389 Other: &ResourceAddress{ 390 Mode: config.ManagedResourceMode, 391 Type: "aws_instance", 392 Name: "foo", 393 InstanceType: TypePrimary, 394 Index: 13, 395 }, 396 Expect: false, 397 }, 398 "different type": { 399 Address: &ResourceAddress{ 400 Mode: config.ManagedResourceMode, 401 Type: "aws_instance", 402 Name: "foo", 403 InstanceType: TypePrimary, 404 Index: 0, 405 }, 406 Other: &ResourceAddress{ 407 Mode: config.ManagedResourceMode, 408 Type: "aws_vpc", 409 Name: "foo", 410 InstanceType: TypePrimary, 411 Index: 0, 412 }, 413 Expect: false, 414 }, 415 "different mode": { 416 Address: &ResourceAddress{ 417 Mode: config.ManagedResourceMode, 418 Type: "aws_instance", 419 Name: "foo", 420 InstanceType: TypePrimary, 421 Index: 0, 422 }, 423 Other: &ResourceAddress{ 424 Mode: config.DataResourceMode, 425 Type: "aws_instance", 426 Name: "foo", 427 InstanceType: TypePrimary, 428 Index: 0, 429 }, 430 Expect: false, 431 }, 432 "different name": { 433 Address: &ResourceAddress{ 434 Mode: config.ManagedResourceMode, 435 Type: "aws_instance", 436 Name: "foo", 437 InstanceType: TypePrimary, 438 Index: 0, 439 }, 440 Other: &ResourceAddress{ 441 Mode: config.ManagedResourceMode, 442 Type: "aws_instance", 443 Name: "bar", 444 InstanceType: TypePrimary, 445 Index: 0, 446 }, 447 Expect: false, 448 }, 449 "different instance type": { 450 Address: &ResourceAddress{ 451 Mode: config.ManagedResourceMode, 452 Type: "aws_instance", 453 Name: "foo", 454 InstanceType: TypePrimary, 455 Index: 0, 456 }, 457 Other: &ResourceAddress{ 458 Mode: config.ManagedResourceMode, 459 Type: "aws_instance", 460 Name: "foo", 461 InstanceType: TypeTainted, 462 Index: 0, 463 }, 464 Expect: false, 465 }, 466 "different index": { 467 Address: &ResourceAddress{ 468 Mode: config.ManagedResourceMode, 469 Type: "aws_instance", 470 Name: "foo", 471 InstanceType: TypePrimary, 472 Index: 0, 473 }, 474 Other: &ResourceAddress{ 475 Mode: config.ManagedResourceMode, 476 Type: "aws_instance", 477 Name: "foo", 478 InstanceType: TypePrimary, 479 Index: 1, 480 }, 481 Expect: false, 482 }, 483 "module address matches address of managed resource inside module": { 484 Address: &ResourceAddress{ 485 Path: []string{"a", "b"}, 486 Type: "", 487 Name: "", 488 InstanceType: TypePrimary, 489 Index: -1, 490 }, 491 Other: &ResourceAddress{ 492 Path: []string{"a", "b"}, 493 Mode: config.ManagedResourceMode, 494 Type: "aws_instance", 495 Name: "foo", 496 InstanceType: TypePrimary, 497 Index: 0, 498 }, 499 Expect: true, 500 }, 501 "module address matches address of data resource inside module": { 502 Address: &ResourceAddress{ 503 Path: []string{"a", "b"}, 504 Type: "", 505 Name: "", 506 InstanceType: TypePrimary, 507 Index: -1, 508 }, 509 Other: &ResourceAddress{ 510 Path: []string{"a", "b"}, 511 Mode: config.DataResourceMode, 512 Type: "aws_instance", 513 Name: "foo", 514 InstanceType: TypePrimary, 515 Index: 0, 516 }, 517 Expect: true, 518 }, 519 "module address doesn't match managed resource outside module": { 520 Address: &ResourceAddress{ 521 Path: []string{"a", "b"}, 522 Type: "", 523 Name: "", 524 InstanceType: TypePrimary, 525 Index: -1, 526 }, 527 Other: &ResourceAddress{ 528 Path: []string{"a"}, 529 Mode: config.ManagedResourceMode, 530 Type: "aws_instance", 531 Name: "foo", 532 InstanceType: TypePrimary, 533 Index: 0, 534 }, 535 Expect: false, 536 }, 537 "module address doesn't match data resource outside module": { 538 Address: &ResourceAddress{ 539 Path: []string{"a", "b"}, 540 Type: "", 541 Name: "", 542 InstanceType: TypePrimary, 543 Index: -1, 544 }, 545 Other: &ResourceAddress{ 546 Path: []string{"a"}, 547 Mode: config.DataResourceMode, 548 Type: "aws_instance", 549 Name: "foo", 550 InstanceType: TypePrimary, 551 Index: 0, 552 }, 553 Expect: false, 554 }, 555 "nil path vs empty path should match": { 556 Address: &ResourceAddress{ 557 Path: []string{}, 558 Mode: config.ManagedResourceMode, 559 Type: "aws_instance", 560 Name: "foo", 561 InstanceType: TypePrimary, 562 Index: -1, 563 }, 564 Other: &ResourceAddress{ 565 Path: nil, 566 Mode: config.ManagedResourceMode, 567 Type: "aws_instance", 568 Name: "foo", 569 InstanceType: TypePrimary, 570 Index: 0, 571 }, 572 Expect: true, 573 }, 574 } 575 576 for tn, tc := range cases { 577 actual := tc.Address.Equals(tc.Other) 578 if actual != tc.Expect { 579 t.Fatalf("%q: expected equals: %t, got %t for:\n%#v\n%#v", 580 tn, tc.Expect, actual, tc.Address, tc.Other) 581 } 582 } 583 } 584 585 func TestResourceAddressStateId(t *testing.T) { 586 cases := map[string]struct { 587 Input *ResourceAddress 588 Expected string 589 }{ 590 "basic resource": { 591 &ResourceAddress{ 592 Mode: config.ManagedResourceMode, 593 Type: "aws_instance", 594 Name: "foo", 595 InstanceType: TypePrimary, 596 Index: -1, 597 }, 598 "aws_instance.foo", 599 }, 600 601 "basic resource with index": { 602 &ResourceAddress{ 603 Mode: config.ManagedResourceMode, 604 Type: "aws_instance", 605 Name: "foo", 606 InstanceType: TypePrimary, 607 Index: 2, 608 }, 609 "aws_instance.foo.2", 610 }, 611 612 "data resource": { 613 &ResourceAddress{ 614 Mode: config.DataResourceMode, 615 Type: "aws_instance", 616 Name: "foo", 617 InstanceType: TypePrimary, 618 Index: -1, 619 }, 620 "data.aws_instance.foo", 621 }, 622 } 623 624 for tn, tc := range cases { 625 t.Run(tn, func(t *testing.T) { 626 actual := tc.Input.stateId() 627 if actual != tc.Expected { 628 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, tc.Expected, actual) 629 } 630 }) 631 } 632 } 633 634 func TestResourceAddressHasResourceSpec(t *testing.T) { 635 cases := []struct { 636 Input string 637 Want bool 638 }{ 639 { 640 "module.foo", 641 false, 642 }, 643 { 644 "module.foo.module.bar", 645 false, 646 }, 647 { 648 "null_resource.baz", 649 true, 650 }, 651 { 652 "null_resource.baz[0]", 653 true, 654 }, 655 { 656 "data.null_data_source.baz", 657 true, 658 }, 659 { 660 "data.null_data_source.baz[0]", 661 true, 662 }, 663 { 664 "module.foo.null_resource.baz", 665 true, 666 }, 667 { 668 "module.foo.data.null_data_source.baz", 669 true, 670 }, 671 { 672 "module.foo.module.bar.null_resource.baz", 673 true, 674 }, 675 } 676 677 for _, test := range cases { 678 t.Run(test.Input, func(t *testing.T) { 679 addr, err := ParseResourceAddress(test.Input) 680 if err != nil { 681 t.Fatalf("error parsing address: %s", err) 682 } 683 got := addr.HasResourceSpec() 684 if got != test.Want { 685 t.Fatalf("%q: wrong result %#v; want %#v", test.Input, got, test.Want) 686 } 687 }) 688 } 689 } 690 691 func TestResourceAddressWholeModuleAddress(t *testing.T) { 692 cases := []struct { 693 Input string 694 Want string 695 }{ 696 { 697 "module.foo", 698 "module.foo", 699 }, 700 { 701 "module.foo.module.bar", 702 "module.foo.module.bar", 703 }, 704 { 705 "null_resource.baz", 706 "", 707 }, 708 { 709 "null_resource.baz[0]", 710 "", 711 }, 712 { 713 "data.null_data_source.baz", 714 "", 715 }, 716 { 717 "data.null_data_source.baz[0]", 718 "", 719 }, 720 { 721 "module.foo.null_resource.baz", 722 "module.foo", 723 }, 724 { 725 "module.foo.data.null_data_source.baz", 726 "module.foo", 727 }, 728 { 729 "module.foo.module.bar.null_resource.baz", 730 "module.foo.module.bar", 731 }, 732 } 733 734 for _, test := range cases { 735 t.Run(test.Input, func(t *testing.T) { 736 addr, err := ParseResourceAddress(test.Input) 737 if err != nil { 738 t.Fatalf("error parsing address: %s", err) 739 } 740 gotAddr := addr.WholeModuleAddress() 741 got := gotAddr.String() 742 if got != test.Want { 743 t.Fatalf("%q: wrong result %#v; want %#v", test.Input, got, test.Want) 744 } 745 }) 746 } 747 } 748 749 func TestResourceAddressMatchesConfig(t *testing.T) { 750 root := testModule(t, "empty-with-child-module") 751 child := root.Child([]string{"child"}) 752 grandchild := root.Child([]string{"child", "grandchild"}) 753 754 tests := []struct { 755 Addr *ResourceAddress 756 Module *module.Tree 757 Resource *config.Resource 758 Want bool 759 }{ 760 { 761 &ResourceAddress{ 762 Mode: config.ManagedResourceMode, 763 Type: "null_resource", 764 Name: "baz", 765 Index: -1, 766 }, 767 root, 768 &config.Resource{ 769 Mode: config.ManagedResourceMode, 770 Type: "null_resource", 771 Name: "baz", 772 }, 773 true, 774 }, 775 { 776 &ResourceAddress{ 777 Path: []string{"child"}, 778 Mode: config.ManagedResourceMode, 779 Type: "null_resource", 780 Name: "baz", 781 Index: -1, 782 }, 783 child, 784 &config.Resource{ 785 Mode: config.ManagedResourceMode, 786 Type: "null_resource", 787 Name: "baz", 788 }, 789 true, 790 }, 791 { 792 &ResourceAddress{ 793 Path: []string{"child", "grandchild"}, 794 Mode: config.ManagedResourceMode, 795 Type: "null_resource", 796 Name: "baz", 797 Index: -1, 798 }, 799 grandchild, 800 &config.Resource{ 801 Mode: config.ManagedResourceMode, 802 Type: "null_resource", 803 Name: "baz", 804 }, 805 true, 806 }, 807 { 808 &ResourceAddress{ 809 Path: []string{"child"}, 810 Index: -1, 811 }, 812 child, 813 &config.Resource{ 814 Mode: config.ManagedResourceMode, 815 Type: "null_resource", 816 Name: "baz", 817 }, 818 true, 819 }, 820 { 821 &ResourceAddress{ 822 Path: []string{"child", "grandchild"}, 823 Index: -1, 824 }, 825 grandchild, 826 &config.Resource{ 827 Mode: config.ManagedResourceMode, 828 Type: "null_resource", 829 Name: "baz", 830 }, 831 true, 832 }, 833 { 834 &ResourceAddress{ 835 Mode: config.DataResourceMode, 836 Type: "null_resource", 837 Name: "baz", 838 Index: -1, 839 }, 840 module.NewEmptyTree(), 841 &config.Resource{ 842 Mode: config.ManagedResourceMode, 843 Type: "null_resource", 844 Name: "baz", 845 }, 846 false, 847 }, 848 { 849 &ResourceAddress{ 850 Mode: config.ManagedResourceMode, 851 Type: "null_resource", 852 Name: "baz", 853 Index: -1, 854 }, 855 module.NewEmptyTree(), 856 &config.Resource{ 857 Mode: config.ManagedResourceMode, 858 Type: "null_resource", 859 Name: "pizza", 860 }, 861 false, 862 }, 863 { 864 &ResourceAddress{ 865 Mode: config.ManagedResourceMode, 866 Type: "null_resource", 867 Name: "baz", 868 Index: -1, 869 }, 870 module.NewEmptyTree(), 871 &config.Resource{ 872 Mode: config.ManagedResourceMode, 873 Type: "aws_instance", 874 Name: "baz", 875 }, 876 false, 877 }, 878 { 879 &ResourceAddress{ 880 Path: []string{"child", "grandchild"}, 881 Mode: config.ManagedResourceMode, 882 Type: "null_resource", 883 Name: "baz", 884 Index: -1, 885 }, 886 child, 887 &config.Resource{ 888 Mode: config.ManagedResourceMode, 889 Type: "null_resource", 890 Name: "baz", 891 }, 892 false, 893 }, 894 { 895 &ResourceAddress{ 896 Path: []string{"child"}, 897 Mode: config.ManagedResourceMode, 898 Type: "null_resource", 899 Name: "baz", 900 Index: -1, 901 }, 902 grandchild, 903 &config.Resource{ 904 Mode: config.ManagedResourceMode, 905 Type: "null_resource", 906 Name: "baz", 907 }, 908 false, 909 }, 910 } 911 912 for i, test := range tests { 913 t.Run(fmt.Sprintf("%02d-%s", i, test.Addr), func(t *testing.T) { 914 got := test.Addr.MatchesConfig(test.Module, test.Resource) 915 if got != test.Want { 916 t.Errorf( 917 "wrong result\naddr: %s\nmod: %#v\nrsrc: %#v\ngot: %#v\nwant: %#v", 918 test.Addr, test.Module.Path(), test.Resource, got, test.Want, 919 ) 920 } 921 }) 922 } 923 }