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