github.com/rmenn/terraform@v0.3.8-0.20150225065417-fc84b3a78802/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 cases := []struct { 1182 Schema map[string]*Schema 1183 State *terraform.InstanceState 1184 Diff *terraform.InstanceDiff 1185 Key string 1186 Value interface{} 1187 Err bool 1188 GetKey string 1189 GetValue interface{} 1190 1191 // GetPreProcess can be set to munge the return value before being 1192 // compared to GetValue 1193 GetPreProcess func(interface{}) interface{} 1194 }{ 1195 // #0: Basic good 1196 { 1197 Schema: map[string]*Schema{ 1198 "availability_zone": &Schema{ 1199 Type: TypeString, 1200 Optional: true, 1201 Computed: true, 1202 ForceNew: true, 1203 }, 1204 }, 1205 1206 State: nil, 1207 1208 Diff: nil, 1209 1210 Key: "availability_zone", 1211 Value: "foo", 1212 1213 GetKey: "availability_zone", 1214 GetValue: "foo", 1215 }, 1216 1217 // #1: Basic int 1218 { 1219 Schema: map[string]*Schema{ 1220 "port": &Schema{ 1221 Type: TypeInt, 1222 Optional: true, 1223 Computed: true, 1224 ForceNew: true, 1225 }, 1226 }, 1227 1228 State: nil, 1229 1230 Diff: nil, 1231 1232 Key: "port", 1233 Value: 80, 1234 1235 GetKey: "port", 1236 GetValue: 80, 1237 }, 1238 1239 // #2: Basic bool 1240 { 1241 Schema: map[string]*Schema{ 1242 "vpc": &Schema{ 1243 Type: TypeBool, 1244 Optional: true, 1245 }, 1246 }, 1247 1248 State: nil, 1249 1250 Diff: nil, 1251 1252 Key: "vpc", 1253 Value: true, 1254 1255 GetKey: "vpc", 1256 GetValue: true, 1257 }, 1258 1259 // #3 1260 { 1261 Schema: map[string]*Schema{ 1262 "vpc": &Schema{ 1263 Type: TypeBool, 1264 Optional: true, 1265 }, 1266 }, 1267 1268 State: nil, 1269 1270 Diff: nil, 1271 1272 Key: "vpc", 1273 Value: false, 1274 1275 GetKey: "vpc", 1276 GetValue: false, 1277 }, 1278 1279 // #4: Invalid type 1280 { 1281 Schema: map[string]*Schema{ 1282 "availability_zone": &Schema{ 1283 Type: TypeString, 1284 Optional: true, 1285 Computed: true, 1286 ForceNew: true, 1287 }, 1288 }, 1289 1290 State: nil, 1291 1292 Diff: nil, 1293 1294 Key: "availability_zone", 1295 Value: 80, 1296 Err: true, 1297 1298 GetKey: "availability_zone", 1299 GetValue: "", 1300 }, 1301 1302 // #5: List of primitives, set list 1303 { 1304 Schema: map[string]*Schema{ 1305 "ports": &Schema{ 1306 Type: TypeList, 1307 Computed: true, 1308 Elem: &Schema{Type: TypeInt}, 1309 }, 1310 }, 1311 1312 State: nil, 1313 1314 Diff: nil, 1315 1316 Key: "ports", 1317 Value: []int{1, 2, 5}, 1318 1319 GetKey: "ports", 1320 GetValue: []interface{}{1, 2, 5}, 1321 }, 1322 1323 // #6: List of primitives, set list with error 1324 { 1325 Schema: map[string]*Schema{ 1326 "ports": &Schema{ 1327 Type: TypeList, 1328 Computed: true, 1329 Elem: &Schema{Type: TypeInt}, 1330 }, 1331 }, 1332 1333 State: nil, 1334 1335 Diff: nil, 1336 1337 Key: "ports", 1338 Value: []interface{}{1, "NOPE", 5}, 1339 Err: true, 1340 1341 GetKey: "ports", 1342 GetValue: []interface{}{}, 1343 }, 1344 1345 // #7: Set a list of maps 1346 { 1347 Schema: map[string]*Schema{ 1348 "config_vars": &Schema{ 1349 Type: TypeList, 1350 Optional: true, 1351 Computed: true, 1352 Elem: &Schema{ 1353 Type: TypeMap, 1354 }, 1355 }, 1356 }, 1357 1358 State: nil, 1359 1360 Diff: nil, 1361 1362 Key: "config_vars", 1363 Value: []interface{}{ 1364 map[string]interface{}{ 1365 "foo": "bar", 1366 }, 1367 map[string]interface{}{ 1368 "bar": "baz", 1369 }, 1370 }, 1371 Err: false, 1372 1373 GetKey: "config_vars", 1374 GetValue: []interface{}{ 1375 map[string]interface{}{ 1376 "foo": "bar", 1377 }, 1378 map[string]interface{}{ 1379 "bar": "baz", 1380 }, 1381 }, 1382 }, 1383 1384 // #8: Set, with list 1385 { 1386 Schema: map[string]*Schema{ 1387 "ports": &Schema{ 1388 Type: TypeSet, 1389 Optional: true, 1390 Computed: true, 1391 Elem: &Schema{Type: TypeInt}, 1392 Set: func(a interface{}) int { 1393 return a.(int) 1394 }, 1395 }, 1396 }, 1397 1398 State: &terraform.InstanceState{ 1399 Attributes: map[string]string{ 1400 "ports.#": "3", 1401 "ports.0": "100", 1402 "ports.1": "80", 1403 "ports.2": "80", 1404 }, 1405 }, 1406 1407 Key: "ports", 1408 Value: []interface{}{100, 125, 125}, 1409 1410 GetKey: "ports", 1411 GetValue: []interface{}{100, 125}, 1412 }, 1413 1414 // #9: Set, with Set 1415 { 1416 Schema: map[string]*Schema{ 1417 "ports": &Schema{ 1418 Type: TypeSet, 1419 Optional: true, 1420 Computed: true, 1421 Elem: &Schema{Type: TypeInt}, 1422 Set: func(a interface{}) int { 1423 return a.(int) 1424 }, 1425 }, 1426 }, 1427 1428 State: &terraform.InstanceState{ 1429 Attributes: map[string]string{ 1430 "ports.#": "3", 1431 "ports.100": "100", 1432 "ports.80": "80", 1433 "ports.81": "81", 1434 }, 1435 }, 1436 1437 Key: "ports", 1438 Value: &Set{ 1439 m: map[int]interface{}{ 1440 1: 1, 1441 2: 2, 1442 }, 1443 }, 1444 1445 GetKey: "ports", 1446 GetValue: []interface{}{1, 2}, 1447 }, 1448 1449 // #10: Set single item 1450 { 1451 Schema: map[string]*Schema{ 1452 "ports": &Schema{ 1453 Type: TypeSet, 1454 Optional: true, 1455 Computed: true, 1456 Elem: &Schema{Type: TypeInt}, 1457 Set: func(a interface{}) int { 1458 return a.(int) 1459 }, 1460 }, 1461 }, 1462 1463 State: &terraform.InstanceState{ 1464 Attributes: map[string]string{ 1465 "ports.#": "2", 1466 "ports.100": "100", 1467 "ports.80": "80", 1468 }, 1469 }, 1470 1471 Key: "ports.100", 1472 Value: 256, 1473 Err: true, 1474 1475 GetKey: "ports", 1476 GetValue: []interface{}{80, 100}, 1477 }, 1478 1479 // #11: Set with nested set 1480 { 1481 Schema: map[string]*Schema{ 1482 "ports": &Schema{ 1483 Type: TypeSet, 1484 Elem: &Resource{ 1485 Schema: map[string]*Schema{ 1486 "port": &Schema{ 1487 Type: TypeInt, 1488 }, 1489 1490 "set": &Schema{ 1491 Type: TypeSet, 1492 Elem: &Schema{Type: TypeInt}, 1493 Set: func(a interface{}) int { 1494 return a.(int) 1495 }, 1496 }, 1497 }, 1498 }, 1499 Set: func(a interface{}) int { 1500 return a.(map[string]interface{})["port"].(int) 1501 }, 1502 }, 1503 }, 1504 1505 State: nil, 1506 1507 Key: "ports", 1508 Value: []interface{}{ 1509 map[string]interface{}{ 1510 "port": 80, 1511 }, 1512 }, 1513 1514 GetKey: "ports", 1515 GetValue: []interface{}{ 1516 map[string]interface{}{ 1517 "port": 80, 1518 "set": []interface{}{}, 1519 }, 1520 }, 1521 1522 GetPreProcess: func(v interface{}) interface{} { 1523 if v == nil { 1524 return v 1525 } 1526 s, ok := v.([]interface{}) 1527 if !ok { 1528 return v 1529 } 1530 for _, v := range s { 1531 m, ok := v.(map[string]interface{}) 1532 if !ok { 1533 continue 1534 } 1535 if m["set"] == nil { 1536 continue 1537 } 1538 if s, ok := m["set"].(*Set); ok { 1539 m["set"] = s.List() 1540 } 1541 } 1542 1543 return v 1544 }, 1545 }, 1546 1547 // #12: List of floats, set list 1548 { 1549 Schema: map[string]*Schema{ 1550 "ratios": &Schema{ 1551 Type: TypeList, 1552 Computed: true, 1553 Elem: &Schema{Type: TypeFloat}, 1554 }, 1555 }, 1556 1557 State: nil, 1558 1559 Diff: nil, 1560 1561 Key: "ratios", 1562 Value: []float64{1.0, 2.2, 5.5}, 1563 1564 GetKey: "ratios", 1565 GetValue: []interface{}{1.0, 2.2, 5.5}, 1566 }, 1567 1568 // #12: Set of floats, set list 1569 { 1570 Schema: map[string]*Schema{ 1571 "ratios": &Schema{ 1572 Type: TypeSet, 1573 Computed: true, 1574 Elem: &Schema{Type: TypeFloat}, 1575 Set: func(a interface{}) int { 1576 return int(math.Float64bits(a.(float64))) 1577 }, 1578 }, 1579 }, 1580 1581 State: nil, 1582 1583 Diff: nil, 1584 1585 Key: "ratios", 1586 Value: []float64{1.0, 2.2, 5.5}, 1587 1588 GetKey: "ratios", 1589 GetValue: []interface{}{1.0, 2.2, 5.5}, 1590 }, 1591 } 1592 1593 for i, tc := range cases { 1594 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 1595 if err != nil { 1596 t.Fatalf("err: %s", err) 1597 } 1598 1599 err = d.Set(tc.Key, tc.Value) 1600 if (err != nil) != tc.Err { 1601 t.Fatalf("%d err: %s", i, err) 1602 } 1603 1604 v := d.Get(tc.GetKey) 1605 if s, ok := v.(*Set); ok { 1606 v = s.List() 1607 } 1608 1609 if tc.GetPreProcess != nil { 1610 v = tc.GetPreProcess(v) 1611 } 1612 1613 if !reflect.DeepEqual(v, tc.GetValue) { 1614 t.Fatalf("Get Bad: %d\n\n%#v", i, v) 1615 } 1616 } 1617 } 1618 1619 func TestResourceDataState(t *testing.T) { 1620 cases := []struct { 1621 Schema map[string]*Schema 1622 State *terraform.InstanceState 1623 Diff *terraform.InstanceDiff 1624 Set map[string]interface{} 1625 Result *terraform.InstanceState 1626 Partial []string 1627 }{ 1628 // #0 Basic primitive in diff 1629 { 1630 Schema: map[string]*Schema{ 1631 "availability_zone": &Schema{ 1632 Type: TypeString, 1633 Optional: true, 1634 Computed: true, 1635 ForceNew: true, 1636 }, 1637 }, 1638 1639 State: nil, 1640 1641 Diff: &terraform.InstanceDiff{ 1642 Attributes: map[string]*terraform.ResourceAttrDiff{ 1643 "availability_zone": &terraform.ResourceAttrDiff{ 1644 Old: "", 1645 New: "foo", 1646 RequiresNew: true, 1647 }, 1648 }, 1649 }, 1650 1651 Result: &terraform.InstanceState{ 1652 Attributes: map[string]string{ 1653 "availability_zone": "foo", 1654 }, 1655 }, 1656 }, 1657 1658 // #1 Basic primitive set override 1659 { 1660 Schema: map[string]*Schema{ 1661 "availability_zone": &Schema{ 1662 Type: TypeString, 1663 Optional: true, 1664 Computed: true, 1665 ForceNew: true, 1666 }, 1667 }, 1668 1669 State: nil, 1670 1671 Diff: &terraform.InstanceDiff{ 1672 Attributes: map[string]*terraform.ResourceAttrDiff{ 1673 "availability_zone": &terraform.ResourceAttrDiff{ 1674 Old: "", 1675 New: "foo", 1676 RequiresNew: true, 1677 }, 1678 }, 1679 }, 1680 1681 Set: map[string]interface{}{ 1682 "availability_zone": "bar", 1683 }, 1684 1685 Result: &terraform.InstanceState{ 1686 Attributes: map[string]string{ 1687 "availability_zone": "bar", 1688 }, 1689 }, 1690 }, 1691 1692 // #2 1693 { 1694 Schema: map[string]*Schema{ 1695 "vpc": &Schema{ 1696 Type: TypeBool, 1697 Optional: true, 1698 }, 1699 }, 1700 1701 State: nil, 1702 1703 Diff: nil, 1704 1705 Set: map[string]interface{}{ 1706 "vpc": true, 1707 }, 1708 1709 Result: &terraform.InstanceState{ 1710 Attributes: map[string]string{ 1711 "vpc": "true", 1712 }, 1713 }, 1714 }, 1715 1716 // #3 Basic primitive with StateFunc set 1717 { 1718 Schema: map[string]*Schema{ 1719 "availability_zone": &Schema{ 1720 Type: TypeString, 1721 Optional: true, 1722 Computed: true, 1723 StateFunc: func(interface{}) string { return "" }, 1724 }, 1725 }, 1726 1727 State: nil, 1728 1729 Diff: &terraform.InstanceDiff{ 1730 Attributes: map[string]*terraform.ResourceAttrDiff{ 1731 "availability_zone": &terraform.ResourceAttrDiff{ 1732 Old: "", 1733 New: "foo", 1734 NewExtra: "foo!", 1735 }, 1736 }, 1737 }, 1738 1739 Result: &terraform.InstanceState{ 1740 Attributes: map[string]string{ 1741 "availability_zone": "foo", 1742 }, 1743 }, 1744 }, 1745 1746 // #4 List 1747 { 1748 Schema: map[string]*Schema{ 1749 "ports": &Schema{ 1750 Type: TypeList, 1751 Required: true, 1752 Elem: &Schema{Type: TypeInt}, 1753 }, 1754 }, 1755 1756 State: &terraform.InstanceState{ 1757 Attributes: map[string]string{ 1758 "ports.#": "1", 1759 "ports.0": "80", 1760 }, 1761 }, 1762 1763 Diff: &terraform.InstanceDiff{ 1764 Attributes: map[string]*terraform.ResourceAttrDiff{ 1765 "ports.#": &terraform.ResourceAttrDiff{ 1766 Old: "1", 1767 New: "2", 1768 }, 1769 "ports.1": &terraform.ResourceAttrDiff{ 1770 Old: "", 1771 New: "100", 1772 }, 1773 }, 1774 }, 1775 1776 Result: &terraform.InstanceState{ 1777 Attributes: map[string]string{ 1778 "ports.#": "2", 1779 "ports.0": "80", 1780 "ports.1": "100", 1781 }, 1782 }, 1783 }, 1784 1785 // #5 List of resources 1786 { 1787 Schema: map[string]*Schema{ 1788 "ingress": &Schema{ 1789 Type: TypeList, 1790 Required: true, 1791 Elem: &Resource{ 1792 Schema: map[string]*Schema{ 1793 "from": &Schema{ 1794 Type: TypeInt, 1795 Required: true, 1796 }, 1797 }, 1798 }, 1799 }, 1800 }, 1801 1802 State: &terraform.InstanceState{ 1803 Attributes: map[string]string{ 1804 "ingress.#": "1", 1805 "ingress.0.from": "80", 1806 }, 1807 }, 1808 1809 Diff: &terraform.InstanceDiff{ 1810 Attributes: map[string]*terraform.ResourceAttrDiff{ 1811 "ingress.#": &terraform.ResourceAttrDiff{ 1812 Old: "1", 1813 New: "2", 1814 }, 1815 "ingress.0.from": &terraform.ResourceAttrDiff{ 1816 Old: "80", 1817 New: "150", 1818 }, 1819 "ingress.1.from": &terraform.ResourceAttrDiff{ 1820 Old: "", 1821 New: "100", 1822 }, 1823 }, 1824 }, 1825 1826 Result: &terraform.InstanceState{ 1827 Attributes: map[string]string{ 1828 "ingress.#": "2", 1829 "ingress.0.from": "150", 1830 "ingress.1.from": "100", 1831 }, 1832 }, 1833 }, 1834 1835 // #6 List of maps 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.#": "2", 1851 "config_vars.0.#": "2", 1852 "config_vars.0.foo": "bar", 1853 "config_vars.0.bar": "bar", 1854 "config_vars.1.#": "1", 1855 "config_vars.1.bar": "baz", 1856 }, 1857 }, 1858 1859 Diff: &terraform.InstanceDiff{ 1860 Attributes: map[string]*terraform.ResourceAttrDiff{ 1861 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1862 NewRemoved: true, 1863 }, 1864 }, 1865 }, 1866 1867 Set: map[string]interface{}{ 1868 "config_vars": []map[string]interface{}{ 1869 map[string]interface{}{ 1870 "foo": "bar", 1871 }, 1872 map[string]interface{}{ 1873 "baz": "bang", 1874 }, 1875 }, 1876 }, 1877 1878 Result: &terraform.InstanceState{ 1879 Attributes: map[string]string{ 1880 "config_vars.#": "2", 1881 "config_vars.0.#": "1", 1882 "config_vars.0.foo": "bar", 1883 "config_vars.1.#": "1", 1884 "config_vars.1.baz": "bang", 1885 }, 1886 }, 1887 }, 1888 1889 // #7 List of maps with removal in diff 1890 { 1891 Schema: map[string]*Schema{ 1892 "config_vars": &Schema{ 1893 Type: TypeList, 1894 Optional: true, 1895 Computed: true, 1896 Elem: &Schema{ 1897 Type: TypeMap, 1898 }, 1899 }, 1900 }, 1901 1902 State: &terraform.InstanceState{ 1903 Attributes: map[string]string{ 1904 "config_vars.#": "1", 1905 "config_vars.0.FOO": "bar", 1906 }, 1907 }, 1908 1909 Diff: &terraform.InstanceDiff{ 1910 Attributes: map[string]*terraform.ResourceAttrDiff{ 1911 "config_vars.#": &terraform.ResourceAttrDiff{ 1912 Old: "1", 1913 New: "0", 1914 }, 1915 "config_vars.0.FOO": &terraform.ResourceAttrDiff{ 1916 Old: "bar", 1917 NewRemoved: true, 1918 }, 1919 }, 1920 }, 1921 1922 Result: &terraform.InstanceState{ 1923 Attributes: map[string]string{ 1924 "config_vars.#": "0", 1925 }, 1926 }, 1927 }, 1928 1929 // #8 Basic state with other keys 1930 { 1931 Schema: map[string]*Schema{ 1932 "availability_zone": &Schema{ 1933 Type: TypeString, 1934 Optional: true, 1935 Computed: true, 1936 ForceNew: true, 1937 }, 1938 }, 1939 1940 State: &terraform.InstanceState{ 1941 ID: "bar", 1942 Attributes: map[string]string{ 1943 "id": "bar", 1944 }, 1945 }, 1946 1947 Diff: &terraform.InstanceDiff{ 1948 Attributes: map[string]*terraform.ResourceAttrDiff{ 1949 "availability_zone": &terraform.ResourceAttrDiff{ 1950 Old: "", 1951 New: "foo", 1952 RequiresNew: true, 1953 }, 1954 }, 1955 }, 1956 1957 Result: &terraform.InstanceState{ 1958 ID: "bar", 1959 Attributes: map[string]string{ 1960 "id": "bar", 1961 "availability_zone": "foo", 1962 }, 1963 }, 1964 }, 1965 1966 // #9 Sets 1967 { 1968 Schema: map[string]*Schema{ 1969 "ports": &Schema{ 1970 Type: TypeSet, 1971 Optional: true, 1972 Computed: true, 1973 Elem: &Schema{Type: TypeInt}, 1974 Set: func(a interface{}) int { 1975 return a.(int) 1976 }, 1977 }, 1978 }, 1979 1980 State: &terraform.InstanceState{ 1981 Attributes: map[string]string{ 1982 "ports.#": "3", 1983 "ports.100": "100", 1984 "ports.80": "80", 1985 "ports.81": "81", 1986 }, 1987 }, 1988 1989 Diff: nil, 1990 1991 Result: &terraform.InstanceState{ 1992 Attributes: map[string]string{ 1993 "ports.#": "3", 1994 "ports.80": "80", 1995 "ports.81": "81", 1996 "ports.100": "100", 1997 }, 1998 }, 1999 }, 2000 2001 // #10 2002 { 2003 Schema: map[string]*Schema{ 2004 "ports": &Schema{ 2005 Type: TypeSet, 2006 Optional: true, 2007 Computed: true, 2008 Elem: &Schema{Type: TypeInt}, 2009 Set: func(a interface{}) int { 2010 return a.(int) 2011 }, 2012 }, 2013 }, 2014 2015 State: nil, 2016 2017 Diff: nil, 2018 2019 Set: map[string]interface{}{ 2020 "ports": []interface{}{100, 80}, 2021 }, 2022 2023 Result: &terraform.InstanceState{ 2024 Attributes: map[string]string{ 2025 "ports.#": "2", 2026 "ports.80": "80", 2027 "ports.100": "100", 2028 }, 2029 }, 2030 }, 2031 2032 // #11 2033 { 2034 Schema: map[string]*Schema{ 2035 "ports": &Schema{ 2036 Type: TypeSet, 2037 Optional: true, 2038 Computed: true, 2039 Elem: &Resource{ 2040 Schema: map[string]*Schema{ 2041 "order": &Schema{ 2042 Type: TypeInt, 2043 }, 2044 2045 "a": &Schema{ 2046 Type: TypeList, 2047 Elem: &Schema{Type: TypeInt}, 2048 }, 2049 2050 "b": &Schema{ 2051 Type: TypeList, 2052 Elem: &Schema{Type: TypeInt}, 2053 }, 2054 }, 2055 }, 2056 Set: func(a interface{}) int { 2057 m := a.(map[string]interface{}) 2058 return m["order"].(int) 2059 }, 2060 }, 2061 }, 2062 2063 State: &terraform.InstanceState{ 2064 Attributes: map[string]string{ 2065 "ports.#": "2", 2066 "ports.10.order": "10", 2067 "ports.10.a.#": "1", 2068 "ports.10.a.0": "80", 2069 "ports.20.order": "20", 2070 "ports.20.b.#": "1", 2071 "ports.20.b.0": "100", 2072 }, 2073 }, 2074 2075 Set: map[string]interface{}{ 2076 "ports": []interface{}{ 2077 map[string]interface{}{ 2078 "order": 20, 2079 "b": []interface{}{100}, 2080 }, 2081 map[string]interface{}{ 2082 "order": 10, 2083 "a": []interface{}{80}, 2084 }, 2085 }, 2086 }, 2087 2088 Result: &terraform.InstanceState{ 2089 Attributes: map[string]string{ 2090 "ports.#": "2", 2091 "ports.10.order": "10", 2092 "ports.10.a.#": "1", 2093 "ports.10.a.0": "80", 2094 "ports.10.b.#": "0", 2095 "ports.20.order": "20", 2096 "ports.20.a.#": "0", 2097 "ports.20.b.#": "1", 2098 "ports.20.b.0": "100", 2099 }, 2100 }, 2101 }, 2102 2103 /* 2104 * PARTIAL STATES 2105 */ 2106 2107 // #12 Basic primitive 2108 { 2109 Schema: map[string]*Schema{ 2110 "availability_zone": &Schema{ 2111 Type: TypeString, 2112 Optional: true, 2113 Computed: true, 2114 ForceNew: true, 2115 }, 2116 }, 2117 2118 State: nil, 2119 2120 Diff: &terraform.InstanceDiff{ 2121 Attributes: map[string]*terraform.ResourceAttrDiff{ 2122 "availability_zone": &terraform.ResourceAttrDiff{ 2123 Old: "", 2124 New: "foo", 2125 RequiresNew: true, 2126 }, 2127 }, 2128 }, 2129 2130 Partial: []string{}, 2131 2132 Result: &terraform.InstanceState{ 2133 Attributes: map[string]string{}, 2134 }, 2135 }, 2136 2137 // #13 List 2138 { 2139 Schema: map[string]*Schema{ 2140 "ports": &Schema{ 2141 Type: TypeList, 2142 Required: true, 2143 Elem: &Schema{Type: TypeInt}, 2144 }, 2145 }, 2146 2147 State: &terraform.InstanceState{ 2148 Attributes: map[string]string{ 2149 "ports.#": "1", 2150 "ports.0": "80", 2151 }, 2152 }, 2153 2154 Diff: &terraform.InstanceDiff{ 2155 Attributes: map[string]*terraform.ResourceAttrDiff{ 2156 "ports.#": &terraform.ResourceAttrDiff{ 2157 Old: "1", 2158 New: "2", 2159 }, 2160 "ports.1": &terraform.ResourceAttrDiff{ 2161 Old: "", 2162 New: "100", 2163 }, 2164 }, 2165 }, 2166 2167 Partial: []string{}, 2168 2169 Result: &terraform.InstanceState{ 2170 Attributes: map[string]string{ 2171 "ports.#": "1", 2172 "ports.0": "80", 2173 }, 2174 }, 2175 }, 2176 2177 // #14 2178 { 2179 Schema: map[string]*Schema{ 2180 "ports": &Schema{ 2181 Type: TypeList, 2182 Optional: true, 2183 Computed: true, 2184 Elem: &Schema{Type: TypeInt}, 2185 }, 2186 }, 2187 2188 State: nil, 2189 2190 Diff: &terraform.InstanceDiff{ 2191 Attributes: map[string]*terraform.ResourceAttrDiff{ 2192 "ports.#": &terraform.ResourceAttrDiff{ 2193 Old: "", 2194 NewComputed: true, 2195 }, 2196 }, 2197 }, 2198 2199 Partial: []string{}, 2200 2201 Set: map[string]interface{}{ 2202 "ports": []interface{}{}, 2203 }, 2204 2205 Result: &terraform.InstanceState{ 2206 Attributes: map[string]string{}, 2207 }, 2208 }, 2209 2210 // #15 List of resources 2211 { 2212 Schema: map[string]*Schema{ 2213 "ingress": &Schema{ 2214 Type: TypeList, 2215 Required: true, 2216 Elem: &Resource{ 2217 Schema: map[string]*Schema{ 2218 "from": &Schema{ 2219 Type: TypeInt, 2220 Required: true, 2221 }, 2222 }, 2223 }, 2224 }, 2225 }, 2226 2227 State: &terraform.InstanceState{ 2228 Attributes: map[string]string{ 2229 "ingress.#": "1", 2230 "ingress.0.from": "80", 2231 }, 2232 }, 2233 2234 Diff: &terraform.InstanceDiff{ 2235 Attributes: map[string]*terraform.ResourceAttrDiff{ 2236 "ingress.#": &terraform.ResourceAttrDiff{ 2237 Old: "1", 2238 New: "2", 2239 }, 2240 "ingress.0.from": &terraform.ResourceAttrDiff{ 2241 Old: "80", 2242 New: "150", 2243 }, 2244 "ingress.1.from": &terraform.ResourceAttrDiff{ 2245 Old: "", 2246 New: "100", 2247 }, 2248 }, 2249 }, 2250 2251 Partial: []string{}, 2252 2253 Result: &terraform.InstanceState{ 2254 Attributes: map[string]string{ 2255 "ingress.#": "1", 2256 "ingress.0.from": "80", 2257 }, 2258 }, 2259 }, 2260 2261 // #16 List of maps 2262 { 2263 Schema: map[string]*Schema{ 2264 "config_vars": &Schema{ 2265 Type: TypeList, 2266 Optional: true, 2267 Computed: true, 2268 Elem: &Schema{ 2269 Type: TypeMap, 2270 }, 2271 }, 2272 }, 2273 2274 State: &terraform.InstanceState{ 2275 Attributes: map[string]string{ 2276 "config_vars.#": "2", 2277 "config_vars.0.foo": "bar", 2278 "config_vars.0.bar": "bar", 2279 "config_vars.1.bar": "baz", 2280 }, 2281 }, 2282 2283 Diff: &terraform.InstanceDiff{ 2284 Attributes: map[string]*terraform.ResourceAttrDiff{ 2285 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 2286 NewRemoved: true, 2287 }, 2288 }, 2289 }, 2290 2291 Set: map[string]interface{}{ 2292 "config_vars": []map[string]interface{}{ 2293 map[string]interface{}{ 2294 "foo": "bar", 2295 }, 2296 map[string]interface{}{ 2297 "baz": "bang", 2298 }, 2299 }, 2300 }, 2301 2302 Partial: []string{}, 2303 2304 Result: &terraform.InstanceState{ 2305 Attributes: map[string]string{ 2306 // TODO: broken, shouldn't bar be removed? 2307 "config_vars.#": "2", 2308 "config_vars.0.#": "2", 2309 "config_vars.0.foo": "bar", 2310 "config_vars.0.bar": "bar", 2311 "config_vars.1.#": "1", 2312 "config_vars.1.bar": "baz", 2313 }, 2314 }, 2315 }, 2316 2317 // #17 Sets 2318 { 2319 Schema: map[string]*Schema{ 2320 "ports": &Schema{ 2321 Type: TypeSet, 2322 Optional: true, 2323 Computed: true, 2324 Elem: &Schema{Type: TypeInt}, 2325 Set: func(a interface{}) int { 2326 return a.(int) 2327 }, 2328 }, 2329 }, 2330 2331 State: &terraform.InstanceState{ 2332 Attributes: map[string]string{ 2333 "ports.#": "3", 2334 "ports.100": "100", 2335 "ports.80": "80", 2336 "ports.81": "81", 2337 }, 2338 }, 2339 2340 Diff: &terraform.InstanceDiff{ 2341 Attributes: map[string]*terraform.ResourceAttrDiff{ 2342 "ports.120": &terraform.ResourceAttrDiff{ 2343 New: "120", 2344 }, 2345 }, 2346 }, 2347 2348 Partial: []string{}, 2349 2350 Result: &terraform.InstanceState{ 2351 Attributes: map[string]string{ 2352 "ports.#": "3", 2353 "ports.80": "80", 2354 "ports.81": "81", 2355 "ports.100": "100", 2356 }, 2357 }, 2358 }, 2359 2360 // #18 2361 { 2362 Schema: map[string]*Schema{ 2363 "ports": &Schema{ 2364 Type: TypeSet, 2365 Optional: true, 2366 Computed: true, 2367 Elem: &Schema{Type: TypeInt}, 2368 Set: func(a interface{}) int { 2369 return a.(int) 2370 }, 2371 }, 2372 }, 2373 2374 State: nil, 2375 2376 Diff: &terraform.InstanceDiff{ 2377 Attributes: map[string]*terraform.ResourceAttrDiff{ 2378 "ports.#": &terraform.ResourceAttrDiff{ 2379 Old: "", 2380 NewComputed: true, 2381 }, 2382 }, 2383 }, 2384 2385 Partial: []string{}, 2386 2387 Result: &terraform.InstanceState{ 2388 Attributes: map[string]string{}, 2389 }, 2390 }, 2391 2392 // #19 Maps 2393 { 2394 Schema: map[string]*Schema{ 2395 "tags": &Schema{ 2396 Type: TypeMap, 2397 Optional: true, 2398 Computed: true, 2399 }, 2400 }, 2401 2402 State: nil, 2403 2404 Diff: &terraform.InstanceDiff{ 2405 Attributes: map[string]*terraform.ResourceAttrDiff{ 2406 "tags.Name": &terraform.ResourceAttrDiff{ 2407 Old: "", 2408 New: "foo", 2409 }, 2410 }, 2411 }, 2412 2413 Result: &terraform.InstanceState{ 2414 Attributes: map[string]string{ 2415 "tags.#": "1", 2416 "tags.Name": "foo", 2417 }, 2418 }, 2419 }, 2420 2421 // #20 2422 { 2423 Schema: map[string]*Schema{ 2424 "tags": &Schema{ 2425 Type: TypeMap, 2426 Optional: true, 2427 Computed: true, 2428 }, 2429 }, 2430 2431 State: nil, 2432 2433 Diff: &terraform.InstanceDiff{ 2434 Attributes: map[string]*terraform.ResourceAttrDiff{ 2435 "tags.Name": &terraform.ResourceAttrDiff{ 2436 Old: "", 2437 New: "foo", 2438 }, 2439 }, 2440 }, 2441 2442 Set: map[string]interface{}{ 2443 "tags": map[string]string{}, 2444 }, 2445 2446 Result: &terraform.InstanceState{ 2447 Attributes: map[string]string{}, 2448 }, 2449 }, 2450 2451 // #21 2452 { 2453 Schema: map[string]*Schema{ 2454 "foo": &Schema{ 2455 Type: TypeString, 2456 Optional: true, 2457 Computed: true, 2458 }, 2459 }, 2460 2461 State: nil, 2462 2463 Diff: &terraform.InstanceDiff{ 2464 Attributes: map[string]*terraform.ResourceAttrDiff{ 2465 "foo": &terraform.ResourceAttrDiff{ 2466 NewComputed: true, 2467 }, 2468 }, 2469 }, 2470 2471 Result: &terraform.InstanceState{ 2472 Attributes: map[string]string{}, 2473 }, 2474 }, 2475 2476 // #22 2477 { 2478 Schema: map[string]*Schema{ 2479 "foo": &Schema{ 2480 Type: TypeString, 2481 Optional: true, 2482 Computed: true, 2483 }, 2484 }, 2485 2486 State: nil, 2487 2488 Diff: &terraform.InstanceDiff{ 2489 Attributes: map[string]*terraform.ResourceAttrDiff{ 2490 "foo": &terraform.ResourceAttrDiff{ 2491 NewComputed: true, 2492 }, 2493 }, 2494 }, 2495 2496 Set: map[string]interface{}{ 2497 "foo": "bar", 2498 }, 2499 2500 Result: &terraform.InstanceState{ 2501 Attributes: map[string]string{ 2502 "foo": "bar", 2503 }, 2504 }, 2505 }, 2506 2507 // #23 Set of maps 2508 { 2509 Schema: map[string]*Schema{ 2510 "ports": &Schema{ 2511 Type: TypeSet, 2512 Optional: true, 2513 Computed: true, 2514 Elem: &Resource{ 2515 Schema: map[string]*Schema{ 2516 "index": &Schema{Type: TypeInt}, 2517 "uuids": &Schema{Type: TypeMap}, 2518 }, 2519 }, 2520 Set: func(a interface{}) int { 2521 m := a.(map[string]interface{}) 2522 return m["index"].(int) 2523 }, 2524 }, 2525 }, 2526 2527 State: nil, 2528 2529 Diff: &terraform.InstanceDiff{ 2530 Attributes: map[string]*terraform.ResourceAttrDiff{ 2531 "ports.10.uuids.#": &terraform.ResourceAttrDiff{ 2532 NewComputed: true, 2533 }, 2534 }, 2535 }, 2536 2537 Set: map[string]interface{}{ 2538 "ports": []interface{}{ 2539 map[string]interface{}{ 2540 "index": 10, 2541 "uuids": map[string]interface{}{ 2542 "80": "value", 2543 }, 2544 }, 2545 }, 2546 }, 2547 2548 Result: &terraform.InstanceState{ 2549 Attributes: map[string]string{ 2550 "ports.#": "1", 2551 "ports.10.index": "10", 2552 "ports.10.uuids.#": "1", 2553 "ports.10.uuids.80": "value", 2554 }, 2555 }, 2556 }, 2557 2558 // #24 2559 { 2560 Schema: map[string]*Schema{ 2561 "ports": &Schema{ 2562 Type: TypeSet, 2563 Optional: true, 2564 Computed: true, 2565 Elem: &Schema{Type: TypeInt}, 2566 Set: func(a interface{}) int { 2567 return a.(int) 2568 }, 2569 }, 2570 }, 2571 2572 State: &terraform.InstanceState{ 2573 Attributes: map[string]string{ 2574 "ports.#": "3", 2575 "ports.100": "100", 2576 "ports.80": "80", 2577 "ports.81": "81", 2578 }, 2579 }, 2580 2581 Diff: &terraform.InstanceDiff{ 2582 Attributes: map[string]*terraform.ResourceAttrDiff{ 2583 "ports.#": &terraform.ResourceAttrDiff{ 2584 Old: "3", 2585 New: "0", 2586 }, 2587 }, 2588 }, 2589 2590 Result: &terraform.InstanceState{ 2591 Attributes: map[string]string{ 2592 "ports.#": "0", 2593 }, 2594 }, 2595 }, 2596 2597 // #25 2598 { 2599 Schema: map[string]*Schema{ 2600 "ports": &Schema{ 2601 Type: TypeSet, 2602 Optional: true, 2603 Computed: true, 2604 Elem: &Schema{Type: TypeInt}, 2605 Set: func(a interface{}) int { 2606 return a.(int) 2607 }, 2608 }, 2609 }, 2610 2611 State: nil, 2612 2613 Diff: nil, 2614 2615 Set: map[string]interface{}{ 2616 "ports": []interface{}{}, 2617 }, 2618 2619 Result: &terraform.InstanceState{ 2620 Attributes: map[string]string{ 2621 "ports.#": "0", 2622 }, 2623 }, 2624 }, 2625 2626 // #26 2627 { 2628 Schema: map[string]*Schema{ 2629 "ports": &Schema{ 2630 Type: TypeList, 2631 Optional: true, 2632 Computed: true, 2633 Elem: &Schema{Type: TypeInt}, 2634 }, 2635 }, 2636 2637 State: nil, 2638 2639 Diff: nil, 2640 2641 Set: map[string]interface{}{ 2642 "ports": []interface{}{}, 2643 }, 2644 2645 Result: &terraform.InstanceState{ 2646 Attributes: map[string]string{ 2647 "ports.#": "0", 2648 }, 2649 }, 2650 }, 2651 2652 // #27 Set lists 2653 { 2654 Schema: map[string]*Schema{ 2655 "ports": &Schema{ 2656 Type: TypeList, 2657 Optional: true, 2658 Computed: true, 2659 Elem: &Resource{ 2660 Schema: map[string]*Schema{ 2661 "index": &Schema{Type: TypeInt}, 2662 "uuids": &Schema{Type: TypeMap}, 2663 }, 2664 }, 2665 }, 2666 }, 2667 2668 State: nil, 2669 2670 Diff: &terraform.InstanceDiff{ 2671 Attributes: map[string]*terraform.ResourceAttrDiff{ 2672 "ports.#": &terraform.ResourceAttrDiff{ 2673 NewComputed: true, 2674 }, 2675 }, 2676 }, 2677 2678 Set: map[string]interface{}{ 2679 "ports": []interface{}{ 2680 map[string]interface{}{ 2681 "index": 10, 2682 "uuids": map[string]interface{}{ 2683 "80": "value", 2684 }, 2685 }, 2686 }, 2687 }, 2688 2689 Result: &terraform.InstanceState{ 2690 Attributes: map[string]string{ 2691 "ports.#": "1", 2692 "ports.0.index": "10", 2693 "ports.0.uuids.#": "1", 2694 "ports.0.uuids.80": "value", 2695 }, 2696 }, 2697 }, 2698 } 2699 2700 for i, tc := range cases { 2701 d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) 2702 if err != nil { 2703 t.Fatalf("err: %s", err) 2704 } 2705 2706 for k, v := range tc.Set { 2707 if err := d.Set(k, v); err != nil { 2708 t.Fatalf("%d err: %s", i, err) 2709 } 2710 } 2711 2712 // Set an ID so that the state returned is not nil 2713 idSet := false 2714 if d.Id() == "" { 2715 idSet = true 2716 d.SetId("foo") 2717 } 2718 2719 // If we have partial, then enable partial state mode. 2720 if tc.Partial != nil { 2721 d.Partial(true) 2722 for _, k := range tc.Partial { 2723 d.SetPartial(k) 2724 } 2725 } 2726 2727 actual := d.State() 2728 2729 // If we set an ID, then undo what we did so the comparison works 2730 if actual != nil && idSet { 2731 actual.ID = "" 2732 delete(actual.Attributes, "id") 2733 } 2734 2735 if !reflect.DeepEqual(actual, tc.Result) { 2736 t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) 2737 } 2738 } 2739 } 2740 2741 func TestResourceDataSetConnInfo(t *testing.T) { 2742 d := &ResourceData{} 2743 d.SetId("foo") 2744 d.SetConnInfo(map[string]string{ 2745 "foo": "bar", 2746 }) 2747 2748 expected := map[string]string{ 2749 "foo": "bar", 2750 } 2751 2752 actual := d.State() 2753 if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) { 2754 t.Fatalf("bad: %#v", actual) 2755 } 2756 } 2757 2758 func TestResourceDataSetId(t *testing.T) { 2759 d := &ResourceData{} 2760 d.SetId("foo") 2761 2762 actual := d.State() 2763 if actual.ID != "foo" { 2764 t.Fatalf("bad: %#v", actual) 2765 } 2766 } 2767 2768 func TestResourceDataSetId_clear(t *testing.T) { 2769 d := &ResourceData{ 2770 state: &terraform.InstanceState{ID: "bar"}, 2771 } 2772 d.SetId("") 2773 2774 actual := d.State() 2775 if actual != nil { 2776 t.Fatalf("bad: %#v", actual) 2777 } 2778 } 2779 2780 func TestResourceDataSetId_override(t *testing.T) { 2781 d := &ResourceData{ 2782 state: &terraform.InstanceState{ID: "bar"}, 2783 } 2784 d.SetId("foo") 2785 2786 actual := d.State() 2787 if actual.ID != "foo" { 2788 t.Fatalf("bad: %#v", actual) 2789 } 2790 }