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