github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/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 TestResourceAddressContains(t *testing.T) { 308 tests := []struct { 309 Address *ResourceAddress 310 Other *ResourceAddress 311 Want bool 312 }{ 313 { 314 &ResourceAddress{ 315 Mode: config.ManagedResourceMode, 316 Type: "aws_instance", 317 Name: "foo", 318 InstanceTypeSet: true, 319 InstanceType: TypePrimary, 320 Index: 0, 321 }, 322 &ResourceAddress{ 323 Mode: config.ManagedResourceMode, 324 Type: "aws_instance", 325 Name: "foo", 326 InstanceTypeSet: true, 327 InstanceType: TypePrimary, 328 Index: 0, 329 }, 330 true, 331 }, 332 { 333 &ResourceAddress{ 334 Mode: config.ManagedResourceMode, 335 Type: "aws_instance", 336 Name: "foo", 337 InstanceTypeSet: false, 338 Index: 0, 339 }, 340 &ResourceAddress{ 341 Mode: config.ManagedResourceMode, 342 Type: "aws_instance", 343 Name: "foo", 344 InstanceTypeSet: true, 345 InstanceType: TypePrimary, 346 Index: 0, 347 }, 348 true, 349 }, 350 { 351 &ResourceAddress{ 352 Mode: config.ManagedResourceMode, 353 Type: "aws_instance", 354 Name: "foo", 355 InstanceTypeSet: false, 356 Index: -1, 357 }, 358 &ResourceAddress{ 359 Mode: config.ManagedResourceMode, 360 Type: "aws_instance", 361 Name: "foo", 362 InstanceTypeSet: true, 363 InstanceType: TypePrimary, 364 Index: 0, 365 }, 366 true, 367 }, 368 { 369 &ResourceAddress{ 370 Mode: config.ManagedResourceMode, 371 Type: "aws_instance", 372 Name: "foo", 373 InstanceTypeSet: false, 374 Index: -1, 375 }, 376 &ResourceAddress{ 377 Mode: config.ManagedResourceMode, 378 Type: "aws_instance", 379 Name: "foo", 380 InstanceTypeSet: false, 381 Index: -1, 382 }, 383 true, 384 }, 385 { 386 &ResourceAddress{ 387 InstanceTypeSet: false, 388 Index: -1, 389 }, 390 &ResourceAddress{ 391 Mode: config.ManagedResourceMode, 392 Type: "aws_instance", 393 Name: "foo", 394 InstanceTypeSet: false, 395 Index: -1, 396 }, 397 true, 398 }, 399 { 400 &ResourceAddress{ 401 InstanceTypeSet: false, 402 Index: -1, 403 }, 404 &ResourceAddress{ 405 Path: []string{"bar"}, 406 Mode: config.ManagedResourceMode, 407 Type: "aws_instance", 408 Name: "foo", 409 InstanceTypeSet: false, 410 Index: -1, 411 }, 412 true, 413 }, 414 { 415 &ResourceAddress{ 416 Path: []string{"bar"}, 417 InstanceTypeSet: false, 418 Index: -1, 419 }, 420 &ResourceAddress{ 421 Path: []string{"bar"}, 422 Mode: config.ManagedResourceMode, 423 Type: "aws_instance", 424 Name: "foo", 425 InstanceTypeSet: false, 426 Index: -1, 427 }, 428 true, 429 }, 430 { 431 &ResourceAddress{ 432 Path: []string{"bar"}, 433 InstanceTypeSet: false, 434 Index: -1, 435 }, 436 &ResourceAddress{ 437 Path: []string{"bar", "baz"}, 438 Mode: config.ManagedResourceMode, 439 Type: "aws_instance", 440 Name: "foo", 441 InstanceTypeSet: false, 442 Index: -1, 443 }, 444 true, 445 }, 446 { 447 &ResourceAddress{ 448 Path: []string{"bar"}, 449 InstanceTypeSet: false, 450 Index: -1, 451 }, 452 &ResourceAddress{ 453 Path: []string{"bar", "baz"}, 454 InstanceTypeSet: false, 455 Index: -1, 456 }, 457 true, 458 }, 459 { 460 &ResourceAddress{ 461 Path: []string{"bar"}, 462 InstanceTypeSet: false, 463 Index: -1, 464 }, 465 &ResourceAddress{ 466 Path: []string{"bar", "baz", "foo", "pizza"}, 467 InstanceTypeSet: false, 468 Index: -1, 469 }, 470 true, 471 }, 472 473 { 474 &ResourceAddress{ 475 Mode: config.ManagedResourceMode, 476 Type: "aws_instance", 477 Name: "bar", 478 InstanceTypeSet: true, 479 InstanceType: TypePrimary, 480 Index: 0, 481 }, 482 &ResourceAddress{ 483 Mode: config.ManagedResourceMode, 484 Type: "aws_instance", 485 Name: "foo", 486 InstanceTypeSet: true, 487 InstanceType: TypePrimary, 488 Index: 0, 489 }, 490 false, 491 }, 492 { 493 &ResourceAddress{ 494 Mode: config.ManagedResourceMode, 495 Type: "aws_instance", 496 Name: "foo", 497 InstanceTypeSet: true, 498 InstanceType: TypePrimary, 499 Index: 0, 500 }, 501 &ResourceAddress{ 502 Mode: config.DataResourceMode, 503 Type: "aws_instance", 504 Name: "foo", 505 InstanceTypeSet: true, 506 InstanceType: TypePrimary, 507 Index: 0, 508 }, 509 false, 510 }, 511 { 512 &ResourceAddress{ 513 Path: []string{"bar"}, 514 InstanceTypeSet: false, 515 Index: -1, 516 }, 517 &ResourceAddress{ 518 Path: []string{"baz"}, 519 Mode: config.ManagedResourceMode, 520 Type: "aws_instance", 521 Name: "foo", 522 InstanceTypeSet: false, 523 Index: -1, 524 }, 525 false, 526 }, 527 { 528 &ResourceAddress{ 529 Path: []string{"bar"}, 530 InstanceTypeSet: false, 531 Index: -1, 532 }, 533 &ResourceAddress{ 534 Path: []string{"baz", "bar"}, 535 Mode: config.ManagedResourceMode, 536 Type: "aws_instance", 537 Name: "foo", 538 InstanceTypeSet: false, 539 Index: -1, 540 }, 541 false, 542 }, 543 { 544 &ResourceAddress{ 545 Mode: config.ManagedResourceMode, 546 Type: "aws_instance", 547 Name: "foo", 548 InstanceTypeSet: true, 549 InstanceType: TypePrimary, 550 Index: 0, 551 }, 552 &ResourceAddress{ 553 Mode: config.ManagedResourceMode, 554 Type: "aws_instance", 555 Name: "foo", 556 InstanceTypeSet: false, 557 Index: 0, 558 }, 559 false, 560 }, 561 { 562 &ResourceAddress{ 563 Path: []string{"bar", "baz"}, 564 InstanceTypeSet: false, 565 Index: -1, 566 }, 567 &ResourceAddress{ 568 Path: []string{"bar"}, 569 InstanceTypeSet: false, 570 Index: -1, 571 }, 572 false, 573 }, 574 { 575 &ResourceAddress{ 576 Type: "aws_instance", 577 Name: "foo", 578 Index: 1, 579 InstanceType: TypePrimary, 580 Mode: config.ManagedResourceMode, 581 }, 582 &ResourceAddress{ 583 Type: "aws_instance", 584 Name: "foo", 585 Index: -1, 586 InstanceType: TypePrimary, 587 Mode: config.ManagedResourceMode, 588 }, 589 false, 590 }, 591 } 592 593 for _, test := range tests { 594 t.Run(fmt.Sprintf("%s contains %s", test.Address, test.Other), func(t *testing.T) { 595 got := test.Address.Contains(test.Other) 596 if got != test.Want { 597 t.Errorf( 598 "wrong result\nrecv: %s\ngiven: %s\ngot: %#v\nwant: %#v", 599 test.Address, test.Other, 600 got, test.Want, 601 ) 602 } 603 }) 604 } 605 } 606 607 func TestResourceAddressEquals(t *testing.T) { 608 cases := map[string]struct { 609 Address *ResourceAddress 610 Other interface{} 611 Expect bool 612 }{ 613 "basic match": { 614 Address: &ResourceAddress{ 615 Mode: config.ManagedResourceMode, 616 Type: "aws_instance", 617 Name: "foo", 618 InstanceType: TypePrimary, 619 Index: 0, 620 }, 621 Other: &ResourceAddress{ 622 Mode: config.ManagedResourceMode, 623 Type: "aws_instance", 624 Name: "foo", 625 InstanceType: TypePrimary, 626 Index: 0, 627 }, 628 Expect: true, 629 }, 630 "address does not set index": { 631 Address: &ResourceAddress{ 632 Mode: config.ManagedResourceMode, 633 Type: "aws_instance", 634 Name: "foo", 635 InstanceType: TypePrimary, 636 Index: -1, 637 }, 638 Other: &ResourceAddress{ 639 Mode: config.ManagedResourceMode, 640 Type: "aws_instance", 641 Name: "foo", 642 InstanceType: TypePrimary, 643 Index: 3, 644 }, 645 Expect: true, 646 }, 647 "other does not set index": { 648 Address: &ResourceAddress{ 649 Mode: config.ManagedResourceMode, 650 Type: "aws_instance", 651 Name: "foo", 652 InstanceType: TypePrimary, 653 Index: 3, 654 }, 655 Other: &ResourceAddress{ 656 Mode: config.ManagedResourceMode, 657 Type: "aws_instance", 658 Name: "foo", 659 InstanceType: TypePrimary, 660 Index: -1, 661 }, 662 Expect: true, 663 }, 664 "neither sets index": { 665 Address: &ResourceAddress{ 666 Mode: config.ManagedResourceMode, 667 Type: "aws_instance", 668 Name: "foo", 669 InstanceType: TypePrimary, 670 Index: -1, 671 }, 672 Other: &ResourceAddress{ 673 Mode: config.ManagedResourceMode, 674 Type: "aws_instance", 675 Name: "foo", 676 InstanceType: TypePrimary, 677 Index: -1, 678 }, 679 Expect: true, 680 }, 681 "index over ten": { 682 Address: &ResourceAddress{ 683 Mode: config.ManagedResourceMode, 684 Type: "aws_instance", 685 Name: "foo", 686 InstanceType: TypePrimary, 687 Index: 1, 688 }, 689 Other: &ResourceAddress{ 690 Mode: config.ManagedResourceMode, 691 Type: "aws_instance", 692 Name: "foo", 693 InstanceType: TypePrimary, 694 Index: 13, 695 }, 696 Expect: false, 697 }, 698 "different type": { 699 Address: &ResourceAddress{ 700 Mode: config.ManagedResourceMode, 701 Type: "aws_instance", 702 Name: "foo", 703 InstanceType: TypePrimary, 704 Index: 0, 705 }, 706 Other: &ResourceAddress{ 707 Mode: config.ManagedResourceMode, 708 Type: "aws_vpc", 709 Name: "foo", 710 InstanceType: TypePrimary, 711 Index: 0, 712 }, 713 Expect: false, 714 }, 715 "different mode": { 716 Address: &ResourceAddress{ 717 Mode: config.ManagedResourceMode, 718 Type: "aws_instance", 719 Name: "foo", 720 InstanceType: TypePrimary, 721 Index: 0, 722 }, 723 Other: &ResourceAddress{ 724 Mode: config.DataResourceMode, 725 Type: "aws_instance", 726 Name: "foo", 727 InstanceType: TypePrimary, 728 Index: 0, 729 }, 730 Expect: false, 731 }, 732 "different name": { 733 Address: &ResourceAddress{ 734 Mode: config.ManagedResourceMode, 735 Type: "aws_instance", 736 Name: "foo", 737 InstanceType: TypePrimary, 738 Index: 0, 739 }, 740 Other: &ResourceAddress{ 741 Mode: config.ManagedResourceMode, 742 Type: "aws_instance", 743 Name: "bar", 744 InstanceType: TypePrimary, 745 Index: 0, 746 }, 747 Expect: false, 748 }, 749 "different instance type": { 750 Address: &ResourceAddress{ 751 Mode: config.ManagedResourceMode, 752 Type: "aws_instance", 753 Name: "foo", 754 InstanceType: TypePrimary, 755 Index: 0, 756 }, 757 Other: &ResourceAddress{ 758 Mode: config.ManagedResourceMode, 759 Type: "aws_instance", 760 Name: "foo", 761 InstanceType: TypeTainted, 762 Index: 0, 763 }, 764 Expect: false, 765 }, 766 "different index": { 767 Address: &ResourceAddress{ 768 Mode: config.ManagedResourceMode, 769 Type: "aws_instance", 770 Name: "foo", 771 InstanceType: TypePrimary, 772 Index: 0, 773 }, 774 Other: &ResourceAddress{ 775 Mode: config.ManagedResourceMode, 776 Type: "aws_instance", 777 Name: "foo", 778 InstanceType: TypePrimary, 779 Index: 1, 780 }, 781 Expect: false, 782 }, 783 "module address matches address of managed resource inside module": { 784 Address: &ResourceAddress{ 785 Path: []string{"a", "b"}, 786 Type: "", 787 Name: "", 788 InstanceType: TypePrimary, 789 Index: -1, 790 }, 791 Other: &ResourceAddress{ 792 Path: []string{"a", "b"}, 793 Mode: config.ManagedResourceMode, 794 Type: "aws_instance", 795 Name: "foo", 796 InstanceType: TypePrimary, 797 Index: 0, 798 }, 799 Expect: true, 800 }, 801 "module address matches address of data resource inside module": { 802 Address: &ResourceAddress{ 803 Path: []string{"a", "b"}, 804 Type: "", 805 Name: "", 806 InstanceType: TypePrimary, 807 Index: -1, 808 }, 809 Other: &ResourceAddress{ 810 Path: []string{"a", "b"}, 811 Mode: config.DataResourceMode, 812 Type: "aws_instance", 813 Name: "foo", 814 InstanceType: TypePrimary, 815 Index: 0, 816 }, 817 Expect: true, 818 }, 819 "module address doesn't match managed resource outside module": { 820 Address: &ResourceAddress{ 821 Path: []string{"a", "b"}, 822 Type: "", 823 Name: "", 824 InstanceType: TypePrimary, 825 Index: -1, 826 }, 827 Other: &ResourceAddress{ 828 Path: []string{"a"}, 829 Mode: config.ManagedResourceMode, 830 Type: "aws_instance", 831 Name: "foo", 832 InstanceType: TypePrimary, 833 Index: 0, 834 }, 835 Expect: false, 836 }, 837 "module address doesn't match data resource outside module": { 838 Address: &ResourceAddress{ 839 Path: []string{"a", "b"}, 840 Type: "", 841 Name: "", 842 InstanceType: TypePrimary, 843 Index: -1, 844 }, 845 Other: &ResourceAddress{ 846 Path: []string{"a"}, 847 Mode: config.DataResourceMode, 848 Type: "aws_instance", 849 Name: "foo", 850 InstanceType: TypePrimary, 851 Index: 0, 852 }, 853 Expect: false, 854 }, 855 "nil path vs empty path should match": { 856 Address: &ResourceAddress{ 857 Path: []string{}, 858 Mode: config.ManagedResourceMode, 859 Type: "aws_instance", 860 Name: "foo", 861 InstanceType: TypePrimary, 862 Index: -1, 863 }, 864 Other: &ResourceAddress{ 865 Path: nil, 866 Mode: config.ManagedResourceMode, 867 Type: "aws_instance", 868 Name: "foo", 869 InstanceType: TypePrimary, 870 Index: 0, 871 }, 872 Expect: true, 873 }, 874 } 875 876 for tn, tc := range cases { 877 actual := tc.Address.Equals(tc.Other) 878 if actual != tc.Expect { 879 t.Fatalf("%q: expected equals: %t, got %t for:\n%#v\n%#v", 880 tn, tc.Expect, actual, tc.Address, tc.Other) 881 } 882 } 883 } 884 885 func TestResourceAddressStateId(t *testing.T) { 886 cases := map[string]struct { 887 Input *ResourceAddress 888 Expected string 889 }{ 890 "basic resource": { 891 &ResourceAddress{ 892 Mode: config.ManagedResourceMode, 893 Type: "aws_instance", 894 Name: "foo", 895 InstanceType: TypePrimary, 896 Index: -1, 897 }, 898 "aws_instance.foo", 899 }, 900 901 "basic resource with index": { 902 &ResourceAddress{ 903 Mode: config.ManagedResourceMode, 904 Type: "aws_instance", 905 Name: "foo", 906 InstanceType: TypePrimary, 907 Index: 2, 908 }, 909 "aws_instance.foo.2", 910 }, 911 912 "data resource": { 913 &ResourceAddress{ 914 Mode: config.DataResourceMode, 915 Type: "aws_instance", 916 Name: "foo", 917 InstanceType: TypePrimary, 918 Index: -1, 919 }, 920 "data.aws_instance.foo", 921 }, 922 } 923 924 for tn, tc := range cases { 925 t.Run(tn, func(t *testing.T) { 926 actual := tc.Input.stateId() 927 if actual != tc.Expected { 928 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, tc.Expected, actual) 929 } 930 }) 931 } 932 } 933 934 func TestResourceAddressHasResourceSpec(t *testing.T) { 935 cases := []struct { 936 Input string 937 Want bool 938 }{ 939 { 940 "module.foo", 941 false, 942 }, 943 { 944 "module.foo.module.bar", 945 false, 946 }, 947 { 948 "null_resource.baz", 949 true, 950 }, 951 { 952 "null_resource.baz[0]", 953 true, 954 }, 955 { 956 "data.null_data_source.baz", 957 true, 958 }, 959 { 960 "data.null_data_source.baz[0]", 961 true, 962 }, 963 { 964 "module.foo.null_resource.baz", 965 true, 966 }, 967 { 968 "module.foo.data.null_data_source.baz", 969 true, 970 }, 971 { 972 "module.foo.module.bar.null_resource.baz", 973 true, 974 }, 975 } 976 977 for _, test := range cases { 978 t.Run(test.Input, func(t *testing.T) { 979 addr, err := ParseResourceAddress(test.Input) 980 if err != nil { 981 t.Fatalf("error parsing address: %s", err) 982 } 983 got := addr.HasResourceSpec() 984 if got != test.Want { 985 t.Fatalf("%q: wrong result %#v; want %#v", test.Input, got, test.Want) 986 } 987 }) 988 } 989 } 990 991 func TestResourceAddressWholeModuleAddress(t *testing.T) { 992 cases := []struct { 993 Input string 994 Want string 995 }{ 996 { 997 "module.foo", 998 "module.foo", 999 }, 1000 { 1001 "module.foo.module.bar", 1002 "module.foo.module.bar", 1003 }, 1004 { 1005 "null_resource.baz", 1006 "", 1007 }, 1008 { 1009 "null_resource.baz[0]", 1010 "", 1011 }, 1012 { 1013 "data.null_data_source.baz", 1014 "", 1015 }, 1016 { 1017 "data.null_data_source.baz[0]", 1018 "", 1019 }, 1020 { 1021 "module.foo.null_resource.baz", 1022 "module.foo", 1023 }, 1024 { 1025 "module.foo.data.null_data_source.baz", 1026 "module.foo", 1027 }, 1028 { 1029 "module.foo.module.bar.null_resource.baz", 1030 "module.foo.module.bar", 1031 }, 1032 } 1033 1034 for _, test := range cases { 1035 t.Run(test.Input, func(t *testing.T) { 1036 addr, err := ParseResourceAddress(test.Input) 1037 if err != nil { 1038 t.Fatalf("error parsing address: %s", err) 1039 } 1040 gotAddr := addr.WholeModuleAddress() 1041 got := gotAddr.String() 1042 if got != test.Want { 1043 t.Fatalf("%q: wrong result %#v; want %#v", test.Input, got, test.Want) 1044 } 1045 }) 1046 } 1047 } 1048 1049 func TestResourceAddressMatchesConfig(t *testing.T) { 1050 root := testModule(t, "empty-with-child-module") 1051 child := root.Child([]string{"child"}) 1052 grandchild := root.Child([]string{"child", "grandchild"}) 1053 1054 tests := []struct { 1055 Addr *ResourceAddress 1056 Module *module.Tree 1057 Resource *config.Resource 1058 Want bool 1059 }{ 1060 { 1061 &ResourceAddress{ 1062 Mode: config.ManagedResourceMode, 1063 Type: "null_resource", 1064 Name: "baz", 1065 Index: -1, 1066 }, 1067 root, 1068 &config.Resource{ 1069 Mode: config.ManagedResourceMode, 1070 Type: "null_resource", 1071 Name: "baz", 1072 }, 1073 true, 1074 }, 1075 { 1076 &ResourceAddress{ 1077 Path: []string{"child"}, 1078 Mode: config.ManagedResourceMode, 1079 Type: "null_resource", 1080 Name: "baz", 1081 Index: -1, 1082 }, 1083 child, 1084 &config.Resource{ 1085 Mode: config.ManagedResourceMode, 1086 Type: "null_resource", 1087 Name: "baz", 1088 }, 1089 true, 1090 }, 1091 { 1092 &ResourceAddress{ 1093 Path: []string{"child", "grandchild"}, 1094 Mode: config.ManagedResourceMode, 1095 Type: "null_resource", 1096 Name: "baz", 1097 Index: -1, 1098 }, 1099 grandchild, 1100 &config.Resource{ 1101 Mode: config.ManagedResourceMode, 1102 Type: "null_resource", 1103 Name: "baz", 1104 }, 1105 true, 1106 }, 1107 { 1108 &ResourceAddress{ 1109 Path: []string{"child"}, 1110 Index: -1, 1111 }, 1112 child, 1113 &config.Resource{ 1114 Mode: config.ManagedResourceMode, 1115 Type: "null_resource", 1116 Name: "baz", 1117 }, 1118 true, 1119 }, 1120 { 1121 &ResourceAddress{ 1122 Path: []string{"child", "grandchild"}, 1123 Index: -1, 1124 }, 1125 grandchild, 1126 &config.Resource{ 1127 Mode: config.ManagedResourceMode, 1128 Type: "null_resource", 1129 Name: "baz", 1130 }, 1131 true, 1132 }, 1133 { 1134 &ResourceAddress{ 1135 Mode: config.DataResourceMode, 1136 Type: "null_resource", 1137 Name: "baz", 1138 Index: -1, 1139 }, 1140 module.NewEmptyTree(), 1141 &config.Resource{ 1142 Mode: config.ManagedResourceMode, 1143 Type: "null_resource", 1144 Name: "baz", 1145 }, 1146 false, 1147 }, 1148 { 1149 &ResourceAddress{ 1150 Mode: config.ManagedResourceMode, 1151 Type: "null_resource", 1152 Name: "baz", 1153 Index: -1, 1154 }, 1155 module.NewEmptyTree(), 1156 &config.Resource{ 1157 Mode: config.ManagedResourceMode, 1158 Type: "null_resource", 1159 Name: "pizza", 1160 }, 1161 false, 1162 }, 1163 { 1164 &ResourceAddress{ 1165 Mode: config.ManagedResourceMode, 1166 Type: "null_resource", 1167 Name: "baz", 1168 Index: -1, 1169 }, 1170 module.NewEmptyTree(), 1171 &config.Resource{ 1172 Mode: config.ManagedResourceMode, 1173 Type: "aws_instance", 1174 Name: "baz", 1175 }, 1176 false, 1177 }, 1178 { 1179 &ResourceAddress{ 1180 Path: []string{"child", "grandchild"}, 1181 Mode: config.ManagedResourceMode, 1182 Type: "null_resource", 1183 Name: "baz", 1184 Index: -1, 1185 }, 1186 child, 1187 &config.Resource{ 1188 Mode: config.ManagedResourceMode, 1189 Type: "null_resource", 1190 Name: "baz", 1191 }, 1192 false, 1193 }, 1194 { 1195 &ResourceAddress{ 1196 Path: []string{"child"}, 1197 Mode: config.ManagedResourceMode, 1198 Type: "null_resource", 1199 Name: "baz", 1200 Index: -1, 1201 }, 1202 grandchild, 1203 &config.Resource{ 1204 Mode: config.ManagedResourceMode, 1205 Type: "null_resource", 1206 Name: "baz", 1207 }, 1208 false, 1209 }, 1210 } 1211 1212 for i, test := range tests { 1213 t.Run(fmt.Sprintf("%02d-%s", i, test.Addr), func(t *testing.T) { 1214 got := test.Addr.MatchesConfig(test.Module, test.Resource) 1215 if got != test.Want { 1216 t.Errorf( 1217 "wrong result\naddr: %s\nmod: %#v\nrsrc: %#v\ngot: %#v\nwant: %#v", 1218 test.Addr, test.Module.Path(), test.Resource, got, test.Want, 1219 ) 1220 } 1221 }) 1222 } 1223 } 1224 1225 func TestResourceAddressLess(t *testing.T) { 1226 tests := []struct { 1227 A string 1228 B string 1229 Want bool 1230 }{ 1231 { 1232 "foo.bar", 1233 "module.baz.foo.bar", 1234 true, 1235 }, 1236 { 1237 "module.baz.foo.bar", 1238 "zzz.bar", // would sort after "module" in lexicographical sort 1239 false, 1240 }, 1241 { 1242 "module.baz.foo.bar", 1243 "module.baz.foo.bar", 1244 false, 1245 }, 1246 { 1247 "module.baz.foo.bar", 1248 "module.boz.foo.bar", 1249 true, 1250 }, 1251 { 1252 "module.boz.foo.bar", 1253 "module.baz.foo.bar", 1254 false, 1255 }, 1256 { 1257 "a.b", 1258 "b.c", 1259 true, 1260 }, 1261 { 1262 "a.b", 1263 "a.c", 1264 true, 1265 }, 1266 { 1267 "c.b", 1268 "b.c", 1269 false, 1270 }, 1271 { 1272 "a.b[9]", 1273 "a.b[10]", 1274 true, 1275 }, 1276 { 1277 "b.b[9]", 1278 "a.b[10]", 1279 false, 1280 }, 1281 { 1282 "a.b", 1283 "a.b.deposed", 1284 true, 1285 }, 1286 { 1287 "a.b.tainted", 1288 "a.b.deposed", 1289 true, 1290 }, 1291 } 1292 1293 for _, test := range tests { 1294 t.Run(fmt.Sprintf("%s < %s", test.A, test.B), func(t *testing.T) { 1295 addrA, err := ParseResourceAddress(test.A) 1296 if err != nil { 1297 t.Fatal(err) 1298 } 1299 addrB, err := ParseResourceAddress(test.B) 1300 if err != nil { 1301 t.Fatal(err) 1302 } 1303 got := addrA.Less(addrB) 1304 invGot := addrB.Less(addrA) 1305 if got != test.Want { 1306 t.Errorf( 1307 "wrong result\ntest: %s < %s\ngot: %#v\nwant: %#v", 1308 test.A, test.B, got, test.Want, 1309 ) 1310 } 1311 if test.A != test.B { // inverse test doesn't apply when equal 1312 if invGot != !test.Want { 1313 t.Errorf( 1314 "wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v", 1315 test.B, test.A, invGot, !test.Want, 1316 ) 1317 } 1318 } else { 1319 if invGot != test.Want { 1320 t.Errorf( 1321 "wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v", 1322 test.B, test.A, invGot, test.Want, 1323 ) 1324 } 1325 } 1326 }) 1327 } 1328 }