github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/helper/schema/schema_test.go (about) 1 package schema 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "strconv" 9 "testing" 10 11 "github.com/hashicorp/hil" 12 "github.com/hashicorp/hil/ast" 13 "github.com/hashicorp/terraform/config" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/terraform" 16 ) 17 18 func TestEnvDefaultFunc(t *testing.T) { 19 key := "TF_TEST_ENV_DEFAULT_FUNC" 20 defer os.Unsetenv(key) 21 22 f := EnvDefaultFunc(key, "42") 23 if err := os.Setenv(key, "foo"); err != nil { 24 t.Fatalf("err: %s", err) 25 } 26 27 actual, err := f() 28 if err != nil { 29 t.Fatalf("err: %s", err) 30 } 31 if actual != "foo" { 32 t.Fatalf("bad: %#v", actual) 33 } 34 35 if err := os.Unsetenv(key); err != nil { 36 t.Fatalf("err: %s", err) 37 } 38 39 actual, err = f() 40 if err != nil { 41 t.Fatalf("err: %s", err) 42 } 43 if actual != "42" { 44 t.Fatalf("bad: %#v", actual) 45 } 46 } 47 48 func TestMultiEnvDefaultFunc(t *testing.T) { 49 keys := []string{ 50 "TF_TEST_MULTI_ENV_DEFAULT_FUNC1", 51 "TF_TEST_MULTI_ENV_DEFAULT_FUNC2", 52 } 53 defer func() { 54 for _, k := range keys { 55 os.Unsetenv(k) 56 } 57 }() 58 59 // Test that the first key is returned first 60 f := MultiEnvDefaultFunc(keys, "42") 61 if err := os.Setenv(keys[0], "foo"); err != nil { 62 t.Fatalf("err: %s", err) 63 } 64 65 actual, err := f() 66 if err != nil { 67 t.Fatalf("err: %s", err) 68 } 69 if actual != "foo" { 70 t.Fatalf("bad: %#v", actual) 71 } 72 73 if err := os.Unsetenv(keys[0]); err != nil { 74 t.Fatalf("err: %s", err) 75 } 76 77 // Test that the second key is returned if the first one is empty 78 f = MultiEnvDefaultFunc(keys, "42") 79 if err := os.Setenv(keys[1], "foo"); err != nil { 80 t.Fatalf("err: %s", err) 81 } 82 83 actual, err = f() 84 if err != nil { 85 t.Fatalf("err: %s", err) 86 } 87 if actual != "foo" { 88 t.Fatalf("bad: %#v", actual) 89 } 90 91 if err := os.Unsetenv(keys[1]); err != nil { 92 t.Fatalf("err: %s", err) 93 } 94 95 // Test that the default value is returned when no keys are set 96 actual, err = f() 97 if err != nil { 98 t.Fatalf("err: %s", err) 99 } 100 if actual != "42" { 101 t.Fatalf("bad: %#v", actual) 102 } 103 } 104 105 func TestValueType_Zero(t *testing.T) { 106 cases := []struct { 107 Type ValueType 108 Value interface{} 109 }{ 110 {TypeBool, false}, 111 {TypeInt, 0}, 112 {TypeFloat, 0.0}, 113 {TypeString, ""}, 114 {TypeList, []interface{}{}}, 115 {TypeMap, map[string]interface{}{}}, 116 {TypeSet, new(Set)}, 117 } 118 119 for i, tc := range cases { 120 actual := tc.Type.Zero() 121 if !reflect.DeepEqual(actual, tc.Value) { 122 t.Fatalf("%d: %#v != %#v", i, actual, tc.Value) 123 } 124 } 125 } 126 127 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 128 variable, _ := hil.InterfaceToVariable(input) 129 return variable 130 } 131 132 func TestSchemaMap_Diff(t *testing.T) { 133 cases := map[string]struct { 134 Schema map[string]*Schema 135 State *terraform.InstanceState 136 Config map[string]interface{} 137 ConfigVariables map[string]ast.Variable 138 Diff *terraform.InstanceDiff 139 Err bool 140 }{ 141 "#0": { 142 Schema: map[string]*Schema{ 143 "availability_zone": &Schema{ 144 Type: TypeString, 145 Optional: true, 146 Computed: true, 147 ForceNew: true, 148 }, 149 }, 150 151 State: nil, 152 153 Config: map[string]interface{}{ 154 "availability_zone": "foo", 155 }, 156 157 Diff: &terraform.InstanceDiff{ 158 Attributes: map[string]*terraform.ResourceAttrDiff{ 159 "availability_zone": &terraform.ResourceAttrDiff{ 160 Old: "", 161 New: "foo", 162 RequiresNew: true, 163 }, 164 }, 165 }, 166 167 Err: false, 168 }, 169 170 "#1": { 171 Schema: map[string]*Schema{ 172 "availability_zone": &Schema{ 173 Type: TypeString, 174 Optional: true, 175 Computed: true, 176 ForceNew: true, 177 }, 178 }, 179 180 State: nil, 181 182 Config: map[string]interface{}{}, 183 184 Diff: &terraform.InstanceDiff{ 185 Attributes: map[string]*terraform.ResourceAttrDiff{ 186 "availability_zone": &terraform.ResourceAttrDiff{ 187 Old: "", 188 NewComputed: true, 189 RequiresNew: true, 190 }, 191 }, 192 }, 193 194 Err: false, 195 }, 196 197 "#2": { 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 Schema: map[string]*Schema{ 220 "availability_zone": &Schema{ 221 Type: TypeString, 222 Optional: true, 223 Computed: true, 224 }, 225 }, 226 227 State: &terraform.InstanceState{ 228 Attributes: map[string]string{ 229 "availability_zone": "foo", 230 }, 231 }, 232 233 Config: map[string]interface{}{ 234 "availability_zone": "bar", 235 }, 236 237 Diff: &terraform.InstanceDiff{ 238 Attributes: map[string]*terraform.ResourceAttrDiff{ 239 "availability_zone": &terraform.ResourceAttrDiff{ 240 Old: "foo", 241 New: "bar", 242 }, 243 }, 244 }, 245 246 Err: false, 247 }, 248 249 "#4 Default": { 250 Schema: map[string]*Schema{ 251 "availability_zone": &Schema{ 252 Type: TypeString, 253 Optional: true, 254 Default: "foo", 255 }, 256 }, 257 258 State: nil, 259 260 Config: nil, 261 262 Diff: &terraform.InstanceDiff{ 263 Attributes: map[string]*terraform.ResourceAttrDiff{ 264 "availability_zone": &terraform.ResourceAttrDiff{ 265 Old: "", 266 New: "foo", 267 }, 268 }, 269 }, 270 271 Err: false, 272 }, 273 274 "#5 DefaultFunc, value": { 275 Schema: map[string]*Schema{ 276 "availability_zone": &Schema{ 277 Type: TypeString, 278 Optional: true, 279 DefaultFunc: func() (interface{}, error) { 280 return "foo", nil 281 }, 282 }, 283 }, 284 285 State: nil, 286 287 Config: nil, 288 289 Diff: &terraform.InstanceDiff{ 290 Attributes: map[string]*terraform.ResourceAttrDiff{ 291 "availability_zone": &terraform.ResourceAttrDiff{ 292 Old: "", 293 New: "foo", 294 }, 295 }, 296 }, 297 298 Err: false, 299 }, 300 301 "#6 DefaultFunc, configuration set": { 302 Schema: map[string]*Schema{ 303 "availability_zone": &Schema{ 304 Type: TypeString, 305 Optional: true, 306 DefaultFunc: func() (interface{}, error) { 307 return "foo", nil 308 }, 309 }, 310 }, 311 312 State: nil, 313 314 Config: map[string]interface{}{ 315 "availability_zone": "bar", 316 }, 317 318 Diff: &terraform.InstanceDiff{ 319 Attributes: map[string]*terraform.ResourceAttrDiff{ 320 "availability_zone": &terraform.ResourceAttrDiff{ 321 Old: "", 322 New: "bar", 323 }, 324 }, 325 }, 326 327 Err: false, 328 }, 329 330 "String with StateFunc": { 331 Schema: map[string]*Schema{ 332 "availability_zone": &Schema{ 333 Type: TypeString, 334 Optional: true, 335 Computed: true, 336 StateFunc: func(a interface{}) string { 337 return a.(string) + "!" 338 }, 339 }, 340 }, 341 342 State: nil, 343 344 Config: map[string]interface{}{ 345 "availability_zone": "foo", 346 }, 347 348 Diff: &terraform.InstanceDiff{ 349 Attributes: map[string]*terraform.ResourceAttrDiff{ 350 "availability_zone": &terraform.ResourceAttrDiff{ 351 Old: "", 352 New: "foo!", 353 NewExtra: "foo", 354 }, 355 }, 356 }, 357 358 Err: false, 359 }, 360 361 "StateFunc not called with nil value": { 362 Schema: map[string]*Schema{ 363 "availability_zone": &Schema{ 364 Type: TypeString, 365 Optional: true, 366 Computed: true, 367 StateFunc: func(a interface{}) string { 368 t.Fatalf("should not get here!") 369 return "" 370 }, 371 }, 372 }, 373 374 State: nil, 375 376 Config: map[string]interface{}{}, 377 378 Diff: &terraform.InstanceDiff{ 379 Attributes: map[string]*terraform.ResourceAttrDiff{ 380 "availability_zone": &terraform.ResourceAttrDiff{ 381 Old: "", 382 New: "", 383 NewComputed: true, 384 }, 385 }, 386 }, 387 388 Err: false, 389 }, 390 391 "#8 Variable (just checking)": { 392 Schema: map[string]*Schema{ 393 "availability_zone": &Schema{ 394 Type: TypeString, 395 Optional: true, 396 }, 397 }, 398 399 State: nil, 400 401 Config: map[string]interface{}{ 402 "availability_zone": "${var.foo}", 403 }, 404 405 ConfigVariables: map[string]ast.Variable{ 406 "var.foo": interfaceToVariableSwallowError("bar"), 407 }, 408 409 Diff: &terraform.InstanceDiff{ 410 Attributes: map[string]*terraform.ResourceAttrDiff{ 411 "availability_zone": &terraform.ResourceAttrDiff{ 412 Old: "", 413 New: "bar", 414 }, 415 }, 416 }, 417 418 Err: false, 419 }, 420 421 "#9 Variable computed": { 422 Schema: map[string]*Schema{ 423 "availability_zone": &Schema{ 424 Type: TypeString, 425 Optional: true, 426 }, 427 }, 428 429 State: nil, 430 431 Config: map[string]interface{}{ 432 "availability_zone": "${var.foo}", 433 }, 434 435 ConfigVariables: map[string]ast.Variable{ 436 "var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue), 437 }, 438 439 Diff: &terraform.InstanceDiff{ 440 Attributes: map[string]*terraform.ResourceAttrDiff{ 441 "availability_zone": &terraform.ResourceAttrDiff{ 442 Old: "", 443 New: "${var.foo}", 444 }, 445 }, 446 }, 447 448 Err: false, 449 }, 450 451 "#10 Int decode": { 452 Schema: map[string]*Schema{ 453 "port": &Schema{ 454 Type: TypeInt, 455 Optional: true, 456 Computed: true, 457 ForceNew: true, 458 }, 459 }, 460 461 State: nil, 462 463 Config: map[string]interface{}{ 464 "port": 27, 465 }, 466 467 Diff: &terraform.InstanceDiff{ 468 Attributes: map[string]*terraform.ResourceAttrDiff{ 469 "port": &terraform.ResourceAttrDiff{ 470 Old: "", 471 New: "27", 472 RequiresNew: true, 473 }, 474 }, 475 }, 476 477 Err: false, 478 }, 479 480 "#11 bool decode": { 481 Schema: map[string]*Schema{ 482 "port": &Schema{ 483 Type: TypeBool, 484 Optional: true, 485 Computed: true, 486 ForceNew: true, 487 }, 488 }, 489 490 State: nil, 491 492 Config: map[string]interface{}{ 493 "port": false, 494 }, 495 496 Diff: &terraform.InstanceDiff{ 497 Attributes: map[string]*terraform.ResourceAttrDiff{ 498 "port": &terraform.ResourceAttrDiff{ 499 Old: "", 500 New: "false", 501 RequiresNew: true, 502 }, 503 }, 504 }, 505 506 Err: false, 507 }, 508 509 "#12 Bool": { 510 Schema: map[string]*Schema{ 511 "delete": &Schema{ 512 Type: TypeBool, 513 Optional: true, 514 Default: false, 515 }, 516 }, 517 518 State: &terraform.InstanceState{ 519 Attributes: map[string]string{ 520 "delete": "false", 521 }, 522 }, 523 524 Config: nil, 525 526 Diff: nil, 527 528 Err: false, 529 }, 530 531 "#13 List decode": { 532 Schema: map[string]*Schema{ 533 "ports": &Schema{ 534 Type: TypeList, 535 Required: true, 536 Elem: &Schema{Type: TypeInt}, 537 }, 538 }, 539 540 State: nil, 541 542 Config: map[string]interface{}{ 543 "ports": []interface{}{1, 2, 5}, 544 }, 545 546 Diff: &terraform.InstanceDiff{ 547 Attributes: map[string]*terraform.ResourceAttrDiff{ 548 "ports.#": &terraform.ResourceAttrDiff{ 549 Old: "0", 550 New: "3", 551 }, 552 "ports.0": &terraform.ResourceAttrDiff{ 553 Old: "", 554 New: "1", 555 }, 556 "ports.1": &terraform.ResourceAttrDiff{ 557 Old: "", 558 New: "2", 559 }, 560 "ports.2": &terraform.ResourceAttrDiff{ 561 Old: "", 562 New: "5", 563 }, 564 }, 565 }, 566 567 Err: false, 568 }, 569 570 "#14": { 571 Schema: map[string]*Schema{ 572 "ports": &Schema{ 573 Type: TypeList, 574 Required: true, 575 Elem: &Schema{Type: TypeInt}, 576 }, 577 }, 578 579 State: nil, 580 581 Config: map[string]interface{}{ 582 "ports": []interface{}{1, "${var.foo}"}, 583 }, 584 585 ConfigVariables: map[string]ast.Variable{ 586 "var.foo": interfaceToVariableSwallowError([]interface{}{"2", "5"}), 587 }, 588 589 Diff: &terraform.InstanceDiff{ 590 Attributes: map[string]*terraform.ResourceAttrDiff{ 591 "ports.#": &terraform.ResourceAttrDiff{ 592 Old: "0", 593 New: "3", 594 }, 595 "ports.0": &terraform.ResourceAttrDiff{ 596 Old: "", 597 New: "1", 598 }, 599 "ports.1": &terraform.ResourceAttrDiff{ 600 Old: "", 601 New: "2", 602 }, 603 "ports.2": &terraform.ResourceAttrDiff{ 604 Old: "", 605 New: "5", 606 }, 607 }, 608 }, 609 610 Err: false, 611 }, 612 613 "#15": { 614 Schema: map[string]*Schema{ 615 "ports": &Schema{ 616 Type: TypeList, 617 Required: true, 618 Elem: &Schema{Type: TypeInt}, 619 }, 620 }, 621 622 State: nil, 623 624 Config: map[string]interface{}{ 625 "ports": []interface{}{1, "${var.foo}"}, 626 }, 627 628 ConfigVariables: map[string]ast.Variable{ 629 "var.foo": interfaceToVariableSwallowError([]interface{}{ 630 config.UnknownVariableValue, "5"}), 631 }, 632 633 Diff: &terraform.InstanceDiff{ 634 Attributes: map[string]*terraform.ResourceAttrDiff{ 635 "ports.#": &terraform.ResourceAttrDiff{ 636 Old: "0", 637 New: "", 638 NewComputed: true, 639 }, 640 }, 641 }, 642 643 Err: false, 644 }, 645 646 "#16": { 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 Schema: map[string]*Schema{ 675 "ports": &Schema{ 676 Type: TypeList, 677 Required: true, 678 Elem: &Schema{Type: TypeInt}, 679 }, 680 }, 681 682 State: &terraform.InstanceState{ 683 Attributes: map[string]string{ 684 "ports.#": "2", 685 "ports.0": "1", 686 "ports.1": "2", 687 }, 688 }, 689 690 Config: map[string]interface{}{ 691 "ports": []interface{}{1, 2, 5}, 692 }, 693 694 Diff: &terraform.InstanceDiff{ 695 Attributes: map[string]*terraform.ResourceAttrDiff{ 696 "ports.#": &terraform.ResourceAttrDiff{ 697 Old: "2", 698 New: "3", 699 }, 700 "ports.2": &terraform.ResourceAttrDiff{ 701 Old: "", 702 New: "5", 703 }, 704 }, 705 }, 706 707 Err: false, 708 }, 709 710 "#18": { 711 Schema: map[string]*Schema{ 712 "ports": &Schema{ 713 Type: TypeList, 714 Required: true, 715 Elem: &Schema{Type: TypeInt}, 716 ForceNew: true, 717 }, 718 }, 719 720 State: nil, 721 722 Config: map[string]interface{}{ 723 "ports": []interface{}{1, 2, 5}, 724 }, 725 726 Diff: &terraform.InstanceDiff{ 727 Attributes: map[string]*terraform.ResourceAttrDiff{ 728 "ports.#": &terraform.ResourceAttrDiff{ 729 Old: "0", 730 New: "3", 731 RequiresNew: true, 732 }, 733 "ports.0": &terraform.ResourceAttrDiff{ 734 Old: "", 735 New: "1", 736 RequiresNew: true, 737 }, 738 "ports.1": &terraform.ResourceAttrDiff{ 739 Old: "", 740 New: "2", 741 RequiresNew: true, 742 }, 743 "ports.2": &terraform.ResourceAttrDiff{ 744 Old: "", 745 New: "5", 746 RequiresNew: true, 747 }, 748 }, 749 }, 750 751 Err: false, 752 }, 753 754 "#19": { 755 Schema: map[string]*Schema{ 756 "ports": &Schema{ 757 Type: TypeList, 758 Optional: true, 759 Computed: true, 760 Elem: &Schema{Type: TypeInt}, 761 }, 762 }, 763 764 State: nil, 765 766 Config: map[string]interface{}{}, 767 768 Diff: &terraform.InstanceDiff{ 769 Attributes: map[string]*terraform.ResourceAttrDiff{ 770 "ports.#": &terraform.ResourceAttrDiff{ 771 Old: "", 772 NewComputed: true, 773 }, 774 }, 775 }, 776 777 Err: false, 778 }, 779 780 "#20 Set": { 781 Schema: map[string]*Schema{ 782 "ports": &Schema{ 783 Type: TypeSet, 784 Required: true, 785 Elem: &Schema{Type: TypeInt}, 786 Set: func(a interface{}) int { 787 return a.(int) 788 }, 789 }, 790 }, 791 792 State: nil, 793 794 Config: map[string]interface{}{ 795 "ports": []interface{}{5, 2, 1}, 796 }, 797 798 Diff: &terraform.InstanceDiff{ 799 Attributes: map[string]*terraform.ResourceAttrDiff{ 800 "ports.#": &terraform.ResourceAttrDiff{ 801 Old: "0", 802 New: "3", 803 }, 804 "ports.1": &terraform.ResourceAttrDiff{ 805 Old: "", 806 New: "1", 807 }, 808 "ports.2": &terraform.ResourceAttrDiff{ 809 Old: "", 810 New: "2", 811 }, 812 "ports.5": &terraform.ResourceAttrDiff{ 813 Old: "", 814 New: "5", 815 }, 816 }, 817 }, 818 819 Err: false, 820 }, 821 822 "#21 Set": { 823 Schema: map[string]*Schema{ 824 "ports": &Schema{ 825 Type: TypeSet, 826 Computed: true, 827 Required: true, 828 Elem: &Schema{Type: TypeInt}, 829 Set: func(a interface{}) int { 830 return a.(int) 831 }, 832 }, 833 }, 834 835 State: &terraform.InstanceState{ 836 Attributes: map[string]string{ 837 "ports.#": "0", 838 }, 839 }, 840 841 Config: nil, 842 843 Diff: nil, 844 845 Err: false, 846 }, 847 848 "#22 Set": { 849 Schema: map[string]*Schema{ 850 "ports": &Schema{ 851 Type: TypeSet, 852 Optional: true, 853 Computed: true, 854 Elem: &Schema{Type: TypeInt}, 855 Set: func(a interface{}) int { 856 return a.(int) 857 }, 858 }, 859 }, 860 861 State: nil, 862 863 Config: nil, 864 865 Diff: &terraform.InstanceDiff{ 866 Attributes: map[string]*terraform.ResourceAttrDiff{ 867 "ports.#": &terraform.ResourceAttrDiff{ 868 Old: "", 869 NewComputed: true, 870 }, 871 }, 872 }, 873 874 Err: false, 875 }, 876 877 "#23 Set": { 878 Schema: map[string]*Schema{ 879 "ports": &Schema{ 880 Type: TypeSet, 881 Required: true, 882 Elem: &Schema{Type: TypeInt}, 883 Set: func(a interface{}) int { 884 return a.(int) 885 }, 886 }, 887 }, 888 889 State: nil, 890 891 Config: map[string]interface{}{ 892 "ports": []interface{}{"${var.foo}", 1}, 893 }, 894 895 ConfigVariables: map[string]ast.Variable{ 896 "var.foo": interfaceToVariableSwallowError([]interface{}{"2", "5"}), 897 }, 898 899 Diff: &terraform.InstanceDiff{ 900 Attributes: map[string]*terraform.ResourceAttrDiff{ 901 "ports.#": &terraform.ResourceAttrDiff{ 902 Old: "0", 903 New: "3", 904 }, 905 "ports.1": &terraform.ResourceAttrDiff{ 906 Old: "", 907 New: "1", 908 }, 909 "ports.2": &terraform.ResourceAttrDiff{ 910 Old: "", 911 New: "2", 912 }, 913 "ports.5": &terraform.ResourceAttrDiff{ 914 Old: "", 915 New: "5", 916 }, 917 }, 918 }, 919 920 Err: false, 921 }, 922 923 "#24 Set": { 924 Schema: map[string]*Schema{ 925 "ports": &Schema{ 926 Type: TypeSet, 927 Required: true, 928 Elem: &Schema{Type: TypeInt}, 929 Set: func(a interface{}) int { 930 return a.(int) 931 }, 932 }, 933 }, 934 935 State: nil, 936 937 Config: map[string]interface{}{ 938 "ports": []interface{}{1, "${var.foo}"}, 939 }, 940 941 ConfigVariables: map[string]ast.Variable{ 942 "var.foo": interfaceToVariableSwallowError([]interface{}{ 943 config.UnknownVariableValue, "5"}), 944 }, 945 946 Diff: &terraform.InstanceDiff{ 947 Attributes: map[string]*terraform.ResourceAttrDiff{ 948 "ports.#": &terraform.ResourceAttrDiff{ 949 Old: "", 950 New: "", 951 NewComputed: true, 952 }, 953 }, 954 }, 955 956 Err: false, 957 }, 958 959 "#25 Set": { 960 Schema: map[string]*Schema{ 961 "ports": &Schema{ 962 Type: TypeSet, 963 Required: true, 964 Elem: &Schema{Type: TypeInt}, 965 Set: func(a interface{}) int { 966 return a.(int) 967 }, 968 }, 969 }, 970 971 State: &terraform.InstanceState{ 972 Attributes: map[string]string{ 973 "ports.#": "2", 974 "ports.1": "1", 975 "ports.2": "2", 976 }, 977 }, 978 979 Config: map[string]interface{}{ 980 "ports": []interface{}{5, 2, 1}, 981 }, 982 983 Diff: &terraform.InstanceDiff{ 984 Attributes: map[string]*terraform.ResourceAttrDiff{ 985 "ports.#": &terraform.ResourceAttrDiff{ 986 Old: "2", 987 New: "3", 988 }, 989 "ports.1": &terraform.ResourceAttrDiff{ 990 Old: "1", 991 New: "1", 992 }, 993 "ports.2": &terraform.ResourceAttrDiff{ 994 Old: "2", 995 New: "2", 996 }, 997 "ports.5": &terraform.ResourceAttrDiff{ 998 Old: "", 999 New: "5", 1000 }, 1001 }, 1002 }, 1003 1004 Err: false, 1005 }, 1006 1007 "#26 Set": { 1008 Schema: map[string]*Schema{ 1009 "ports": &Schema{ 1010 Type: TypeSet, 1011 Required: true, 1012 Elem: &Schema{Type: TypeInt}, 1013 Set: func(a interface{}) int { 1014 return a.(int) 1015 }, 1016 }, 1017 }, 1018 1019 State: &terraform.InstanceState{ 1020 Attributes: map[string]string{ 1021 "ports.#": "2", 1022 "ports.1": "1", 1023 "ports.2": "2", 1024 }, 1025 }, 1026 1027 Config: map[string]interface{}{}, 1028 1029 Diff: &terraform.InstanceDiff{ 1030 Attributes: map[string]*terraform.ResourceAttrDiff{ 1031 "ports.#": &terraform.ResourceAttrDiff{ 1032 Old: "2", 1033 New: "0", 1034 }, 1035 "ports.1": &terraform.ResourceAttrDiff{ 1036 Old: "1", 1037 New: "0", 1038 NewRemoved: true, 1039 }, 1040 "ports.2": &terraform.ResourceAttrDiff{ 1041 Old: "2", 1042 New: "0", 1043 NewRemoved: true, 1044 }, 1045 }, 1046 }, 1047 1048 Err: false, 1049 }, 1050 1051 "#27 Set": { 1052 Schema: map[string]*Schema{ 1053 "ports": &Schema{ 1054 Type: TypeSet, 1055 Optional: true, 1056 Computed: true, 1057 Elem: &Schema{Type: TypeInt}, 1058 Set: func(a interface{}) int { 1059 return a.(int) 1060 }, 1061 }, 1062 }, 1063 1064 State: &terraform.InstanceState{ 1065 Attributes: map[string]string{ 1066 "availability_zone": "bar", 1067 "ports.#": "1", 1068 "ports.80": "80", 1069 }, 1070 }, 1071 1072 Config: map[string]interface{}{}, 1073 1074 Diff: nil, 1075 1076 Err: false, 1077 }, 1078 1079 "#28 Set": { 1080 Schema: map[string]*Schema{ 1081 "ingress": &Schema{ 1082 Type: TypeSet, 1083 Required: true, 1084 Elem: &Resource{ 1085 Schema: map[string]*Schema{ 1086 "ports": &Schema{ 1087 Type: TypeList, 1088 Optional: true, 1089 Elem: &Schema{Type: TypeInt}, 1090 }, 1091 }, 1092 }, 1093 Set: func(v interface{}) int { 1094 m := v.(map[string]interface{}) 1095 ps := m["ports"].([]interface{}) 1096 result := 0 1097 for _, p := range ps { 1098 result += p.(int) 1099 } 1100 return result 1101 }, 1102 }, 1103 }, 1104 1105 State: &terraform.InstanceState{ 1106 Attributes: map[string]string{ 1107 "ingress.#": "2", 1108 "ingress.80.ports.#": "1", 1109 "ingress.80.ports.0": "80", 1110 "ingress.443.ports.#": "1", 1111 "ingress.443.ports.0": "443", 1112 }, 1113 }, 1114 1115 Config: map[string]interface{}{ 1116 "ingress": []map[string]interface{}{ 1117 map[string]interface{}{ 1118 "ports": []interface{}{443}, 1119 }, 1120 map[string]interface{}{ 1121 "ports": []interface{}{80}, 1122 }, 1123 }, 1124 }, 1125 1126 Diff: nil, 1127 1128 Err: false, 1129 }, 1130 1131 "#29 List of structure decode": { 1132 Schema: map[string]*Schema{ 1133 "ingress": &Schema{ 1134 Type: TypeList, 1135 Required: true, 1136 Elem: &Resource{ 1137 Schema: map[string]*Schema{ 1138 "from": &Schema{ 1139 Type: TypeInt, 1140 Required: true, 1141 }, 1142 }, 1143 }, 1144 }, 1145 }, 1146 1147 State: nil, 1148 1149 Config: map[string]interface{}{ 1150 "ingress": []interface{}{ 1151 map[string]interface{}{ 1152 "from": 8080, 1153 }, 1154 }, 1155 }, 1156 1157 Diff: &terraform.InstanceDiff{ 1158 Attributes: map[string]*terraform.ResourceAttrDiff{ 1159 "ingress.#": &terraform.ResourceAttrDiff{ 1160 Old: "0", 1161 New: "1", 1162 }, 1163 "ingress.0.from": &terraform.ResourceAttrDiff{ 1164 Old: "", 1165 New: "8080", 1166 }, 1167 }, 1168 }, 1169 1170 Err: false, 1171 }, 1172 1173 "#30 ComputedWhen": { 1174 Schema: map[string]*Schema{ 1175 "availability_zone": &Schema{ 1176 Type: TypeString, 1177 Computed: true, 1178 ComputedWhen: []string{"port"}, 1179 }, 1180 1181 "port": &Schema{ 1182 Type: TypeInt, 1183 Optional: true, 1184 }, 1185 }, 1186 1187 State: &terraform.InstanceState{ 1188 Attributes: map[string]string{ 1189 "availability_zone": "foo", 1190 "port": "80", 1191 }, 1192 }, 1193 1194 Config: map[string]interface{}{ 1195 "port": 80, 1196 }, 1197 1198 Diff: nil, 1199 1200 Err: false, 1201 }, 1202 1203 "#31": { 1204 Schema: map[string]*Schema{ 1205 "availability_zone": &Schema{ 1206 Type: TypeString, 1207 Computed: true, 1208 ComputedWhen: []string{"port"}, 1209 }, 1210 1211 "port": &Schema{ 1212 Type: TypeInt, 1213 Optional: true, 1214 }, 1215 }, 1216 1217 State: &terraform.InstanceState{ 1218 Attributes: map[string]string{ 1219 "port": "80", 1220 }, 1221 }, 1222 1223 Config: map[string]interface{}{ 1224 "port": 80, 1225 }, 1226 1227 Diff: &terraform.InstanceDiff{ 1228 Attributes: map[string]*terraform.ResourceAttrDiff{ 1229 "availability_zone": &terraform.ResourceAttrDiff{ 1230 NewComputed: true, 1231 }, 1232 }, 1233 }, 1234 1235 Err: false, 1236 }, 1237 1238 /* TODO 1239 { 1240 Schema: map[string]*Schema{ 1241 "availability_zone": &Schema{ 1242 Type: TypeString, 1243 Computed: true, 1244 ComputedWhen: []string{"port"}, 1245 }, 1246 1247 "port": &Schema{ 1248 Type: TypeInt, 1249 Optional: true, 1250 }, 1251 }, 1252 1253 State: &terraform.InstanceState{ 1254 Attributes: map[string]string{ 1255 "availability_zone": "foo", 1256 "port": "80", 1257 }, 1258 }, 1259 1260 Config: map[string]interface{}{ 1261 "port": 8080, 1262 }, 1263 1264 Diff: &terraform.ResourceDiff{ 1265 Attributes: map[string]*terraform.ResourceAttrDiff{ 1266 "availability_zone": &terraform.ResourceAttrDiff{ 1267 Old: "foo", 1268 NewComputed: true, 1269 }, 1270 "port": &terraform.ResourceAttrDiff{ 1271 Old: "80", 1272 New: "8080", 1273 }, 1274 }, 1275 }, 1276 1277 Err: false, 1278 }, 1279 */ 1280 1281 "#32 Maps": { 1282 Schema: map[string]*Schema{ 1283 "config_vars": &Schema{ 1284 Type: TypeMap, 1285 }, 1286 }, 1287 1288 State: nil, 1289 1290 Config: map[string]interface{}{ 1291 "config_vars": []interface{}{ 1292 map[string]interface{}{ 1293 "bar": "baz", 1294 }, 1295 }, 1296 }, 1297 1298 Diff: &terraform.InstanceDiff{ 1299 Attributes: map[string]*terraform.ResourceAttrDiff{ 1300 "config_vars.%": &terraform.ResourceAttrDiff{ 1301 Old: "0", 1302 New: "1", 1303 }, 1304 1305 "config_vars.bar": &terraform.ResourceAttrDiff{ 1306 Old: "", 1307 New: "baz", 1308 }, 1309 }, 1310 }, 1311 1312 Err: false, 1313 }, 1314 1315 "#33 Maps": { 1316 Schema: map[string]*Schema{ 1317 "config_vars": &Schema{ 1318 Type: TypeMap, 1319 }, 1320 }, 1321 1322 State: &terraform.InstanceState{ 1323 Attributes: map[string]string{ 1324 "config_vars.foo": "bar", 1325 }, 1326 }, 1327 1328 Config: map[string]interface{}{ 1329 "config_vars": []interface{}{ 1330 map[string]interface{}{ 1331 "bar": "baz", 1332 }, 1333 }, 1334 }, 1335 1336 Diff: &terraform.InstanceDiff{ 1337 Attributes: map[string]*terraform.ResourceAttrDiff{ 1338 "config_vars.foo": &terraform.ResourceAttrDiff{ 1339 Old: "bar", 1340 NewRemoved: true, 1341 }, 1342 "config_vars.bar": &terraform.ResourceAttrDiff{ 1343 Old: "", 1344 New: "baz", 1345 }, 1346 }, 1347 }, 1348 1349 Err: false, 1350 }, 1351 1352 "#34 Maps": { 1353 Schema: map[string]*Schema{ 1354 "vars": &Schema{ 1355 Type: TypeMap, 1356 Optional: true, 1357 Computed: true, 1358 }, 1359 }, 1360 1361 State: &terraform.InstanceState{ 1362 Attributes: map[string]string{ 1363 "vars.foo": "bar", 1364 }, 1365 }, 1366 1367 Config: map[string]interface{}{ 1368 "vars": []interface{}{ 1369 map[string]interface{}{ 1370 "bar": "baz", 1371 }, 1372 }, 1373 }, 1374 1375 Diff: &terraform.InstanceDiff{ 1376 Attributes: map[string]*terraform.ResourceAttrDiff{ 1377 "vars.foo": &terraform.ResourceAttrDiff{ 1378 Old: "bar", 1379 New: "", 1380 NewRemoved: true, 1381 }, 1382 "vars.bar": &terraform.ResourceAttrDiff{ 1383 Old: "", 1384 New: "baz", 1385 }, 1386 }, 1387 }, 1388 1389 Err: false, 1390 }, 1391 1392 "#35 Maps": { 1393 Schema: map[string]*Schema{ 1394 "vars": &Schema{ 1395 Type: TypeMap, 1396 Computed: true, 1397 }, 1398 }, 1399 1400 State: &terraform.InstanceState{ 1401 Attributes: map[string]string{ 1402 "vars.foo": "bar", 1403 }, 1404 }, 1405 1406 Config: nil, 1407 1408 Diff: nil, 1409 1410 Err: false, 1411 }, 1412 1413 "#36 Maps": { 1414 Schema: map[string]*Schema{ 1415 "config_vars": &Schema{ 1416 Type: TypeList, 1417 Elem: &Schema{Type: TypeMap}, 1418 }, 1419 }, 1420 1421 State: &terraform.InstanceState{ 1422 Attributes: map[string]string{ 1423 "config_vars.#": "1", 1424 "config_vars.0.foo": "bar", 1425 }, 1426 }, 1427 1428 Config: map[string]interface{}{ 1429 "config_vars": []interface{}{ 1430 map[string]interface{}{ 1431 "bar": "baz", 1432 }, 1433 }, 1434 }, 1435 1436 Diff: &terraform.InstanceDiff{ 1437 Attributes: map[string]*terraform.ResourceAttrDiff{ 1438 "config_vars.0.foo": &terraform.ResourceAttrDiff{ 1439 Old: "bar", 1440 NewRemoved: true, 1441 }, 1442 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1443 Old: "", 1444 New: "baz", 1445 }, 1446 }, 1447 }, 1448 1449 Err: false, 1450 }, 1451 1452 "#37 Maps": { 1453 Schema: map[string]*Schema{ 1454 "config_vars": &Schema{ 1455 Type: TypeList, 1456 Elem: &Schema{Type: TypeMap}, 1457 }, 1458 }, 1459 1460 State: &terraform.InstanceState{ 1461 Attributes: map[string]string{ 1462 "config_vars.#": "1", 1463 "config_vars.0.foo": "bar", 1464 "config_vars.0.bar": "baz", 1465 }, 1466 }, 1467 1468 Config: map[string]interface{}{}, 1469 1470 Diff: &terraform.InstanceDiff{ 1471 Attributes: map[string]*terraform.ResourceAttrDiff{ 1472 "config_vars.#": &terraform.ResourceAttrDiff{ 1473 Old: "1", 1474 New: "0", 1475 }, 1476 "config_vars.0.%": &terraform.ResourceAttrDiff{ 1477 Old: "2", 1478 New: "0", 1479 }, 1480 "config_vars.0.foo": &terraform.ResourceAttrDiff{ 1481 Old: "bar", 1482 NewRemoved: true, 1483 }, 1484 "config_vars.0.bar": &terraform.ResourceAttrDiff{ 1485 Old: "baz", 1486 NewRemoved: true, 1487 }, 1488 }, 1489 }, 1490 1491 Err: false, 1492 }, 1493 1494 "#38 ForceNews": { 1495 Schema: map[string]*Schema{ 1496 "availability_zone": &Schema{ 1497 Type: TypeString, 1498 Optional: true, 1499 ForceNew: true, 1500 }, 1501 1502 "address": &Schema{ 1503 Type: TypeString, 1504 Optional: true, 1505 Computed: true, 1506 }, 1507 }, 1508 1509 State: &terraform.InstanceState{ 1510 Attributes: map[string]string{ 1511 "availability_zone": "bar", 1512 "address": "foo", 1513 }, 1514 }, 1515 1516 Config: map[string]interface{}{ 1517 "availability_zone": "foo", 1518 }, 1519 1520 Diff: &terraform.InstanceDiff{ 1521 Attributes: map[string]*terraform.ResourceAttrDiff{ 1522 "availability_zone": &terraform.ResourceAttrDiff{ 1523 Old: "bar", 1524 New: "foo", 1525 RequiresNew: true, 1526 }, 1527 1528 "address": &terraform.ResourceAttrDiff{ 1529 Old: "foo", 1530 New: "", 1531 NewComputed: true, 1532 }, 1533 }, 1534 }, 1535 1536 Err: false, 1537 }, 1538 1539 "#39 Set": { 1540 Schema: map[string]*Schema{ 1541 "availability_zone": &Schema{ 1542 Type: TypeString, 1543 Optional: true, 1544 ForceNew: true, 1545 }, 1546 1547 "ports": &Schema{ 1548 Type: TypeSet, 1549 Optional: true, 1550 Computed: true, 1551 Elem: &Schema{Type: TypeInt}, 1552 Set: func(a interface{}) int { 1553 return a.(int) 1554 }, 1555 }, 1556 }, 1557 1558 State: &terraform.InstanceState{ 1559 Attributes: map[string]string{ 1560 "availability_zone": "bar", 1561 "ports.#": "1", 1562 "ports.80": "80", 1563 }, 1564 }, 1565 1566 Config: map[string]interface{}{ 1567 "availability_zone": "foo", 1568 }, 1569 1570 Diff: &terraform.InstanceDiff{ 1571 Attributes: map[string]*terraform.ResourceAttrDiff{ 1572 "availability_zone": &terraform.ResourceAttrDiff{ 1573 Old: "bar", 1574 New: "foo", 1575 RequiresNew: true, 1576 }, 1577 1578 "ports.#": &terraform.ResourceAttrDiff{ 1579 Old: "1", 1580 New: "", 1581 NewComputed: true, 1582 }, 1583 }, 1584 }, 1585 1586 Err: false, 1587 }, 1588 1589 "#40 Set": { 1590 Schema: map[string]*Schema{ 1591 "instances": &Schema{ 1592 Type: TypeSet, 1593 Elem: &Schema{Type: TypeString}, 1594 Optional: true, 1595 Computed: true, 1596 Set: func(v interface{}) int { 1597 return len(v.(string)) 1598 }, 1599 }, 1600 }, 1601 1602 State: &terraform.InstanceState{ 1603 Attributes: map[string]string{ 1604 "instances.#": "0", 1605 }, 1606 }, 1607 1608 Config: map[string]interface{}{ 1609 "instances": []interface{}{"${var.foo}"}, 1610 }, 1611 1612 ConfigVariables: map[string]ast.Variable{ 1613 "var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue), 1614 }, 1615 1616 Diff: &terraform.InstanceDiff{ 1617 Attributes: map[string]*terraform.ResourceAttrDiff{ 1618 "instances.#": &terraform.ResourceAttrDiff{ 1619 NewComputed: true, 1620 }, 1621 }, 1622 }, 1623 1624 Err: false, 1625 }, 1626 1627 "#41 Set": { 1628 Schema: map[string]*Schema{ 1629 "route": &Schema{ 1630 Type: TypeSet, 1631 Optional: true, 1632 Elem: &Resource{ 1633 Schema: map[string]*Schema{ 1634 "index": &Schema{ 1635 Type: TypeInt, 1636 Required: true, 1637 }, 1638 1639 "gateway": &Schema{ 1640 Type: TypeString, 1641 Optional: true, 1642 }, 1643 }, 1644 }, 1645 Set: func(v interface{}) int { 1646 m := v.(map[string]interface{}) 1647 return m["index"].(int) 1648 }, 1649 }, 1650 }, 1651 1652 State: nil, 1653 1654 Config: map[string]interface{}{ 1655 "route": []map[string]interface{}{ 1656 map[string]interface{}{ 1657 "index": "1", 1658 "gateway": "${var.foo}", 1659 }, 1660 }, 1661 }, 1662 1663 ConfigVariables: map[string]ast.Variable{ 1664 "var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue), 1665 }, 1666 1667 Diff: &terraform.InstanceDiff{ 1668 Attributes: map[string]*terraform.ResourceAttrDiff{ 1669 "route.#": &terraform.ResourceAttrDiff{ 1670 Old: "0", 1671 New: "1", 1672 }, 1673 "route.~1.index": &terraform.ResourceAttrDiff{ 1674 Old: "", 1675 New: "1", 1676 }, 1677 "route.~1.gateway": &terraform.ResourceAttrDiff{ 1678 Old: "", 1679 New: "${var.foo}", 1680 }, 1681 }, 1682 }, 1683 1684 Err: false, 1685 }, 1686 1687 "#42 Set": { 1688 Schema: map[string]*Schema{ 1689 "route": &Schema{ 1690 Type: TypeSet, 1691 Optional: true, 1692 Elem: &Resource{ 1693 Schema: map[string]*Schema{ 1694 "index": &Schema{ 1695 Type: TypeInt, 1696 Required: true, 1697 }, 1698 1699 "gateway": &Schema{ 1700 Type: TypeSet, 1701 Optional: true, 1702 Elem: &Schema{Type: TypeInt}, 1703 Set: func(a interface{}) int { 1704 return a.(int) 1705 }, 1706 }, 1707 }, 1708 }, 1709 Set: func(v interface{}) int { 1710 m := v.(map[string]interface{}) 1711 return m["index"].(int) 1712 }, 1713 }, 1714 }, 1715 1716 State: nil, 1717 1718 Config: map[string]interface{}{ 1719 "route": []map[string]interface{}{ 1720 map[string]interface{}{ 1721 "index": "1", 1722 "gateway": []interface{}{ 1723 "${var.foo}", 1724 }, 1725 }, 1726 }, 1727 }, 1728 1729 ConfigVariables: map[string]ast.Variable{ 1730 "var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue), 1731 }, 1732 1733 Diff: &terraform.InstanceDiff{ 1734 Attributes: map[string]*terraform.ResourceAttrDiff{ 1735 "route.#": &terraform.ResourceAttrDiff{ 1736 Old: "0", 1737 New: "1", 1738 }, 1739 "route.~1.index": &terraform.ResourceAttrDiff{ 1740 Old: "", 1741 New: "1", 1742 }, 1743 "route.~1.gateway.#": &terraform.ResourceAttrDiff{ 1744 NewComputed: true, 1745 }, 1746 }, 1747 }, 1748 1749 Err: false, 1750 }, 1751 1752 "#43 - Computed maps": { 1753 Schema: map[string]*Schema{ 1754 "vars": &Schema{ 1755 Type: TypeMap, 1756 Computed: true, 1757 }, 1758 }, 1759 1760 State: nil, 1761 1762 Config: nil, 1763 1764 Diff: &terraform.InstanceDiff{ 1765 Attributes: map[string]*terraform.ResourceAttrDiff{ 1766 "vars.%": &terraform.ResourceAttrDiff{ 1767 Old: "", 1768 NewComputed: true, 1769 }, 1770 }, 1771 }, 1772 1773 Err: false, 1774 }, 1775 1776 "#44 - Computed maps": { 1777 Schema: map[string]*Schema{ 1778 "vars": &Schema{ 1779 Type: TypeMap, 1780 Computed: true, 1781 }, 1782 }, 1783 1784 State: &terraform.InstanceState{ 1785 Attributes: map[string]string{ 1786 "vars.%": "0", 1787 }, 1788 }, 1789 1790 Config: map[string]interface{}{ 1791 "vars": map[string]interface{}{ 1792 "bar": "${var.foo}", 1793 }, 1794 }, 1795 1796 ConfigVariables: map[string]ast.Variable{ 1797 "var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue), 1798 }, 1799 1800 Diff: &terraform.InstanceDiff{ 1801 Attributes: map[string]*terraform.ResourceAttrDiff{ 1802 "vars.%": &terraform.ResourceAttrDiff{ 1803 Old: "", 1804 NewComputed: true, 1805 }, 1806 }, 1807 }, 1808 1809 Err: false, 1810 }, 1811 1812 "#45 - Empty": { 1813 Schema: map[string]*Schema{}, 1814 1815 State: &terraform.InstanceState{}, 1816 1817 Config: map[string]interface{}{}, 1818 1819 Diff: nil, 1820 1821 Err: false, 1822 }, 1823 1824 "#46 - Float": { 1825 Schema: map[string]*Schema{ 1826 "some_threshold": &Schema{ 1827 Type: TypeFloat, 1828 }, 1829 }, 1830 1831 State: &terraform.InstanceState{ 1832 Attributes: map[string]string{ 1833 "some_threshold": "567.8", 1834 }, 1835 }, 1836 1837 Config: map[string]interface{}{ 1838 "some_threshold": 12.34, 1839 }, 1840 1841 Diff: &terraform.InstanceDiff{ 1842 Attributes: map[string]*terraform.ResourceAttrDiff{ 1843 "some_threshold": &terraform.ResourceAttrDiff{ 1844 Old: "567.8", 1845 New: "12.34", 1846 }, 1847 }, 1848 }, 1849 1850 Err: false, 1851 }, 1852 1853 "#47 - https://github.com/hashicorp/terraform/issues/824": { 1854 Schema: map[string]*Schema{ 1855 "block_device": &Schema{ 1856 Type: TypeSet, 1857 Optional: true, 1858 Computed: true, 1859 Elem: &Resource{ 1860 Schema: map[string]*Schema{ 1861 "device_name": &Schema{ 1862 Type: TypeString, 1863 Required: true, 1864 }, 1865 "delete_on_termination": &Schema{ 1866 Type: TypeBool, 1867 Optional: true, 1868 Default: true, 1869 }, 1870 }, 1871 }, 1872 Set: func(v interface{}) int { 1873 var buf bytes.Buffer 1874 m := v.(map[string]interface{}) 1875 buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) 1876 buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) 1877 return hashcode.String(buf.String()) 1878 }, 1879 }, 1880 }, 1881 1882 State: &terraform.InstanceState{ 1883 Attributes: map[string]string{ 1884 "block_device.#": "2", 1885 "block_device.616397234.delete_on_termination": "true", 1886 "block_device.616397234.device_name": "/dev/sda1", 1887 "block_device.2801811477.delete_on_termination": "true", 1888 "block_device.2801811477.device_name": "/dev/sdx", 1889 }, 1890 }, 1891 1892 Config: map[string]interface{}{ 1893 "block_device": []map[string]interface{}{ 1894 map[string]interface{}{ 1895 "device_name": "/dev/sda1", 1896 }, 1897 map[string]interface{}{ 1898 "device_name": "/dev/sdx", 1899 }, 1900 }, 1901 }, 1902 Diff: nil, 1903 Err: false, 1904 }, 1905 1906 "#48 - Zero value in state shouldn't result in diff": { 1907 Schema: map[string]*Schema{ 1908 "port": &Schema{ 1909 Type: TypeBool, 1910 Optional: true, 1911 ForceNew: true, 1912 }, 1913 }, 1914 1915 State: &terraform.InstanceState{ 1916 Attributes: map[string]string{ 1917 "port": "false", 1918 }, 1919 }, 1920 1921 Config: map[string]interface{}{}, 1922 1923 Diff: nil, 1924 1925 Err: false, 1926 }, 1927 1928 "#49 Set - Same as #48 but for sets": { 1929 Schema: map[string]*Schema{ 1930 "route": &Schema{ 1931 Type: TypeSet, 1932 Optional: true, 1933 Elem: &Resource{ 1934 Schema: map[string]*Schema{ 1935 "index": &Schema{ 1936 Type: TypeInt, 1937 Required: true, 1938 }, 1939 1940 "gateway": &Schema{ 1941 Type: TypeSet, 1942 Optional: true, 1943 Elem: &Schema{Type: TypeInt}, 1944 Set: func(a interface{}) int { 1945 return a.(int) 1946 }, 1947 }, 1948 }, 1949 }, 1950 Set: func(v interface{}) int { 1951 m := v.(map[string]interface{}) 1952 return m["index"].(int) 1953 }, 1954 }, 1955 }, 1956 1957 State: &terraform.InstanceState{ 1958 Attributes: map[string]string{ 1959 "route.#": "0", 1960 }, 1961 }, 1962 1963 Config: map[string]interface{}{}, 1964 1965 Diff: nil, 1966 1967 Err: false, 1968 }, 1969 1970 "#50 - A set computed element shouldn't cause a diff": { 1971 Schema: map[string]*Schema{ 1972 "active": &Schema{ 1973 Type: TypeBool, 1974 Computed: true, 1975 ForceNew: true, 1976 }, 1977 }, 1978 1979 State: &terraform.InstanceState{ 1980 Attributes: map[string]string{ 1981 "active": "true", 1982 }, 1983 }, 1984 1985 Config: map[string]interface{}{}, 1986 1987 Diff: nil, 1988 1989 Err: false, 1990 }, 1991 1992 "#51 - An empty set should show up in the diff": { 1993 Schema: map[string]*Schema{ 1994 "instances": &Schema{ 1995 Type: TypeSet, 1996 Elem: &Schema{Type: TypeString}, 1997 Optional: true, 1998 ForceNew: true, 1999 Set: func(v interface{}) int { 2000 return len(v.(string)) 2001 }, 2002 }, 2003 }, 2004 2005 State: &terraform.InstanceState{ 2006 Attributes: map[string]string{ 2007 "instances.#": "1", 2008 "instances.3": "foo", 2009 }, 2010 }, 2011 2012 Config: map[string]interface{}{}, 2013 2014 Diff: &terraform.InstanceDiff{ 2015 Attributes: map[string]*terraform.ResourceAttrDiff{ 2016 "instances.#": &terraform.ResourceAttrDiff{ 2017 Old: "1", 2018 New: "0", 2019 RequiresNew: true, 2020 }, 2021 "instances.3": &terraform.ResourceAttrDiff{ 2022 Old: "foo", 2023 New: "", 2024 NewRemoved: true, 2025 }, 2026 }, 2027 }, 2028 2029 Err: false, 2030 }, 2031 2032 "#52 - Map with empty value": { 2033 Schema: map[string]*Schema{ 2034 "vars": &Schema{ 2035 Type: TypeMap, 2036 }, 2037 }, 2038 2039 State: nil, 2040 2041 Config: map[string]interface{}{ 2042 "vars": map[string]interface{}{ 2043 "foo": "", 2044 }, 2045 }, 2046 2047 Diff: &terraform.InstanceDiff{ 2048 Attributes: map[string]*terraform.ResourceAttrDiff{ 2049 "vars.%": &terraform.ResourceAttrDiff{ 2050 Old: "0", 2051 New: "1", 2052 }, 2053 "vars.foo": &terraform.ResourceAttrDiff{ 2054 Old: "", 2055 New: "", 2056 }, 2057 }, 2058 }, 2059 2060 Err: false, 2061 }, 2062 2063 "#53 - Unset bool, not in state": { 2064 Schema: map[string]*Schema{ 2065 "force": &Schema{ 2066 Type: TypeBool, 2067 Optional: true, 2068 ForceNew: true, 2069 }, 2070 }, 2071 2072 State: nil, 2073 2074 Config: map[string]interface{}{}, 2075 2076 Diff: nil, 2077 2078 Err: false, 2079 }, 2080 2081 "#54 - Unset set, not in state": { 2082 Schema: map[string]*Schema{ 2083 "metadata_keys": &Schema{ 2084 Type: TypeSet, 2085 Optional: true, 2086 ForceNew: true, 2087 Elem: &Schema{Type: TypeInt}, 2088 Set: func(interface{}) int { return 0 }, 2089 }, 2090 }, 2091 2092 State: nil, 2093 2094 Config: map[string]interface{}{}, 2095 2096 Diff: nil, 2097 2098 Err: false, 2099 }, 2100 2101 "#55 - Unset list in state, should not show up computed": { 2102 Schema: map[string]*Schema{ 2103 "metadata_keys": &Schema{ 2104 Type: TypeList, 2105 Optional: true, 2106 Computed: true, 2107 ForceNew: true, 2108 Elem: &Schema{Type: TypeInt}, 2109 }, 2110 }, 2111 2112 State: &terraform.InstanceState{ 2113 Attributes: map[string]string{ 2114 "metadata_keys.#": "0", 2115 }, 2116 }, 2117 2118 Config: map[string]interface{}{}, 2119 2120 Diff: nil, 2121 2122 Err: false, 2123 }, 2124 2125 "#56 - Set element computed substring": { 2126 Schema: map[string]*Schema{ 2127 "ports": &Schema{ 2128 Type: TypeSet, 2129 Required: true, 2130 Elem: &Schema{Type: TypeInt}, 2131 Set: func(a interface{}) int { 2132 return a.(int) 2133 }, 2134 }, 2135 }, 2136 2137 State: nil, 2138 2139 Config: map[string]interface{}{ 2140 "ports": []interface{}{1, "${var.foo}32"}, 2141 }, 2142 2143 ConfigVariables: map[string]ast.Variable{ 2144 "var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue), 2145 }, 2146 2147 Diff: &terraform.InstanceDiff{ 2148 Attributes: map[string]*terraform.ResourceAttrDiff{ 2149 "ports.#": &terraform.ResourceAttrDiff{ 2150 Old: "", 2151 New: "", 2152 NewComputed: true, 2153 }, 2154 }, 2155 }, 2156 2157 Err: false, 2158 }, 2159 2160 "#57 Computed map without config that's known to be empty does not generate diff": { 2161 Schema: map[string]*Schema{ 2162 "tags": &Schema{ 2163 Type: TypeMap, 2164 Computed: true, 2165 }, 2166 }, 2167 2168 Config: nil, 2169 2170 State: &terraform.InstanceState{ 2171 Attributes: map[string]string{ 2172 "tags.%": "0", 2173 }, 2174 }, 2175 2176 Diff: nil, 2177 2178 Err: false, 2179 }, 2180 2181 "#58 Set with hyphen keys": { 2182 Schema: map[string]*Schema{ 2183 "route": &Schema{ 2184 Type: TypeSet, 2185 Optional: true, 2186 Elem: &Resource{ 2187 Schema: map[string]*Schema{ 2188 "index": &Schema{ 2189 Type: TypeInt, 2190 Required: true, 2191 }, 2192 2193 "gateway-name": &Schema{ 2194 Type: TypeString, 2195 Optional: true, 2196 }, 2197 }, 2198 }, 2199 Set: func(v interface{}) int { 2200 m := v.(map[string]interface{}) 2201 return m["index"].(int) 2202 }, 2203 }, 2204 }, 2205 2206 State: nil, 2207 2208 Config: map[string]interface{}{ 2209 "route": []map[string]interface{}{ 2210 map[string]interface{}{ 2211 "index": "1", 2212 "gateway-name": "hello", 2213 }, 2214 }, 2215 }, 2216 2217 Diff: &terraform.InstanceDiff{ 2218 Attributes: map[string]*terraform.ResourceAttrDiff{ 2219 "route.#": &terraform.ResourceAttrDiff{ 2220 Old: "0", 2221 New: "1", 2222 }, 2223 "route.1.index": &terraform.ResourceAttrDiff{ 2224 Old: "", 2225 New: "1", 2226 }, 2227 "route.1.gateway-name": &terraform.ResourceAttrDiff{ 2228 Old: "", 2229 New: "hello", 2230 }, 2231 }, 2232 }, 2233 2234 Err: false, 2235 }, 2236 2237 "#59: StateFunc in nested set (#1759)": { 2238 Schema: map[string]*Schema{ 2239 "service_account": &Schema{ 2240 Type: TypeList, 2241 Optional: true, 2242 ForceNew: true, 2243 Elem: &Resource{ 2244 Schema: map[string]*Schema{ 2245 "scopes": &Schema{ 2246 Type: TypeSet, 2247 Required: true, 2248 ForceNew: true, 2249 Elem: &Schema{ 2250 Type: TypeString, 2251 StateFunc: func(v interface{}) string { 2252 return v.(string) + "!" 2253 }, 2254 }, 2255 Set: func(v interface{}) int { 2256 i, err := strconv.Atoi(v.(string)) 2257 if err != nil { 2258 t.Fatalf("err: %s", err) 2259 } 2260 return i 2261 }, 2262 }, 2263 }, 2264 }, 2265 }, 2266 }, 2267 2268 State: nil, 2269 2270 Config: map[string]interface{}{ 2271 "service_account": []map[string]interface{}{ 2272 { 2273 "scopes": []interface{}{"123"}, 2274 }, 2275 }, 2276 }, 2277 2278 Diff: &terraform.InstanceDiff{ 2279 Attributes: map[string]*terraform.ResourceAttrDiff{ 2280 "service_account.#": &terraform.ResourceAttrDiff{ 2281 Old: "0", 2282 New: "1", 2283 RequiresNew: true, 2284 }, 2285 "service_account.0.scopes.#": &terraform.ResourceAttrDiff{ 2286 Old: "0", 2287 New: "1", 2288 RequiresNew: true, 2289 }, 2290 "service_account.0.scopes.123": &terraform.ResourceAttrDiff{ 2291 Old: "", 2292 New: "123!", 2293 NewExtra: "123", 2294 RequiresNew: true, 2295 }, 2296 }, 2297 }, 2298 2299 Err: false, 2300 }, 2301 2302 "#60 - Removing set elements": { 2303 Schema: map[string]*Schema{ 2304 "instances": &Schema{ 2305 Type: TypeSet, 2306 Elem: &Schema{Type: TypeString}, 2307 Optional: true, 2308 ForceNew: true, 2309 Set: func(v interface{}) int { 2310 return len(v.(string)) 2311 }, 2312 }, 2313 }, 2314 2315 State: &terraform.InstanceState{ 2316 Attributes: map[string]string{ 2317 "instances.#": "2", 2318 "instances.3": "333", 2319 "instances.2": "22", 2320 }, 2321 }, 2322 2323 Config: map[string]interface{}{ 2324 "instances": []interface{}{"333", "4444"}, 2325 }, 2326 2327 Diff: &terraform.InstanceDiff{ 2328 Attributes: map[string]*terraform.ResourceAttrDiff{ 2329 "instances.#": &terraform.ResourceAttrDiff{ 2330 Old: "2", 2331 New: "2", 2332 }, 2333 "instances.2": &terraform.ResourceAttrDiff{ 2334 Old: "22", 2335 New: "", 2336 NewRemoved: true, 2337 }, 2338 "instances.3": &terraform.ResourceAttrDiff{ 2339 Old: "333", 2340 New: "333", 2341 RequiresNew: true, 2342 }, 2343 "instances.4": &terraform.ResourceAttrDiff{ 2344 Old: "", 2345 New: "4444", 2346 RequiresNew: true, 2347 }, 2348 }, 2349 }, 2350 2351 Err: false, 2352 }, 2353 2354 "Bools can be set with 0/1 in config, still get true/false": { 2355 Schema: map[string]*Schema{ 2356 "one": &Schema{ 2357 Type: TypeBool, 2358 Optional: true, 2359 }, 2360 "two": &Schema{ 2361 Type: TypeBool, 2362 Optional: true, 2363 }, 2364 "three": &Schema{ 2365 Type: TypeBool, 2366 Optional: true, 2367 }, 2368 }, 2369 2370 State: &terraform.InstanceState{ 2371 Attributes: map[string]string{ 2372 "one": "false", 2373 "two": "true", 2374 "three": "true", 2375 }, 2376 }, 2377 2378 Config: map[string]interface{}{ 2379 "one": "1", 2380 "two": "0", 2381 }, 2382 2383 Diff: &terraform.InstanceDiff{ 2384 Attributes: map[string]*terraform.ResourceAttrDiff{ 2385 "one": &terraform.ResourceAttrDiff{ 2386 Old: "false", 2387 New: "true", 2388 }, 2389 "two": &terraform.ResourceAttrDiff{ 2390 Old: "true", 2391 New: "false", 2392 }, 2393 "three": &terraform.ResourceAttrDiff{ 2394 Old: "true", 2395 New: "false", 2396 NewRemoved: true, 2397 }, 2398 }, 2399 }, 2400 2401 Err: false, 2402 }, 2403 } 2404 2405 for tn, tc := range cases { 2406 c, err := config.NewRawConfig(tc.Config) 2407 if err != nil { 2408 t.Fatalf("#%q err: %s", tn, err) 2409 } 2410 2411 if len(tc.ConfigVariables) > 0 { 2412 if err := c.Interpolate(tc.ConfigVariables); err != nil { 2413 t.Fatalf("#%q err: %s", tn, err) 2414 } 2415 } 2416 2417 d, err := schemaMap(tc.Schema).Diff( 2418 tc.State, terraform.NewResourceConfig(c)) 2419 if err != nil != tc.Err { 2420 t.Fatalf("#%q err: %s", tn, err) 2421 } 2422 2423 if !reflect.DeepEqual(tc.Diff, d) { 2424 t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Diff, d) 2425 } 2426 } 2427 } 2428 2429 func TestSchemaMap_Input(t *testing.T) { 2430 cases := map[string]struct { 2431 Schema map[string]*Schema 2432 Config map[string]interface{} 2433 Input map[string]string 2434 Result map[string]interface{} 2435 Err bool 2436 }{ 2437 /* 2438 * String decode 2439 */ 2440 2441 "uses input on optional field with no config": { 2442 Schema: map[string]*Schema{ 2443 "availability_zone": &Schema{ 2444 Type: TypeString, 2445 Optional: true, 2446 }, 2447 }, 2448 2449 Input: map[string]string{ 2450 "availability_zone": "foo", 2451 }, 2452 2453 Result: map[string]interface{}{ 2454 "availability_zone": "foo", 2455 }, 2456 2457 Err: false, 2458 }, 2459 2460 "input ignored when config has a value": { 2461 Schema: map[string]*Schema{ 2462 "availability_zone": &Schema{ 2463 Type: TypeString, 2464 Optional: true, 2465 }, 2466 }, 2467 2468 Config: map[string]interface{}{ 2469 "availability_zone": "bar", 2470 }, 2471 2472 Input: map[string]string{ 2473 "availability_zone": "foo", 2474 }, 2475 2476 Result: map[string]interface{}{}, 2477 2478 Err: false, 2479 }, 2480 2481 "input ignored when schema has a default": { 2482 Schema: map[string]*Schema{ 2483 "availability_zone": &Schema{ 2484 Type: TypeString, 2485 Default: "foo", 2486 Optional: true, 2487 }, 2488 }, 2489 2490 Input: map[string]string{ 2491 "availability_zone": "bar", 2492 }, 2493 2494 Result: map[string]interface{}{}, 2495 2496 Err: false, 2497 }, 2498 2499 "input ignored when default function returns a value": { 2500 Schema: map[string]*Schema{ 2501 "availability_zone": &Schema{ 2502 Type: TypeString, 2503 DefaultFunc: func() (interface{}, error) { 2504 return "foo", nil 2505 }, 2506 Optional: true, 2507 }, 2508 }, 2509 2510 Input: map[string]string{ 2511 "availability_zone": "bar", 2512 }, 2513 2514 Result: map[string]interface{}{}, 2515 2516 Err: false, 2517 }, 2518 2519 "input ignored when default function returns an empty string": { 2520 Schema: map[string]*Schema{ 2521 "availability_zone": &Schema{ 2522 Type: TypeString, 2523 Default: "", 2524 Optional: true, 2525 }, 2526 }, 2527 2528 Input: map[string]string{ 2529 "availability_zone": "bar", 2530 }, 2531 2532 Result: map[string]interface{}{}, 2533 2534 Err: false, 2535 }, 2536 2537 "input used when default function returns nil": { 2538 Schema: map[string]*Schema{ 2539 "availability_zone": &Schema{ 2540 Type: TypeString, 2541 DefaultFunc: func() (interface{}, error) { 2542 return nil, nil 2543 }, 2544 Optional: true, 2545 }, 2546 }, 2547 2548 Input: map[string]string{ 2549 "availability_zone": "bar", 2550 }, 2551 2552 Result: map[string]interface{}{ 2553 "availability_zone": "bar", 2554 }, 2555 2556 Err: false, 2557 }, 2558 } 2559 2560 for i, tc := range cases { 2561 if tc.Config == nil { 2562 tc.Config = make(map[string]interface{}) 2563 } 2564 2565 c, err := config.NewRawConfig(tc.Config) 2566 if err != nil { 2567 t.Fatalf("err: %s", err) 2568 } 2569 2570 input := new(terraform.MockUIInput) 2571 input.InputReturnMap = tc.Input 2572 2573 rc := terraform.NewResourceConfig(c) 2574 rc.Config = make(map[string]interface{}) 2575 2576 actual, err := schemaMap(tc.Schema).Input(input, rc) 2577 if err != nil != tc.Err { 2578 t.Fatalf("#%v err: %s", i, err) 2579 } 2580 2581 if !reflect.DeepEqual(tc.Result, actual.Config) { 2582 t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result) 2583 } 2584 } 2585 } 2586 2587 func TestSchemaMap_InputDefault(t *testing.T) { 2588 emptyConfig := make(map[string]interface{}) 2589 c, err := config.NewRawConfig(emptyConfig) 2590 if err != nil { 2591 t.Fatalf("err: %s", err) 2592 } 2593 rc := terraform.NewResourceConfig(c) 2594 rc.Config = make(map[string]interface{}) 2595 2596 input := new(terraform.MockUIInput) 2597 input.InputFn = func(opts *terraform.InputOpts) (string, error) { 2598 t.Fatalf("InputFn should not be called on: %#v", opts) 2599 return "", nil 2600 } 2601 2602 schema := map[string]*Schema{ 2603 "availability_zone": &Schema{ 2604 Type: TypeString, 2605 Default: "foo", 2606 Optional: true, 2607 }, 2608 } 2609 actual, err := schemaMap(schema).Input(input, rc) 2610 if err != nil { 2611 t.Fatalf("err: %s", err) 2612 } 2613 2614 expected := map[string]interface{}{} 2615 2616 if !reflect.DeepEqual(expected, actual.Config) { 2617 t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected) 2618 } 2619 } 2620 2621 func TestSchemaMap_InputDeprecated(t *testing.T) { 2622 emptyConfig := make(map[string]interface{}) 2623 c, err := config.NewRawConfig(emptyConfig) 2624 if err != nil { 2625 t.Fatalf("err: %s", err) 2626 } 2627 rc := terraform.NewResourceConfig(c) 2628 rc.Config = make(map[string]interface{}) 2629 2630 input := new(terraform.MockUIInput) 2631 input.InputFn = func(opts *terraform.InputOpts) (string, error) { 2632 t.Fatalf("InputFn should not be called on: %#v", opts) 2633 return "", nil 2634 } 2635 2636 schema := map[string]*Schema{ 2637 "availability_zone": &Schema{ 2638 Type: TypeString, 2639 Deprecated: "long gone", 2640 Optional: true, 2641 }, 2642 } 2643 actual, err := schemaMap(schema).Input(input, rc) 2644 if err != nil { 2645 t.Fatalf("err: %s", err) 2646 } 2647 2648 expected := map[string]interface{}{} 2649 2650 if !reflect.DeepEqual(expected, actual.Config) { 2651 t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected) 2652 } 2653 } 2654 2655 func TestSchemaMap_InternalValidate(t *testing.T) { 2656 cases := map[string]struct { 2657 In map[string]*Schema 2658 Err bool 2659 }{ 2660 "nothing": { 2661 nil, 2662 false, 2663 }, 2664 2665 "Both optional and required": { 2666 map[string]*Schema{ 2667 "foo": &Schema{ 2668 Type: TypeInt, 2669 Optional: true, 2670 Required: true, 2671 }, 2672 }, 2673 true, 2674 }, 2675 2676 "No optional and no required": { 2677 map[string]*Schema{ 2678 "foo": &Schema{ 2679 Type: TypeInt, 2680 }, 2681 }, 2682 true, 2683 }, 2684 2685 "Missing Type": { 2686 map[string]*Schema{ 2687 "foo": &Schema{ 2688 Required: true, 2689 }, 2690 }, 2691 true, 2692 }, 2693 2694 "Required but computed": { 2695 map[string]*Schema{ 2696 "foo": &Schema{ 2697 Type: TypeInt, 2698 Required: true, 2699 Computed: true, 2700 }, 2701 }, 2702 true, 2703 }, 2704 2705 "Looks good": { 2706 map[string]*Schema{ 2707 "foo": &Schema{ 2708 Type: TypeString, 2709 Required: true, 2710 }, 2711 }, 2712 false, 2713 }, 2714 2715 "Computed but has default": { 2716 map[string]*Schema{ 2717 "foo": &Schema{ 2718 Type: TypeInt, 2719 Optional: true, 2720 Computed: true, 2721 Default: "foo", 2722 }, 2723 }, 2724 true, 2725 }, 2726 2727 "Required but has default": { 2728 map[string]*Schema{ 2729 "foo": &Schema{ 2730 Type: TypeInt, 2731 Optional: true, 2732 Required: true, 2733 Default: "foo", 2734 }, 2735 }, 2736 true, 2737 }, 2738 2739 "List element not set": { 2740 map[string]*Schema{ 2741 "foo": &Schema{ 2742 Type: TypeList, 2743 }, 2744 }, 2745 true, 2746 }, 2747 2748 "List default": { 2749 map[string]*Schema{ 2750 "foo": &Schema{ 2751 Type: TypeList, 2752 Elem: &Schema{Type: TypeInt}, 2753 Default: "foo", 2754 }, 2755 }, 2756 true, 2757 }, 2758 2759 "List element computed": { 2760 map[string]*Schema{ 2761 "foo": &Schema{ 2762 Type: TypeList, 2763 Optional: true, 2764 Elem: &Schema{ 2765 Type: TypeInt, 2766 Computed: true, 2767 }, 2768 }, 2769 }, 2770 true, 2771 }, 2772 2773 "List element with Set set": { 2774 map[string]*Schema{ 2775 "foo": &Schema{ 2776 Type: TypeList, 2777 Elem: &Schema{Type: TypeInt}, 2778 Set: func(interface{}) int { return 0 }, 2779 Optional: true, 2780 }, 2781 }, 2782 true, 2783 }, 2784 2785 "Set element with no Set set": { 2786 map[string]*Schema{ 2787 "foo": &Schema{ 2788 Type: TypeSet, 2789 Elem: &Schema{Type: TypeInt}, 2790 Optional: true, 2791 }, 2792 }, 2793 false, 2794 }, 2795 2796 "Required but computedWhen": { 2797 map[string]*Schema{ 2798 "foo": &Schema{ 2799 Type: TypeInt, 2800 Required: true, 2801 ComputedWhen: []string{"foo"}, 2802 }, 2803 }, 2804 true, 2805 }, 2806 2807 "Conflicting attributes cannot be required": { 2808 map[string]*Schema{ 2809 "blacklist": &Schema{ 2810 Type: TypeBool, 2811 Required: true, 2812 }, 2813 "whitelist": &Schema{ 2814 Type: TypeBool, 2815 Optional: true, 2816 ConflictsWith: []string{"blacklist"}, 2817 }, 2818 }, 2819 true, 2820 }, 2821 2822 "Attribute with conflicts cannot be required": { 2823 map[string]*Schema{ 2824 "whitelist": &Schema{ 2825 Type: TypeBool, 2826 Required: true, 2827 ConflictsWith: []string{"blacklist"}, 2828 }, 2829 }, 2830 true, 2831 }, 2832 2833 "ConflictsWith cannot be used w/ Computed": { 2834 map[string]*Schema{ 2835 "blacklist": &Schema{ 2836 Type: TypeBool, 2837 Computed: true, 2838 }, 2839 "whitelist": &Schema{ 2840 Type: TypeBool, 2841 Optional: true, 2842 ConflictsWith: []string{"blacklist"}, 2843 }, 2844 }, 2845 true, 2846 }, 2847 2848 "ConflictsWith cannot be used w/ ComputedWhen": { 2849 map[string]*Schema{ 2850 "blacklist": &Schema{ 2851 Type: TypeBool, 2852 ComputedWhen: []string{"foor"}, 2853 }, 2854 "whitelist": &Schema{ 2855 Type: TypeBool, 2856 Required: true, 2857 ConflictsWith: []string{"blacklist"}, 2858 }, 2859 }, 2860 true, 2861 }, 2862 2863 "Sub-resource invalid": { 2864 map[string]*Schema{ 2865 "foo": &Schema{ 2866 Type: TypeList, 2867 Optional: true, 2868 Elem: &Resource{ 2869 Schema: map[string]*Schema{ 2870 "foo": new(Schema), 2871 }, 2872 }, 2873 }, 2874 }, 2875 true, 2876 }, 2877 2878 "Sub-resource valid": { 2879 map[string]*Schema{ 2880 "foo": &Schema{ 2881 Type: TypeList, 2882 Optional: true, 2883 Elem: &Resource{ 2884 Schema: map[string]*Schema{ 2885 "foo": &Schema{ 2886 Type: TypeInt, 2887 Optional: true, 2888 }, 2889 }, 2890 }, 2891 }, 2892 }, 2893 false, 2894 }, 2895 2896 "ValidateFunc on non-primitive": { 2897 map[string]*Schema{ 2898 "foo": &Schema{ 2899 Type: TypeSet, 2900 Required: true, 2901 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 2902 return 2903 }, 2904 }, 2905 }, 2906 true, 2907 }, 2908 } 2909 2910 for tn, tc := range cases { 2911 err := schemaMap(tc.In).InternalValidate(schemaMap{}) 2912 if err != nil != tc.Err { 2913 if tc.Err { 2914 t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In) 2915 } 2916 t.Fatalf("%q: Unexpected error occurred:\n\n%#v", tn, tc.In) 2917 } 2918 } 2919 2920 } 2921 2922 func TestSchemaMap_Validate(t *testing.T) { 2923 cases := map[string]struct { 2924 Schema map[string]*Schema 2925 Config map[string]interface{} 2926 Vars map[string]string 2927 Err bool 2928 Errors []error 2929 Warnings []string 2930 }{ 2931 "Good": { 2932 Schema: map[string]*Schema{ 2933 "availability_zone": &Schema{ 2934 Type: TypeString, 2935 Optional: true, 2936 Computed: true, 2937 ForceNew: true, 2938 }, 2939 }, 2940 2941 Config: map[string]interface{}{ 2942 "availability_zone": "foo", 2943 }, 2944 }, 2945 2946 "Good, because the var is not set and that error will come elsewhere": { 2947 Schema: map[string]*Schema{ 2948 "size": &Schema{ 2949 Type: TypeInt, 2950 Required: true, 2951 }, 2952 }, 2953 2954 Config: map[string]interface{}{ 2955 "size": "${var.foo}", 2956 }, 2957 2958 Vars: map[string]string{ 2959 "var.foo": config.UnknownVariableValue, 2960 }, 2961 }, 2962 2963 "Required field not set": { 2964 Schema: map[string]*Schema{ 2965 "availability_zone": &Schema{ 2966 Type: TypeString, 2967 Required: true, 2968 }, 2969 }, 2970 2971 Config: map[string]interface{}{}, 2972 2973 Err: true, 2974 }, 2975 2976 "Invalid basic type": { 2977 Schema: map[string]*Schema{ 2978 "port": &Schema{ 2979 Type: TypeInt, 2980 Required: true, 2981 }, 2982 }, 2983 2984 Config: map[string]interface{}{ 2985 "port": "I am invalid", 2986 }, 2987 2988 Err: true, 2989 }, 2990 2991 "Invalid complex type": { 2992 Schema: map[string]*Schema{ 2993 "user_data": &Schema{ 2994 Type: TypeString, 2995 Optional: true, 2996 }, 2997 }, 2998 2999 Config: map[string]interface{}{ 3000 "user_data": []interface{}{ 3001 map[string]interface{}{ 3002 "foo": "bar", 3003 }, 3004 }, 3005 }, 3006 3007 Err: true, 3008 }, 3009 3010 "Bad type, interpolated": { 3011 Schema: map[string]*Schema{ 3012 "size": &Schema{ 3013 Type: TypeInt, 3014 Required: true, 3015 }, 3016 }, 3017 3018 Config: map[string]interface{}{ 3019 "size": "${var.foo}", 3020 }, 3021 3022 Vars: map[string]string{ 3023 "var.foo": "nope", 3024 }, 3025 3026 Err: true, 3027 }, 3028 3029 "Required but has DefaultFunc": { 3030 Schema: map[string]*Schema{ 3031 "availability_zone": &Schema{ 3032 Type: TypeString, 3033 Required: true, 3034 DefaultFunc: func() (interface{}, error) { 3035 return "foo", nil 3036 }, 3037 }, 3038 }, 3039 3040 Config: nil, 3041 }, 3042 3043 "Required but has DefaultFunc return nil": { 3044 Schema: map[string]*Schema{ 3045 "availability_zone": &Schema{ 3046 Type: TypeString, 3047 Required: true, 3048 DefaultFunc: func() (interface{}, error) { 3049 return nil, nil 3050 }, 3051 }, 3052 }, 3053 3054 Config: nil, 3055 3056 Err: true, 3057 }, 3058 3059 "Optional sub-resource": { 3060 Schema: map[string]*Schema{ 3061 "ingress": &Schema{ 3062 Type: TypeList, 3063 Elem: &Resource{ 3064 Schema: map[string]*Schema{ 3065 "from": &Schema{ 3066 Type: TypeInt, 3067 Required: true, 3068 }, 3069 }, 3070 }, 3071 }, 3072 }, 3073 3074 Config: map[string]interface{}{}, 3075 3076 Err: false, 3077 }, 3078 3079 "Sub-resource is the wrong type": { 3080 Schema: map[string]*Schema{ 3081 "ingress": &Schema{ 3082 Type: TypeList, 3083 Required: true, 3084 Elem: &Resource{ 3085 Schema: map[string]*Schema{ 3086 "from": &Schema{ 3087 Type: TypeInt, 3088 Required: true, 3089 }, 3090 }, 3091 }, 3092 }, 3093 }, 3094 3095 Config: map[string]interface{}{ 3096 "ingress": []interface{}{"foo"}, 3097 }, 3098 3099 Err: true, 3100 }, 3101 3102 "Not a list": { 3103 Schema: map[string]*Schema{ 3104 "ingress": &Schema{ 3105 Type: TypeList, 3106 Elem: &Resource{ 3107 Schema: map[string]*Schema{ 3108 "from": &Schema{ 3109 Type: TypeInt, 3110 Required: true, 3111 }, 3112 }, 3113 }, 3114 }, 3115 }, 3116 3117 Config: map[string]interface{}{ 3118 "ingress": "foo", 3119 }, 3120 3121 Err: true, 3122 }, 3123 3124 "Required sub-resource field": { 3125 Schema: map[string]*Schema{ 3126 "ingress": &Schema{ 3127 Type: TypeList, 3128 Elem: &Resource{ 3129 Schema: map[string]*Schema{ 3130 "from": &Schema{ 3131 Type: TypeInt, 3132 Required: true, 3133 }, 3134 }, 3135 }, 3136 }, 3137 }, 3138 3139 Config: map[string]interface{}{ 3140 "ingress": []interface{}{ 3141 map[string]interface{}{}, 3142 }, 3143 }, 3144 3145 Err: true, 3146 }, 3147 3148 "Good sub-resource": { 3149 Schema: map[string]*Schema{ 3150 "ingress": &Schema{ 3151 Type: TypeList, 3152 Optional: true, 3153 Elem: &Resource{ 3154 Schema: map[string]*Schema{ 3155 "from": &Schema{ 3156 Type: TypeInt, 3157 Required: true, 3158 }, 3159 }, 3160 }, 3161 }, 3162 }, 3163 3164 Config: map[string]interface{}{ 3165 "ingress": []interface{}{ 3166 map[string]interface{}{ 3167 "from": 80, 3168 }, 3169 }, 3170 }, 3171 3172 Err: false, 3173 }, 3174 3175 "Invalid/unknown field": { 3176 Schema: map[string]*Schema{ 3177 "availability_zone": &Schema{ 3178 Type: TypeString, 3179 Optional: true, 3180 Computed: true, 3181 ForceNew: true, 3182 }, 3183 }, 3184 3185 Config: map[string]interface{}{ 3186 "foo": "bar", 3187 }, 3188 3189 Err: true, 3190 }, 3191 3192 "Invalid/unknown field with computed value": { 3193 Schema: map[string]*Schema{ 3194 "availability_zone": &Schema{ 3195 Type: TypeString, 3196 Optional: true, 3197 Computed: true, 3198 ForceNew: true, 3199 }, 3200 }, 3201 3202 Config: map[string]interface{}{ 3203 "foo": "${var.foo}", 3204 }, 3205 3206 Vars: map[string]string{ 3207 "var.foo": config.UnknownVariableValue, 3208 }, 3209 3210 Err: true, 3211 }, 3212 3213 "Computed field set": { 3214 Schema: map[string]*Schema{ 3215 "availability_zone": &Schema{ 3216 Type: TypeString, 3217 Computed: true, 3218 }, 3219 }, 3220 3221 Config: map[string]interface{}{ 3222 "availability_zone": "bar", 3223 }, 3224 3225 Err: true, 3226 }, 3227 3228 "Not a set": { 3229 Schema: map[string]*Schema{ 3230 "ports": &Schema{ 3231 Type: TypeSet, 3232 Required: true, 3233 Elem: &Schema{Type: TypeInt}, 3234 Set: func(a interface{}) int { 3235 return a.(int) 3236 }, 3237 }, 3238 }, 3239 3240 Config: map[string]interface{}{ 3241 "ports": "foo", 3242 }, 3243 3244 Err: true, 3245 }, 3246 3247 "Maps": { 3248 Schema: map[string]*Schema{ 3249 "user_data": &Schema{ 3250 Type: TypeMap, 3251 Optional: true, 3252 }, 3253 }, 3254 3255 Config: map[string]interface{}{ 3256 "user_data": "foo", 3257 }, 3258 3259 Err: true, 3260 }, 3261 3262 "Good map: data surrounded by extra slice": { 3263 Schema: map[string]*Schema{ 3264 "user_data": &Schema{ 3265 Type: TypeMap, 3266 Optional: true, 3267 }, 3268 }, 3269 3270 Config: map[string]interface{}{ 3271 "user_data": []interface{}{ 3272 map[string]interface{}{ 3273 "foo": "bar", 3274 }, 3275 }, 3276 }, 3277 }, 3278 3279 "Good map": { 3280 Schema: map[string]*Schema{ 3281 "user_data": &Schema{ 3282 Type: TypeMap, 3283 Optional: true, 3284 }, 3285 }, 3286 3287 Config: map[string]interface{}{ 3288 "user_data": map[string]interface{}{ 3289 "foo": "bar", 3290 }, 3291 }, 3292 }, 3293 3294 "Bad map: just a slice": { 3295 Schema: map[string]*Schema{ 3296 "user_data": &Schema{ 3297 Type: TypeMap, 3298 Optional: true, 3299 }, 3300 }, 3301 3302 Config: map[string]interface{}{ 3303 "user_data": []interface{}{ 3304 "foo", 3305 }, 3306 }, 3307 3308 Err: true, 3309 }, 3310 3311 "Good set: config has slice with single interpolated value": { 3312 Schema: map[string]*Schema{ 3313 "security_groups": &Schema{ 3314 Type: TypeSet, 3315 Optional: true, 3316 Computed: true, 3317 ForceNew: true, 3318 Elem: &Schema{Type: TypeString}, 3319 Set: func(v interface{}) int { 3320 return len(v.(string)) 3321 }, 3322 }, 3323 }, 3324 3325 Config: map[string]interface{}{ 3326 "security_groups": []interface{}{"${var.foo}"}, 3327 }, 3328 3329 Err: false, 3330 }, 3331 3332 "Bad set: config has single interpolated value": { 3333 Schema: map[string]*Schema{ 3334 "security_groups": &Schema{ 3335 Type: TypeSet, 3336 Optional: true, 3337 Computed: true, 3338 ForceNew: true, 3339 Elem: &Schema{Type: TypeString}, 3340 }, 3341 }, 3342 3343 Config: map[string]interface{}{ 3344 "security_groups": "${var.foo}", 3345 }, 3346 3347 Err: true, 3348 }, 3349 3350 "Bad, subresource should not allow unknown elements": { 3351 Schema: map[string]*Schema{ 3352 "ingress": &Schema{ 3353 Type: TypeList, 3354 Optional: true, 3355 Elem: &Resource{ 3356 Schema: map[string]*Schema{ 3357 "port": &Schema{ 3358 Type: TypeInt, 3359 Required: true, 3360 }, 3361 }, 3362 }, 3363 }, 3364 }, 3365 3366 Config: map[string]interface{}{ 3367 "ingress": []interface{}{ 3368 map[string]interface{}{ 3369 "port": 80, 3370 "other": "yes", 3371 }, 3372 }, 3373 }, 3374 3375 Err: true, 3376 }, 3377 3378 "Bad, subresource should not allow invalid types": { 3379 Schema: map[string]*Schema{ 3380 "ingress": &Schema{ 3381 Type: TypeList, 3382 Optional: true, 3383 Elem: &Resource{ 3384 Schema: map[string]*Schema{ 3385 "port": &Schema{ 3386 Type: TypeInt, 3387 Required: true, 3388 }, 3389 }, 3390 }, 3391 }, 3392 }, 3393 3394 Config: map[string]interface{}{ 3395 "ingress": []interface{}{ 3396 map[string]interface{}{ 3397 "port": "bad", 3398 }, 3399 }, 3400 }, 3401 3402 Err: true, 3403 }, 3404 3405 "Bad, should not allow lists to be assigned to string attributes": { 3406 Schema: map[string]*Schema{ 3407 "availability_zone": &Schema{ 3408 Type: TypeString, 3409 Required: true, 3410 }, 3411 }, 3412 3413 Config: map[string]interface{}{ 3414 "availability_zone": []interface{}{"foo", "bar", "baz"}, 3415 }, 3416 3417 Err: true, 3418 }, 3419 3420 "Bad, should not allow maps to be assigned to string attributes": { 3421 Schema: map[string]*Schema{ 3422 "availability_zone": &Schema{ 3423 Type: TypeString, 3424 Required: true, 3425 }, 3426 }, 3427 3428 Config: map[string]interface{}{ 3429 "availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"}, 3430 }, 3431 3432 Err: true, 3433 }, 3434 3435 "Deprecated attribute usage generates warning, but not error": { 3436 Schema: map[string]*Schema{ 3437 "old_news": &Schema{ 3438 Type: TypeString, 3439 Optional: true, 3440 Deprecated: "please use 'new_news' instead", 3441 }, 3442 }, 3443 3444 Config: map[string]interface{}{ 3445 "old_news": "extra extra!", 3446 }, 3447 3448 Err: false, 3449 3450 Warnings: []string{ 3451 "\"old_news\": [DEPRECATED] please use 'new_news' instead", 3452 }, 3453 }, 3454 3455 "Deprecated generates no warnings if attr not used": { 3456 Schema: map[string]*Schema{ 3457 "old_news": &Schema{ 3458 Type: TypeString, 3459 Optional: true, 3460 Deprecated: "please use 'new_news' instead", 3461 }, 3462 }, 3463 3464 Err: false, 3465 3466 Warnings: nil, 3467 }, 3468 3469 "Removed attribute usage generates error": { 3470 Schema: map[string]*Schema{ 3471 "long_gone": &Schema{ 3472 Type: TypeString, 3473 Optional: true, 3474 Removed: "no longer supported by Cloud API", 3475 }, 3476 }, 3477 3478 Config: map[string]interface{}{ 3479 "long_gone": "still here!", 3480 }, 3481 3482 Err: true, 3483 Errors: []error{ 3484 fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"), 3485 }, 3486 }, 3487 3488 "Removed generates no errors if attr not used": { 3489 Schema: map[string]*Schema{ 3490 "long_gone": &Schema{ 3491 Type: TypeString, 3492 Optional: true, 3493 Removed: "no longer supported by Cloud API", 3494 }, 3495 }, 3496 3497 Err: false, 3498 }, 3499 3500 "Conflicting attributes generate error": { 3501 Schema: map[string]*Schema{ 3502 "whitelist": &Schema{ 3503 Type: TypeString, 3504 Optional: true, 3505 }, 3506 "blacklist": &Schema{ 3507 Type: TypeString, 3508 Optional: true, 3509 ConflictsWith: []string{"whitelist"}, 3510 }, 3511 }, 3512 3513 Config: map[string]interface{}{ 3514 "whitelist": "white-val", 3515 "blacklist": "black-val", 3516 }, 3517 3518 Err: true, 3519 Errors: []error{ 3520 fmt.Errorf("\"blacklist\": conflicts with whitelist (\"white-val\")"), 3521 }, 3522 }, 3523 3524 "Required attribute & undefined conflicting optional are good": { 3525 Schema: map[string]*Schema{ 3526 "required_att": &Schema{ 3527 Type: TypeString, 3528 Required: true, 3529 }, 3530 "optional_att": &Schema{ 3531 Type: TypeString, 3532 Optional: true, 3533 ConflictsWith: []string{"required_att"}, 3534 }, 3535 }, 3536 3537 Config: map[string]interface{}{ 3538 "required_att": "required-val", 3539 }, 3540 3541 Err: false, 3542 }, 3543 3544 "Required conflicting attribute & defined optional generate error": { 3545 Schema: map[string]*Schema{ 3546 "required_att": &Schema{ 3547 Type: TypeString, 3548 Required: true, 3549 }, 3550 "optional_att": &Schema{ 3551 Type: TypeString, 3552 Optional: true, 3553 ConflictsWith: []string{"required_att"}, 3554 }, 3555 }, 3556 3557 Config: map[string]interface{}{ 3558 "required_att": "required-val", 3559 "optional_att": "optional-val", 3560 }, 3561 3562 Err: true, 3563 Errors: []error{ 3564 fmt.Errorf(`"optional_att": conflicts with required_att ("required-val")`), 3565 }, 3566 }, 3567 3568 "Good with ValidateFunc": { 3569 Schema: map[string]*Schema{ 3570 "validate_me": &Schema{ 3571 Type: TypeString, 3572 Required: true, 3573 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 3574 return 3575 }, 3576 }, 3577 }, 3578 Config: map[string]interface{}{ 3579 "validate_me": "valid", 3580 }, 3581 Err: false, 3582 }, 3583 3584 "Bad with ValidateFunc": { 3585 Schema: map[string]*Schema{ 3586 "validate_me": &Schema{ 3587 Type: TypeString, 3588 Required: true, 3589 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 3590 es = append(es, fmt.Errorf("something is not right here")) 3591 return 3592 }, 3593 }, 3594 }, 3595 Config: map[string]interface{}{ 3596 "validate_me": "invalid", 3597 }, 3598 Err: true, 3599 Errors: []error{ 3600 fmt.Errorf(`something is not right here`), 3601 }, 3602 }, 3603 3604 "ValidateFunc not called when type does not match": { 3605 Schema: map[string]*Schema{ 3606 "number": &Schema{ 3607 Type: TypeInt, 3608 Required: true, 3609 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 3610 t.Fatalf("Should not have gotten validate call") 3611 return 3612 }, 3613 }, 3614 }, 3615 Config: map[string]interface{}{ 3616 "number": "NaN", 3617 }, 3618 Err: true, 3619 }, 3620 3621 "ValidateFunc gets decoded type": { 3622 Schema: map[string]*Schema{ 3623 "maybe": &Schema{ 3624 Type: TypeBool, 3625 Required: true, 3626 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 3627 if _, ok := v.(bool); !ok { 3628 t.Fatalf("Expected bool, got: %#v", v) 3629 } 3630 return 3631 }, 3632 }, 3633 }, 3634 Config: map[string]interface{}{ 3635 "maybe": "true", 3636 }, 3637 }, 3638 3639 "ValidateFunc is not called with a computed value": { 3640 Schema: map[string]*Schema{ 3641 "validate_me": &Schema{ 3642 Type: TypeString, 3643 Required: true, 3644 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 3645 es = append(es, fmt.Errorf("something is not right here")) 3646 return 3647 }, 3648 }, 3649 }, 3650 Config: map[string]interface{}{ 3651 "validate_me": "${var.foo}", 3652 }, 3653 Vars: map[string]string{ 3654 "var.foo": config.UnknownVariableValue, 3655 }, 3656 3657 Err: false, 3658 }, 3659 } 3660 3661 for tn, tc := range cases { 3662 c, err := config.NewRawConfig(tc.Config) 3663 if err != nil { 3664 t.Fatalf("err: %s", err) 3665 } 3666 if tc.Vars != nil { 3667 vars := make(map[string]ast.Variable) 3668 for k, v := range tc.Vars { 3669 vars[k] = ast.Variable{Value: v, Type: ast.TypeString} 3670 } 3671 3672 if err := c.Interpolate(vars); err != nil { 3673 t.Fatalf("err: %s", err) 3674 } 3675 } 3676 3677 ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c)) 3678 if len(es) > 0 != tc.Err { 3679 if len(es) == 0 { 3680 t.Errorf("%q: no errors", tn) 3681 } 3682 3683 for _, e := range es { 3684 t.Errorf("%q: err: %s", tn, e) 3685 } 3686 3687 t.FailNow() 3688 } 3689 3690 if !reflect.DeepEqual(ws, tc.Warnings) { 3691 t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws) 3692 } 3693 3694 if tc.Errors != nil { 3695 if !reflect.DeepEqual(es, tc.Errors) { 3696 t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es) 3697 } 3698 } 3699 } 3700 } 3701 3702 func TestSchemaSet_ValidateMaxItems(t *testing.T) { 3703 cases := map[string]struct { 3704 Schema map[string]*Schema 3705 State *terraform.InstanceState 3706 Config map[string]interface{} 3707 ConfigVariables map[string]string 3708 Diff *terraform.InstanceDiff 3709 Err bool 3710 Errors []error 3711 }{ 3712 "#0": { 3713 Schema: map[string]*Schema{ 3714 "aliases": &Schema{ 3715 Type: TypeSet, 3716 Optional: true, 3717 MaxItems: 1, 3718 Elem: &Schema{Type: TypeString}, 3719 }, 3720 }, 3721 State: nil, 3722 Config: map[string]interface{}{ 3723 "aliases": []interface{}{"foo", "bar"}, 3724 }, 3725 Diff: nil, 3726 Err: true, 3727 Errors: []error{ 3728 fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"), 3729 }, 3730 }, 3731 "#1": { 3732 Schema: map[string]*Schema{ 3733 "aliases": &Schema{ 3734 Type: TypeSet, 3735 Optional: true, 3736 Elem: &Schema{Type: TypeString}, 3737 }, 3738 }, 3739 State: nil, 3740 Config: map[string]interface{}{ 3741 "aliases": []interface{}{"foo", "bar"}, 3742 }, 3743 Diff: nil, 3744 Err: false, 3745 Errors: nil, 3746 }, 3747 "#2": { 3748 Schema: map[string]*Schema{ 3749 "aliases": &Schema{ 3750 Type: TypeSet, 3751 Optional: true, 3752 MaxItems: 1, 3753 Elem: &Schema{Type: TypeString}, 3754 }, 3755 }, 3756 State: nil, 3757 Config: map[string]interface{}{ 3758 "aliases": []interface{}{"foo"}, 3759 }, 3760 Diff: nil, 3761 Err: false, 3762 Errors: nil, 3763 }, 3764 } 3765 3766 for tn, tc := range cases { 3767 c, err := config.NewRawConfig(tc.Config) 3768 if err != nil { 3769 t.Fatalf("%q: err: %s", tn, err) 3770 } 3771 _, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c)) 3772 3773 if len(es) > 0 != tc.Err { 3774 if len(es) == 0 { 3775 t.Errorf("%q: no errors", tn) 3776 } 3777 3778 for _, e := range es { 3779 t.Errorf("%q: err: %s", tn, e) 3780 } 3781 3782 t.FailNow() 3783 } 3784 3785 if tc.Errors != nil { 3786 if !reflect.DeepEqual(es, tc.Errors) { 3787 t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es) 3788 } 3789 } 3790 } 3791 }