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