github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/helper/schema/resource_data_test.go (about) 1 package schema 2 3 import ( 4 "math" 5 "reflect" 6 "testing" 7 8 "github.com/hashicorp/terraform/terraform" 9 ) 10 11 func TestResourceDataGet(t *testing.T) { 12 cases := []struct { 13 Schema map[string]*Schema 14 State *terraform.InstanceState 15 Diff *terraform.InstanceDiff 16 Key string 17 Value interface{} 18 }{ 19 // #0 20 { 21 Schema: map[string]*Schema{ 22 "availability_zone": &Schema{ 23 Type: TypeString, 24 Optional: true, 25 Computed: true, 26 ForceNew: true, 27 }, 28 }, 29 30 State: nil, 31 32 Diff: &terraform.InstanceDiff{ 33 Attributes: map[string]*terraform.ResourceAttrDiff{ 34 "availability_zone": &terraform.ResourceAttrDiff{ 35 Old: "foo", 36 New: "bar", 37 NewComputed: true, 38 }, 39 }, 40 }, 41 42 Key: "availability_zone", 43 Value: "", 44 }, 45 46 // #1 47 { 48 Schema: map[string]*Schema{ 49 "availability_zone": &Schema{ 50 Type: TypeString, 51 Optional: true, 52 Computed: true, 53 ForceNew: true, 54 }, 55 }, 56 57 State: nil, 58 59 Diff: &terraform.InstanceDiff{ 60 Attributes: map[string]*terraform.ResourceAttrDiff{ 61 "availability_zone": &terraform.ResourceAttrDiff{ 62 Old: "", 63 New: "foo", 64 RequiresNew: true, 65 }, 66 }, 67 }, 68 69 Key: "availability_zone", 70 71 Value: "foo", 72 }, 73 74 // #2 75 { 76 Schema: map[string]*Schema{ 77 "availability_zone": &Schema{ 78 Type: TypeString, 79 Optional: true, 80 Computed: true, 81 ForceNew: true, 82 }, 83 }, 84 85 State: nil, 86 87 Diff: &terraform.InstanceDiff{ 88 Attributes: map[string]*terraform.ResourceAttrDiff{ 89 "availability_zone": &terraform.ResourceAttrDiff{ 90 Old: "", 91 New: "foo!", 92 NewExtra: "foo", 93 }, 94 }, 95 }, 96 97 Key: "availability_zone", 98 Value: "foo", 99 }, 100 101 // #3 102 { 103 Schema: map[string]*Schema{ 104 "availability_zone": &Schema{ 105 Type: TypeString, 106 Optional: true, 107 Computed: true, 108 ForceNew: true, 109 }, 110 }, 111 112 State: &terraform.InstanceState{ 113 Attributes: map[string]string{ 114 "availability_zone": "bar", 115 }, 116 }, 117 118 Diff: nil, 119 120 Key: "availability_zone", 121 122 Value: "bar", 123 }, 124 125 // #4 126 { 127 Schema: map[string]*Schema{ 128 "availability_zone": &Schema{ 129 Type: TypeString, 130 Optional: true, 131 Computed: true, 132 ForceNew: true, 133 }, 134 }, 135 136 State: &terraform.InstanceState{ 137 Attributes: map[string]string{ 138 "availability_zone": "foo", 139 }, 140 }, 141 142 Diff: &terraform.InstanceDiff{ 143 Attributes: map[string]*terraform.ResourceAttrDiff{ 144 "availability_zone": &terraform.ResourceAttrDiff{ 145 Old: "foo", 146 New: "bar", 147 NewComputed: true, 148 }, 149 }, 150 }, 151 152 Key: "availability_zone", 153 Value: "", 154 }, 155 156 // #5 157 { 158 Schema: map[string]*Schema{ 159 "port": &Schema{ 160 Type: TypeInt, 161 Optional: true, 162 Computed: true, 163 ForceNew: true, 164 }, 165 }, 166 167 State: &terraform.InstanceState{ 168 Attributes: map[string]string{ 169 "port": "80", 170 }, 171 }, 172 173 Diff: nil, 174 175 Key: "port", 176 177 Value: 80, 178 }, 179 180 // #6 181 { 182 Schema: map[string]*Schema{ 183 "ports": &Schema{ 184 Type: TypeList, 185 Required: true, 186 Elem: &Schema{Type: TypeInt}, 187 }, 188 }, 189 190 State: &terraform.InstanceState{ 191 Attributes: map[string]string{ 192 "ports.#": "3", 193 "ports.0": "1", 194 "ports.1": "2", 195 "ports.2": "5", 196 }, 197 }, 198 199 Key: "ports.1", 200 201 Value: 2, 202 }, 203 204 // #7 205 { 206 Schema: map[string]*Schema{ 207 "ports": &Schema{ 208 Type: TypeList, 209 Required: true, 210 Elem: &Schema{Type: TypeInt}, 211 }, 212 }, 213 214 State: &terraform.InstanceState{ 215 Attributes: map[string]string{ 216 "ports.#": "3", 217 "ports.0": "1", 218 "ports.1": "2", 219 "ports.2": "5", 220 }, 221 }, 222 223 Key: "ports.#", 224 225 Value: 3, 226 }, 227 228 // #8 229 { 230 Schema: map[string]*Schema{ 231 "ports": &Schema{ 232 Type: TypeList, 233 Required: true, 234 Elem: &Schema{Type: TypeInt}, 235 }, 236 }, 237 238 State: nil, 239 240 Key: "ports.#", 241 242 Value: 0, 243 }, 244 245 // #9 246 { 247 Schema: map[string]*Schema{ 248 "ports": &Schema{ 249 Type: TypeList, 250 Required: true, 251 Elem: &Schema{Type: TypeInt}, 252 }, 253 }, 254 255 State: &terraform.InstanceState{ 256 Attributes: map[string]string{ 257 "ports.#": "3", 258 "ports.0": "1", 259 "ports.1": "2", 260 "ports.2": "5", 261 }, 262 }, 263 264 Key: "ports", 265 266 Value: []interface{}{1, 2, 5}, 267 }, 268 269 // #10 270 { 271 Schema: map[string]*Schema{ 272 "ingress": &Schema{ 273 Type: TypeList, 274 Required: true, 275 Elem: &Resource{ 276 Schema: map[string]*Schema{ 277 "from": &Schema{ 278 Type: TypeInt, 279 Required: true, 280 }, 281 }, 282 }, 283 }, 284 }, 285 286 State: nil, 287 288 Diff: &terraform.InstanceDiff{ 289 Attributes: map[string]*terraform.ResourceAttrDiff{ 290 "ingress.#": &terraform.ResourceAttrDiff{ 291 Old: "", 292 New: "1", 293 }, 294 "ingress.0.from": &terraform.ResourceAttrDiff{ 295 Old: "", 296 New: "8080", 297 }, 298 }, 299 }, 300 301 Key: "ingress.0", 302 303 Value: map[string]interface{}{ 304 "from": 8080, 305 }, 306 }, 307 308 // #11 309 { 310 Schema: map[string]*Schema{ 311 "ingress": &Schema{ 312 Type: TypeList, 313 Required: true, 314 Elem: &Resource{ 315 Schema: map[string]*Schema{ 316 "from": &Schema{ 317 Type: TypeInt, 318 Required: true, 319 }, 320 }, 321 }, 322 }, 323 }, 324 325 State: nil, 326 327 Diff: &terraform.InstanceDiff{ 328 Attributes: map[string]*terraform.ResourceAttrDiff{ 329 "ingress.#": &terraform.ResourceAttrDiff{ 330 Old: "", 331 New: "1", 332 }, 333 "ingress.0.from": &terraform.ResourceAttrDiff{ 334 Old: "", 335 New: "8080", 336 }, 337 }, 338 }, 339 340 Key: "ingress", 341 342 Value: []interface{}{ 343 map[string]interface{}{ 344 "from": 8080, 345 }, 346 }, 347 }, 348 349 // #12 Computed get 350 { 351 Schema: map[string]*Schema{ 352 "availability_zone": &Schema{ 353 Type: TypeString, 354 Computed: true, 355 }, 356 }, 357 358 State: &terraform.InstanceState{ 359 Attributes: map[string]string{ 360 "availability_zone": "foo", 361 }, 362 }, 363 364 Key: "availability_zone", 365 366 Value: "foo", 367 }, 368 369 // #13 Full object 370 { 371 Schema: map[string]*Schema{ 372 "availability_zone": &Schema{ 373 Type: TypeString, 374 Optional: true, 375 Computed: true, 376 ForceNew: true, 377 }, 378 }, 379 380 State: nil, 381 382 Diff: &terraform.InstanceDiff{ 383 Attributes: map[string]*terraform.ResourceAttrDiff{ 384 "availability_zone": &terraform.ResourceAttrDiff{ 385 Old: "", 386 New: "foo", 387 RequiresNew: true, 388 }, 389 }, 390 }, 391 392 Key: "", 393 394 Value: map[string]interface{}{ 395 "availability_zone": "foo", 396 }, 397 }, 398 399 // #14 List of maps 400 { 401 Schema: map[string]*Schema{ 402 "config_vars": &Schema{ 403 Type: TypeList, 404 Optional: true, 405 Computed: true, 406 Elem: &Schema{ 407 Type: TypeMap, 408 }, 409 }, 410 }, 411 412 State: nil, 413 414 Diff: &terraform.InstanceDiff{ 415 Attributes: map[string]*terraform.ResourceAttrDiff{ 416 "config_vars.#": &terraform.ResourceAttrDiff{ 417 Old: "0", 418 New: "2", 419 }, 420 "config_vars.0.foo": &terraform.ResourceAttrDiff{ 421 Old: "", 422 New: "bar", 423 }, 424 "config_vars.1.bar": &terraform.ResourceAttrDiff{ 425 Old: "", 426 New: "baz", 427 }, 428 }, 429 }, 430 431 Key: "config_vars", 432 433 Value: []interface{}{ 434 map[string]interface{}{ 435 "foo": "bar", 436 }, 437 map[string]interface{}{ 438 "bar": "baz", 439 }, 440 }, 441 }, 442 443 // #15 List of maps in state 444 { 445 Schema: map[string]*Schema{ 446 "config_vars": &Schema{ 447 Type: TypeList, 448 Optional: true, 449 Computed: true, 450 Elem: &Schema{ 451 Type: TypeMap, 452 }, 453 }, 454 }, 455 456 State: &terraform.InstanceState{ 457 Attributes: map[string]string{ 458 "config_vars.#": "2", 459 "config_vars.0.foo": "baz", 460 "config_vars.1.bar": "bar", 461 }, 462 }, 463 464 Diff: nil, 465 466 Key: "config_vars", 467 468 Value: []interface{}{ 469 map[string]interface{}{ 470 "foo": "baz", 471 }, 472 map[string]interface{}{ 473 "bar": "bar", 474 }, 475 }, 476 }, 477 478 // #16 List of maps with removal in diff 479 { 480 Schema: map[string]*Schema{ 481 "config_vars": &Schema{ 482 Type: TypeList, 483 Optional: true, 484 Computed: true, 485 Elem: &Schema{ 486 Type: TypeMap, 487 }, 488 }, 489 }, 490 491 State: &terraform.InstanceState{ 492 Attributes: map[string]string{ 493 "config_vars.#": "1", 494 "config_vars.0.FOO": "bar", 495 }, 496 }, 497 498 Diff: &terraform.InstanceDiff{ 499 Attributes: map[string]*terraform.ResourceAttrDiff{ 500 "config_vars.#": &terraform.ResourceAttrDiff{ 501 Old: "1", 502 New: "0", 503 }, 504 "config_vars.0.FOO": &terraform.ResourceAttrDiff{ 505 Old: "bar", 506 NewRemoved: true, 507 }, 508 }, 509 }, 510 511 Key: "config_vars", 512 513 Value: []interface{}{}, 514 }, 515 516 // #17 Sets 517 { 518 Schema: map[string]*Schema{ 519 "ports": &Schema{ 520 Type: TypeSet, 521 Optional: true, 522 Computed: true, 523 Elem: &Schema{Type: TypeInt}, 524 Set: func(a interface{}) int { 525 return a.(int) 526 }, 527 }, 528 }, 529 530 State: &terraform.InstanceState{ 531 Attributes: map[string]string{ 532 "ports.#": "1", 533 "ports.80": "80", 534 }, 535 }, 536 537 Diff: nil, 538 539 Key: "ports", 540 541 Value: []interface{}{80}, 542 }, 543 544 // #18 545 { 546 Schema: map[string]*Schema{ 547 "data": &Schema{ 548 Type: TypeSet, 549 Optional: true, 550 Elem: &Resource{ 551 Schema: map[string]*Schema{ 552 "index": &Schema{ 553 Type: TypeInt, 554 Required: true, 555 }, 556 557 "value": &Schema{ 558 Type: TypeString, 559 Required: true, 560 }, 561 }, 562 }, 563 Set: func(a interface{}) int { 564 m := a.(map[string]interface{}) 565 return m["index"].(int) 566 }, 567 }, 568 }, 569 570 State: &terraform.InstanceState{ 571 Attributes: map[string]string{ 572 "data.#": "1", 573 "data.10.index": "10", 574 "data.10.value": "50", 575 }, 576 }, 577 578 Diff: &terraform.InstanceDiff{ 579 Attributes: map[string]*terraform.ResourceAttrDiff{ 580 "data.10.value": &terraform.ResourceAttrDiff{ 581 Old: "50", 582 New: "80", 583 }, 584 }, 585 }, 586 587 Key: "data", 588 589 Value: []interface{}{ 590 map[string]interface{}{ 591 "index": 10, 592 "value": "80", 593 }, 594 }, 595 }, 596 597 // #19 Empty Set 598 { 599 Schema: map[string]*Schema{ 600 "ports": &Schema{ 601 Type: TypeSet, 602 Optional: true, 603 Computed: true, 604 Elem: &Schema{Type: TypeInt}, 605 Set: func(a interface{}) int { 606 return a.(int) 607 }, 608 }, 609 }, 610 611 State: nil, 612 613 Diff: nil, 614 615 Key: "ports", 616 617 Value: []interface{}{}, 618 }, 619 620 // #20 Float zero 621 { 622 Schema: map[string]*Schema{ 623 "ratio": &Schema{ 624 Type: TypeFloat, 625 Optional: true, 626 Computed: true, 627 }, 628 }, 629 630 State: nil, 631 632 Diff: nil, 633 634 Key: "ratio", 635 636 Value: 0.0, 637 }, 638 639 // #21 Float given 640 { 641 Schema: map[string]*Schema{ 642 "ratio": &Schema{ 643 Type: TypeFloat, 644 Optional: true, 645 Computed: true, 646 }, 647 }, 648 649 State: &terraform.InstanceState{ 650 Attributes: map[string]string{ 651 "ratio": "0.5", 652 }, 653 }, 654 655 Diff: nil, 656 657 Key: "ratio", 658 659 Value: 0.5, 660 }, 661 662 // #22 Float diff 663 { 664 Schema: map[string]*Schema{ 665 "ratio": &Schema{ 666 Type: TypeFloat, 667 Optional: true, 668 Computed: true, 669 }, 670 }, 671 672 State: &terraform.InstanceState{ 673 Attributes: map[string]string{ 674 "ratio": "-0.5", 675 }, 676 }, 677 678 Diff: &terraform.InstanceDiff{ 679 Attributes: map[string]*terraform.ResourceAttrDiff{ 680 "ratio": &terraform.ResourceAttrDiff{ 681 Old: "-0.5", 682 New: "33.0", 683 }, 684 }, 685 }, 686 687 Key: "ratio", 688 689 Value: 33.0, 690 }, 691 } 692 693 for i, tc := range cases { 694 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 695 if err != nil { 696 t.Fatalf("err: %s", err) 697 } 698 699 v := d.Get(tc.Key) 700 if s, ok := v.(*Set); ok { 701 v = s.List() 702 } 703 704 if !reflect.DeepEqual(v, tc.Value) { 705 t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value) 706 } 707 } 708 } 709 710 func TestResourceDataGetChange(t *testing.T) { 711 cases := []struct { 712 Schema map[string]*Schema 713 State *terraform.InstanceState 714 Diff *terraform.InstanceDiff 715 Key string 716 OldValue interface{} 717 NewValue interface{} 718 }{ 719 { 720 Schema: map[string]*Schema{ 721 "availability_zone": &Schema{ 722 Type: TypeString, 723 Optional: true, 724 Computed: true, 725 ForceNew: true, 726 }, 727 }, 728 729 State: nil, 730 731 Diff: &terraform.InstanceDiff{ 732 Attributes: map[string]*terraform.ResourceAttrDiff{ 733 "availability_zone": &terraform.ResourceAttrDiff{ 734 Old: "", 735 New: "foo", 736 RequiresNew: true, 737 }, 738 }, 739 }, 740 741 Key: "availability_zone", 742 743 OldValue: "", 744 NewValue: "foo", 745 }, 746 747 { 748 Schema: map[string]*Schema{ 749 "availability_zone": &Schema{ 750 Type: TypeString, 751 Optional: true, 752 Computed: true, 753 ForceNew: true, 754 }, 755 }, 756 757 State: &terraform.InstanceState{ 758 Attributes: map[string]string{ 759 "availability_zone": "foo", 760 }, 761 }, 762 763 Diff: &terraform.InstanceDiff{ 764 Attributes: map[string]*terraform.ResourceAttrDiff{ 765 "availability_zone": &terraform.ResourceAttrDiff{ 766 Old: "", 767 New: "foo", 768 RequiresNew: true, 769 }, 770 }, 771 }, 772 773 Key: "availability_zone", 774 775 OldValue: "foo", 776 NewValue: "foo", 777 }, 778 } 779 780 for i, tc := range cases { 781 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 782 if err != nil { 783 t.Fatalf("err: %s", err) 784 } 785 786 o, n := d.GetChange(tc.Key) 787 if !reflect.DeepEqual(o, tc.OldValue) { 788 t.Fatalf("Old Bad: %d\n\n%#v", i, o) 789 } 790 if !reflect.DeepEqual(n, tc.NewValue) { 791 t.Fatalf("New Bad: %d\n\n%#v", i, n) 792 } 793 } 794 } 795 796 func TestResourceDataGetOk(t *testing.T) { 797 cases := []struct { 798 Schema map[string]*Schema 799 State *terraform.InstanceState 800 Diff *terraform.InstanceDiff 801 Key string 802 Value interface{} 803 Ok bool 804 }{ 805 /* 806 * Primitives 807 */ 808 { 809 Schema: map[string]*Schema{ 810 "availability_zone": &Schema{ 811 Type: TypeString, 812 Optional: true, 813 Computed: true, 814 ForceNew: true, 815 }, 816 }, 817 818 State: nil, 819 820 Diff: &terraform.InstanceDiff{ 821 Attributes: map[string]*terraform.ResourceAttrDiff{ 822 "availability_zone": &terraform.ResourceAttrDiff{ 823 Old: "", 824 New: "", 825 }, 826 }, 827 }, 828 829 Key: "availability_zone", 830 Value: "", 831 Ok: false, 832 }, 833 834 { 835 Schema: map[string]*Schema{ 836 "availability_zone": &Schema{ 837 Type: TypeString, 838 Optional: true, 839 Computed: true, 840 ForceNew: true, 841 }, 842 }, 843 844 State: nil, 845 846 Diff: &terraform.InstanceDiff{ 847 Attributes: map[string]*terraform.ResourceAttrDiff{ 848 "availability_zone": &terraform.ResourceAttrDiff{ 849 Old: "", 850 New: "", 851 NewComputed: true, 852 }, 853 }, 854 }, 855 856 Key: "availability_zone", 857 Value: "", 858 Ok: false, 859 }, 860 861 { 862 Schema: map[string]*Schema{ 863 "availability_zone": &Schema{ 864 Type: TypeString, 865 Optional: true, 866 Computed: true, 867 ForceNew: true, 868 }, 869 }, 870 871 State: nil, 872 873 Diff: nil, 874 875 Key: "availability_zone", 876 Value: "", 877 Ok: false, 878 }, 879 880 /* 881 * Lists 882 */ 883 884 { 885 Schema: map[string]*Schema{ 886 "ports": &Schema{ 887 Type: TypeList, 888 Optional: true, 889 Elem: &Schema{Type: TypeInt}, 890 }, 891 }, 892 893 State: nil, 894 895 Diff: nil, 896 897 Key: "ports", 898 Value: []interface{}{}, 899 Ok: false, 900 }, 901 902 /* 903 * Map 904 */ 905 906 { 907 Schema: map[string]*Schema{ 908 "ports": &Schema{ 909 Type: TypeMap, 910 Optional: true, 911 }, 912 }, 913 914 State: nil, 915 916 Diff: nil, 917 918 Key: "ports", 919 Value: map[string]interface{}{}, 920 Ok: false, 921 }, 922 923 /* 924 * Set 925 */ 926 927 { 928 Schema: map[string]*Schema{ 929 "ports": &Schema{ 930 Type: TypeSet, 931 Optional: true, 932 Elem: &Schema{Type: TypeInt}, 933 Set: func(a interface{}) int { return a.(int) }, 934 }, 935 }, 936 937 State: nil, 938 939 Diff: nil, 940 941 Key: "ports", 942 Value: []interface{}{}, 943 Ok: false, 944 }, 945 946 { 947 Schema: map[string]*Schema{ 948 "ports": &Schema{ 949 Type: TypeSet, 950 Optional: true, 951 Elem: &Schema{Type: TypeInt}, 952 Set: func(a interface{}) int { return a.(int) }, 953 }, 954 }, 955 956 State: nil, 957 958 Diff: nil, 959 960 Key: "ports.0", 961 Value: 0, 962 Ok: false, 963 }, 964 965 { 966 Schema: map[string]*Schema{ 967 "ports": &Schema{ 968 Type: TypeSet, 969 Optional: true, 970 Elem: &Schema{Type: TypeInt}, 971 Set: func(a interface{}) int { return a.(int) }, 972 }, 973 }, 974 975 State: nil, 976 977 Diff: &terraform.InstanceDiff{ 978 Attributes: map[string]*terraform.ResourceAttrDiff{ 979 "ports.#": &terraform.ResourceAttrDiff{ 980 Old: "0", 981 New: "0", 982 }, 983 }, 984 }, 985 986 Key: "ports", 987 Value: []interface{}{}, 988 Ok: false, 989 }, 990 } 991 992 for i, tc := range cases { 993 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 994 if err != nil { 995 t.Fatalf("err: %s", err) 996 } 997 998 v, ok := d.GetOk(tc.Key) 999 if s, ok := v.(*Set); ok { 1000 v = s.List() 1001 } 1002 1003 if !reflect.DeepEqual(v, tc.Value) { 1004 t.Fatalf("Bad: %d\n\n%#v", i, v) 1005 } 1006 if ok != tc.Ok { 1007 t.Fatalf("Bad: %d\n\n%#v", i, ok) 1008 } 1009 } 1010 } 1011 1012 func TestResourceDataHasChange(t *testing.T) { 1013 cases := []struct { 1014 Schema map[string]*Schema 1015 State *terraform.InstanceState 1016 Diff *terraform.InstanceDiff 1017 Key string 1018 Change bool 1019 }{ 1020 { 1021 Schema: map[string]*Schema{ 1022 "availability_zone": &Schema{ 1023 Type: TypeString, 1024 Optional: true, 1025 Computed: true, 1026 ForceNew: true, 1027 }, 1028 }, 1029 1030 State: nil, 1031 1032 Diff: &terraform.InstanceDiff{ 1033 Attributes: map[string]*terraform.ResourceAttrDiff{ 1034 "availability_zone": &terraform.ResourceAttrDiff{ 1035 Old: "", 1036 New: "foo", 1037 RequiresNew: true, 1038 }, 1039 }, 1040 }, 1041 1042 Key: "availability_zone", 1043 1044 Change: true, 1045 }, 1046 1047 { 1048 Schema: map[string]*Schema{ 1049 "availability_zone": &Schema{ 1050 Type: TypeString, 1051 Optional: true, 1052 Computed: true, 1053 ForceNew: true, 1054 }, 1055 }, 1056 1057 State: &terraform.InstanceState{ 1058 Attributes: map[string]string{ 1059 "availability_zone": "foo", 1060 }, 1061 }, 1062 1063 Diff: &terraform.InstanceDiff{ 1064 Attributes: map[string]*terraform.ResourceAttrDiff{ 1065 "availability_zone": &terraform.ResourceAttrDiff{ 1066 Old: "", 1067 New: "foo", 1068 RequiresNew: true, 1069 }, 1070 }, 1071 }, 1072 1073 Key: "availability_zone", 1074 1075 Change: false, 1076 }, 1077 1078 { 1079 Schema: map[string]*Schema{ 1080 "tags": &Schema{ 1081 Type: TypeMap, 1082 Optional: true, 1083 Computed: true, 1084 }, 1085 }, 1086 1087 State: nil, 1088 1089 Diff: &terraform.InstanceDiff{ 1090 Attributes: map[string]*terraform.ResourceAttrDiff{ 1091 "tags.Name": &terraform.ResourceAttrDiff{ 1092 Old: "foo", 1093 New: "foo", 1094 }, 1095 }, 1096 }, 1097 1098 Key: "tags", 1099 1100 Change: true, 1101 }, 1102 1103 { 1104 Schema: map[string]*Schema{ 1105 "ports": &Schema{ 1106 Type: TypeSet, 1107 Optional: true, 1108 Elem: &Schema{Type: TypeInt}, 1109 Set: func(a interface{}) int { return a.(int) }, 1110 }, 1111 }, 1112 1113 State: &terraform.InstanceState{ 1114 Attributes: map[string]string{ 1115 "ports.#": "1", 1116 "ports.80": "80", 1117 }, 1118 }, 1119 1120 Diff: &terraform.InstanceDiff{ 1121 Attributes: map[string]*terraform.ResourceAttrDiff{ 1122 "ports.#": &terraform.ResourceAttrDiff{ 1123 Old: "1", 1124 New: "0", 1125 }, 1126 }, 1127 }, 1128 1129 Key: "ports", 1130 1131 Change: true, 1132 }, 1133 1134 // https://github.com/hashicorp/terraform/issues/927 1135 { 1136 Schema: map[string]*Schema{ 1137 "ports": &Schema{ 1138 Type: TypeSet, 1139 Optional: true, 1140 Elem: &Schema{Type: TypeInt}, 1141 Set: func(a interface{}) int { return a.(int) }, 1142 }, 1143 }, 1144 1145 State: &terraform.InstanceState{ 1146 Attributes: map[string]string{ 1147 "ports.#": "1", 1148 "ports.80": "80", 1149 }, 1150 }, 1151 1152 Diff: &terraform.InstanceDiff{ 1153 Attributes: map[string]*terraform.ResourceAttrDiff{ 1154 "tags.foo": &terraform.ResourceAttrDiff{ 1155 Old: "", 1156 New: "bar", 1157 }, 1158 }, 1159 }, 1160 1161 Key: "ports", 1162 1163 Change: false, 1164 }, 1165 } 1166 1167 for i, tc := range cases { 1168 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 1169 if err != nil { 1170 t.Fatalf("err: %s", err) 1171 } 1172 1173 actual := d.HasChange(tc.Key) 1174 if actual != tc.Change { 1175 t.Fatalf("Bad: %d %#v", i, actual) 1176 } 1177 } 1178 } 1179 1180 func TestResourceDataSet(t *testing.T) { 1181 var testNilPtr *string 1182 1183 cases := []struct { 1184 Schema map[string]*Schema 1185 State *terraform.InstanceState 1186 Diff *terraform.InstanceDiff 1187 Key string 1188 Value interface{} 1189 Err bool 1190 GetKey string 1191 GetValue interface{} 1192 1193 // GetPreProcess can be set to munge the return value before being 1194 // compared to GetValue 1195 GetPreProcess func(interface{}) interface{} 1196 }{ 1197 // #0: Basic good 1198 { 1199 Schema: map[string]*Schema{ 1200 "availability_zone": &Schema{ 1201 Type: TypeString, 1202 Optional: true, 1203 Computed: true, 1204 ForceNew: true, 1205 }, 1206 }, 1207 1208 State: nil, 1209 1210 Diff: nil, 1211 1212 Key: "availability_zone", 1213 Value: "foo", 1214 1215 GetKey: "availability_zone", 1216 GetValue: "foo", 1217 }, 1218 1219 // #1: Basic int 1220 { 1221 Schema: map[string]*Schema{ 1222 "port": &Schema{ 1223 Type: TypeInt, 1224 Optional: true, 1225 Computed: true, 1226 ForceNew: true, 1227 }, 1228 }, 1229 1230 State: nil, 1231 1232 Diff: nil, 1233 1234 Key: "port", 1235 Value: 80, 1236 1237 GetKey: "port", 1238 GetValue: 80, 1239 }, 1240 1241 // #2: Basic bool 1242 { 1243 Schema: map[string]*Schema{ 1244 "vpc": &Schema{ 1245 Type: TypeBool, 1246 Optional: true, 1247 }, 1248 }, 1249 1250 State: nil, 1251 1252 Diff: nil, 1253 1254 Key: "vpc", 1255 Value: true, 1256 1257 GetKey: "vpc", 1258 GetValue: true, 1259 }, 1260 1261 // #3 1262 { 1263 Schema: map[string]*Schema{ 1264 "vpc": &Schema{ 1265 Type: TypeBool, 1266 Optional: true, 1267 }, 1268 }, 1269 1270 State: nil, 1271 1272 Diff: nil, 1273 1274 Key: "vpc", 1275 Value: false, 1276 1277 GetKey: "vpc", 1278 GetValue: false, 1279 }, 1280 1281 // #4: Invalid type 1282 { 1283 Schema: map[string]*Schema{ 1284 "availability_zone": &Schema{ 1285 Type: TypeString, 1286 Optional: true, 1287 Computed: true, 1288 ForceNew: true, 1289 }, 1290 }, 1291 1292 State: nil, 1293 1294 Diff: nil, 1295 1296 Key: "availability_zone", 1297 Value: 80, 1298 Err: true, 1299 1300 GetKey: "availability_zone", 1301 GetValue: "", 1302 }, 1303 1304 // #5: List of primitives, set list 1305 { 1306 Schema: map[string]*Schema{ 1307 "ports": &Schema{ 1308 Type: TypeList, 1309 Computed: true, 1310 Elem: &Schema{Type: TypeInt}, 1311 }, 1312 }, 1313 1314 State: nil, 1315 1316 Diff: nil, 1317 1318 Key: "ports", 1319 Value: []int{1, 2, 5}, 1320 1321 GetKey: "ports", 1322 GetValue: []interface{}{1, 2, 5}, 1323 }, 1324 1325 // #6: List of primitives, set list with error 1326 { 1327 Schema: map[string]*Schema{ 1328 "ports": &Schema{ 1329 Type: TypeList, 1330 Computed: true, 1331 Elem: &Schema{Type: TypeInt}, 1332 }, 1333 }, 1334 1335 State: nil, 1336 1337 Diff: nil, 1338 1339 Key: "ports", 1340 Value: []interface{}{1, "NOPE", 5}, 1341 Err: true, 1342 1343 GetKey: "ports", 1344 GetValue: []interface{}{}, 1345 }, 1346 1347 // #7: Set a list of maps 1348 { 1349 Schema: map[string]*Schema{ 1350 "config_vars": &Schema{ 1351 Type: TypeList, 1352 Optional: true, 1353 Computed: true, 1354 Elem: &Schema{ 1355 Type: TypeMap, 1356 }, 1357 }, 1358 }, 1359 1360 State: nil, 1361 1362 Diff: nil, 1363 1364 Key: "config_vars", 1365 Value: []interface{}{ 1366 map[string]interface{}{ 1367 "foo": "bar", 1368 }, 1369 map[string]interface{}{ 1370 "bar": "baz", 1371 }, 1372 }, 1373 Err: false, 1374 1375 GetKey: "config_vars", 1376 GetValue: []interface{}{ 1377 map[string]interface{}{ 1378 "foo": "bar", 1379 }, 1380 map[string]interface{}{ 1381 "bar": "baz", 1382 }, 1383 }, 1384 }, 1385 1386 // #8: Set, with list 1387 { 1388 Schema: map[string]*Schema{ 1389 "ports": &Schema{ 1390 Type: TypeSet, 1391 Optional: true, 1392 Computed: true, 1393 Elem: &Schema{Type: TypeInt}, 1394 Set: func(a interface{}) int { 1395 return a.(int) 1396 }, 1397 }, 1398 }, 1399 1400 State: &terraform.InstanceState{ 1401 Attributes: map[string]string{ 1402 "ports.#": "3", 1403 "ports.0": "100", 1404 "ports.1": "80", 1405 "ports.2": "80", 1406 }, 1407 }, 1408 1409 Key: "ports", 1410 Value: []interface{}{100, 125, 125}, 1411 1412 GetKey: "ports", 1413 GetValue: []interface{}{100, 125}, 1414 }, 1415 1416 // #9: Set, with Set 1417 { 1418 Schema: map[string]*Schema{ 1419 "ports": &Schema{ 1420 Type: TypeSet, 1421 Optional: true, 1422 Computed: true, 1423 Elem: &Schema{Type: TypeInt}, 1424 Set: func(a interface{}) int { 1425 return a.(int) 1426 }, 1427 }, 1428 }, 1429 1430 State: &terraform.InstanceState{ 1431 Attributes: map[string]string{ 1432 "ports.#": "3", 1433 "ports.100": "100", 1434 "ports.80": "80", 1435 "ports.81": "81", 1436 }, 1437 }, 1438 1439 Key: "ports", 1440 Value: &Set{ 1441 m: map[int]interface{}{ 1442 1: 1, 1443 2: 2, 1444 }, 1445 }, 1446 1447 GetKey: "ports", 1448 GetValue: []interface{}{1, 2}, 1449 }, 1450 1451 // #10: Set single item 1452 { 1453 Schema: map[string]*Schema{ 1454 "ports": &Schema{ 1455 Type: TypeSet, 1456 Optional: true, 1457 Computed: true, 1458 Elem: &Schema{Type: TypeInt}, 1459 Set: func(a interface{}) int { 1460 return a.(int) 1461 }, 1462 }, 1463 }, 1464 1465 State: &terraform.InstanceState{ 1466 Attributes: map[string]string{ 1467 "ports.#": "2", 1468 "ports.100": "100", 1469 "ports.80": "80", 1470 }, 1471 }, 1472 1473 Key: "ports.100", 1474 Value: 256, 1475 Err: true, 1476 1477 GetKey: "ports", 1478 GetValue: []interface{}{80, 100}, 1479 }, 1480 1481 // #11: Set with nested set 1482 { 1483 Schema: map[string]*Schema{ 1484 "ports": &Schema{ 1485 Type: TypeSet, 1486 Elem: &Resource{ 1487 Schema: map[string]*Schema{ 1488 "port": &Schema{ 1489 Type: TypeInt, 1490 }, 1491 1492 "set": &Schema{ 1493 Type: TypeSet, 1494 Elem: &Schema{Type: TypeInt}, 1495 Set: func(a interface{}) int { 1496 return a.(int) 1497 }, 1498 }, 1499 }, 1500 }, 1501 Set: func(a interface{}) int { 1502 return a.(map[string]interface{})["port"].(int) 1503 }, 1504 }, 1505 }, 1506 1507 State: nil, 1508 1509 Key: "ports", 1510 Value: []interface{}{ 1511 map[string]interface{}{ 1512 "port": 80, 1513 }, 1514 }, 1515 1516 GetKey: "ports", 1517 GetValue: []interface{}{ 1518 map[string]interface{}{ 1519 "port": 80, 1520 "set": []interface{}{}, 1521 }, 1522 }, 1523 1524 GetPreProcess: func(v interface{}) interface{} { 1525 if v == nil { 1526 return v 1527 } 1528 s, ok := v.([]interface{}) 1529 if !ok { 1530 return v 1531 } 1532 for _, v := range s { 1533 m, ok := v.(map[string]interface{}) 1534 if !ok { 1535 continue 1536 } 1537 if m["set"] == nil { 1538 continue 1539 } 1540 if s, ok := m["set"].(*Set); ok { 1541 m["set"] = s.List() 1542 } 1543 } 1544 1545 return v 1546 }, 1547 }, 1548 1549 // #12: List of floats, set list 1550 { 1551 Schema: map[string]*Schema{ 1552 "ratios": &Schema{ 1553 Type: TypeList, 1554 Computed: true, 1555 Elem: &Schema{Type: TypeFloat}, 1556 }, 1557 }, 1558 1559 State: nil, 1560 1561 Diff: nil, 1562 1563 Key: "ratios", 1564 Value: []float64{1.0, 2.2, 5.5}, 1565 1566 GetKey: "ratios", 1567 GetValue: []interface{}{1.0, 2.2, 5.5}, 1568 }, 1569 1570 // #12: Set of floats, set list 1571 { 1572 Schema: map[string]*Schema{ 1573 "ratios": &Schema{ 1574 Type: TypeSet, 1575 Computed: true, 1576 Elem: &Schema{Type: TypeFloat}, 1577 Set: func(a interface{}) int { 1578 return int(math.Float64bits(a.(float64))) 1579 }, 1580 }, 1581 }, 1582 1583 State: nil, 1584 1585 Diff: nil, 1586 1587 Key: "ratios", 1588 Value: []float64{1.0, 2.2, 5.5}, 1589 1590 GetKey: "ratios", 1591 GetValue: []interface{}{1.0, 2.2, 5.5}, 1592 }, 1593 1594 // #13: Basic pointer 1595 { 1596 Schema: map[string]*Schema{ 1597 "availability_zone": &Schema{ 1598 Type: TypeString, 1599 Optional: true, 1600 Computed: true, 1601 ForceNew: true, 1602 }, 1603 }, 1604 1605 State: nil, 1606 1607 Diff: nil, 1608 1609 Key: "availability_zone", 1610 Value: testPtrTo("foo"), 1611 1612 GetKey: "availability_zone", 1613 GetValue: "foo", 1614 }, 1615 1616 // #14: Basic nil value 1617 { 1618 Schema: map[string]*Schema{ 1619 "availability_zone": &Schema{ 1620 Type: TypeString, 1621 Optional: true, 1622 Computed: true, 1623 ForceNew: true, 1624 }, 1625 }, 1626 1627 State: nil, 1628 1629 Diff: nil, 1630 1631 Key: "availability_zone", 1632 Value: testPtrTo(nil), 1633 1634 GetKey: "availability_zone", 1635 GetValue: "", 1636 }, 1637 1638 // #15: Basic nil pointer 1639 { 1640 Schema: map[string]*Schema{ 1641 "availability_zone": &Schema{ 1642 Type: TypeString, 1643 Optional: true, 1644 Computed: true, 1645 ForceNew: true, 1646 }, 1647 }, 1648 1649 State: nil, 1650 1651 Diff: nil, 1652 1653 Key: "availability_zone", 1654 Value: testNilPtr, 1655 1656 GetKey: "availability_zone", 1657 GetValue: "", 1658 }, 1659 } 1660 1661 for i, tc := range cases { 1662 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 1663 if err != nil { 1664 t.Fatalf("err: %s", err) 1665 } 1666 1667 err = d.Set(tc.Key, tc.Value) 1668 if (err != nil) != tc.Err { 1669 t.Fatalf("%d err: %s", i, err) 1670 } 1671 1672 v := d.Get(tc.GetKey) 1673 if s, ok := v.(*Set); ok { 1674 v = s.List() 1675 } 1676 1677 if tc.GetPreProcess != nil { 1678 v = tc.GetPreProcess(v) 1679 } 1680 1681 if !reflect.DeepEqual(v, tc.GetValue) { 1682 t.Fatalf("Get Bad: %d\n\n%#v", i, v) 1683 } 1684 } 1685 } 1686 1687 func TestResourceDataState(t *testing.T) { 1688 cases := []struct { 1689 Schema map[string]*Schema 1690 State *terraform.InstanceState 1691 Diff *terraform.InstanceDiff 1692 Set map[string]interface{} 1693 Result *terraform.InstanceState 1694 Partial []string 1695 }{ 1696 // #0 Basic primitive in diff 1697 { 1698 Schema: map[string]*Schema{ 1699 "availability_zone": &Schema{ 1700 Type: TypeString, 1701 Optional: true, 1702 Computed: true, 1703 ForceNew: true, 1704 }, 1705 }, 1706 1707 State: nil, 1708 1709 Diff: &terraform.InstanceDiff{ 1710 Attributes: map[string]*terraform.ResourceAttrDiff{ 1711 "availability_zone": &terraform.ResourceAttrDiff{ 1712 Old: "", 1713 New: "foo", 1714 RequiresNew: true, 1715 }, 1716 }, 1717 }, 1718 1719 Result: &terraform.InstanceState{ 1720 Attributes: map[string]string{ 1721 "availability_zone": "foo", 1722 }, 1723 }, 1724 }, 1725 1726 // #1 Basic primitive set override 1727 { 1728 Schema: map[string]*Schema{ 1729 "availability_zone": &Schema{ 1730 Type: TypeString, 1731 Optional: true, 1732 Computed: true, 1733 ForceNew: true, 1734 }, 1735 }, 1736 1737 State: nil, 1738 1739 Diff: &terraform.InstanceDiff{ 1740 Attributes: map[string]*terraform.ResourceAttrDiff{ 1741 "availability_zone": &terraform.ResourceAttrDiff{ 1742 Old: "", 1743 New: "foo", 1744 RequiresNew: true, 1745 }, 1746 }, 1747 }, 1748 1749 Set: map[string]interface{}{ 1750 "availability_zone": "bar", 1751 }, 1752 1753 Result: &terraform.InstanceState{ 1754 Attributes: map[string]string{ 1755 "availability_zone": "bar", 1756 }, 1757 }, 1758 }, 1759 1760 // #2 1761 { 1762 Schema: map[string]*Schema{ 1763 "vpc": &Schema{ 1764 Type: TypeBool, 1765 Optional: true, 1766 }, 1767 }, 1768 1769 State: nil, 1770 1771 Diff: nil, 1772 1773 Set: map[string]interface{}{ 1774 "vpc": true, 1775 }, 1776 1777 Result: &terraform.InstanceState{ 1778 Attributes: map[string]string{ 1779 "vpc": "true", 1780 }, 1781 }, 1782 }, 1783 1784 // #3 Basic primitive with StateFunc set 1785 { 1786 Schema: map[string]*Schema{ 1787 "availability_zone": &Schema{ 1788 Type: TypeString, 1789 Optional: true, 1790 Computed: true, 1791 StateFunc: func(interface{}) string { return "" }, 1792 }, 1793 }, 1794 1795 State: nil, 1796 1797 Diff: &terraform.InstanceDiff{ 1798 Attributes: map[string]*terraform.ResourceAttrDiff{ 1799 "availability_zone": &terraform.ResourceAttrDiff{ 1800 Old: "", 1801 New: "foo", 1802 NewExtra: "foo!", 1803 }, 1804 }, 1805 }, 1806 1807 Result: &terraform.InstanceState{ 1808 Attributes: map[string]string{ 1809 "availability_zone": "foo", 1810 }, 1811 }, 1812 }, 1813 1814 // #4 List 1815 { 1816 Schema: map[string]*Schema{ 1817 "ports": &Schema{ 1818 Type: TypeList, 1819 Required: true, 1820 Elem: &Schema{Type: TypeInt}, 1821 }, 1822 }, 1823 1824 State: &terraform.InstanceState{ 1825 Attributes: map[string]string{ 1826 "ports.#": "1", 1827 "ports.0": "80", 1828 }, 1829 }, 1830 1831 Diff: &terraform.InstanceDiff{ 1832 Attributes: map[string]*terraform.ResourceAttrDiff{ 1833 "ports.#": &terraform.ResourceAttrDiff{ 1834 Old: "1", 1835 New: "2", 1836 }, 1837 "ports.1": &terraform.ResourceAttrDiff{ 1838 Old: "", 1839 New: "100", 1840 }, 1841 }, 1842 }, 1843 1844 Result: &terraform.InstanceState{ 1845 Attributes: map[string]string{ 1846 "ports.#": "2", 1847 "ports.0": "80", 1848 "ports.1": "100", 1849 }, 1850 }, 1851 }, 1852 1853 // #5 List of resources 1854 { 1855 Schema: map[string]*Schema{ 1856 "ingress": &Schema{ 1857 Type: TypeList, 1858 Required: true, 1859 Elem: &Resource{ 1860 Schema: map[string]*Schema{ 1861 "from": &Schema{ 1862 Type: TypeInt, 1863 Required: true, 1864 }, 1865 }, 1866 }, 1867 }, 1868 }, 1869 1870 State: &terraform.InstanceState{ 1871 Attributes: map[string]string{ 1872 "ingress.#": "1", 1873 "ingress.0.from": "80", 1874 }, 1875 }, 1876 1877 Diff: &terraform.InstanceDiff{ 1878 Attributes: map[string]*terraform.ResourceAttrDiff{ 1879 "ingress.#": &terraform.ResourceAttrDiff{ 1880 Old: "1", 1881 New: "2", 1882 }, 1883 "ingress.0.from": &terraform.ResourceAttrDiff{ 1884 Old: "80", 1885 New: "150", 1886 }, 1887 "ingress.1.from": &terraform.ResourceAttrDiff{ 1888 Old: "", 1889 New: "100", 1890 }, 1891 }, 1892 }, 1893 1894 Result: &terraform.InstanceState{ 1895 Attributes: map[string]string{ 1896 "ingress.#": "2", 1897 "ingress.0.from": "150", 1898 "ingress.1.from": "100", 1899 }, 1900 }, 1901 }, 1902 1903 // #6 List of maps 1904 { 1905 Schema: map[string]*Schema{ 1906 "config_vars": &Schema{ 1907 Type: TypeList, 1908 Optional: true, 1909 Computed: true, 1910 Elem: &Schema{ 1911 Type: TypeMap, 1912 }, 1913 }, 1914 }, 1915 1916 State: &terraform.InstanceState{ 1917 Attributes: map[string]string{ 1918 "config_vars.#": "2", 1919 "config_vars.0.#": "2", 1920 "config_vars.0.foo": "bar", 1921 "config_vars.0.bar": "bar", 1922 "config_vars.1.#": "1", 1923 "config_vars.1.bar": "baz", 1924 }, 1925 }, 1926 1927 Diff: &terraform.InstanceDiff{ 1928 Attributes: map[string]*terraform.ResourceAttrDiff{ 1929 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1930 NewRemoved: true, 1931 }, 1932 }, 1933 }, 1934 1935 Set: map[string]interface{}{ 1936 "config_vars": []map[string]interface{}{ 1937 map[string]interface{}{ 1938 "foo": "bar", 1939 }, 1940 map[string]interface{}{ 1941 "baz": "bang", 1942 }, 1943 }, 1944 }, 1945 1946 Result: &terraform.InstanceState{ 1947 Attributes: map[string]string{ 1948 "config_vars.#": "2", 1949 "config_vars.0.#": "1", 1950 "config_vars.0.foo": "bar", 1951 "config_vars.1.#": "1", 1952 "config_vars.1.baz": "bang", 1953 }, 1954 }, 1955 }, 1956 1957 // #7 List of maps with removal in diff 1958 { 1959 Schema: map[string]*Schema{ 1960 "config_vars": &Schema{ 1961 Type: TypeList, 1962 Optional: true, 1963 Computed: true, 1964 Elem: &Schema{ 1965 Type: TypeMap, 1966 }, 1967 }, 1968 }, 1969 1970 State: &terraform.InstanceState{ 1971 Attributes: map[string]string{ 1972 "config_vars.#": "1", 1973 "config_vars.0.FOO": "bar", 1974 }, 1975 }, 1976 1977 Diff: &terraform.InstanceDiff{ 1978 Attributes: map[string]*terraform.ResourceAttrDiff{ 1979 "config_vars.#": &terraform.ResourceAttrDiff{ 1980 Old: "1", 1981 New: "0", 1982 }, 1983 "config_vars.0.FOO": &terraform.ResourceAttrDiff{ 1984 Old: "bar", 1985 NewRemoved: true, 1986 }, 1987 }, 1988 }, 1989 1990 Result: &terraform.InstanceState{ 1991 Attributes: map[string]string{ 1992 "config_vars.#": "0", 1993 }, 1994 }, 1995 }, 1996 1997 // #8 Basic state with other keys 1998 { 1999 Schema: map[string]*Schema{ 2000 "availability_zone": &Schema{ 2001 Type: TypeString, 2002 Optional: true, 2003 Computed: true, 2004 ForceNew: true, 2005 }, 2006 }, 2007 2008 State: &terraform.InstanceState{ 2009 ID: "bar", 2010 Attributes: map[string]string{ 2011 "id": "bar", 2012 }, 2013 }, 2014 2015 Diff: &terraform.InstanceDiff{ 2016 Attributes: map[string]*terraform.ResourceAttrDiff{ 2017 "availability_zone": &terraform.ResourceAttrDiff{ 2018 Old: "", 2019 New: "foo", 2020 RequiresNew: true, 2021 }, 2022 }, 2023 }, 2024 2025 Result: &terraform.InstanceState{ 2026 ID: "bar", 2027 Attributes: map[string]string{ 2028 "id": "bar", 2029 "availability_zone": "foo", 2030 }, 2031 }, 2032 }, 2033 2034 // #9 Sets 2035 { 2036 Schema: map[string]*Schema{ 2037 "ports": &Schema{ 2038 Type: TypeSet, 2039 Optional: true, 2040 Computed: true, 2041 Elem: &Schema{Type: TypeInt}, 2042 Set: func(a interface{}) int { 2043 return a.(int) 2044 }, 2045 }, 2046 }, 2047 2048 State: &terraform.InstanceState{ 2049 Attributes: map[string]string{ 2050 "ports.#": "3", 2051 "ports.100": "100", 2052 "ports.80": "80", 2053 "ports.81": "81", 2054 }, 2055 }, 2056 2057 Diff: nil, 2058 2059 Result: &terraform.InstanceState{ 2060 Attributes: map[string]string{ 2061 "ports.#": "3", 2062 "ports.80": "80", 2063 "ports.81": "81", 2064 "ports.100": "100", 2065 }, 2066 }, 2067 }, 2068 2069 // #10 2070 { 2071 Schema: map[string]*Schema{ 2072 "ports": &Schema{ 2073 Type: TypeSet, 2074 Optional: true, 2075 Computed: true, 2076 Elem: &Schema{Type: TypeInt}, 2077 Set: func(a interface{}) int { 2078 return a.(int) 2079 }, 2080 }, 2081 }, 2082 2083 State: nil, 2084 2085 Diff: nil, 2086 2087 Set: map[string]interface{}{ 2088 "ports": []interface{}{100, 80}, 2089 }, 2090 2091 Result: &terraform.InstanceState{ 2092 Attributes: map[string]string{ 2093 "ports.#": "2", 2094 "ports.80": "80", 2095 "ports.100": "100", 2096 }, 2097 }, 2098 }, 2099 2100 // #11 2101 { 2102 Schema: map[string]*Schema{ 2103 "ports": &Schema{ 2104 Type: TypeSet, 2105 Optional: true, 2106 Computed: true, 2107 Elem: &Resource{ 2108 Schema: map[string]*Schema{ 2109 "order": &Schema{ 2110 Type: TypeInt, 2111 }, 2112 2113 "a": &Schema{ 2114 Type: TypeList, 2115 Elem: &Schema{Type: TypeInt}, 2116 }, 2117 2118 "b": &Schema{ 2119 Type: TypeList, 2120 Elem: &Schema{Type: TypeInt}, 2121 }, 2122 }, 2123 }, 2124 Set: func(a interface{}) int { 2125 m := a.(map[string]interface{}) 2126 return m["order"].(int) 2127 }, 2128 }, 2129 }, 2130 2131 State: &terraform.InstanceState{ 2132 Attributes: map[string]string{ 2133 "ports.#": "2", 2134 "ports.10.order": "10", 2135 "ports.10.a.#": "1", 2136 "ports.10.a.0": "80", 2137 "ports.20.order": "20", 2138 "ports.20.b.#": "1", 2139 "ports.20.b.0": "100", 2140 }, 2141 }, 2142 2143 Set: map[string]interface{}{ 2144 "ports": []interface{}{ 2145 map[string]interface{}{ 2146 "order": 20, 2147 "b": []interface{}{100}, 2148 }, 2149 map[string]interface{}{ 2150 "order": 10, 2151 "a": []interface{}{80}, 2152 }, 2153 }, 2154 }, 2155 2156 Result: &terraform.InstanceState{ 2157 Attributes: map[string]string{ 2158 "ports.#": "2", 2159 "ports.10.order": "10", 2160 "ports.10.a.#": "1", 2161 "ports.10.a.0": "80", 2162 "ports.10.b.#": "0", 2163 "ports.20.order": "20", 2164 "ports.20.a.#": "0", 2165 "ports.20.b.#": "1", 2166 "ports.20.b.0": "100", 2167 }, 2168 }, 2169 }, 2170 2171 /* 2172 * PARTIAL STATES 2173 */ 2174 2175 // #12 Basic primitive 2176 { 2177 Schema: map[string]*Schema{ 2178 "availability_zone": &Schema{ 2179 Type: TypeString, 2180 Optional: true, 2181 Computed: true, 2182 ForceNew: true, 2183 }, 2184 }, 2185 2186 State: nil, 2187 2188 Diff: &terraform.InstanceDiff{ 2189 Attributes: map[string]*terraform.ResourceAttrDiff{ 2190 "availability_zone": &terraform.ResourceAttrDiff{ 2191 Old: "", 2192 New: "foo", 2193 RequiresNew: true, 2194 }, 2195 }, 2196 }, 2197 2198 Partial: []string{}, 2199 2200 Result: &terraform.InstanceState{ 2201 Attributes: map[string]string{}, 2202 }, 2203 }, 2204 2205 // #13 List 2206 { 2207 Schema: map[string]*Schema{ 2208 "ports": &Schema{ 2209 Type: TypeList, 2210 Required: true, 2211 Elem: &Schema{Type: TypeInt}, 2212 }, 2213 }, 2214 2215 State: &terraform.InstanceState{ 2216 Attributes: map[string]string{ 2217 "ports.#": "1", 2218 "ports.0": "80", 2219 }, 2220 }, 2221 2222 Diff: &terraform.InstanceDiff{ 2223 Attributes: map[string]*terraform.ResourceAttrDiff{ 2224 "ports.#": &terraform.ResourceAttrDiff{ 2225 Old: "1", 2226 New: "2", 2227 }, 2228 "ports.1": &terraform.ResourceAttrDiff{ 2229 Old: "", 2230 New: "100", 2231 }, 2232 }, 2233 }, 2234 2235 Partial: []string{}, 2236 2237 Result: &terraform.InstanceState{ 2238 Attributes: map[string]string{ 2239 "ports.#": "1", 2240 "ports.0": "80", 2241 }, 2242 }, 2243 }, 2244 2245 // #14 2246 { 2247 Schema: map[string]*Schema{ 2248 "ports": &Schema{ 2249 Type: TypeList, 2250 Optional: true, 2251 Computed: true, 2252 Elem: &Schema{Type: TypeInt}, 2253 }, 2254 }, 2255 2256 State: nil, 2257 2258 Diff: &terraform.InstanceDiff{ 2259 Attributes: map[string]*terraform.ResourceAttrDiff{ 2260 "ports.#": &terraform.ResourceAttrDiff{ 2261 Old: "", 2262 NewComputed: true, 2263 }, 2264 }, 2265 }, 2266 2267 Partial: []string{}, 2268 2269 Set: map[string]interface{}{ 2270 "ports": []interface{}{}, 2271 }, 2272 2273 Result: &terraform.InstanceState{ 2274 Attributes: map[string]string{}, 2275 }, 2276 }, 2277 2278 // #15 List of resources 2279 { 2280 Schema: map[string]*Schema{ 2281 "ingress": &Schema{ 2282 Type: TypeList, 2283 Required: true, 2284 Elem: &Resource{ 2285 Schema: map[string]*Schema{ 2286 "from": &Schema{ 2287 Type: TypeInt, 2288 Required: true, 2289 }, 2290 }, 2291 }, 2292 }, 2293 }, 2294 2295 State: &terraform.InstanceState{ 2296 Attributes: map[string]string{ 2297 "ingress.#": "1", 2298 "ingress.0.from": "80", 2299 }, 2300 }, 2301 2302 Diff: &terraform.InstanceDiff{ 2303 Attributes: map[string]*terraform.ResourceAttrDiff{ 2304 "ingress.#": &terraform.ResourceAttrDiff{ 2305 Old: "1", 2306 New: "2", 2307 }, 2308 "ingress.0.from": &terraform.ResourceAttrDiff{ 2309 Old: "80", 2310 New: "150", 2311 }, 2312 "ingress.1.from": &terraform.ResourceAttrDiff{ 2313 Old: "", 2314 New: "100", 2315 }, 2316 }, 2317 }, 2318 2319 Partial: []string{}, 2320 2321 Result: &terraform.InstanceState{ 2322 Attributes: map[string]string{ 2323 "ingress.#": "1", 2324 "ingress.0.from": "80", 2325 }, 2326 }, 2327 }, 2328 2329 // #16 List of maps 2330 { 2331 Schema: map[string]*Schema{ 2332 "config_vars": &Schema{ 2333 Type: TypeList, 2334 Optional: true, 2335 Computed: true, 2336 Elem: &Schema{ 2337 Type: TypeMap, 2338 }, 2339 }, 2340 }, 2341 2342 State: &terraform.InstanceState{ 2343 Attributes: map[string]string{ 2344 "config_vars.#": "2", 2345 "config_vars.0.foo": "bar", 2346 "config_vars.0.bar": "bar", 2347 "config_vars.1.bar": "baz", 2348 }, 2349 }, 2350 2351 Diff: &terraform.InstanceDiff{ 2352 Attributes: map[string]*terraform.ResourceAttrDiff{ 2353 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 2354 NewRemoved: true, 2355 }, 2356 }, 2357 }, 2358 2359 Set: map[string]interface{}{ 2360 "config_vars": []map[string]interface{}{ 2361 map[string]interface{}{ 2362 "foo": "bar", 2363 }, 2364 map[string]interface{}{ 2365 "baz": "bang", 2366 }, 2367 }, 2368 }, 2369 2370 Partial: []string{}, 2371 2372 Result: &terraform.InstanceState{ 2373 Attributes: map[string]string{ 2374 // TODO: broken, shouldn't bar be removed? 2375 "config_vars.#": "2", 2376 "config_vars.0.#": "2", 2377 "config_vars.0.foo": "bar", 2378 "config_vars.0.bar": "bar", 2379 "config_vars.1.#": "1", 2380 "config_vars.1.bar": "baz", 2381 }, 2382 }, 2383 }, 2384 2385 // #17 Sets 2386 { 2387 Schema: map[string]*Schema{ 2388 "ports": &Schema{ 2389 Type: TypeSet, 2390 Optional: true, 2391 Computed: true, 2392 Elem: &Schema{Type: TypeInt}, 2393 Set: func(a interface{}) int { 2394 return a.(int) 2395 }, 2396 }, 2397 }, 2398 2399 State: &terraform.InstanceState{ 2400 Attributes: map[string]string{ 2401 "ports.#": "3", 2402 "ports.100": "100", 2403 "ports.80": "80", 2404 "ports.81": "81", 2405 }, 2406 }, 2407 2408 Diff: &terraform.InstanceDiff{ 2409 Attributes: map[string]*terraform.ResourceAttrDiff{ 2410 "ports.120": &terraform.ResourceAttrDiff{ 2411 New: "120", 2412 }, 2413 }, 2414 }, 2415 2416 Partial: []string{}, 2417 2418 Result: &terraform.InstanceState{ 2419 Attributes: map[string]string{ 2420 "ports.#": "3", 2421 "ports.80": "80", 2422 "ports.81": "81", 2423 "ports.100": "100", 2424 }, 2425 }, 2426 }, 2427 2428 // #18 2429 { 2430 Schema: map[string]*Schema{ 2431 "ports": &Schema{ 2432 Type: TypeSet, 2433 Optional: true, 2434 Computed: true, 2435 Elem: &Schema{Type: TypeInt}, 2436 Set: func(a interface{}) int { 2437 return a.(int) 2438 }, 2439 }, 2440 }, 2441 2442 State: nil, 2443 2444 Diff: &terraform.InstanceDiff{ 2445 Attributes: map[string]*terraform.ResourceAttrDiff{ 2446 "ports.#": &terraform.ResourceAttrDiff{ 2447 Old: "", 2448 NewComputed: true, 2449 }, 2450 }, 2451 }, 2452 2453 Partial: []string{}, 2454 2455 Result: &terraform.InstanceState{ 2456 Attributes: map[string]string{}, 2457 }, 2458 }, 2459 2460 // #19 Maps 2461 { 2462 Schema: map[string]*Schema{ 2463 "tags": &Schema{ 2464 Type: TypeMap, 2465 Optional: true, 2466 Computed: true, 2467 }, 2468 }, 2469 2470 State: nil, 2471 2472 Diff: &terraform.InstanceDiff{ 2473 Attributes: map[string]*terraform.ResourceAttrDiff{ 2474 "tags.Name": &terraform.ResourceAttrDiff{ 2475 Old: "", 2476 New: "foo", 2477 }, 2478 }, 2479 }, 2480 2481 Result: &terraform.InstanceState{ 2482 Attributes: map[string]string{ 2483 "tags.#": "1", 2484 "tags.Name": "foo", 2485 }, 2486 }, 2487 }, 2488 2489 // #20 2490 { 2491 Schema: map[string]*Schema{ 2492 "tags": &Schema{ 2493 Type: TypeMap, 2494 Optional: true, 2495 Computed: true, 2496 }, 2497 }, 2498 2499 State: nil, 2500 2501 Diff: &terraform.InstanceDiff{ 2502 Attributes: map[string]*terraform.ResourceAttrDiff{ 2503 "tags.Name": &terraform.ResourceAttrDiff{ 2504 Old: "", 2505 New: "foo", 2506 }, 2507 }, 2508 }, 2509 2510 Set: map[string]interface{}{ 2511 "tags": map[string]string{}, 2512 }, 2513 2514 Result: &terraform.InstanceState{ 2515 Attributes: map[string]string{}, 2516 }, 2517 }, 2518 2519 // #21 2520 { 2521 Schema: map[string]*Schema{ 2522 "foo": &Schema{ 2523 Type: TypeString, 2524 Optional: true, 2525 Computed: true, 2526 }, 2527 }, 2528 2529 State: nil, 2530 2531 Diff: &terraform.InstanceDiff{ 2532 Attributes: map[string]*terraform.ResourceAttrDiff{ 2533 "foo": &terraform.ResourceAttrDiff{ 2534 NewComputed: true, 2535 }, 2536 }, 2537 }, 2538 2539 Result: &terraform.InstanceState{ 2540 Attributes: map[string]string{}, 2541 }, 2542 }, 2543 2544 // #22 2545 { 2546 Schema: map[string]*Schema{ 2547 "foo": &Schema{ 2548 Type: TypeString, 2549 Optional: true, 2550 Computed: true, 2551 }, 2552 }, 2553 2554 State: nil, 2555 2556 Diff: &terraform.InstanceDiff{ 2557 Attributes: map[string]*terraform.ResourceAttrDiff{ 2558 "foo": &terraform.ResourceAttrDiff{ 2559 NewComputed: true, 2560 }, 2561 }, 2562 }, 2563 2564 Set: map[string]interface{}{ 2565 "foo": "bar", 2566 }, 2567 2568 Result: &terraform.InstanceState{ 2569 Attributes: map[string]string{ 2570 "foo": "bar", 2571 }, 2572 }, 2573 }, 2574 2575 // #23 Set of maps 2576 { 2577 Schema: map[string]*Schema{ 2578 "ports": &Schema{ 2579 Type: TypeSet, 2580 Optional: true, 2581 Computed: true, 2582 Elem: &Resource{ 2583 Schema: map[string]*Schema{ 2584 "index": &Schema{Type: TypeInt}, 2585 "uuids": &Schema{Type: TypeMap}, 2586 }, 2587 }, 2588 Set: func(a interface{}) int { 2589 m := a.(map[string]interface{}) 2590 return m["index"].(int) 2591 }, 2592 }, 2593 }, 2594 2595 State: nil, 2596 2597 Diff: &terraform.InstanceDiff{ 2598 Attributes: map[string]*terraform.ResourceAttrDiff{ 2599 "ports.10.uuids.#": &terraform.ResourceAttrDiff{ 2600 NewComputed: true, 2601 }, 2602 }, 2603 }, 2604 2605 Set: map[string]interface{}{ 2606 "ports": []interface{}{ 2607 map[string]interface{}{ 2608 "index": 10, 2609 "uuids": map[string]interface{}{ 2610 "80": "value", 2611 }, 2612 }, 2613 }, 2614 }, 2615 2616 Result: &terraform.InstanceState{ 2617 Attributes: map[string]string{ 2618 "ports.#": "1", 2619 "ports.10.index": "10", 2620 "ports.10.uuids.#": "1", 2621 "ports.10.uuids.80": "value", 2622 }, 2623 }, 2624 }, 2625 2626 // #24 2627 { 2628 Schema: map[string]*Schema{ 2629 "ports": &Schema{ 2630 Type: TypeSet, 2631 Optional: true, 2632 Computed: true, 2633 Elem: &Schema{Type: TypeInt}, 2634 Set: func(a interface{}) int { 2635 return a.(int) 2636 }, 2637 }, 2638 }, 2639 2640 State: &terraform.InstanceState{ 2641 Attributes: map[string]string{ 2642 "ports.#": "3", 2643 "ports.100": "100", 2644 "ports.80": "80", 2645 "ports.81": "81", 2646 }, 2647 }, 2648 2649 Diff: &terraform.InstanceDiff{ 2650 Attributes: map[string]*terraform.ResourceAttrDiff{ 2651 "ports.#": &terraform.ResourceAttrDiff{ 2652 Old: "3", 2653 New: "0", 2654 }, 2655 }, 2656 }, 2657 2658 Result: &terraform.InstanceState{ 2659 Attributes: map[string]string{ 2660 "ports.#": "0", 2661 }, 2662 }, 2663 }, 2664 2665 // #25 2666 { 2667 Schema: map[string]*Schema{ 2668 "ports": &Schema{ 2669 Type: TypeSet, 2670 Optional: true, 2671 Computed: true, 2672 Elem: &Schema{Type: TypeInt}, 2673 Set: func(a interface{}) int { 2674 return a.(int) 2675 }, 2676 }, 2677 }, 2678 2679 State: nil, 2680 2681 Diff: nil, 2682 2683 Set: map[string]interface{}{ 2684 "ports": []interface{}{}, 2685 }, 2686 2687 Result: &terraform.InstanceState{ 2688 Attributes: map[string]string{ 2689 "ports.#": "0", 2690 }, 2691 }, 2692 }, 2693 2694 // #26 2695 { 2696 Schema: map[string]*Schema{ 2697 "ports": &Schema{ 2698 Type: TypeList, 2699 Optional: true, 2700 Computed: true, 2701 Elem: &Schema{Type: TypeInt}, 2702 }, 2703 }, 2704 2705 State: nil, 2706 2707 Diff: nil, 2708 2709 Set: map[string]interface{}{ 2710 "ports": []interface{}{}, 2711 }, 2712 2713 Result: &terraform.InstanceState{ 2714 Attributes: map[string]string{ 2715 "ports.#": "0", 2716 }, 2717 }, 2718 }, 2719 2720 // #27 Set lists 2721 { 2722 Schema: map[string]*Schema{ 2723 "ports": &Schema{ 2724 Type: TypeList, 2725 Optional: true, 2726 Computed: true, 2727 Elem: &Resource{ 2728 Schema: map[string]*Schema{ 2729 "index": &Schema{Type: TypeInt}, 2730 "uuids": &Schema{Type: TypeMap}, 2731 }, 2732 }, 2733 }, 2734 }, 2735 2736 State: nil, 2737 2738 Diff: &terraform.InstanceDiff{ 2739 Attributes: map[string]*terraform.ResourceAttrDiff{ 2740 "ports.#": &terraform.ResourceAttrDiff{ 2741 NewComputed: true, 2742 }, 2743 }, 2744 }, 2745 2746 Set: map[string]interface{}{ 2747 "ports": []interface{}{ 2748 map[string]interface{}{ 2749 "index": 10, 2750 "uuids": map[string]interface{}{ 2751 "80": "value", 2752 }, 2753 }, 2754 }, 2755 }, 2756 2757 Result: &terraform.InstanceState{ 2758 Attributes: map[string]string{ 2759 "ports.#": "1", 2760 "ports.0.index": "10", 2761 "ports.0.uuids.#": "1", 2762 "ports.0.uuids.80": "value", 2763 }, 2764 }, 2765 }, 2766 } 2767 2768 for i, tc := range cases { 2769 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 2770 if err != nil { 2771 t.Fatalf("err: %s", err) 2772 } 2773 2774 for k, v := range tc.Set { 2775 if err := d.Set(k, v); err != nil { 2776 t.Fatalf("%d err: %s", i, err) 2777 } 2778 } 2779 2780 // Set an ID so that the state returned is not nil 2781 idSet := false 2782 if d.Id() == "" { 2783 idSet = true 2784 d.SetId("foo") 2785 } 2786 2787 // If we have partial, then enable partial state mode. 2788 if tc.Partial != nil { 2789 d.Partial(true) 2790 for _, k := range tc.Partial { 2791 d.SetPartial(k) 2792 } 2793 } 2794 2795 actual := d.State() 2796 2797 // If we set an ID, then undo what we did so the comparison works 2798 if actual != nil && idSet { 2799 actual.ID = "" 2800 delete(actual.Attributes, "id") 2801 } 2802 2803 if !reflect.DeepEqual(actual, tc.Result) { 2804 t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) 2805 } 2806 } 2807 } 2808 2809 func TestResourceDataSetConnInfo(t *testing.T) { 2810 d := &ResourceData{} 2811 d.SetId("foo") 2812 d.SetConnInfo(map[string]string{ 2813 "foo": "bar", 2814 }) 2815 2816 expected := map[string]string{ 2817 "foo": "bar", 2818 } 2819 2820 actual := d.State() 2821 if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) { 2822 t.Fatalf("bad: %#v", actual) 2823 } 2824 } 2825 2826 func TestResourceDataSetId(t *testing.T) { 2827 d := &ResourceData{} 2828 d.SetId("foo") 2829 2830 actual := d.State() 2831 if actual.ID != "foo" { 2832 t.Fatalf("bad: %#v", actual) 2833 } 2834 } 2835 2836 func TestResourceDataSetId_clear(t *testing.T) { 2837 d := &ResourceData{ 2838 state: &terraform.InstanceState{ID: "bar"}, 2839 } 2840 d.SetId("") 2841 2842 actual := d.State() 2843 if actual != nil { 2844 t.Fatalf("bad: %#v", actual) 2845 } 2846 } 2847 2848 func TestResourceDataSetId_override(t *testing.T) { 2849 d := &ResourceData{ 2850 state: &terraform.InstanceState{ID: "bar"}, 2851 } 2852 d.SetId("foo") 2853 2854 actual := d.State() 2855 if actual.ID != "foo" { 2856 t.Fatalf("bad: %#v", actual) 2857 } 2858 } 2859 2860 func testPtrTo(raw interface{}) interface{} { 2861 return &raw 2862 }