github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/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 // #56 - Set element computed substring 2170 { 2171 Schema: map[string]*Schema{ 2172 "ports": &Schema{ 2173 Type: TypeSet, 2174 Required: true, 2175 Elem: &Schema{Type: TypeInt}, 2176 Set: func(a interface{}) int { 2177 return a.(int) 2178 }, 2179 }, 2180 }, 2181 2182 State: nil, 2183 2184 Config: map[string]interface{}{ 2185 "ports": []interface{}{1, "${var.foo}32"}, 2186 }, 2187 2188 ConfigVariables: map[string]string{ 2189 "var.foo": config.UnknownVariableValue, 2190 }, 2191 2192 Diff: &terraform.InstanceDiff{ 2193 Attributes: map[string]*terraform.ResourceAttrDiff{ 2194 "ports.#": &terraform.ResourceAttrDiff{ 2195 Old: "", 2196 New: "", 2197 NewComputed: true, 2198 }, 2199 }, 2200 }, 2201 2202 Err: false, 2203 }, 2204 } 2205 2206 for i, tc := range cases { 2207 c, err := config.NewRawConfig(tc.Config) 2208 if err != nil { 2209 t.Fatalf("#%d err: %s", i, err) 2210 } 2211 2212 if len(tc.ConfigVariables) > 0 { 2213 vars := make(map[string]ast.Variable) 2214 for k, v := range tc.ConfigVariables { 2215 vars[k] = ast.Variable{Value: v, Type: ast.TypeString} 2216 } 2217 2218 if err := c.Interpolate(vars); err != nil { 2219 t.Fatalf("#%d err: %s", i, err) 2220 } 2221 } 2222 2223 d, err := schemaMap(tc.Schema).Diff( 2224 tc.State, terraform.NewResourceConfig(c)) 2225 if (err != nil) != tc.Err { 2226 t.Fatalf("#%d err: %s", i, err) 2227 } 2228 2229 if !reflect.DeepEqual(tc.Diff, d) { 2230 t.Fatalf("#%d: bad:\n\n%#v", i, d) 2231 } 2232 } 2233 } 2234 2235 func TestSchemaMap_Input(t *testing.T) { 2236 cases := map[string]struct { 2237 Schema map[string]*Schema 2238 Config map[string]interface{} 2239 Input map[string]string 2240 Result map[string]interface{} 2241 Err bool 2242 }{ 2243 /* 2244 * String decode 2245 */ 2246 2247 "uses input on optional field with no config": { 2248 Schema: map[string]*Schema{ 2249 "availability_zone": &Schema{ 2250 Type: TypeString, 2251 Optional: true, 2252 }, 2253 }, 2254 2255 Input: map[string]string{ 2256 "availability_zone": "foo", 2257 }, 2258 2259 Result: map[string]interface{}{ 2260 "availability_zone": "foo", 2261 }, 2262 2263 Err: false, 2264 }, 2265 2266 "input ignored when config has a value": { 2267 Schema: map[string]*Schema{ 2268 "availability_zone": &Schema{ 2269 Type: TypeString, 2270 Optional: true, 2271 }, 2272 }, 2273 2274 Config: map[string]interface{}{ 2275 "availability_zone": "bar", 2276 }, 2277 2278 Input: map[string]string{ 2279 "availability_zone": "foo", 2280 }, 2281 2282 Result: map[string]interface{}{}, 2283 2284 Err: false, 2285 }, 2286 2287 "input ignored when schema has a default": { 2288 Schema: map[string]*Schema{ 2289 "availability_zone": &Schema{ 2290 Type: TypeString, 2291 Default: "foo", 2292 Optional: true, 2293 }, 2294 }, 2295 2296 Input: map[string]string{ 2297 "availability_zone": "bar", 2298 }, 2299 2300 Result: map[string]interface{}{}, 2301 2302 Err: false, 2303 }, 2304 2305 "input ignored when default function returns a value": { 2306 Schema: map[string]*Schema{ 2307 "availability_zone": &Schema{ 2308 Type: TypeString, 2309 DefaultFunc: func() (interface{}, error) { 2310 return "foo", nil 2311 }, 2312 Optional: true, 2313 }, 2314 }, 2315 2316 Input: map[string]string{ 2317 "availability_zone": "bar", 2318 }, 2319 2320 Result: map[string]interface{}{}, 2321 2322 Err: false, 2323 }, 2324 2325 "input used when default function returns nil": { 2326 Schema: map[string]*Schema{ 2327 "availability_zone": &Schema{ 2328 Type: TypeString, 2329 DefaultFunc: func() (interface{}, error) { 2330 return nil, nil 2331 }, 2332 Optional: true, 2333 }, 2334 }, 2335 2336 Input: map[string]string{ 2337 "availability_zone": "bar", 2338 }, 2339 2340 Result: map[string]interface{}{ 2341 "availability_zone": "bar", 2342 }, 2343 2344 Err: false, 2345 }, 2346 } 2347 2348 for i, tc := range cases { 2349 if tc.Config == nil { 2350 tc.Config = make(map[string]interface{}) 2351 } 2352 2353 c, err := config.NewRawConfig(tc.Config) 2354 if err != nil { 2355 t.Fatalf("err: %s", err) 2356 } 2357 2358 input := new(terraform.MockUIInput) 2359 input.InputReturnMap = tc.Input 2360 2361 rc := terraform.NewResourceConfig(c) 2362 rc.Config = make(map[string]interface{}) 2363 2364 actual, err := schemaMap(tc.Schema).Input(input, rc) 2365 if (err != nil) != tc.Err { 2366 t.Fatalf("#%v err: %s", i, err) 2367 } 2368 2369 if !reflect.DeepEqual(tc.Result, actual.Config) { 2370 t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result) 2371 } 2372 } 2373 } 2374 2375 func TestSchemaMap_InternalValidate(t *testing.T) { 2376 cases := []struct { 2377 In map[string]*Schema 2378 Err bool 2379 }{ 2380 { 2381 nil, 2382 false, 2383 }, 2384 2385 // No optional and no required 2386 { 2387 map[string]*Schema{ 2388 "foo": &Schema{ 2389 Type: TypeInt, 2390 Optional: true, 2391 Required: true, 2392 }, 2393 }, 2394 true, 2395 }, 2396 2397 // No optional and no required 2398 { 2399 map[string]*Schema{ 2400 "foo": &Schema{ 2401 Type: TypeInt, 2402 }, 2403 }, 2404 true, 2405 }, 2406 2407 // Missing Type 2408 { 2409 map[string]*Schema{ 2410 "foo": &Schema{ 2411 Required: true, 2412 }, 2413 }, 2414 true, 2415 }, 2416 2417 // Required but computed 2418 { 2419 map[string]*Schema{ 2420 "foo": &Schema{ 2421 Type: TypeInt, 2422 Required: true, 2423 Computed: true, 2424 }, 2425 }, 2426 true, 2427 }, 2428 2429 // Looks good 2430 { 2431 map[string]*Schema{ 2432 "foo": &Schema{ 2433 Type: TypeString, 2434 Required: true, 2435 }, 2436 }, 2437 false, 2438 }, 2439 2440 // Computed but has default 2441 { 2442 map[string]*Schema{ 2443 "foo": &Schema{ 2444 Type: TypeInt, 2445 Optional: true, 2446 Computed: true, 2447 Default: "foo", 2448 }, 2449 }, 2450 true, 2451 }, 2452 2453 // Required but has default 2454 { 2455 map[string]*Schema{ 2456 "foo": &Schema{ 2457 Type: TypeInt, 2458 Optional: true, 2459 Required: true, 2460 Default: "foo", 2461 }, 2462 }, 2463 true, 2464 }, 2465 2466 // List element not set 2467 { 2468 map[string]*Schema{ 2469 "foo": &Schema{ 2470 Type: TypeList, 2471 }, 2472 }, 2473 true, 2474 }, 2475 2476 // List default 2477 { 2478 map[string]*Schema{ 2479 "foo": &Schema{ 2480 Type: TypeList, 2481 Elem: &Schema{Type: TypeInt}, 2482 Default: "foo", 2483 }, 2484 }, 2485 true, 2486 }, 2487 2488 // List element computed 2489 { 2490 map[string]*Schema{ 2491 "foo": &Schema{ 2492 Type: TypeList, 2493 Optional: true, 2494 Elem: &Schema{ 2495 Type: TypeInt, 2496 Computed: true, 2497 }, 2498 }, 2499 }, 2500 true, 2501 }, 2502 2503 // List element with Set set 2504 { 2505 map[string]*Schema{ 2506 "foo": &Schema{ 2507 Type: TypeList, 2508 Elem: &Schema{Type: TypeInt}, 2509 Set: func(interface{}) int { return 0 }, 2510 Optional: true, 2511 }, 2512 }, 2513 true, 2514 }, 2515 2516 // Set element with no Set set 2517 { 2518 map[string]*Schema{ 2519 "foo": &Schema{ 2520 Type: TypeSet, 2521 Elem: &Schema{Type: TypeInt}, 2522 Optional: true, 2523 }, 2524 }, 2525 true, 2526 }, 2527 2528 // Required but computed 2529 { 2530 map[string]*Schema{ 2531 "foo": &Schema{ 2532 Type: TypeInt, 2533 Required: true, 2534 ComputedWhen: []string{"foo"}, 2535 }, 2536 }, 2537 true, 2538 }, 2539 2540 // Sub-resource invalid 2541 { 2542 map[string]*Schema{ 2543 "foo": &Schema{ 2544 Type: TypeList, 2545 Optional: true, 2546 Elem: &Resource{ 2547 Schema: map[string]*Schema{ 2548 "foo": new(Schema), 2549 }, 2550 }, 2551 }, 2552 }, 2553 true, 2554 }, 2555 2556 // Sub-resource valid 2557 { 2558 map[string]*Schema{ 2559 "foo": &Schema{ 2560 Type: TypeList, 2561 Optional: true, 2562 Elem: &Resource{ 2563 Schema: map[string]*Schema{ 2564 "foo": &Schema{ 2565 Type: TypeInt, 2566 Optional: true, 2567 }, 2568 }, 2569 }, 2570 }, 2571 }, 2572 false, 2573 }, 2574 } 2575 2576 for i, tc := range cases { 2577 err := schemaMap(tc.In).InternalValidate() 2578 if (err != nil) != tc.Err { 2579 t.Fatalf("%d: bad: %s\n\n%#v", i, err, tc.In) 2580 } 2581 } 2582 2583 } 2584 2585 func TestSchemaMap_Validate(t *testing.T) { 2586 cases := map[string]struct { 2587 Schema map[string]*Schema 2588 Config map[string]interface{} 2589 Vars map[string]string 2590 Err bool 2591 Errors []error 2592 Warnings []string 2593 }{ 2594 "Good": { 2595 Schema: map[string]*Schema{ 2596 "availability_zone": &Schema{ 2597 Type: TypeString, 2598 Optional: true, 2599 Computed: true, 2600 ForceNew: true, 2601 }, 2602 }, 2603 2604 Config: map[string]interface{}{ 2605 "availability_zone": "foo", 2606 }, 2607 }, 2608 2609 "Good, because the var is not set and that error will come elsewhere": { 2610 Schema: map[string]*Schema{ 2611 "size": &Schema{ 2612 Type: TypeInt, 2613 Required: true, 2614 }, 2615 }, 2616 2617 Config: map[string]interface{}{ 2618 "size": "${var.foo}", 2619 }, 2620 2621 Vars: map[string]string{ 2622 "var.foo": config.UnknownVariableValue, 2623 }, 2624 }, 2625 2626 "Required field not set": { 2627 Schema: map[string]*Schema{ 2628 "availability_zone": &Schema{ 2629 Type: TypeString, 2630 Required: true, 2631 }, 2632 }, 2633 2634 Config: map[string]interface{}{}, 2635 2636 Err: true, 2637 }, 2638 2639 "Invalid basic type": { 2640 Schema: map[string]*Schema{ 2641 "port": &Schema{ 2642 Type: TypeInt, 2643 Required: true, 2644 }, 2645 }, 2646 2647 Config: map[string]interface{}{ 2648 "port": "I am invalid", 2649 }, 2650 2651 Err: true, 2652 }, 2653 2654 "Invalid complex type": { 2655 Schema: map[string]*Schema{ 2656 "user_data": &Schema{ 2657 Type: TypeString, 2658 Optional: true, 2659 }, 2660 }, 2661 2662 Config: map[string]interface{}{ 2663 "user_data": []interface{}{ 2664 map[string]interface{}{ 2665 "foo": "bar", 2666 }, 2667 }, 2668 }, 2669 2670 Err: true, 2671 }, 2672 2673 "Bad type, interpolated": { 2674 Schema: map[string]*Schema{ 2675 "size": &Schema{ 2676 Type: TypeInt, 2677 Required: true, 2678 }, 2679 }, 2680 2681 Config: map[string]interface{}{ 2682 "size": "${var.foo}", 2683 }, 2684 2685 Vars: map[string]string{ 2686 "var.foo": "nope", 2687 }, 2688 2689 Err: true, 2690 }, 2691 2692 "Required but has DefaultFunc": { 2693 Schema: map[string]*Schema{ 2694 "availability_zone": &Schema{ 2695 Type: TypeString, 2696 Required: true, 2697 DefaultFunc: func() (interface{}, error) { 2698 return "foo", nil 2699 }, 2700 }, 2701 }, 2702 2703 Config: nil, 2704 }, 2705 2706 "Required but has DefaultFunc return nil": { 2707 Schema: map[string]*Schema{ 2708 "availability_zone": &Schema{ 2709 Type: TypeString, 2710 Required: true, 2711 DefaultFunc: func() (interface{}, error) { 2712 return nil, nil 2713 }, 2714 }, 2715 }, 2716 2717 Config: nil, 2718 2719 Err: true, 2720 }, 2721 2722 "Optional sub-resource": { 2723 Schema: map[string]*Schema{ 2724 "ingress": &Schema{ 2725 Type: TypeList, 2726 Elem: &Resource{ 2727 Schema: map[string]*Schema{ 2728 "from": &Schema{ 2729 Type: TypeInt, 2730 Required: true, 2731 }, 2732 }, 2733 }, 2734 }, 2735 }, 2736 2737 Config: map[string]interface{}{}, 2738 2739 Err: false, 2740 }, 2741 2742 "Not a list": { 2743 Schema: map[string]*Schema{ 2744 "ingress": &Schema{ 2745 Type: TypeList, 2746 Elem: &Resource{ 2747 Schema: map[string]*Schema{ 2748 "from": &Schema{ 2749 Type: TypeInt, 2750 Required: true, 2751 }, 2752 }, 2753 }, 2754 }, 2755 }, 2756 2757 Config: map[string]interface{}{ 2758 "ingress": "foo", 2759 }, 2760 2761 Err: true, 2762 }, 2763 2764 "Required sub-resource field": { 2765 Schema: map[string]*Schema{ 2766 "ingress": &Schema{ 2767 Type: TypeList, 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 }, 2783 }, 2784 2785 Err: true, 2786 }, 2787 2788 "Good sub-resource": { 2789 Schema: map[string]*Schema{ 2790 "ingress": &Schema{ 2791 Type: TypeList, 2792 Optional: true, 2793 Elem: &Resource{ 2794 Schema: map[string]*Schema{ 2795 "from": &Schema{ 2796 Type: TypeInt, 2797 Required: true, 2798 }, 2799 }, 2800 }, 2801 }, 2802 }, 2803 2804 Config: map[string]interface{}{ 2805 "ingress": []interface{}{ 2806 map[string]interface{}{ 2807 "from": 80, 2808 }, 2809 }, 2810 }, 2811 2812 Err: false, 2813 }, 2814 2815 "Invalid/unknown field": { 2816 Schema: map[string]*Schema{ 2817 "availability_zone": &Schema{ 2818 Type: TypeString, 2819 Optional: true, 2820 Computed: true, 2821 ForceNew: true, 2822 }, 2823 }, 2824 2825 Config: map[string]interface{}{ 2826 "foo": "bar", 2827 }, 2828 2829 Err: true, 2830 }, 2831 2832 "Computed field set": { 2833 Schema: map[string]*Schema{ 2834 "availability_zone": &Schema{ 2835 Type: TypeString, 2836 Computed: true, 2837 }, 2838 }, 2839 2840 Config: map[string]interface{}{ 2841 "availability_zone": "bar", 2842 }, 2843 2844 Err: true, 2845 }, 2846 2847 "Not a set": { 2848 Schema: map[string]*Schema{ 2849 "ports": &Schema{ 2850 Type: TypeSet, 2851 Required: true, 2852 Elem: &Schema{Type: TypeInt}, 2853 Set: func(a interface{}) int { 2854 return a.(int) 2855 }, 2856 }, 2857 }, 2858 2859 Config: map[string]interface{}{ 2860 "ports": "foo", 2861 }, 2862 2863 Err: true, 2864 }, 2865 2866 "Maps": { 2867 Schema: map[string]*Schema{ 2868 "user_data": &Schema{ 2869 Type: TypeMap, 2870 Optional: true, 2871 }, 2872 }, 2873 2874 Config: map[string]interface{}{ 2875 "user_data": "foo", 2876 }, 2877 2878 Err: true, 2879 }, 2880 2881 "Good map: data surrounded by extra slice": { 2882 Schema: map[string]*Schema{ 2883 "user_data": &Schema{ 2884 Type: TypeMap, 2885 Optional: true, 2886 }, 2887 }, 2888 2889 Config: map[string]interface{}{ 2890 "user_data": []interface{}{ 2891 map[string]interface{}{ 2892 "foo": "bar", 2893 }, 2894 }, 2895 }, 2896 }, 2897 2898 "Good map": { 2899 Schema: map[string]*Schema{ 2900 "user_data": &Schema{ 2901 Type: TypeMap, 2902 Optional: true, 2903 }, 2904 }, 2905 2906 Config: map[string]interface{}{ 2907 "user_data": map[string]interface{}{ 2908 "foo": "bar", 2909 }, 2910 }, 2911 }, 2912 2913 "Bad map: just a slice": { 2914 Schema: map[string]*Schema{ 2915 "user_data": &Schema{ 2916 Type: TypeMap, 2917 Optional: true, 2918 }, 2919 }, 2920 2921 Config: map[string]interface{}{ 2922 "user_data": []interface{}{ 2923 "foo", 2924 }, 2925 }, 2926 2927 Err: true, 2928 }, 2929 2930 "Good set: config has slice with single interpolated value": { 2931 Schema: map[string]*Schema{ 2932 "security_groups": &Schema{ 2933 Type: TypeSet, 2934 Optional: true, 2935 Computed: true, 2936 ForceNew: true, 2937 Elem: &Schema{Type: TypeString}, 2938 Set: func(v interface{}) int { 2939 return len(v.(string)) 2940 }, 2941 }, 2942 }, 2943 2944 Config: map[string]interface{}{ 2945 "security_groups": []interface{}{"${var.foo}"}, 2946 }, 2947 2948 Err: false, 2949 }, 2950 2951 "Bad set: config has single interpolated value": { 2952 Schema: map[string]*Schema{ 2953 "security_groups": &Schema{ 2954 Type: TypeSet, 2955 Optional: true, 2956 Computed: true, 2957 ForceNew: true, 2958 Elem: &Schema{Type: TypeString}, 2959 }, 2960 }, 2961 2962 Config: map[string]interface{}{ 2963 "security_groups": "${var.foo}", 2964 }, 2965 2966 Err: true, 2967 }, 2968 2969 "Bad, subresource should not allow unknown elements": { 2970 Schema: map[string]*Schema{ 2971 "ingress": &Schema{ 2972 Type: TypeList, 2973 Optional: true, 2974 Elem: &Resource{ 2975 Schema: map[string]*Schema{ 2976 "port": &Schema{ 2977 Type: TypeInt, 2978 Required: true, 2979 }, 2980 }, 2981 }, 2982 }, 2983 }, 2984 2985 Config: map[string]interface{}{ 2986 "ingress": []interface{}{ 2987 map[string]interface{}{ 2988 "port": 80, 2989 "other": "yes", 2990 }, 2991 }, 2992 }, 2993 2994 Err: true, 2995 }, 2996 2997 "Bad, subresource should not allow invalid types": { 2998 Schema: map[string]*Schema{ 2999 "ingress": &Schema{ 3000 Type: TypeList, 3001 Optional: true, 3002 Elem: &Resource{ 3003 Schema: map[string]*Schema{ 3004 "port": &Schema{ 3005 Type: TypeInt, 3006 Required: true, 3007 }, 3008 }, 3009 }, 3010 }, 3011 }, 3012 3013 Config: map[string]interface{}{ 3014 "ingress": []interface{}{ 3015 map[string]interface{}{ 3016 "port": "bad", 3017 }, 3018 }, 3019 }, 3020 3021 Err: true, 3022 }, 3023 3024 "Deprecated attribute usage generates warning, but not error": { 3025 Schema: map[string]*Schema{ 3026 "old_news": &Schema{ 3027 Type: TypeString, 3028 Optional: true, 3029 Deprecated: "please use 'new_news' instead", 3030 }, 3031 }, 3032 3033 Config: map[string]interface{}{ 3034 "old_news": "extra extra!", 3035 }, 3036 3037 Err: false, 3038 3039 Warnings: []string{ 3040 "\"old_news\": [DEPRECATED] please use 'new_news' instead", 3041 }, 3042 }, 3043 3044 "Deprecated generates no warnings if attr not used": { 3045 Schema: map[string]*Schema{ 3046 "old_news": &Schema{ 3047 Type: TypeString, 3048 Optional: true, 3049 Deprecated: "please use 'new_news' instead", 3050 }, 3051 }, 3052 3053 Err: false, 3054 3055 Warnings: nil, 3056 }, 3057 3058 "Removed attribute usage generates error": { 3059 Schema: map[string]*Schema{ 3060 "long_gone": &Schema{ 3061 Type: TypeString, 3062 Optional: true, 3063 Removed: "no longer supported by Cloud API", 3064 }, 3065 }, 3066 3067 Config: map[string]interface{}{ 3068 "long_gone": "still here!", 3069 }, 3070 3071 Err: true, 3072 Errors: []error{ 3073 fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"), 3074 }, 3075 }, 3076 3077 "Removed generates no errors if attr not used": { 3078 Schema: map[string]*Schema{ 3079 "long_gone": &Schema{ 3080 Type: TypeString, 3081 Optional: true, 3082 Removed: "no longer supported by Cloud API", 3083 }, 3084 }, 3085 3086 Err: false, 3087 }, 3088 } 3089 3090 for tn, tc := range cases { 3091 c, err := config.NewRawConfig(tc.Config) 3092 if err != nil { 3093 t.Fatalf("err: %s", err) 3094 } 3095 if tc.Vars != nil { 3096 vars := make(map[string]ast.Variable) 3097 for k, v := range tc.Vars { 3098 vars[k] = ast.Variable{Value: v, Type: ast.TypeString} 3099 } 3100 3101 if err := c.Interpolate(vars); err != nil { 3102 t.Fatalf("err: %s", err) 3103 } 3104 } 3105 3106 ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c)) 3107 if (len(es) > 0) != tc.Err { 3108 if len(es) == 0 { 3109 t.Errorf("%q: no errors", tn) 3110 } 3111 3112 for _, e := range es { 3113 t.Errorf("%q: err: %s", tn, e) 3114 } 3115 3116 t.FailNow() 3117 } 3118 3119 if !reflect.DeepEqual(ws, tc.Warnings) { 3120 t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws) 3121 } 3122 3123 if tc.Errors != nil { 3124 if !reflect.DeepEqual(es, tc.Errors) { 3125 t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es) 3126 } 3127 } 3128 } 3129 }