github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/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 656 Diff: nil, 657 658 Key: "ratio", 659 660 Value: 0.5, 661 }, 662 663 // #22 Float diff 664 { 665 Schema: map[string]*Schema{ 666 "ratio": &Schema{ 667 Type: TypeFloat, 668 Optional: true, 669 Computed: true, 670 }, 671 }, 672 673 State: &terraform.InstanceState{ 674 Attributes: map[string]string{ 675 "ratio": "-0.5", 676 }, 677 }, 678 679 680 Diff: &terraform.InstanceDiff{ 681 Attributes: map[string]*terraform.ResourceAttrDiff{ 682 "ratio": &terraform.ResourceAttrDiff{ 683 Old: "-0.5", 684 New: "33.0", 685 }, 686 }, 687 }, 688 689 690 Key: "ratio", 691 692 Value: 33.0, 693 }, 694 } 695 696 for i, tc := range cases { 697 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 698 if err != nil { 699 t.Fatalf("err: %s", err) 700 } 701 702 v := d.Get(tc.Key) 703 if s, ok := v.(*Set); ok { 704 v = s.List() 705 } 706 707 if !reflect.DeepEqual(v, tc.Value) { 708 t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value) 709 } 710 } 711 } 712 713 func TestResourceDataGetChange(t *testing.T) { 714 cases := []struct { 715 Schema map[string]*Schema 716 State *terraform.InstanceState 717 Diff *terraform.InstanceDiff 718 Key string 719 OldValue interface{} 720 NewValue interface{} 721 }{ 722 { 723 Schema: map[string]*Schema{ 724 "availability_zone": &Schema{ 725 Type: TypeString, 726 Optional: true, 727 Computed: true, 728 ForceNew: true, 729 }, 730 }, 731 732 State: nil, 733 734 Diff: &terraform.InstanceDiff{ 735 Attributes: map[string]*terraform.ResourceAttrDiff{ 736 "availability_zone": &terraform.ResourceAttrDiff{ 737 Old: "", 738 New: "foo", 739 RequiresNew: true, 740 }, 741 }, 742 }, 743 744 Key: "availability_zone", 745 746 OldValue: "", 747 NewValue: "foo", 748 }, 749 750 { 751 Schema: map[string]*Schema{ 752 "availability_zone": &Schema{ 753 Type: TypeString, 754 Optional: true, 755 Computed: true, 756 ForceNew: true, 757 }, 758 }, 759 760 State: &terraform.InstanceState{ 761 Attributes: map[string]string{ 762 "availability_zone": "foo", 763 }, 764 }, 765 766 Diff: &terraform.InstanceDiff{ 767 Attributes: map[string]*terraform.ResourceAttrDiff{ 768 "availability_zone": &terraform.ResourceAttrDiff{ 769 Old: "", 770 New: "foo", 771 RequiresNew: true, 772 }, 773 }, 774 }, 775 776 Key: "availability_zone", 777 778 OldValue: "foo", 779 NewValue: "foo", 780 }, 781 } 782 783 for i, tc := range cases { 784 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 785 if err != nil { 786 t.Fatalf("err: %s", err) 787 } 788 789 o, n := d.GetChange(tc.Key) 790 if !reflect.DeepEqual(o, tc.OldValue) { 791 t.Fatalf("Old Bad: %d\n\n%#v", i, o) 792 } 793 if !reflect.DeepEqual(n, tc.NewValue) { 794 t.Fatalf("New Bad: %d\n\n%#v", i, n) 795 } 796 } 797 } 798 799 func TestResourceDataGetOk(t *testing.T) { 800 cases := []struct { 801 Schema map[string]*Schema 802 State *terraform.InstanceState 803 Diff *terraform.InstanceDiff 804 Key string 805 Value interface{} 806 Ok bool 807 }{ 808 /* 809 * Primitives 810 */ 811 { 812 Schema: map[string]*Schema{ 813 "availability_zone": &Schema{ 814 Type: TypeString, 815 Optional: true, 816 Computed: true, 817 ForceNew: true, 818 }, 819 }, 820 821 State: nil, 822 823 Diff: &terraform.InstanceDiff{ 824 Attributes: map[string]*terraform.ResourceAttrDiff{ 825 "availability_zone": &terraform.ResourceAttrDiff{ 826 Old: "", 827 New: "", 828 }, 829 }, 830 }, 831 832 Key: "availability_zone", 833 Value: "", 834 Ok: true, 835 }, 836 837 { 838 Schema: map[string]*Schema{ 839 "availability_zone": &Schema{ 840 Type: TypeString, 841 Optional: true, 842 Computed: true, 843 ForceNew: true, 844 }, 845 }, 846 847 State: nil, 848 849 Diff: &terraform.InstanceDiff{ 850 Attributes: map[string]*terraform.ResourceAttrDiff{ 851 "availability_zone": &terraform.ResourceAttrDiff{ 852 Old: "", 853 New: "", 854 NewComputed: true, 855 }, 856 }, 857 }, 858 859 Key: "availability_zone", 860 Value: "", 861 Ok: false, 862 }, 863 864 { 865 Schema: map[string]*Schema{ 866 "availability_zone": &Schema{ 867 Type: TypeString, 868 Optional: true, 869 Computed: true, 870 ForceNew: true, 871 }, 872 }, 873 874 State: nil, 875 876 Diff: nil, 877 878 Key: "availability_zone", 879 Value: "", 880 Ok: false, 881 }, 882 883 /* 884 * Lists 885 */ 886 887 { 888 Schema: map[string]*Schema{ 889 "ports": &Schema{ 890 Type: TypeList, 891 Optional: true, 892 Elem: &Schema{Type: TypeInt}, 893 }, 894 }, 895 896 State: nil, 897 898 Diff: nil, 899 900 Key: "ports", 901 Value: []interface{}{}, 902 Ok: false, 903 }, 904 905 /* 906 * Map 907 */ 908 909 { 910 Schema: map[string]*Schema{ 911 "ports": &Schema{ 912 Type: TypeMap, 913 Optional: true, 914 }, 915 }, 916 917 State: nil, 918 919 Diff: nil, 920 921 Key: "ports", 922 Value: map[string]interface{}{}, 923 Ok: false, 924 }, 925 926 /* 927 * Set 928 */ 929 930 { 931 Schema: map[string]*Schema{ 932 "ports": &Schema{ 933 Type: TypeSet, 934 Optional: true, 935 Elem: &Schema{Type: TypeInt}, 936 Set: func(a interface{}) int { return a.(int) }, 937 }, 938 }, 939 940 State: nil, 941 942 Diff: nil, 943 944 Key: "ports", 945 Value: []interface{}{}, 946 Ok: false, 947 }, 948 949 { 950 Schema: map[string]*Schema{ 951 "ports": &Schema{ 952 Type: TypeSet, 953 Optional: true, 954 Elem: &Schema{Type: TypeInt}, 955 Set: func(a interface{}) int { return a.(int) }, 956 }, 957 }, 958 959 State: nil, 960 961 Diff: nil, 962 963 Key: "ports.0", 964 Value: 0, 965 Ok: false, 966 }, 967 } 968 969 for i, tc := range cases { 970 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 971 if err != nil { 972 t.Fatalf("err: %s", err) 973 } 974 975 v, ok := d.GetOk(tc.Key) 976 if s, ok := v.(*Set); ok { 977 v = s.List() 978 } 979 980 if !reflect.DeepEqual(v, tc.Value) { 981 t.Fatalf("Bad: %d\n\n%#v", i, v) 982 } 983 if ok != tc.Ok { 984 t.Fatalf("Bad: %d\n\n%#v", i, ok) 985 } 986 } 987 } 988 989 func TestResourceDataHasChange(t *testing.T) { 990 cases := []struct { 991 Schema map[string]*Schema 992 State *terraform.InstanceState 993 Diff *terraform.InstanceDiff 994 Key string 995 Change bool 996 }{ 997 { 998 Schema: map[string]*Schema{ 999 "availability_zone": &Schema{ 1000 Type: TypeString, 1001 Optional: true, 1002 Computed: true, 1003 ForceNew: true, 1004 }, 1005 }, 1006 1007 State: nil, 1008 1009 Diff: &terraform.InstanceDiff{ 1010 Attributes: map[string]*terraform.ResourceAttrDiff{ 1011 "availability_zone": &terraform.ResourceAttrDiff{ 1012 Old: "", 1013 New: "foo", 1014 RequiresNew: true, 1015 }, 1016 }, 1017 }, 1018 1019 Key: "availability_zone", 1020 1021 Change: true, 1022 }, 1023 1024 { 1025 Schema: map[string]*Schema{ 1026 "availability_zone": &Schema{ 1027 Type: TypeString, 1028 Optional: true, 1029 Computed: true, 1030 ForceNew: true, 1031 }, 1032 }, 1033 1034 State: &terraform.InstanceState{ 1035 Attributes: map[string]string{ 1036 "availability_zone": "foo", 1037 }, 1038 }, 1039 1040 Diff: &terraform.InstanceDiff{ 1041 Attributes: map[string]*terraform.ResourceAttrDiff{ 1042 "availability_zone": &terraform.ResourceAttrDiff{ 1043 Old: "", 1044 New: "foo", 1045 RequiresNew: true, 1046 }, 1047 }, 1048 }, 1049 1050 Key: "availability_zone", 1051 1052 Change: false, 1053 }, 1054 1055 { 1056 Schema: map[string]*Schema{ 1057 "tags": &Schema{ 1058 Type: TypeMap, 1059 Optional: true, 1060 Computed: true, 1061 }, 1062 }, 1063 1064 State: nil, 1065 1066 Diff: &terraform.InstanceDiff{ 1067 Attributes: map[string]*terraform.ResourceAttrDiff{ 1068 "tags.Name": &terraform.ResourceAttrDiff{ 1069 Old: "foo", 1070 New: "foo", 1071 }, 1072 }, 1073 }, 1074 1075 Key: "tags", 1076 1077 Change: true, 1078 }, 1079 1080 { 1081 Schema: map[string]*Schema{ 1082 "ports": &Schema{ 1083 Type: TypeSet, 1084 Optional: true, 1085 Elem: &Schema{Type: TypeInt}, 1086 Set: func(a interface{}) int { return a.(int) }, 1087 }, 1088 }, 1089 1090 State: &terraform.InstanceState{ 1091 Attributes: map[string]string{ 1092 "ports.#": "1", 1093 "ports.80": "80", 1094 }, 1095 }, 1096 1097 Diff: &terraform.InstanceDiff{ 1098 Attributes: map[string]*terraform.ResourceAttrDiff{ 1099 "ports.#": &terraform.ResourceAttrDiff{ 1100 Old: "1", 1101 New: "0", 1102 }, 1103 }, 1104 }, 1105 1106 Key: "ports", 1107 1108 Change: true, 1109 }, 1110 } 1111 1112 for i, tc := range cases { 1113 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 1114 if err != nil { 1115 t.Fatalf("err: %s", err) 1116 } 1117 1118 actual := d.HasChange(tc.Key) 1119 if actual != tc.Change { 1120 t.Fatalf("Bad: %d %#v", i, actual) 1121 } 1122 } 1123 } 1124 1125 func TestResourceDataSet(t *testing.T) { 1126 cases := []struct { 1127 Schema map[string]*Schema 1128 State *terraform.InstanceState 1129 Diff *terraform.InstanceDiff 1130 Key string 1131 Value interface{} 1132 Err bool 1133 GetKey string 1134 GetValue interface{} 1135 1136 // GetPreProcess can be set to munge the return value before being 1137 // compared to GetValue 1138 GetPreProcess func(interface{}) interface{} 1139 }{ 1140 // #0: Basic good 1141 { 1142 Schema: map[string]*Schema{ 1143 "availability_zone": &Schema{ 1144 Type: TypeString, 1145 Optional: true, 1146 Computed: true, 1147 ForceNew: true, 1148 }, 1149 }, 1150 1151 State: nil, 1152 1153 Diff: nil, 1154 1155 Key: "availability_zone", 1156 Value: "foo", 1157 1158 GetKey: "availability_zone", 1159 GetValue: "foo", 1160 }, 1161 1162 // #1: Basic int 1163 { 1164 Schema: map[string]*Schema{ 1165 "port": &Schema{ 1166 Type: TypeInt, 1167 Optional: true, 1168 Computed: true, 1169 ForceNew: true, 1170 }, 1171 }, 1172 1173 State: nil, 1174 1175 Diff: nil, 1176 1177 Key: "port", 1178 Value: 80, 1179 1180 GetKey: "port", 1181 GetValue: 80, 1182 }, 1183 1184 // #2: Basic bool 1185 { 1186 Schema: map[string]*Schema{ 1187 "vpc": &Schema{ 1188 Type: TypeBool, 1189 Optional: true, 1190 }, 1191 }, 1192 1193 State: nil, 1194 1195 Diff: nil, 1196 1197 Key: "vpc", 1198 Value: true, 1199 1200 GetKey: "vpc", 1201 GetValue: true, 1202 }, 1203 1204 // #3 1205 { 1206 Schema: map[string]*Schema{ 1207 "vpc": &Schema{ 1208 Type: TypeBool, 1209 Optional: true, 1210 }, 1211 }, 1212 1213 State: nil, 1214 1215 Diff: nil, 1216 1217 Key: "vpc", 1218 Value: false, 1219 1220 GetKey: "vpc", 1221 GetValue: false, 1222 }, 1223 1224 // #4: Invalid type 1225 { 1226 Schema: map[string]*Schema{ 1227 "availability_zone": &Schema{ 1228 Type: TypeString, 1229 Optional: true, 1230 Computed: true, 1231 ForceNew: true, 1232 }, 1233 }, 1234 1235 State: nil, 1236 1237 Diff: nil, 1238 1239 Key: "availability_zone", 1240 Value: 80, 1241 Err: true, 1242 1243 GetKey: "availability_zone", 1244 GetValue: "", 1245 }, 1246 1247 // #5: List of primitives, set list 1248 { 1249 Schema: map[string]*Schema{ 1250 "ports": &Schema{ 1251 Type: TypeList, 1252 Computed: true, 1253 Elem: &Schema{Type: TypeInt}, 1254 }, 1255 }, 1256 1257 State: nil, 1258 1259 Diff: nil, 1260 1261 Key: "ports", 1262 Value: []int{1, 2, 5}, 1263 1264 GetKey: "ports", 1265 GetValue: []interface{}{1, 2, 5}, 1266 }, 1267 1268 // #6: List of primitives, set list with error 1269 { 1270 Schema: map[string]*Schema{ 1271 "ports": &Schema{ 1272 Type: TypeList, 1273 Computed: true, 1274 Elem: &Schema{Type: TypeInt}, 1275 }, 1276 }, 1277 1278 State: nil, 1279 1280 Diff: nil, 1281 1282 Key: "ports", 1283 Value: []interface{}{1, "NOPE", 5}, 1284 Err: true, 1285 1286 GetKey: "ports", 1287 GetValue: []interface{}{}, 1288 }, 1289 1290 // #7: Set a list of maps 1291 { 1292 Schema: map[string]*Schema{ 1293 "config_vars": &Schema{ 1294 Type: TypeList, 1295 Optional: true, 1296 Computed: true, 1297 Elem: &Schema{ 1298 Type: TypeMap, 1299 }, 1300 }, 1301 }, 1302 1303 State: nil, 1304 1305 Diff: nil, 1306 1307 Key: "config_vars", 1308 Value: []interface{}{ 1309 map[string]interface{}{ 1310 "foo": "bar", 1311 }, 1312 map[string]interface{}{ 1313 "bar": "baz", 1314 }, 1315 }, 1316 Err: false, 1317 1318 GetKey: "config_vars", 1319 GetValue: []interface{}{ 1320 map[string]interface{}{ 1321 "foo": "bar", 1322 }, 1323 map[string]interface{}{ 1324 "bar": "baz", 1325 }, 1326 }, 1327 }, 1328 1329 // #8: Set, with list 1330 { 1331 Schema: map[string]*Schema{ 1332 "ports": &Schema{ 1333 Type: TypeSet, 1334 Optional: true, 1335 Computed: true, 1336 Elem: &Schema{Type: TypeInt}, 1337 Set: func(a interface{}) int { 1338 return a.(int) 1339 }, 1340 }, 1341 }, 1342 1343 State: &terraform.InstanceState{ 1344 Attributes: map[string]string{ 1345 "ports.#": "3", 1346 "ports.0": "100", 1347 "ports.1": "80", 1348 "ports.2": "80", 1349 }, 1350 }, 1351 1352 Key: "ports", 1353 Value: []interface{}{100, 125, 125}, 1354 1355 GetKey: "ports", 1356 GetValue: []interface{}{100, 125}, 1357 }, 1358 1359 // #9: Set, with Set 1360 { 1361 Schema: map[string]*Schema{ 1362 "ports": &Schema{ 1363 Type: TypeSet, 1364 Optional: true, 1365 Computed: true, 1366 Elem: &Schema{Type: TypeInt}, 1367 Set: func(a interface{}) int { 1368 return a.(int) 1369 }, 1370 }, 1371 }, 1372 1373 State: &terraform.InstanceState{ 1374 Attributes: map[string]string{ 1375 "ports.#": "3", 1376 "ports.100": "100", 1377 "ports.80": "80", 1378 "ports.81": "81", 1379 }, 1380 }, 1381 1382 Key: "ports", 1383 Value: &Set{ 1384 m: map[int]interface{}{ 1385 1: 1, 1386 2: 2, 1387 }, 1388 }, 1389 1390 GetKey: "ports", 1391 GetValue: []interface{}{1, 2}, 1392 }, 1393 1394 // #10: Set single item 1395 { 1396 Schema: map[string]*Schema{ 1397 "ports": &Schema{ 1398 Type: TypeSet, 1399 Optional: true, 1400 Computed: true, 1401 Elem: &Schema{Type: TypeInt}, 1402 Set: func(a interface{}) int { 1403 return a.(int) 1404 }, 1405 }, 1406 }, 1407 1408 State: &terraform.InstanceState{ 1409 Attributes: map[string]string{ 1410 "ports.#": "2", 1411 "ports.100": "100", 1412 "ports.80": "80", 1413 }, 1414 }, 1415 1416 Key: "ports.100", 1417 Value: 256, 1418 Err: true, 1419 1420 GetKey: "ports", 1421 GetValue: []interface{}{80, 100}, 1422 }, 1423 1424 // #11: Set with nested set 1425 { 1426 Schema: map[string]*Schema{ 1427 "ports": &Schema{ 1428 Type: TypeSet, 1429 Elem: &Resource{ 1430 Schema: map[string]*Schema{ 1431 "port": &Schema{ 1432 Type: TypeInt, 1433 }, 1434 1435 "set": &Schema{ 1436 Type: TypeSet, 1437 Elem: &Schema{Type: TypeInt}, 1438 Set: func(a interface{}) int { 1439 return a.(int) 1440 }, 1441 }, 1442 }, 1443 }, 1444 Set: func(a interface{}) int { 1445 return a.(map[string]interface{})["port"].(int) 1446 }, 1447 }, 1448 }, 1449 1450 State: nil, 1451 1452 Key: "ports", 1453 Value: []interface{}{ 1454 map[string]interface{}{ 1455 "port": 80, 1456 }, 1457 }, 1458 1459 GetKey: "ports", 1460 GetValue: []interface{}{ 1461 map[string]interface{}{ 1462 "port": 80, 1463 "set": []interface{}{}, 1464 }, 1465 }, 1466 1467 GetPreProcess: func(v interface{}) interface{} { 1468 if v == nil { 1469 return v 1470 } 1471 s, ok := v.([]interface{}) 1472 if !ok { 1473 return v 1474 } 1475 for _, v := range s { 1476 m, ok := v.(map[string]interface{}) 1477 if !ok { 1478 continue 1479 } 1480 if m["set"] == nil { 1481 continue 1482 } 1483 if s, ok := m["set"].(*Set); ok { 1484 m["set"] = s.List() 1485 } 1486 } 1487 1488 return v 1489 }, 1490 }, 1491 1492 // #12: List of floats, set list 1493 { 1494 Schema: map[string]*Schema{ 1495 "ratios": &Schema{ 1496 Type: TypeList, 1497 Computed: true, 1498 Elem: &Schema{Type: TypeFloat}, 1499 }, 1500 }, 1501 1502 State: nil, 1503 1504 Diff: nil, 1505 1506 Key: "ratios", 1507 Value: []float64{1.0, 2.2, 5.5}, 1508 1509 GetKey: "ratios", 1510 GetValue: []interface{}{1.0, 2.2, 5.5}, 1511 }, 1512 1513 // #12: Set of floats, set list 1514 { 1515 Schema: map[string]*Schema{ 1516 "ratios": &Schema{ 1517 Type: TypeSet, 1518 Computed: true, 1519 Elem: &Schema{Type: TypeFloat}, 1520 Set: func(a interface{}) int { 1521 return int(math.Float64bits(a.(float64))) 1522 }, 1523 }, 1524 }, 1525 1526 State: nil, 1527 1528 Diff: nil, 1529 1530 Key: "ratios", 1531 Value: []float64{1.0, 2.2, 5.5}, 1532 1533 GetKey: "ratios", 1534 GetValue: []interface{}{1.0, 2.2, 5.5}, 1535 }, 1536 1537 } 1538 1539 for i, tc := range cases { 1540 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 1541 if err != nil { 1542 t.Fatalf("err: %s", err) 1543 } 1544 1545 err = d.Set(tc.Key, tc.Value) 1546 if (err != nil) != tc.Err { 1547 t.Fatalf("%d err: %s", i, err) 1548 } 1549 1550 v := d.Get(tc.GetKey) 1551 if s, ok := v.(*Set); ok { 1552 v = s.List() 1553 } 1554 1555 if tc.GetPreProcess != nil { 1556 v = tc.GetPreProcess(v) 1557 } 1558 1559 if !reflect.DeepEqual(v, tc.GetValue) { 1560 t.Fatalf("Get Bad: %d\n\n%#v", i, v) 1561 } 1562 } 1563 } 1564 1565 func TestResourceDataState(t *testing.T) { 1566 cases := []struct { 1567 Schema map[string]*Schema 1568 State *terraform.InstanceState 1569 Diff *terraform.InstanceDiff 1570 Set map[string]interface{} 1571 Result *terraform.InstanceState 1572 Partial []string 1573 }{ 1574 // #0 Basic primitive in diff 1575 { 1576 Schema: map[string]*Schema{ 1577 "availability_zone": &Schema{ 1578 Type: TypeString, 1579 Optional: true, 1580 Computed: true, 1581 ForceNew: true, 1582 }, 1583 }, 1584 1585 State: nil, 1586 1587 Diff: &terraform.InstanceDiff{ 1588 Attributes: map[string]*terraform.ResourceAttrDiff{ 1589 "availability_zone": &terraform.ResourceAttrDiff{ 1590 Old: "", 1591 New: "foo", 1592 RequiresNew: true, 1593 }, 1594 }, 1595 }, 1596 1597 Result: &terraform.InstanceState{ 1598 Attributes: map[string]string{ 1599 "availability_zone": "foo", 1600 }, 1601 }, 1602 }, 1603 1604 // #1 Basic primitive set override 1605 { 1606 Schema: map[string]*Schema{ 1607 "availability_zone": &Schema{ 1608 Type: TypeString, 1609 Optional: true, 1610 Computed: true, 1611 ForceNew: true, 1612 }, 1613 }, 1614 1615 State: nil, 1616 1617 Diff: &terraform.InstanceDiff{ 1618 Attributes: map[string]*terraform.ResourceAttrDiff{ 1619 "availability_zone": &terraform.ResourceAttrDiff{ 1620 Old: "", 1621 New: "foo", 1622 RequiresNew: true, 1623 }, 1624 }, 1625 }, 1626 1627 Set: map[string]interface{}{ 1628 "availability_zone": "bar", 1629 }, 1630 1631 Result: &terraform.InstanceState{ 1632 Attributes: map[string]string{ 1633 "availability_zone": "bar", 1634 }, 1635 }, 1636 }, 1637 1638 // #2 1639 { 1640 Schema: map[string]*Schema{ 1641 "vpc": &Schema{ 1642 Type: TypeBool, 1643 Optional: true, 1644 }, 1645 }, 1646 1647 State: nil, 1648 1649 Diff: nil, 1650 1651 Set: map[string]interface{}{ 1652 "vpc": true, 1653 }, 1654 1655 Result: &terraform.InstanceState{ 1656 Attributes: map[string]string{ 1657 "vpc": "true", 1658 }, 1659 }, 1660 }, 1661 1662 // #3 Basic primitive with StateFunc set 1663 { 1664 Schema: map[string]*Schema{ 1665 "availability_zone": &Schema{ 1666 Type: TypeString, 1667 Optional: true, 1668 Computed: true, 1669 StateFunc: func(interface{}) string { return "" }, 1670 }, 1671 }, 1672 1673 State: nil, 1674 1675 Diff: &terraform.InstanceDiff{ 1676 Attributes: map[string]*terraform.ResourceAttrDiff{ 1677 "availability_zone": &terraform.ResourceAttrDiff{ 1678 Old: "", 1679 New: "foo", 1680 NewExtra: "foo!", 1681 }, 1682 }, 1683 }, 1684 1685 Result: &terraform.InstanceState{ 1686 Attributes: map[string]string{ 1687 "availability_zone": "foo", 1688 }, 1689 }, 1690 }, 1691 1692 // #4 List 1693 { 1694 Schema: map[string]*Schema{ 1695 "ports": &Schema{ 1696 Type: TypeList, 1697 Required: true, 1698 Elem: &Schema{Type: TypeInt}, 1699 }, 1700 }, 1701 1702 State: &terraform.InstanceState{ 1703 Attributes: map[string]string{ 1704 "ports.#": "1", 1705 "ports.0": "80", 1706 }, 1707 }, 1708 1709 Diff: &terraform.InstanceDiff{ 1710 Attributes: map[string]*terraform.ResourceAttrDiff{ 1711 "ports.#": &terraform.ResourceAttrDiff{ 1712 Old: "1", 1713 New: "2", 1714 }, 1715 "ports.1": &terraform.ResourceAttrDiff{ 1716 Old: "", 1717 New: "100", 1718 }, 1719 }, 1720 }, 1721 1722 Result: &terraform.InstanceState{ 1723 Attributes: map[string]string{ 1724 "ports.#": "2", 1725 "ports.0": "80", 1726 "ports.1": "100", 1727 }, 1728 }, 1729 }, 1730 1731 // #5 List of resources 1732 { 1733 Schema: map[string]*Schema{ 1734 "ingress": &Schema{ 1735 Type: TypeList, 1736 Required: true, 1737 Elem: &Resource{ 1738 Schema: map[string]*Schema{ 1739 "from": &Schema{ 1740 Type: TypeInt, 1741 Required: true, 1742 }, 1743 }, 1744 }, 1745 }, 1746 }, 1747 1748 State: &terraform.InstanceState{ 1749 Attributes: map[string]string{ 1750 "ingress.#": "1", 1751 "ingress.0.from": "80", 1752 }, 1753 }, 1754 1755 Diff: &terraform.InstanceDiff{ 1756 Attributes: map[string]*terraform.ResourceAttrDiff{ 1757 "ingress.#": &terraform.ResourceAttrDiff{ 1758 Old: "1", 1759 New: "2", 1760 }, 1761 "ingress.0.from": &terraform.ResourceAttrDiff{ 1762 Old: "80", 1763 New: "150", 1764 }, 1765 "ingress.1.from": &terraform.ResourceAttrDiff{ 1766 Old: "", 1767 New: "100", 1768 }, 1769 }, 1770 }, 1771 1772 Result: &terraform.InstanceState{ 1773 Attributes: map[string]string{ 1774 "ingress.#": "2", 1775 "ingress.0.from": "150", 1776 "ingress.1.from": "100", 1777 }, 1778 }, 1779 }, 1780 1781 // #6 List of maps 1782 { 1783 Schema: map[string]*Schema{ 1784 "config_vars": &Schema{ 1785 Type: TypeList, 1786 Optional: true, 1787 Computed: true, 1788 Elem: &Schema{ 1789 Type: TypeMap, 1790 }, 1791 }, 1792 }, 1793 1794 State: &terraform.InstanceState{ 1795 Attributes: map[string]string{ 1796 "config_vars.#": "2", 1797 "config_vars.0.#": "2", 1798 "config_vars.0.foo": "bar", 1799 "config_vars.0.bar": "bar", 1800 "config_vars.1.#": "1", 1801 "config_vars.1.bar": "baz", 1802 }, 1803 }, 1804 1805 Diff: &terraform.InstanceDiff{ 1806 Attributes: map[string]*terraform.ResourceAttrDiff{ 1807 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1808 NewRemoved: true, 1809 }, 1810 }, 1811 }, 1812 1813 Set: map[string]interface{}{ 1814 "config_vars": []map[string]interface{}{ 1815 map[string]interface{}{ 1816 "foo": "bar", 1817 }, 1818 map[string]interface{}{ 1819 "baz": "bang", 1820 }, 1821 }, 1822 }, 1823 1824 Result: &terraform.InstanceState{ 1825 Attributes: map[string]string{ 1826 "config_vars.#": "2", 1827 "config_vars.0.#": "1", 1828 "config_vars.0.foo": "bar", 1829 "config_vars.1.#": "1", 1830 "config_vars.1.baz": "bang", 1831 }, 1832 }, 1833 }, 1834 1835 // #7 List of maps with removal in diff 1836 { 1837 Schema: map[string]*Schema{ 1838 "config_vars": &Schema{ 1839 Type: TypeList, 1840 Optional: true, 1841 Computed: true, 1842 Elem: &Schema{ 1843 Type: TypeMap, 1844 }, 1845 }, 1846 }, 1847 1848 State: &terraform.InstanceState{ 1849 Attributes: map[string]string{ 1850 "config_vars.#": "1", 1851 "config_vars.0.FOO": "bar", 1852 }, 1853 }, 1854 1855 Diff: &terraform.InstanceDiff{ 1856 Attributes: map[string]*terraform.ResourceAttrDiff{ 1857 "config_vars.#": &terraform.ResourceAttrDiff{ 1858 Old: "1", 1859 New: "0", 1860 }, 1861 "config_vars.0.FOO": &terraform.ResourceAttrDiff{ 1862 Old: "bar", 1863 NewRemoved: true, 1864 }, 1865 }, 1866 }, 1867 1868 Result: &terraform.InstanceState{ 1869 Attributes: map[string]string{ 1870 "config_vars.#": "0", 1871 }, 1872 }, 1873 }, 1874 1875 // #8 Basic state with other keys 1876 { 1877 Schema: map[string]*Schema{ 1878 "availability_zone": &Schema{ 1879 Type: TypeString, 1880 Optional: true, 1881 Computed: true, 1882 ForceNew: true, 1883 }, 1884 }, 1885 1886 State: &terraform.InstanceState{ 1887 ID: "bar", 1888 Attributes: map[string]string{ 1889 "id": "bar", 1890 }, 1891 }, 1892 1893 Diff: &terraform.InstanceDiff{ 1894 Attributes: map[string]*terraform.ResourceAttrDiff{ 1895 "availability_zone": &terraform.ResourceAttrDiff{ 1896 Old: "", 1897 New: "foo", 1898 RequiresNew: true, 1899 }, 1900 }, 1901 }, 1902 1903 Result: &terraform.InstanceState{ 1904 ID: "bar", 1905 Attributes: map[string]string{ 1906 "id": "bar", 1907 "availability_zone": "foo", 1908 }, 1909 }, 1910 }, 1911 1912 // #9 Sets 1913 { 1914 Schema: map[string]*Schema{ 1915 "ports": &Schema{ 1916 Type: TypeSet, 1917 Optional: true, 1918 Computed: true, 1919 Elem: &Schema{Type: TypeInt}, 1920 Set: func(a interface{}) int { 1921 return a.(int) 1922 }, 1923 }, 1924 }, 1925 1926 State: &terraform.InstanceState{ 1927 Attributes: map[string]string{ 1928 "ports.#": "3", 1929 "ports.100": "100", 1930 "ports.80": "80", 1931 "ports.81": "81", 1932 }, 1933 }, 1934 1935 Diff: nil, 1936 1937 Result: &terraform.InstanceState{ 1938 Attributes: map[string]string{ 1939 "ports.#": "3", 1940 "ports.80": "80", 1941 "ports.81": "81", 1942 "ports.100": "100", 1943 }, 1944 }, 1945 }, 1946 1947 // #10 1948 { 1949 Schema: map[string]*Schema{ 1950 "ports": &Schema{ 1951 Type: TypeSet, 1952 Optional: true, 1953 Computed: true, 1954 Elem: &Schema{Type: TypeInt}, 1955 Set: func(a interface{}) int { 1956 return a.(int) 1957 }, 1958 }, 1959 }, 1960 1961 State: nil, 1962 1963 Diff: nil, 1964 1965 Set: map[string]interface{}{ 1966 "ports": []interface{}{100, 80}, 1967 }, 1968 1969 Result: &terraform.InstanceState{ 1970 Attributes: map[string]string{ 1971 "ports.#": "2", 1972 "ports.80": "80", 1973 "ports.100": "100", 1974 }, 1975 }, 1976 }, 1977 1978 // #11 1979 { 1980 Schema: map[string]*Schema{ 1981 "ports": &Schema{ 1982 Type: TypeSet, 1983 Optional: true, 1984 Computed: true, 1985 Elem: &Resource{ 1986 Schema: map[string]*Schema{ 1987 "order": &Schema{ 1988 Type: TypeInt, 1989 }, 1990 1991 "a": &Schema{ 1992 Type: TypeList, 1993 Elem: &Schema{Type: TypeInt}, 1994 }, 1995 1996 "b": &Schema{ 1997 Type: TypeList, 1998 Elem: &Schema{Type: TypeInt}, 1999 }, 2000 }, 2001 }, 2002 Set: func(a interface{}) int { 2003 m := a.(map[string]interface{}) 2004 return m["order"].(int) 2005 }, 2006 }, 2007 }, 2008 2009 State: &terraform.InstanceState{ 2010 Attributes: map[string]string{ 2011 "ports.#": "2", 2012 "ports.10.order": "10", 2013 "ports.10.a.#": "1", 2014 "ports.10.a.0": "80", 2015 "ports.20.order": "20", 2016 "ports.20.b.#": "1", 2017 "ports.20.b.0": "100", 2018 }, 2019 }, 2020 2021 Set: map[string]interface{}{ 2022 "ports": []interface{}{ 2023 map[string]interface{}{ 2024 "order": 20, 2025 "b": []interface{}{100}, 2026 }, 2027 map[string]interface{}{ 2028 "order": 10, 2029 "a": []interface{}{80}, 2030 }, 2031 }, 2032 }, 2033 2034 Result: &terraform.InstanceState{ 2035 Attributes: map[string]string{ 2036 "ports.#": "2", 2037 "ports.10.order": "10", 2038 "ports.10.a.#": "1", 2039 "ports.10.a.0": "80", 2040 "ports.10.b.#": "0", 2041 "ports.20.order": "20", 2042 "ports.20.a.#": "0", 2043 "ports.20.b.#": "1", 2044 "ports.20.b.0": "100", 2045 }, 2046 }, 2047 }, 2048 2049 /* 2050 * PARTIAL STATES 2051 */ 2052 2053 // #12 Basic primitive 2054 { 2055 Schema: map[string]*Schema{ 2056 "availability_zone": &Schema{ 2057 Type: TypeString, 2058 Optional: true, 2059 Computed: true, 2060 ForceNew: true, 2061 }, 2062 }, 2063 2064 State: nil, 2065 2066 Diff: &terraform.InstanceDiff{ 2067 Attributes: map[string]*terraform.ResourceAttrDiff{ 2068 "availability_zone": &terraform.ResourceAttrDiff{ 2069 Old: "", 2070 New: "foo", 2071 RequiresNew: true, 2072 }, 2073 }, 2074 }, 2075 2076 Partial: []string{}, 2077 2078 Result: &terraform.InstanceState{ 2079 Attributes: map[string]string{}, 2080 }, 2081 }, 2082 2083 // #13 List 2084 { 2085 Schema: map[string]*Schema{ 2086 "ports": &Schema{ 2087 Type: TypeList, 2088 Required: true, 2089 Elem: &Schema{Type: TypeInt}, 2090 }, 2091 }, 2092 2093 State: &terraform.InstanceState{ 2094 Attributes: map[string]string{ 2095 "ports.#": "1", 2096 "ports.0": "80", 2097 }, 2098 }, 2099 2100 Diff: &terraform.InstanceDiff{ 2101 Attributes: map[string]*terraform.ResourceAttrDiff{ 2102 "ports.#": &terraform.ResourceAttrDiff{ 2103 Old: "1", 2104 New: "2", 2105 }, 2106 "ports.1": &terraform.ResourceAttrDiff{ 2107 Old: "", 2108 New: "100", 2109 }, 2110 }, 2111 }, 2112 2113 Partial: []string{}, 2114 2115 Result: &terraform.InstanceState{ 2116 Attributes: map[string]string{ 2117 "ports.#": "1", 2118 "ports.0": "80", 2119 }, 2120 }, 2121 }, 2122 2123 // #14 2124 { 2125 Schema: map[string]*Schema{ 2126 "ports": &Schema{ 2127 Type: TypeList, 2128 Optional: true, 2129 Computed: true, 2130 Elem: &Schema{Type: TypeInt}, 2131 }, 2132 }, 2133 2134 State: nil, 2135 2136 Diff: &terraform.InstanceDiff{ 2137 Attributes: map[string]*terraform.ResourceAttrDiff{ 2138 "ports.#": &terraform.ResourceAttrDiff{ 2139 Old: "", 2140 NewComputed: true, 2141 }, 2142 }, 2143 }, 2144 2145 Partial: []string{}, 2146 2147 Set: map[string]interface{}{ 2148 "ports": []interface{}{}, 2149 }, 2150 2151 Result: &terraform.InstanceState{ 2152 Attributes: map[string]string{}, 2153 }, 2154 }, 2155 2156 // #15 List of resources 2157 { 2158 Schema: map[string]*Schema{ 2159 "ingress": &Schema{ 2160 Type: TypeList, 2161 Required: true, 2162 Elem: &Resource{ 2163 Schema: map[string]*Schema{ 2164 "from": &Schema{ 2165 Type: TypeInt, 2166 Required: true, 2167 }, 2168 }, 2169 }, 2170 }, 2171 }, 2172 2173 State: &terraform.InstanceState{ 2174 Attributes: map[string]string{ 2175 "ingress.#": "1", 2176 "ingress.0.from": "80", 2177 }, 2178 }, 2179 2180 Diff: &terraform.InstanceDiff{ 2181 Attributes: map[string]*terraform.ResourceAttrDiff{ 2182 "ingress.#": &terraform.ResourceAttrDiff{ 2183 Old: "1", 2184 New: "2", 2185 }, 2186 "ingress.0.from": &terraform.ResourceAttrDiff{ 2187 Old: "80", 2188 New: "150", 2189 }, 2190 "ingress.1.from": &terraform.ResourceAttrDiff{ 2191 Old: "", 2192 New: "100", 2193 }, 2194 }, 2195 }, 2196 2197 Partial: []string{}, 2198 2199 Result: &terraform.InstanceState{ 2200 Attributes: map[string]string{ 2201 "ingress.#": "1", 2202 "ingress.0.from": "80", 2203 }, 2204 }, 2205 }, 2206 2207 // #16 List of maps 2208 { 2209 Schema: map[string]*Schema{ 2210 "config_vars": &Schema{ 2211 Type: TypeList, 2212 Optional: true, 2213 Computed: true, 2214 Elem: &Schema{ 2215 Type: TypeMap, 2216 }, 2217 }, 2218 }, 2219 2220 State: &terraform.InstanceState{ 2221 Attributes: map[string]string{ 2222 "config_vars.#": "2", 2223 "config_vars.0.foo": "bar", 2224 "config_vars.0.bar": "bar", 2225 "config_vars.1.bar": "baz", 2226 }, 2227 }, 2228 2229 Diff: &terraform.InstanceDiff{ 2230 Attributes: map[string]*terraform.ResourceAttrDiff{ 2231 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 2232 NewRemoved: true, 2233 }, 2234 }, 2235 }, 2236 2237 Set: map[string]interface{}{ 2238 "config_vars": []map[string]interface{}{ 2239 map[string]interface{}{ 2240 "foo": "bar", 2241 }, 2242 map[string]interface{}{ 2243 "baz": "bang", 2244 }, 2245 }, 2246 }, 2247 2248 Partial: []string{}, 2249 2250 Result: &terraform.InstanceState{ 2251 Attributes: map[string]string{ 2252 // TODO: broken, shouldn't bar be removed? 2253 "config_vars.#": "2", 2254 "config_vars.0.#": "2", 2255 "config_vars.0.foo": "bar", 2256 "config_vars.0.bar": "bar", 2257 "config_vars.1.#": "1", 2258 "config_vars.1.bar": "baz", 2259 }, 2260 }, 2261 }, 2262 2263 // #17 Sets 2264 { 2265 Schema: map[string]*Schema{ 2266 "ports": &Schema{ 2267 Type: TypeSet, 2268 Optional: true, 2269 Computed: true, 2270 Elem: &Schema{Type: TypeInt}, 2271 Set: func(a interface{}) int { 2272 return a.(int) 2273 }, 2274 }, 2275 }, 2276 2277 State: &terraform.InstanceState{ 2278 Attributes: map[string]string{ 2279 "ports.#": "3", 2280 "ports.100": "100", 2281 "ports.80": "80", 2282 "ports.81": "81", 2283 }, 2284 }, 2285 2286 Diff: &terraform.InstanceDiff{ 2287 Attributes: map[string]*terraform.ResourceAttrDiff{ 2288 "ports.120": &terraform.ResourceAttrDiff{ 2289 New: "120", 2290 }, 2291 }, 2292 }, 2293 2294 Partial: []string{}, 2295 2296 Result: &terraform.InstanceState{ 2297 Attributes: map[string]string{ 2298 "ports.#": "3", 2299 "ports.80": "80", 2300 "ports.81": "81", 2301 "ports.100": "100", 2302 }, 2303 }, 2304 }, 2305 2306 // #18 2307 { 2308 Schema: map[string]*Schema{ 2309 "ports": &Schema{ 2310 Type: TypeSet, 2311 Optional: true, 2312 Computed: true, 2313 Elem: &Schema{Type: TypeInt}, 2314 Set: func(a interface{}) int { 2315 return a.(int) 2316 }, 2317 }, 2318 }, 2319 2320 State: nil, 2321 2322 Diff: &terraform.InstanceDiff{ 2323 Attributes: map[string]*terraform.ResourceAttrDiff{ 2324 "ports.#": &terraform.ResourceAttrDiff{ 2325 Old: "", 2326 NewComputed: true, 2327 }, 2328 }, 2329 }, 2330 2331 Partial: []string{}, 2332 2333 Result: &terraform.InstanceState{ 2334 Attributes: map[string]string{}, 2335 }, 2336 }, 2337 2338 // #19 Maps 2339 { 2340 Schema: map[string]*Schema{ 2341 "tags": &Schema{ 2342 Type: TypeMap, 2343 Optional: true, 2344 Computed: true, 2345 }, 2346 }, 2347 2348 State: nil, 2349 2350 Diff: &terraform.InstanceDiff{ 2351 Attributes: map[string]*terraform.ResourceAttrDiff{ 2352 "tags.Name": &terraform.ResourceAttrDiff{ 2353 Old: "", 2354 New: "foo", 2355 }, 2356 }, 2357 }, 2358 2359 Result: &terraform.InstanceState{ 2360 Attributes: map[string]string{ 2361 "tags.#": "1", 2362 "tags.Name": "foo", 2363 }, 2364 }, 2365 }, 2366 2367 // #20 2368 { 2369 Schema: map[string]*Schema{ 2370 "tags": &Schema{ 2371 Type: TypeMap, 2372 Optional: true, 2373 Computed: true, 2374 }, 2375 }, 2376 2377 State: nil, 2378 2379 Diff: &terraform.InstanceDiff{ 2380 Attributes: map[string]*terraform.ResourceAttrDiff{ 2381 "tags.Name": &terraform.ResourceAttrDiff{ 2382 Old: "", 2383 New: "foo", 2384 }, 2385 }, 2386 }, 2387 2388 Set: map[string]interface{}{ 2389 "tags": map[string]string{}, 2390 }, 2391 2392 Result: &terraform.InstanceState{ 2393 Attributes: map[string]string{}, 2394 }, 2395 }, 2396 2397 // #21 2398 { 2399 Schema: map[string]*Schema{ 2400 "foo": &Schema{ 2401 Type: TypeString, 2402 Optional: true, 2403 Computed: true, 2404 }, 2405 }, 2406 2407 State: nil, 2408 2409 Diff: &terraform.InstanceDiff{ 2410 Attributes: map[string]*terraform.ResourceAttrDiff{ 2411 "foo": &terraform.ResourceAttrDiff{ 2412 NewComputed: true, 2413 }, 2414 }, 2415 }, 2416 2417 Result: &terraform.InstanceState{ 2418 Attributes: map[string]string{}, 2419 }, 2420 }, 2421 2422 // #22 2423 { 2424 Schema: map[string]*Schema{ 2425 "foo": &Schema{ 2426 Type: TypeString, 2427 Optional: true, 2428 Computed: true, 2429 }, 2430 }, 2431 2432 State: nil, 2433 2434 Diff: &terraform.InstanceDiff{ 2435 Attributes: map[string]*terraform.ResourceAttrDiff{ 2436 "foo": &terraform.ResourceAttrDiff{ 2437 NewComputed: true, 2438 }, 2439 }, 2440 }, 2441 2442 Set: map[string]interface{}{ 2443 "foo": "bar", 2444 }, 2445 2446 Result: &terraform.InstanceState{ 2447 Attributes: map[string]string{ 2448 "foo": "bar", 2449 }, 2450 }, 2451 }, 2452 2453 // #23 Set of maps 2454 { 2455 Schema: map[string]*Schema{ 2456 "ports": &Schema{ 2457 Type: TypeSet, 2458 Optional: true, 2459 Computed: true, 2460 Elem: &Resource{ 2461 Schema: map[string]*Schema{ 2462 "index": &Schema{Type: TypeInt}, 2463 "uuids": &Schema{Type: TypeMap}, 2464 }, 2465 }, 2466 Set: func(a interface{}) int { 2467 m := a.(map[string]interface{}) 2468 return m["index"].(int) 2469 }, 2470 }, 2471 }, 2472 2473 State: nil, 2474 2475 Diff: &terraform.InstanceDiff{ 2476 Attributes: map[string]*terraform.ResourceAttrDiff{ 2477 "ports.10.uuids.#": &terraform.ResourceAttrDiff{ 2478 NewComputed: true, 2479 }, 2480 }, 2481 }, 2482 2483 Set: map[string]interface{}{ 2484 "ports": []interface{}{ 2485 map[string]interface{}{ 2486 "index": 10, 2487 "uuids": map[string]interface{}{ 2488 "80": "value", 2489 }, 2490 }, 2491 }, 2492 }, 2493 2494 Result: &terraform.InstanceState{ 2495 Attributes: map[string]string{ 2496 "ports.#": "1", 2497 "ports.10.index": "10", 2498 "ports.10.uuids.#": "1", 2499 "ports.10.uuids.80": "value", 2500 }, 2501 }, 2502 }, 2503 } 2504 2505 for i, tc := range cases { 2506 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 2507 if err != nil { 2508 t.Fatalf("err: %s", err) 2509 } 2510 2511 for k, v := range tc.Set { 2512 if err := d.Set(k, v); err != nil { 2513 t.Fatalf("%d err: %s", i, err) 2514 } 2515 } 2516 2517 // Set an ID so that the state returned is not nil 2518 idSet := false 2519 if d.Id() == "" { 2520 idSet = true 2521 d.SetId("foo") 2522 } 2523 2524 // If we have partial, then enable partial state mode. 2525 if tc.Partial != nil { 2526 d.Partial(true) 2527 for _, k := range tc.Partial { 2528 d.SetPartial(k) 2529 } 2530 } 2531 2532 actual := d.State() 2533 2534 // If we set an ID, then undo what we did so the comparison works 2535 if actual != nil && idSet { 2536 actual.ID = "" 2537 delete(actual.Attributes, "id") 2538 } 2539 2540 if !reflect.DeepEqual(actual, tc.Result) { 2541 t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) 2542 } 2543 } 2544 } 2545 2546 func TestResourceDataSetConnInfo(t *testing.T) { 2547 d := &ResourceData{} 2548 d.SetId("foo") 2549 d.SetConnInfo(map[string]string{ 2550 "foo": "bar", 2551 }) 2552 2553 expected := map[string]string{ 2554 "foo": "bar", 2555 } 2556 2557 actual := d.State() 2558 if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) { 2559 t.Fatalf("bad: %#v", actual) 2560 } 2561 } 2562 2563 func TestResourceDataSetId(t *testing.T) { 2564 d := &ResourceData{} 2565 d.SetId("foo") 2566 2567 actual := d.State() 2568 if actual.ID != "foo" { 2569 t.Fatalf("bad: %#v", actual) 2570 } 2571 } 2572 2573 func TestResourceDataSetId_clear(t *testing.T) { 2574 d := &ResourceData{ 2575 state: &terraform.InstanceState{ID: "bar"}, 2576 } 2577 d.SetId("") 2578 2579 actual := d.State() 2580 if actual != nil { 2581 t.Fatalf("bad: %#v", actual) 2582 } 2583 } 2584 2585 func TestResourceDataSetId_override(t *testing.T) { 2586 d := &ResourceData{ 2587 state: &terraform.InstanceState{ID: "bar"}, 2588 } 2589 d.SetId("foo") 2590 2591 actual := d.State() 2592 if actual.ID != "foo" { 2593 t.Fatalf("bad: %#v", actual) 2594 } 2595 }