github.com/lamielle/terraform@v0.3.2-0.20141121070651-81f008ba53d5/helper/schema/schema_test.go (about) 1 package schema 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/hashicorp/terraform/config" 8 "github.com/hashicorp/terraform/terraform" 9 ) 10 11 func TestSchemaMap_Diff(t *testing.T) { 12 cases := []struct { 13 Schema map[string]*Schema 14 State *terraform.InstanceState 15 Config map[string]interface{} 16 ConfigVariables map[string]string 17 Diff *terraform.InstanceDiff 18 Err bool 19 }{ 20 /* 21 * String decode 22 */ 23 24 { 25 Schema: map[string]*Schema{ 26 "availability_zone": &Schema{ 27 Type: TypeString, 28 Optional: true, 29 Computed: true, 30 ForceNew: true, 31 }, 32 }, 33 34 State: nil, 35 36 Config: map[string]interface{}{ 37 "availability_zone": "foo", 38 }, 39 40 Diff: &terraform.InstanceDiff{ 41 Attributes: map[string]*terraform.ResourceAttrDiff{ 42 "availability_zone": &terraform.ResourceAttrDiff{ 43 Old: "", 44 New: "foo", 45 RequiresNew: true, 46 }, 47 }, 48 }, 49 50 Err: false, 51 }, 52 53 { 54 Schema: map[string]*Schema{ 55 "availability_zone": &Schema{ 56 Type: TypeString, 57 Optional: true, 58 Computed: true, 59 ForceNew: true, 60 }, 61 }, 62 63 State: nil, 64 65 Config: map[string]interface{}{}, 66 67 Diff: &terraform.InstanceDiff{ 68 Attributes: map[string]*terraform.ResourceAttrDiff{ 69 "availability_zone": &terraform.ResourceAttrDiff{ 70 Old: "", 71 NewComputed: true, 72 RequiresNew: true, 73 }, 74 }, 75 }, 76 77 Err: false, 78 }, 79 80 { 81 Schema: map[string]*Schema{ 82 "availability_zone": &Schema{ 83 Type: TypeString, 84 Optional: true, 85 Computed: true, 86 ForceNew: true, 87 }, 88 }, 89 90 State: &terraform.InstanceState{ 91 ID: "foo", 92 }, 93 94 Config: map[string]interface{}{}, 95 96 Diff: nil, 97 98 Err: false, 99 }, 100 101 // Computed, but set in config 102 { 103 Schema: map[string]*Schema{ 104 "availability_zone": &Schema{ 105 Type: TypeString, 106 Optional: true, 107 Computed: true, 108 }, 109 }, 110 111 State: &terraform.InstanceState{ 112 Attributes: map[string]string{ 113 "availability_zone": "foo", 114 }, 115 }, 116 117 Config: map[string]interface{}{ 118 "availability_zone": "bar", 119 }, 120 121 Diff: &terraform.InstanceDiff{ 122 Attributes: map[string]*terraform.ResourceAttrDiff{ 123 "availability_zone": &terraform.ResourceAttrDiff{ 124 Old: "foo", 125 New: "bar", 126 }, 127 }, 128 }, 129 130 Err: false, 131 }, 132 133 // Default 134 { 135 Schema: map[string]*Schema{ 136 "availability_zone": &Schema{ 137 Type: TypeString, 138 Optional: true, 139 Default: "foo", 140 }, 141 }, 142 143 State: nil, 144 145 Config: nil, 146 147 Diff: &terraform.InstanceDiff{ 148 Attributes: map[string]*terraform.ResourceAttrDiff{ 149 "availability_zone": &terraform.ResourceAttrDiff{ 150 Old: "", 151 New: "foo", 152 }, 153 }, 154 }, 155 156 Err: false, 157 }, 158 159 // DefaultFunc, value 160 { 161 Schema: map[string]*Schema{ 162 "availability_zone": &Schema{ 163 Type: TypeString, 164 Optional: true, 165 DefaultFunc: func() (interface{}, error) { 166 return "foo", nil 167 }, 168 }, 169 }, 170 171 State: nil, 172 173 Config: nil, 174 175 Diff: &terraform.InstanceDiff{ 176 Attributes: map[string]*terraform.ResourceAttrDiff{ 177 "availability_zone": &terraform.ResourceAttrDiff{ 178 Old: "", 179 New: "foo", 180 }, 181 }, 182 }, 183 184 Err: false, 185 }, 186 187 // DefaultFunc, configuration set 188 { 189 Schema: map[string]*Schema{ 190 "availability_zone": &Schema{ 191 Type: TypeString, 192 Optional: true, 193 DefaultFunc: func() (interface{}, error) { 194 return "foo", nil 195 }, 196 }, 197 }, 198 199 State: nil, 200 201 Config: map[string]interface{}{ 202 "availability_zone": "bar", 203 }, 204 205 Diff: &terraform.InstanceDiff{ 206 Attributes: map[string]*terraform.ResourceAttrDiff{ 207 "availability_zone": &terraform.ResourceAttrDiff{ 208 Old: "", 209 New: "bar", 210 }, 211 }, 212 }, 213 214 Err: false, 215 }, 216 217 // String with StateFunc 218 { 219 Schema: map[string]*Schema{ 220 "availability_zone": &Schema{ 221 Type: TypeString, 222 Optional: true, 223 Computed: true, 224 StateFunc: func(a interface{}) string { 225 return a.(string) + "!" 226 }, 227 }, 228 }, 229 230 State: nil, 231 232 Config: map[string]interface{}{ 233 "availability_zone": "foo", 234 }, 235 236 Diff: &terraform.InstanceDiff{ 237 Attributes: map[string]*terraform.ResourceAttrDiff{ 238 "availability_zone": &terraform.ResourceAttrDiff{ 239 Old: "", 240 New: "foo!", 241 NewExtra: "foo", 242 }, 243 }, 244 }, 245 246 Err: false, 247 }, 248 249 // Variable (just checking) 250 { 251 Schema: map[string]*Schema{ 252 "availability_zone": &Schema{ 253 Type: TypeString, 254 Optional: true, 255 }, 256 }, 257 258 State: nil, 259 260 Config: map[string]interface{}{ 261 "availability_zone": "${var.foo}", 262 }, 263 264 ConfigVariables: map[string]string{ 265 "var.foo": "bar", 266 }, 267 268 Diff: &terraform.InstanceDiff{ 269 Attributes: map[string]*terraform.ResourceAttrDiff{ 270 "availability_zone": &terraform.ResourceAttrDiff{ 271 Old: "", 272 New: "bar", 273 }, 274 }, 275 }, 276 277 Err: false, 278 }, 279 280 // Variable computed 281 { 282 Schema: map[string]*Schema{ 283 "availability_zone": &Schema{ 284 Type: TypeString, 285 Optional: true, 286 }, 287 }, 288 289 State: nil, 290 291 Config: map[string]interface{}{ 292 "availability_zone": "${var.foo}", 293 }, 294 295 ConfigVariables: map[string]string{ 296 "var.foo": config.UnknownVariableValue, 297 }, 298 299 Diff: &terraform.InstanceDiff{ 300 Attributes: map[string]*terraform.ResourceAttrDiff{ 301 "availability_zone": &terraform.ResourceAttrDiff{ 302 Old: "", 303 New: "${var.foo}", 304 }, 305 }, 306 }, 307 308 Err: false, 309 }, 310 311 /* 312 * Int decode 313 */ 314 315 { 316 Schema: map[string]*Schema{ 317 "port": &Schema{ 318 Type: TypeInt, 319 Optional: true, 320 Computed: true, 321 ForceNew: true, 322 }, 323 }, 324 325 State: nil, 326 327 Config: map[string]interface{}{ 328 "port": 27, 329 }, 330 331 Diff: &terraform.InstanceDiff{ 332 Attributes: map[string]*terraform.ResourceAttrDiff{ 333 "port": &terraform.ResourceAttrDiff{ 334 Old: "", 335 New: "27", 336 RequiresNew: true, 337 }, 338 }, 339 }, 340 341 Err: false, 342 }, 343 344 /* 345 * Bool decode 346 */ 347 348 { 349 Schema: map[string]*Schema{ 350 "port": &Schema{ 351 Type: TypeBool, 352 Optional: true, 353 Computed: true, 354 ForceNew: true, 355 }, 356 }, 357 358 State: nil, 359 360 Config: map[string]interface{}{ 361 "port": false, 362 }, 363 364 Diff: &terraform.InstanceDiff{ 365 Attributes: map[string]*terraform.ResourceAttrDiff{ 366 "port": &terraform.ResourceAttrDiff{ 367 Old: "", 368 New: "0", 369 RequiresNew: true, 370 }, 371 }, 372 }, 373 374 Err: false, 375 }, 376 377 /* 378 * Bool 379 */ 380 { 381 Schema: map[string]*Schema{ 382 "delete": &Schema{ 383 Type: TypeBool, 384 Optional: true, 385 Default: false, 386 }, 387 }, 388 389 State: &terraform.InstanceState{ 390 Attributes: map[string]string{ 391 "delete": "false", 392 }, 393 }, 394 395 Config: nil, 396 397 Diff: nil, 398 399 Err: false, 400 }, 401 402 /* 403 * List decode 404 */ 405 406 { 407 Schema: map[string]*Schema{ 408 "ports": &Schema{ 409 Type: TypeList, 410 Required: true, 411 Elem: &Schema{Type: TypeInt}, 412 }, 413 }, 414 415 State: nil, 416 417 Config: map[string]interface{}{ 418 "ports": []interface{}{1, 2, 5}, 419 }, 420 421 Diff: &terraform.InstanceDiff{ 422 Attributes: map[string]*terraform.ResourceAttrDiff{ 423 "ports.#": &terraform.ResourceAttrDiff{ 424 Old: "0", 425 New: "3", 426 }, 427 "ports.0": &terraform.ResourceAttrDiff{ 428 Old: "", 429 New: "1", 430 }, 431 "ports.1": &terraform.ResourceAttrDiff{ 432 Old: "", 433 New: "2", 434 }, 435 "ports.2": &terraform.ResourceAttrDiff{ 436 Old: "", 437 New: "5", 438 }, 439 }, 440 }, 441 442 Err: false, 443 }, 444 445 { 446 Schema: map[string]*Schema{ 447 "ports": &Schema{ 448 Type: TypeList, 449 Required: true, 450 Elem: &Schema{Type: TypeInt}, 451 }, 452 }, 453 454 State: nil, 455 456 Config: map[string]interface{}{ 457 "ports": []interface{}{1, "${var.foo}"}, 458 }, 459 460 ConfigVariables: map[string]string{ 461 "var.foo": "2" + config.InterpSplitDelim + "5", 462 }, 463 464 Diff: &terraform.InstanceDiff{ 465 Attributes: map[string]*terraform.ResourceAttrDiff{ 466 "ports.#": &terraform.ResourceAttrDiff{ 467 Old: "0", 468 New: "3", 469 }, 470 "ports.0": &terraform.ResourceAttrDiff{ 471 Old: "", 472 New: "1", 473 }, 474 "ports.1": &terraform.ResourceAttrDiff{ 475 Old: "", 476 New: "2", 477 }, 478 "ports.2": &terraform.ResourceAttrDiff{ 479 Old: "", 480 New: "5", 481 }, 482 }, 483 }, 484 485 Err: false, 486 }, 487 488 { 489 Schema: map[string]*Schema{ 490 "ports": &Schema{ 491 Type: TypeList, 492 Required: true, 493 Elem: &Schema{Type: TypeInt}, 494 }, 495 }, 496 497 State: nil, 498 499 Config: map[string]interface{}{ 500 "ports": []interface{}{1, "${var.foo}"}, 501 }, 502 503 ConfigVariables: map[string]string{ 504 "var.foo": config.UnknownVariableValue + 505 config.InterpSplitDelim + "5", 506 }, 507 508 Diff: &terraform.InstanceDiff{ 509 Attributes: map[string]*terraform.ResourceAttrDiff{ 510 "ports.#": &terraform.ResourceAttrDiff{ 511 Old: "0", 512 New: "", 513 NewComputed: true, 514 }, 515 }, 516 }, 517 518 Err: false, 519 }, 520 521 { 522 Schema: map[string]*Schema{ 523 "ports": &Schema{ 524 Type: TypeList, 525 Required: true, 526 Elem: &Schema{Type: TypeInt}, 527 }, 528 }, 529 530 State: &terraform.InstanceState{ 531 Attributes: map[string]string{ 532 "ports.#": "3", 533 "ports.0": "1", 534 "ports.1": "2", 535 "ports.2": "5", 536 }, 537 }, 538 539 Config: map[string]interface{}{ 540 "ports": []interface{}{1, 2, 5}, 541 }, 542 543 Diff: nil, 544 545 Err: false, 546 }, 547 548 { 549 Schema: map[string]*Schema{ 550 "ports": &Schema{ 551 Type: TypeList, 552 Required: true, 553 Elem: &Schema{Type: TypeInt}, 554 }, 555 }, 556 557 State: &terraform.InstanceState{ 558 Attributes: map[string]string{ 559 "ports.#": "2", 560 "ports.0": "1", 561 "ports.1": "2", 562 }, 563 }, 564 565 Config: map[string]interface{}{ 566 "ports": []interface{}{1, 2, 5}, 567 }, 568 569 Diff: &terraform.InstanceDiff{ 570 Attributes: map[string]*terraform.ResourceAttrDiff{ 571 "ports.#": &terraform.ResourceAttrDiff{ 572 Old: "2", 573 New: "3", 574 }, 575 "ports.2": &terraform.ResourceAttrDiff{ 576 Old: "", 577 New: "5", 578 }, 579 }, 580 }, 581 582 Err: false, 583 }, 584 585 { 586 Schema: map[string]*Schema{ 587 "ports": &Schema{ 588 Type: TypeList, 589 Required: true, 590 Elem: &Schema{Type: TypeInt}, 591 ForceNew: true, 592 }, 593 }, 594 595 State: nil, 596 597 Config: map[string]interface{}{ 598 "ports": []interface{}{1, 2, 5}, 599 }, 600 601 Diff: &terraform.InstanceDiff{ 602 Attributes: map[string]*terraform.ResourceAttrDiff{ 603 "ports.#": &terraform.ResourceAttrDiff{ 604 Old: "0", 605 New: "3", 606 RequiresNew: true, 607 }, 608 "ports.0": &terraform.ResourceAttrDiff{ 609 Old: "", 610 New: "1", 611 RequiresNew: true, 612 }, 613 "ports.1": &terraform.ResourceAttrDiff{ 614 Old: "", 615 New: "2", 616 RequiresNew: true, 617 }, 618 "ports.2": &terraform.ResourceAttrDiff{ 619 Old: "", 620 New: "5", 621 RequiresNew: true, 622 }, 623 }, 624 }, 625 626 Err: false, 627 }, 628 629 { 630 Schema: map[string]*Schema{ 631 "ports": &Schema{ 632 Type: TypeList, 633 Optional: true, 634 Computed: true, 635 Elem: &Schema{Type: TypeInt}, 636 }, 637 }, 638 639 State: nil, 640 641 Config: map[string]interface{}{}, 642 643 Diff: &terraform.InstanceDiff{ 644 Attributes: map[string]*terraform.ResourceAttrDiff{ 645 "ports.#": &terraform.ResourceAttrDiff{ 646 Old: "", 647 NewComputed: true, 648 }, 649 }, 650 }, 651 652 Err: false, 653 }, 654 655 /* 656 * Set 657 */ 658 659 { 660 Schema: map[string]*Schema{ 661 "ports": &Schema{ 662 Type: TypeSet, 663 Required: true, 664 Elem: &Schema{Type: TypeInt}, 665 Set: func(a interface{}) int { 666 return a.(int) 667 }, 668 }, 669 }, 670 671 State: nil, 672 673 Config: map[string]interface{}{ 674 "ports": []interface{}{5, 2, 1}, 675 }, 676 677 Diff: &terraform.InstanceDiff{ 678 Attributes: map[string]*terraform.ResourceAttrDiff{ 679 "ports.#": &terraform.ResourceAttrDiff{ 680 Old: "0", 681 New: "3", 682 }, 683 "ports.0": &terraform.ResourceAttrDiff{ 684 Old: "", 685 New: "1", 686 }, 687 "ports.1": &terraform.ResourceAttrDiff{ 688 Old: "", 689 New: "2", 690 }, 691 "ports.2": &terraform.ResourceAttrDiff{ 692 Old: "", 693 New: "5", 694 }, 695 }, 696 }, 697 698 Err: false, 699 }, 700 701 { 702 Schema: map[string]*Schema{ 703 "ports": &Schema{ 704 Type: TypeSet, 705 Computed: true, 706 Required: true, 707 Elem: &Schema{Type: TypeInt}, 708 Set: func(a interface{}) int { 709 return a.(int) 710 }, 711 }, 712 }, 713 714 State: &terraform.InstanceState{ 715 Attributes: map[string]string{ 716 "ports.#": "0", 717 }, 718 }, 719 720 Config: nil, 721 722 Diff: nil, 723 724 Err: false, 725 }, 726 727 { 728 Schema: map[string]*Schema{ 729 "ports": &Schema{ 730 Type: TypeSet, 731 Optional: true, 732 Computed: true, 733 Elem: &Schema{Type: TypeInt}, 734 Set: func(a interface{}) int { 735 return a.(int) 736 }, 737 }, 738 }, 739 740 State: nil, 741 742 Config: nil, 743 744 Diff: &terraform.InstanceDiff{ 745 Attributes: map[string]*terraform.ResourceAttrDiff{ 746 "ports.#": &terraform.ResourceAttrDiff{ 747 Old: "", 748 NewComputed: true, 749 }, 750 }, 751 }, 752 753 Err: false, 754 }, 755 756 { 757 Schema: map[string]*Schema{ 758 "ports": &Schema{ 759 Type: TypeSet, 760 Required: true, 761 Elem: &Schema{Type: TypeInt}, 762 Set: func(a interface{}) int { 763 return a.(int) 764 }, 765 }, 766 }, 767 768 State: nil, 769 770 Config: map[string]interface{}{ 771 "ports": []interface{}{"${var.foo}", 1}, 772 }, 773 774 ConfigVariables: map[string]string{ 775 "var.foo": "2" + config.InterpSplitDelim + "5", 776 }, 777 778 Diff: &terraform.InstanceDiff{ 779 Attributes: map[string]*terraform.ResourceAttrDiff{ 780 "ports.#": &terraform.ResourceAttrDiff{ 781 Old: "0", 782 New: "3", 783 }, 784 "ports.0": &terraform.ResourceAttrDiff{ 785 Old: "", 786 New: "1", 787 }, 788 "ports.1": &terraform.ResourceAttrDiff{ 789 Old: "", 790 New: "2", 791 }, 792 "ports.2": &terraform.ResourceAttrDiff{ 793 Old: "", 794 New: "5", 795 }, 796 }, 797 }, 798 799 Err: false, 800 }, 801 802 { 803 Schema: map[string]*Schema{ 804 "ports": &Schema{ 805 Type: TypeSet, 806 Required: true, 807 Elem: &Schema{Type: TypeInt}, 808 Set: func(a interface{}) int { 809 return a.(int) 810 }, 811 }, 812 }, 813 814 State: nil, 815 816 Config: map[string]interface{}{ 817 "ports": []interface{}{1, "${var.foo}"}, 818 }, 819 820 ConfigVariables: map[string]string{ 821 "var.foo": config.UnknownVariableValue + 822 config.InterpSplitDelim + "5", 823 }, 824 825 Diff: &terraform.InstanceDiff{ 826 Attributes: map[string]*terraform.ResourceAttrDiff{ 827 "ports.#": &terraform.ResourceAttrDiff{ 828 Old: "0", 829 New: "", 830 NewComputed: true, 831 }, 832 }, 833 }, 834 835 Err: false, 836 }, 837 838 { 839 Schema: map[string]*Schema{ 840 "ports": &Schema{ 841 Type: TypeSet, 842 Required: true, 843 Elem: &Schema{Type: TypeInt}, 844 Set: func(a interface{}) int { 845 return a.(int) 846 }, 847 }, 848 }, 849 850 State: &terraform.InstanceState{ 851 Attributes: map[string]string{ 852 "ports.#": "2", 853 "ports.0": "2", 854 "ports.1": "1", 855 }, 856 }, 857 858 Config: map[string]interface{}{ 859 "ports": []interface{}{5, 2, 1}, 860 }, 861 862 Diff: &terraform.InstanceDiff{ 863 Attributes: map[string]*terraform.ResourceAttrDiff{ 864 "ports.#": &terraform.ResourceAttrDiff{ 865 Old: "2", 866 New: "3", 867 }, 868 "ports.0": &terraform.ResourceAttrDiff{ 869 Old: "1", 870 New: "1", 871 }, 872 "ports.1": &terraform.ResourceAttrDiff{ 873 Old: "2", 874 New: "2", 875 }, 876 "ports.2": &terraform.ResourceAttrDiff{ 877 Old: "", 878 New: "5", 879 }, 880 }, 881 }, 882 883 Err: false, 884 }, 885 886 { 887 Schema: map[string]*Schema{ 888 "ports": &Schema{ 889 Type: TypeSet, 890 Required: true, 891 Elem: &Schema{Type: TypeInt}, 892 Set: func(a interface{}) int { 893 return a.(int) 894 }, 895 }, 896 }, 897 898 State: &terraform.InstanceState{ 899 Attributes: map[string]string{ 900 "ports.#": "2", 901 "ports.0": "2", 902 "ports.1": "1", 903 }, 904 }, 905 906 Config: map[string]interface{}{}, 907 908 Diff: &terraform.InstanceDiff{ 909 Attributes: map[string]*terraform.ResourceAttrDiff{ 910 "ports.#": &terraform.ResourceAttrDiff{ 911 Old: "2", 912 New: "0", 913 }, 914 "ports.0": &terraform.ResourceAttrDiff{ 915 Old: "1", 916 NewRemoved: true, 917 }, 918 "ports.1": &terraform.ResourceAttrDiff{ 919 Old: "2", 920 NewRemoved: true, 921 }, 922 }, 923 }, 924 925 Err: false, 926 }, 927 928 { 929 Schema: map[string]*Schema{ 930 "ports": &Schema{ 931 Type: TypeSet, 932 Optional: true, 933 Computed: true, 934 Elem: &Schema{Type: TypeInt}, 935 Set: func(v interface{}) int { return v.(int) }, 936 }, 937 }, 938 939 State: &terraform.InstanceState{ 940 Attributes: map[string]string{ 941 "availability_zone": "bar", 942 "ports.#": "1", 943 "ports.0": "80", 944 }, 945 }, 946 947 Config: map[string]interface{}{}, 948 949 Diff: nil, 950 951 Err: false, 952 }, 953 954 { 955 Schema: map[string]*Schema{ 956 "ingress": &Schema{ 957 Type: TypeSet, 958 Required: true, 959 Elem: &Resource{ 960 Schema: map[string]*Schema{ 961 "ports": &Schema{ 962 Type: TypeList, 963 Optional: true, 964 Elem: &Schema{Type: TypeInt}, 965 }, 966 }, 967 }, 968 Set: func(v interface{}) int { 969 m := v.(map[string]interface{}) 970 ps := m["ports"].([]interface{}) 971 result := 0 972 for _, p := range ps { 973 result += p.(int) 974 } 975 return result 976 }, 977 }, 978 }, 979 980 State: &terraform.InstanceState{ 981 Attributes: map[string]string{ 982 "ingress.#": "2", 983 "ingress.0.ports.#": "1", 984 "ingress.0.ports.0": "80", 985 "ingress.1.ports.#": "1", 986 "ingress.1.ports.0": "443", 987 }, 988 }, 989 990 Config: map[string]interface{}{ 991 "ingress": []interface{}{ 992 map[string]interface{}{ 993 "ports": []interface{}{443}, 994 }, 995 map[string]interface{}{ 996 "ports": []interface{}{80}, 997 }, 998 }, 999 }, 1000 1001 Diff: nil, 1002 1003 Err: false, 1004 }, 1005 1006 /* 1007 * List of structure decode 1008 */ 1009 1010 { 1011 Schema: map[string]*Schema{ 1012 "ingress": &Schema{ 1013 Type: TypeList, 1014 Required: true, 1015 Elem: &Resource{ 1016 Schema: map[string]*Schema{ 1017 "from": &Schema{ 1018 Type: TypeInt, 1019 Required: true, 1020 }, 1021 }, 1022 }, 1023 }, 1024 }, 1025 1026 State: nil, 1027 1028 Config: map[string]interface{}{ 1029 "ingress": []interface{}{ 1030 map[string]interface{}{ 1031 "from": 8080, 1032 }, 1033 }, 1034 }, 1035 1036 Diff: &terraform.InstanceDiff{ 1037 Attributes: map[string]*terraform.ResourceAttrDiff{ 1038 "ingress.#": &terraform.ResourceAttrDiff{ 1039 Old: "0", 1040 New: "1", 1041 }, 1042 "ingress.0.from": &terraform.ResourceAttrDiff{ 1043 Old: "", 1044 New: "8080", 1045 }, 1046 }, 1047 }, 1048 1049 Err: false, 1050 }, 1051 1052 /* 1053 * ComputedWhen 1054 */ 1055 1056 { 1057 Schema: map[string]*Schema{ 1058 "availability_zone": &Schema{ 1059 Type: TypeString, 1060 Computed: true, 1061 ComputedWhen: []string{"port"}, 1062 }, 1063 1064 "port": &Schema{ 1065 Type: TypeInt, 1066 Optional: true, 1067 }, 1068 }, 1069 1070 State: &terraform.InstanceState{ 1071 Attributes: map[string]string{ 1072 "availability_zone": "foo", 1073 "port": "80", 1074 }, 1075 }, 1076 1077 Config: map[string]interface{}{ 1078 "port": 80, 1079 }, 1080 1081 Diff: nil, 1082 1083 Err: false, 1084 }, 1085 1086 { 1087 Schema: map[string]*Schema{ 1088 "availability_zone": &Schema{ 1089 Type: TypeString, 1090 Computed: true, 1091 ComputedWhen: []string{"port"}, 1092 }, 1093 1094 "port": &Schema{ 1095 Type: TypeInt, 1096 Optional: true, 1097 }, 1098 }, 1099 1100 State: &terraform.InstanceState{ 1101 Attributes: map[string]string{ 1102 "port": "80", 1103 }, 1104 }, 1105 1106 Config: map[string]interface{}{ 1107 "port": 80, 1108 }, 1109 1110 Diff: &terraform.InstanceDiff{ 1111 Attributes: map[string]*terraform.ResourceAttrDiff{ 1112 "availability_zone": &terraform.ResourceAttrDiff{ 1113 NewComputed: true, 1114 }, 1115 }, 1116 }, 1117 1118 Err: false, 1119 }, 1120 1121 /* TODO 1122 { 1123 Schema: map[string]*Schema{ 1124 "availability_zone": &Schema{ 1125 Type: TypeString, 1126 Computed: true, 1127 ComputedWhen: []string{"port"}, 1128 }, 1129 1130 "port": &Schema{ 1131 Type: TypeInt, 1132 Optional: true, 1133 }, 1134 }, 1135 1136 State: &terraform.InstanceState{ 1137 Attributes: map[string]string{ 1138 "availability_zone": "foo", 1139 "port": "80", 1140 }, 1141 }, 1142 1143 Config: map[string]interface{}{ 1144 "port": 8080, 1145 }, 1146 1147 Diff: &terraform.ResourceDiff{ 1148 Attributes: map[string]*terraform.ResourceAttrDiff{ 1149 "availability_zone": &terraform.ResourceAttrDiff{ 1150 Old: "foo", 1151 NewComputed: true, 1152 }, 1153 "port": &terraform.ResourceAttrDiff{ 1154 Old: "80", 1155 New: "8080", 1156 }, 1157 }, 1158 }, 1159 1160 Err: false, 1161 }, 1162 */ 1163 1164 /* 1165 * Maps 1166 */ 1167 1168 { 1169 Schema: map[string]*Schema{ 1170 "config_vars": &Schema{ 1171 Type: TypeMap, 1172 }, 1173 }, 1174 1175 State: nil, 1176 1177 Config: map[string]interface{}{ 1178 "config_vars": []interface{}{ 1179 map[string]interface{}{ 1180 "bar": "baz", 1181 }, 1182 }, 1183 }, 1184 1185 Diff: &terraform.InstanceDiff{ 1186 Attributes: map[string]*terraform.ResourceAttrDiff{ 1187 "config_vars.bar": &terraform.ResourceAttrDiff{ 1188 Old: "", 1189 New: "baz", 1190 }, 1191 }, 1192 }, 1193 1194 Err: false, 1195 }, 1196 1197 { 1198 Schema: map[string]*Schema{ 1199 "config_vars": &Schema{ 1200 Type: TypeMap, 1201 }, 1202 }, 1203 1204 State: &terraform.InstanceState{ 1205 Attributes: map[string]string{ 1206 "config_vars.foo": "bar", 1207 }, 1208 }, 1209 1210 Config: map[string]interface{}{ 1211 "config_vars": []interface{}{ 1212 map[string]interface{}{ 1213 "bar": "baz", 1214 }, 1215 }, 1216 }, 1217 1218 Diff: &terraform.InstanceDiff{ 1219 Attributes: map[string]*terraform.ResourceAttrDiff{ 1220 "config_vars.foo": &terraform.ResourceAttrDiff{ 1221 Old: "bar", 1222 NewRemoved: true, 1223 }, 1224 "config_vars.bar": &terraform.ResourceAttrDiff{ 1225 Old: "", 1226 New: "baz", 1227 }, 1228 }, 1229 }, 1230 1231 Err: false, 1232 }, 1233 1234 { 1235 Schema: map[string]*Schema{ 1236 "vars": &Schema{ 1237 Type: TypeMap, 1238 Optional: true, 1239 Computed: true, 1240 }, 1241 }, 1242 1243 State: &terraform.InstanceState{ 1244 Attributes: map[string]string{ 1245 "vars.foo": "bar", 1246 }, 1247 }, 1248 1249 Config: map[string]interface{}{ 1250 "vars": []interface{}{ 1251 map[string]interface{}{ 1252 "bar": "baz", 1253 }, 1254 }, 1255 }, 1256 1257 Diff: &terraform.InstanceDiff{ 1258 Attributes: map[string]*terraform.ResourceAttrDiff{ 1259 "vars.foo": &terraform.ResourceAttrDiff{ 1260 Old: "bar", 1261 New: "", 1262 NewRemoved: true, 1263 }, 1264 "vars.bar": &terraform.ResourceAttrDiff{ 1265 Old: "", 1266 New: "baz", 1267 }, 1268 }, 1269 }, 1270 1271 Err: false, 1272 }, 1273 1274 { 1275 Schema: map[string]*Schema{ 1276 "vars": &Schema{ 1277 Type: TypeMap, 1278 Computed: true, 1279 }, 1280 }, 1281 1282 State: &terraform.InstanceState{ 1283 Attributes: map[string]string{ 1284 "vars.foo": "bar", 1285 }, 1286 }, 1287 1288 Config: nil, 1289 1290 Diff: nil, 1291 1292 Err: false, 1293 }, 1294 1295 { 1296 Schema: map[string]*Schema{ 1297 "config_vars": &Schema{ 1298 Type: TypeList, 1299 Elem: &Schema{Type: TypeMap}, 1300 }, 1301 }, 1302 1303 State: &terraform.InstanceState{ 1304 Attributes: map[string]string{ 1305 "config_vars.#": "1", 1306 "config_vars.0.foo": "bar", 1307 }, 1308 }, 1309 1310 Config: map[string]interface{}{ 1311 "config_vars": []interface{}{ 1312 map[string]interface{}{ 1313 "bar": "baz", 1314 }, 1315 }, 1316 }, 1317 1318 Diff: &terraform.InstanceDiff{ 1319 Attributes: map[string]*terraform.ResourceAttrDiff{ 1320 "config_vars.0.foo": &terraform.ResourceAttrDiff{ 1321 Old: "bar", 1322 NewRemoved: true, 1323 }, 1324 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1325 Old: "", 1326 New: "baz", 1327 }, 1328 }, 1329 }, 1330 1331 Err: false, 1332 }, 1333 1334 { 1335 Schema: map[string]*Schema{ 1336 "config_vars": &Schema{ 1337 Type: TypeList, 1338 Elem: &Schema{Type: TypeMap}, 1339 }, 1340 }, 1341 1342 State: &terraform.InstanceState{ 1343 Attributes: map[string]string{ 1344 "config_vars.#": "1", 1345 "config_vars.0.foo": "bar", 1346 "config_vars.0.bar": "baz", 1347 }, 1348 }, 1349 1350 Config: map[string]interface{}{}, 1351 1352 Diff: &terraform.InstanceDiff{ 1353 Attributes: map[string]*terraform.ResourceAttrDiff{ 1354 "config_vars.#": &terraform.ResourceAttrDiff{ 1355 Old: "1", 1356 New: "0", 1357 }, 1358 "config_vars.0.foo": &terraform.ResourceAttrDiff{ 1359 Old: "bar", 1360 NewRemoved: true, 1361 }, 1362 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1363 Old: "baz", 1364 NewRemoved: true, 1365 }, 1366 }, 1367 }, 1368 1369 Err: false, 1370 }, 1371 1372 /* 1373 * ForceNews 1374 */ 1375 1376 { 1377 Schema: map[string]*Schema{ 1378 "availability_zone": &Schema{ 1379 Type: TypeString, 1380 Optional: true, 1381 ForceNew: true, 1382 }, 1383 1384 "address": &Schema{ 1385 Type: TypeString, 1386 Optional: true, 1387 Computed: true, 1388 }, 1389 }, 1390 1391 State: &terraform.InstanceState{ 1392 Attributes: map[string]string{ 1393 "availability_zone": "bar", 1394 "address": "foo", 1395 }, 1396 }, 1397 1398 Config: map[string]interface{}{ 1399 "availability_zone": "foo", 1400 }, 1401 1402 Diff: &terraform.InstanceDiff{ 1403 Attributes: map[string]*terraform.ResourceAttrDiff{ 1404 "availability_zone": &terraform.ResourceAttrDiff{ 1405 Old: "bar", 1406 New: "foo", 1407 RequiresNew: true, 1408 }, 1409 1410 "address": &terraform.ResourceAttrDiff{ 1411 Old: "foo", 1412 New: "", 1413 NewComputed: true, 1414 }, 1415 }, 1416 }, 1417 1418 Err: false, 1419 }, 1420 1421 // Set 1422 { 1423 Schema: map[string]*Schema{ 1424 "availability_zone": &Schema{ 1425 Type: TypeString, 1426 Optional: true, 1427 ForceNew: true, 1428 }, 1429 1430 "ports": &Schema{ 1431 Type: TypeSet, 1432 Optional: true, 1433 Computed: true, 1434 Elem: &Schema{Type: TypeInt}, 1435 Set: func(v interface{}) int { return v.(int) }, 1436 }, 1437 }, 1438 1439 State: &terraform.InstanceState{ 1440 Attributes: map[string]string{ 1441 "availability_zone": "bar", 1442 "ports.#": "1", 1443 "ports.0": "80", 1444 }, 1445 }, 1446 1447 Config: map[string]interface{}{ 1448 "availability_zone": "foo", 1449 }, 1450 1451 Diff: &terraform.InstanceDiff{ 1452 Attributes: map[string]*terraform.ResourceAttrDiff{ 1453 "availability_zone": &terraform.ResourceAttrDiff{ 1454 Old: "bar", 1455 New: "foo", 1456 RequiresNew: true, 1457 }, 1458 1459 "ports.#": &terraform.ResourceAttrDiff{ 1460 Old: "1", 1461 New: "", 1462 NewComputed: true, 1463 }, 1464 }, 1465 }, 1466 1467 Err: false, 1468 }, 1469 } 1470 1471 for i, tc := range cases { 1472 c, err := config.NewRawConfig(tc.Config) 1473 if err != nil { 1474 t.Fatalf("err: %s", err) 1475 } 1476 1477 if len(tc.ConfigVariables) > 0 { 1478 if err := c.Interpolate(tc.ConfigVariables); err != nil { 1479 t.Fatalf("err: %s", err) 1480 } 1481 } 1482 1483 d, err := schemaMap(tc.Schema).Diff( 1484 tc.State, terraform.NewResourceConfig(c)) 1485 if (err != nil) != tc.Err { 1486 t.Fatalf("#%d err: %s", i, err) 1487 } 1488 1489 if !reflect.DeepEqual(tc.Diff, d) { 1490 t.Fatalf("#%d: bad:\n\n%#v", i, d) 1491 } 1492 } 1493 } 1494 1495 func TestSchemaMap_Input(t *testing.T) { 1496 cases := []struct { 1497 Schema map[string]*Schema 1498 Config map[string]interface{} 1499 Input map[string]string 1500 Result map[string]interface{} 1501 Err bool 1502 }{ 1503 /* 1504 * String decode 1505 */ 1506 1507 { 1508 Schema: map[string]*Schema{ 1509 "availability_zone": &Schema{ 1510 Type: TypeString, 1511 Optional: true, 1512 }, 1513 }, 1514 1515 Input: map[string]string{ 1516 "availability_zone": "foo", 1517 }, 1518 1519 Result: map[string]interface{}{ 1520 "availability_zone": "foo", 1521 }, 1522 1523 Err: false, 1524 }, 1525 1526 { 1527 Schema: map[string]*Schema{ 1528 "availability_zone": &Schema{ 1529 Type: TypeString, 1530 Optional: true, 1531 }, 1532 }, 1533 1534 Config: map[string]interface{}{ 1535 "availability_zone": "bar", 1536 }, 1537 1538 Input: map[string]string{ 1539 "availability_zone": "foo", 1540 }, 1541 1542 Result: map[string]interface{}{}, 1543 1544 Err: false, 1545 }, 1546 1547 { 1548 Schema: map[string]*Schema{ 1549 "availability_zone": &Schema{ 1550 Type: TypeString, 1551 Default: "foo", 1552 Optional: true, 1553 }, 1554 }, 1555 1556 Input: map[string]string{ 1557 "availability_zone": "bar", 1558 }, 1559 1560 Result: map[string]interface{}{}, 1561 1562 Err: false, 1563 }, 1564 1565 { 1566 Schema: map[string]*Schema{ 1567 "availability_zone": &Schema{ 1568 Type: TypeString, 1569 DefaultFunc: func() (interface{}, error) { 1570 return "foo", nil 1571 }, 1572 Optional: true, 1573 }, 1574 }, 1575 1576 Input: map[string]string{ 1577 "availability_zone": "bar", 1578 }, 1579 1580 Result: map[string]interface{}{}, 1581 1582 Err: false, 1583 }, 1584 1585 { 1586 Schema: map[string]*Schema{ 1587 "availability_zone": &Schema{ 1588 Type: TypeString, 1589 DefaultFunc: func() (interface{}, error) { 1590 return nil, nil 1591 }, 1592 Optional: true, 1593 }, 1594 }, 1595 1596 Input: map[string]string{ 1597 "availability_zone": "bar", 1598 }, 1599 1600 Result: map[string]interface{}{ 1601 "availability_zone": "bar", 1602 }, 1603 1604 Err: false, 1605 }, 1606 } 1607 1608 for i, tc := range cases { 1609 if tc.Config == nil { 1610 tc.Config = make(map[string]interface{}) 1611 } 1612 1613 c, err := config.NewRawConfig(tc.Config) 1614 if err != nil { 1615 t.Fatalf("err: %s", err) 1616 } 1617 1618 input := new(terraform.MockUIInput) 1619 input.InputReturnMap = tc.Input 1620 1621 rc := terraform.NewResourceConfig(c) 1622 rc.Config = make(map[string]interface{}) 1623 1624 actual, err := schemaMap(tc.Schema).Input(input, rc) 1625 if (err != nil) != tc.Err { 1626 t.Fatalf("#%d err: %s", i, err) 1627 } 1628 1629 if !reflect.DeepEqual(tc.Result, actual.Config) { 1630 t.Fatalf("#%d: bad:\n\n%#v", i, actual.Config) 1631 } 1632 } 1633 } 1634 1635 func TestSchemaMap_InternalValidate(t *testing.T) { 1636 cases := []struct { 1637 In map[string]*Schema 1638 Err bool 1639 }{ 1640 { 1641 nil, 1642 false, 1643 }, 1644 1645 // No optional and no required 1646 { 1647 map[string]*Schema{ 1648 "foo": &Schema{ 1649 Type: TypeInt, 1650 Optional: true, 1651 Required: true, 1652 }, 1653 }, 1654 true, 1655 }, 1656 1657 // No optional and no required 1658 { 1659 map[string]*Schema{ 1660 "foo": &Schema{ 1661 Type: TypeInt, 1662 }, 1663 }, 1664 true, 1665 }, 1666 1667 // Missing Type 1668 { 1669 map[string]*Schema{ 1670 "foo": &Schema{ 1671 Required: true, 1672 }, 1673 }, 1674 true, 1675 }, 1676 1677 // Required but computed 1678 { 1679 map[string]*Schema{ 1680 "foo": &Schema{ 1681 Type: TypeInt, 1682 Required: true, 1683 Computed: true, 1684 }, 1685 }, 1686 true, 1687 }, 1688 1689 // Looks good 1690 { 1691 map[string]*Schema{ 1692 "foo": &Schema{ 1693 Type: TypeString, 1694 Required: true, 1695 }, 1696 }, 1697 false, 1698 }, 1699 1700 // Computed but has default 1701 { 1702 map[string]*Schema{ 1703 "foo": &Schema{ 1704 Type: TypeInt, 1705 Optional: true, 1706 Computed: true, 1707 Default: "foo", 1708 }, 1709 }, 1710 true, 1711 }, 1712 1713 // Required but has default 1714 { 1715 map[string]*Schema{ 1716 "foo": &Schema{ 1717 Type: TypeInt, 1718 Optional: true, 1719 Required: true, 1720 Default: "foo", 1721 }, 1722 }, 1723 true, 1724 }, 1725 1726 // List element not set 1727 { 1728 map[string]*Schema{ 1729 "foo": &Schema{ 1730 Type: TypeList, 1731 }, 1732 }, 1733 true, 1734 }, 1735 1736 // List default 1737 { 1738 map[string]*Schema{ 1739 "foo": &Schema{ 1740 Type: TypeList, 1741 Elem: &Schema{Type: TypeInt}, 1742 Default: "foo", 1743 }, 1744 }, 1745 true, 1746 }, 1747 1748 // List element computed 1749 { 1750 map[string]*Schema{ 1751 "foo": &Schema{ 1752 Type: TypeList, 1753 Optional: true, 1754 Elem: &Schema{ 1755 Type: TypeInt, 1756 Computed: true, 1757 }, 1758 }, 1759 }, 1760 true, 1761 }, 1762 1763 // List element with Set set 1764 { 1765 map[string]*Schema{ 1766 "foo": &Schema{ 1767 Type: TypeList, 1768 Elem: &Schema{Type: TypeInt}, 1769 Set: func(interface{}) int { return 0 }, 1770 Optional: true, 1771 }, 1772 }, 1773 true, 1774 }, 1775 1776 // Set element with no Set set 1777 { 1778 map[string]*Schema{ 1779 "foo": &Schema{ 1780 Type: TypeSet, 1781 Elem: &Schema{Type: TypeInt}, 1782 Optional: true, 1783 }, 1784 }, 1785 true, 1786 }, 1787 1788 // Required but computed 1789 { 1790 map[string]*Schema{ 1791 "foo": &Schema{ 1792 Type: TypeInt, 1793 Required: true, 1794 ComputedWhen: []string{"foo"}, 1795 }, 1796 }, 1797 true, 1798 }, 1799 1800 // Sub-resource invalid 1801 { 1802 map[string]*Schema{ 1803 "foo": &Schema{ 1804 Type: TypeList, 1805 Optional: true, 1806 Elem: &Resource{ 1807 Schema: map[string]*Schema{ 1808 "foo": new(Schema), 1809 }, 1810 }, 1811 }, 1812 }, 1813 true, 1814 }, 1815 1816 // Sub-resource valid 1817 { 1818 map[string]*Schema{ 1819 "foo": &Schema{ 1820 Type: TypeList, 1821 Optional: true, 1822 Elem: &Resource{ 1823 Schema: map[string]*Schema{ 1824 "foo": &Schema{ 1825 Type: TypeInt, 1826 Optional: true, 1827 }, 1828 }, 1829 }, 1830 }, 1831 }, 1832 false, 1833 }, 1834 } 1835 1836 for i, tc := range cases { 1837 err := schemaMap(tc.In).InternalValidate() 1838 if (err != nil) != tc.Err { 1839 t.Fatalf("%d: bad: %s\n\n%#v", i, err, tc.In) 1840 } 1841 } 1842 1843 } 1844 1845 func TestSchemaMap_Validate(t *testing.T) { 1846 cases := []struct { 1847 Schema map[string]*Schema 1848 Config map[string]interface{} 1849 Vars map[string]string 1850 Warn bool 1851 Err bool 1852 }{ 1853 // Good 1854 { 1855 Schema: map[string]*Schema{ 1856 "availability_zone": &Schema{ 1857 Type: TypeString, 1858 Optional: true, 1859 Computed: true, 1860 ForceNew: true, 1861 }, 1862 }, 1863 1864 Config: map[string]interface{}{ 1865 "availability_zone": "foo", 1866 }, 1867 }, 1868 1869 // Good, because the var is not set and that error will come elsewhere 1870 { 1871 Schema: map[string]*Schema{ 1872 "size": &Schema{ 1873 Type: TypeInt, 1874 Required: true, 1875 }, 1876 }, 1877 1878 Config: map[string]interface{}{ 1879 "size": "${var.foo}", 1880 }, 1881 1882 Vars: map[string]string{ 1883 "var.foo": config.UnknownVariableValue, 1884 }, 1885 }, 1886 1887 // Required field not set 1888 { 1889 Schema: map[string]*Schema{ 1890 "availability_zone": &Schema{ 1891 Type: TypeString, 1892 Required: true, 1893 }, 1894 }, 1895 1896 Config: map[string]interface{}{}, 1897 1898 Err: true, 1899 }, 1900 1901 // Invalid type 1902 { 1903 Schema: map[string]*Schema{ 1904 "port": &Schema{ 1905 Type: TypeInt, 1906 Required: true, 1907 }, 1908 }, 1909 1910 Config: map[string]interface{}{ 1911 "port": "I am invalid", 1912 }, 1913 1914 Err: true, 1915 }, 1916 1917 { 1918 Schema: map[string]*Schema{ 1919 "user_data": &Schema{ 1920 Type: TypeString, 1921 Optional: true, 1922 }, 1923 }, 1924 1925 Config: map[string]interface{}{ 1926 "user_data": []interface{}{ 1927 map[string]interface{}{ 1928 "foo": "bar", 1929 }, 1930 }, 1931 }, 1932 1933 Err: true, 1934 }, 1935 1936 // Bad type, interpolated 1937 { 1938 Schema: map[string]*Schema{ 1939 "size": &Schema{ 1940 Type: TypeInt, 1941 Required: true, 1942 }, 1943 }, 1944 1945 Config: map[string]interface{}{ 1946 "size": "${var.foo}", 1947 }, 1948 1949 Vars: map[string]string{ 1950 "var.foo": "nope", 1951 }, 1952 1953 Err: true, 1954 }, 1955 1956 // Required but has DefaultFunc 1957 { 1958 Schema: map[string]*Schema{ 1959 "availability_zone": &Schema{ 1960 Type: TypeString, 1961 Required: true, 1962 DefaultFunc: func() (interface{}, error) { 1963 return "foo", nil 1964 }, 1965 }, 1966 }, 1967 1968 Config: nil, 1969 }, 1970 1971 // Required but has DefaultFunc return nil 1972 { 1973 Schema: map[string]*Schema{ 1974 "availability_zone": &Schema{ 1975 Type: TypeString, 1976 Required: true, 1977 DefaultFunc: func() (interface{}, error) { 1978 return nil, nil 1979 }, 1980 }, 1981 }, 1982 1983 Config: nil, 1984 1985 Err: true, 1986 }, 1987 1988 // Optional sub-resource 1989 { 1990 Schema: map[string]*Schema{ 1991 "ingress": &Schema{ 1992 Type: TypeList, 1993 Elem: &Resource{ 1994 Schema: map[string]*Schema{ 1995 "from": &Schema{ 1996 Type: TypeInt, 1997 Required: true, 1998 }, 1999 }, 2000 }, 2001 }, 2002 }, 2003 2004 Config: map[string]interface{}{}, 2005 2006 Err: false, 2007 }, 2008 2009 // Not a list 2010 { 2011 Schema: map[string]*Schema{ 2012 "ingress": &Schema{ 2013 Type: TypeList, 2014 Elem: &Resource{ 2015 Schema: map[string]*Schema{ 2016 "from": &Schema{ 2017 Type: TypeInt, 2018 Required: true, 2019 }, 2020 }, 2021 }, 2022 }, 2023 }, 2024 2025 Config: map[string]interface{}{ 2026 "ingress": "foo", 2027 }, 2028 2029 Err: true, 2030 }, 2031 2032 // Required sub-resource field 2033 { 2034 Schema: map[string]*Schema{ 2035 "ingress": &Schema{ 2036 Type: TypeList, 2037 Elem: &Resource{ 2038 Schema: map[string]*Schema{ 2039 "from": &Schema{ 2040 Type: TypeInt, 2041 Required: true, 2042 }, 2043 }, 2044 }, 2045 }, 2046 }, 2047 2048 Config: map[string]interface{}{ 2049 "ingress": []interface{}{ 2050 map[string]interface{}{}, 2051 }, 2052 }, 2053 2054 Err: true, 2055 }, 2056 2057 // Good sub-resource 2058 { 2059 Schema: map[string]*Schema{ 2060 "ingress": &Schema{ 2061 Type: TypeList, 2062 Optional: true, 2063 Elem: &Resource{ 2064 Schema: map[string]*Schema{ 2065 "from": &Schema{ 2066 Type: TypeInt, 2067 Required: true, 2068 }, 2069 }, 2070 }, 2071 }, 2072 }, 2073 2074 Config: map[string]interface{}{ 2075 "ingress": []interface{}{ 2076 map[string]interface{}{ 2077 "from": 80, 2078 }, 2079 }, 2080 }, 2081 2082 Err: false, 2083 }, 2084 2085 // Invalid/unknown field 2086 { 2087 Schema: map[string]*Schema{ 2088 "availability_zone": &Schema{ 2089 Type: TypeString, 2090 Optional: true, 2091 Computed: true, 2092 ForceNew: true, 2093 }, 2094 }, 2095 2096 Config: map[string]interface{}{ 2097 "foo": "bar", 2098 }, 2099 2100 Err: true, 2101 }, 2102 2103 // Computed field set 2104 { 2105 Schema: map[string]*Schema{ 2106 "availability_zone": &Schema{ 2107 Type: TypeString, 2108 Computed: true, 2109 }, 2110 }, 2111 2112 Config: map[string]interface{}{ 2113 "availability_zone": "bar", 2114 }, 2115 2116 Err: true, 2117 }, 2118 2119 // Not a set 2120 { 2121 Schema: map[string]*Schema{ 2122 "ports": &Schema{ 2123 Type: TypeSet, 2124 Required: true, 2125 Elem: &Schema{Type: TypeInt}, 2126 Set: func(a interface{}) int { 2127 return a.(int) 2128 }, 2129 }, 2130 }, 2131 2132 Config: map[string]interface{}{ 2133 "ports": "foo", 2134 }, 2135 2136 Err: true, 2137 }, 2138 2139 // Maps 2140 { 2141 Schema: map[string]*Schema{ 2142 "user_data": &Schema{ 2143 Type: TypeMap, 2144 Optional: true, 2145 }, 2146 }, 2147 2148 Config: map[string]interface{}{ 2149 "user_data": "foo", 2150 }, 2151 2152 Err: true, 2153 }, 2154 2155 { 2156 Schema: map[string]*Schema{ 2157 "user_data": &Schema{ 2158 Type: TypeMap, 2159 Optional: true, 2160 }, 2161 }, 2162 2163 Config: map[string]interface{}{ 2164 "user_data": []interface{}{ 2165 map[string]interface{}{ 2166 "foo": "bar", 2167 }, 2168 }, 2169 }, 2170 }, 2171 2172 { 2173 Schema: map[string]*Schema{ 2174 "user_data": &Schema{ 2175 Type: TypeMap, 2176 Optional: true, 2177 }, 2178 }, 2179 2180 Config: map[string]interface{}{ 2181 "user_data": map[string]interface{}{ 2182 "foo": "bar", 2183 }, 2184 }, 2185 }, 2186 2187 { 2188 Schema: map[string]*Schema{ 2189 "user_data": &Schema{ 2190 Type: TypeMap, 2191 Optional: true, 2192 }, 2193 }, 2194 2195 Config: map[string]interface{}{ 2196 "user_data": []interface{}{ 2197 "foo", 2198 }, 2199 }, 2200 2201 Err: true, 2202 }, 2203 } 2204 2205 for i, tc := range cases { 2206 c, err := config.NewRawConfig(tc.Config) 2207 if err != nil { 2208 t.Fatalf("err: %s", err) 2209 } 2210 if tc.Vars != nil { 2211 if err := c.Interpolate(tc.Vars); err != nil { 2212 t.Fatalf("err: %s", err) 2213 } 2214 } 2215 2216 ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c)) 2217 if (len(es) > 0) != tc.Err { 2218 if len(es) == 0 { 2219 t.Errorf("%d: no errors", i) 2220 } 2221 2222 for _, e := range es { 2223 t.Errorf("%d: err: %s", i, e) 2224 } 2225 2226 t.FailNow() 2227 } 2228 2229 if (len(ws) > 0) != tc.Warn { 2230 t.Fatalf("%d: ws: %#v", i, ws) 2231 } 2232 } 2233 }