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