github.com/archgrove/terraform@v0.9.5-0.20170502093151-adb789f0f8d2/config/interpolate_funcs_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "reflect" 8 "testing" 9 "time" 10 11 "path/filepath" 12 13 "github.com/hashicorp/hil" 14 "github.com/hashicorp/hil/ast" 15 "github.com/mitchellh/go-homedir" 16 ) 17 18 func TestInterpolateFuncZipMap(t *testing.T) { 19 testFunction(t, testFunctionConfig{ 20 Cases: []testFunctionCase{ 21 { 22 `${zipmap(var.list, var.list2)}`, 23 map[string]interface{}{ 24 "Hello": "bar", 25 "World": "baz", 26 }, 27 false, 28 }, 29 { 30 `${zipmap(var.list, var.nonstrings)}`, 31 map[string]interface{}{ 32 "Hello": []interface{}{"bar", "baz"}, 33 "World": []interface{}{"boo", "foo"}, 34 }, 35 false, 36 }, 37 { 38 `${zipmap(var.nonstrings, var.list2)}`, 39 nil, 40 true, 41 }, 42 { 43 `${zipmap(var.list, var.differentlengthlist)}`, 44 nil, 45 true, 46 }, 47 }, 48 Vars: map[string]ast.Variable{ 49 "var.list": { 50 Type: ast.TypeList, 51 Value: []ast.Variable{ 52 { 53 Type: ast.TypeString, 54 Value: "Hello", 55 }, 56 { 57 Type: ast.TypeString, 58 Value: "World", 59 }, 60 }, 61 }, 62 "var.list2": { 63 Type: ast.TypeList, 64 Value: []ast.Variable{ 65 { 66 Type: ast.TypeString, 67 Value: "bar", 68 }, 69 { 70 Type: ast.TypeString, 71 Value: "baz", 72 }, 73 }, 74 }, 75 "var.differentlengthlist": { 76 Type: ast.TypeList, 77 Value: []ast.Variable{ 78 { 79 Type: ast.TypeString, 80 Value: "bar", 81 }, 82 { 83 Type: ast.TypeString, 84 Value: "baz", 85 }, 86 { 87 Type: ast.TypeString, 88 Value: "boo", 89 }, 90 }, 91 }, 92 "var.nonstrings": { 93 Type: ast.TypeList, 94 Value: []ast.Variable{ 95 { 96 Type: ast.TypeList, 97 Value: []ast.Variable{ 98 { 99 Type: ast.TypeString, 100 Value: "bar", 101 }, 102 { 103 Type: ast.TypeString, 104 Value: "baz", 105 }, 106 }, 107 }, 108 { 109 Type: ast.TypeList, 110 Value: []ast.Variable{ 111 { 112 Type: ast.TypeString, 113 Value: "boo", 114 }, 115 { 116 Type: ast.TypeString, 117 Value: "foo", 118 }, 119 }, 120 }, 121 }, 122 }, 123 }, 124 }) 125 } 126 127 func TestInterpolateFuncList(t *testing.T) { 128 testFunction(t, testFunctionConfig{ 129 Cases: []testFunctionCase{ 130 // empty input returns empty list 131 { 132 `${list()}`, 133 []interface{}{}, 134 false, 135 }, 136 137 // single input returns list of length 1 138 { 139 `${list("hello")}`, 140 []interface{}{"hello"}, 141 false, 142 }, 143 144 // two inputs returns list of length 2 145 { 146 `${list("hello", "world")}`, 147 []interface{}{"hello", "world"}, 148 false, 149 }, 150 151 // not a string input gives error 152 { 153 `${list("hello", 42)}`, 154 nil, 155 true, 156 }, 157 158 // list of lists 159 { 160 `${list("${var.list}", "${var.list2}")}`, 161 []interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}}, 162 false, 163 }, 164 165 // list of maps 166 { 167 `${list("${var.map}", "${var.map2}")}`, 168 []interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}}, 169 false, 170 }, 171 172 // error on a heterogeneous list 173 { 174 `${list("first", "${var.list}")}`, 175 nil, 176 true, 177 }, 178 }, 179 Vars: map[string]ast.Variable{ 180 "var.list": { 181 Type: ast.TypeList, 182 Value: []ast.Variable{ 183 { 184 Type: ast.TypeString, 185 Value: "Hello", 186 }, 187 { 188 Type: ast.TypeString, 189 Value: "World", 190 }, 191 }, 192 }, 193 "var.list2": { 194 Type: ast.TypeList, 195 Value: []ast.Variable{ 196 { 197 Type: ast.TypeString, 198 Value: "bar", 199 }, 200 { 201 Type: ast.TypeString, 202 Value: "baz", 203 }, 204 }, 205 }, 206 207 "var.map": { 208 Type: ast.TypeMap, 209 Value: map[string]ast.Variable{ 210 "key": { 211 Type: ast.TypeString, 212 Value: "bar", 213 }, 214 }, 215 }, 216 "var.map2": { 217 Type: ast.TypeMap, 218 Value: map[string]ast.Variable{ 219 "key2": { 220 Type: ast.TypeString, 221 Value: "baz", 222 }, 223 }, 224 }, 225 }, 226 }) 227 } 228 229 func TestInterpolateFuncMax(t *testing.T) { 230 testFunction(t, testFunctionConfig{ 231 Cases: []testFunctionCase{ 232 { 233 `${max()}`, 234 nil, 235 true, 236 }, 237 238 { 239 `${max("")}`, 240 nil, 241 true, 242 }, 243 244 { 245 `${max(-1, 0, 1)}`, 246 "1", 247 false, 248 }, 249 250 { 251 `${max(1, 0, -1)}`, 252 "1", 253 false, 254 }, 255 256 { 257 `${max(-1, -2)}`, 258 "-1", 259 false, 260 }, 261 262 { 263 `${max(-1)}`, 264 "-1", 265 false, 266 }, 267 }, 268 }) 269 } 270 271 func TestInterpolateFuncMin(t *testing.T) { 272 testFunction(t, testFunctionConfig{ 273 Cases: []testFunctionCase{ 274 { 275 `${min()}`, 276 nil, 277 true, 278 }, 279 280 { 281 `${min("")}`, 282 nil, 283 true, 284 }, 285 286 { 287 `${min(-1, 0, 1)}`, 288 "-1", 289 false, 290 }, 291 292 { 293 `${min(1, 0, -1)}`, 294 "-1", 295 false, 296 }, 297 298 { 299 `${min(-1, -2)}`, 300 "-2", 301 false, 302 }, 303 304 { 305 `${min(-1)}`, 306 "-1", 307 false, 308 }, 309 }, 310 }) 311 } 312 313 func TestInterpolateFuncFloor(t *testing.T) { 314 testFunction(t, testFunctionConfig{ 315 Cases: []testFunctionCase{ 316 { 317 `${floor()}`, 318 nil, 319 true, 320 }, 321 322 { 323 `${floor("")}`, 324 nil, 325 true, 326 }, 327 328 { 329 `${floor("-1.3")}`, // there appears to be a AST bug where the parsed argument ends up being -1 without the "s 330 "-2", 331 false, 332 }, 333 334 { 335 `${floor(1.7)}`, 336 "1", 337 false, 338 }, 339 }, 340 }) 341 } 342 343 func TestInterpolateFuncCeil(t *testing.T) { 344 testFunction(t, testFunctionConfig{ 345 Cases: []testFunctionCase{ 346 { 347 `${ceil()}`, 348 nil, 349 true, 350 }, 351 352 { 353 `${ceil("")}`, 354 nil, 355 true, 356 }, 357 358 { 359 `${ceil(-1.8)}`, 360 "-1", 361 false, 362 }, 363 364 { 365 `${ceil(1.2)}`, 366 "2", 367 false, 368 }, 369 }, 370 }) 371 } 372 373 func TestInterpolateFuncChomp(t *testing.T) { 374 testFunction(t, testFunctionConfig{ 375 Cases: []testFunctionCase{ 376 { 377 `${chomp()}`, 378 nil, 379 true, 380 }, 381 382 { 383 `${chomp("hello world")}`, 384 "hello world", 385 false, 386 }, 387 388 { 389 fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld"), 390 "goodbye\ncruel\nworld", 391 false, 392 }, 393 394 { 395 fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld"), 396 "goodbye\r\nwindows\r\nworld", 397 false, 398 }, 399 400 { 401 fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n"), 402 "goodbye\ncruel\nworld", 403 false, 404 }, 405 406 { 407 fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n\n\n\n"), 408 "goodbye\ncruel\nworld", 409 false, 410 }, 411 412 { 413 fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n"), 414 "goodbye\r\nwindows\r\nworld", 415 false, 416 }, 417 418 { 419 fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n\r\n\r\n\r\n"), 420 "goodbye\r\nwindows\r\nworld", 421 false, 422 }, 423 }, 424 }) 425 } 426 427 func TestInterpolateFuncMap(t *testing.T) { 428 testFunction(t, testFunctionConfig{ 429 Cases: []testFunctionCase{ 430 // empty input returns empty map 431 { 432 `${map()}`, 433 map[string]interface{}{}, 434 false, 435 }, 436 437 // odd args is error 438 { 439 `${map("odd")}`, 440 nil, 441 true, 442 }, 443 444 // two args returns map w/ one k/v 445 { 446 `${map("hello", "world")}`, 447 map[string]interface{}{"hello": "world"}, 448 false, 449 }, 450 451 // four args get two k/v 452 { 453 `${map("hello", "world", "what's", "up?")}`, 454 map[string]interface{}{"hello": "world", "what's": "up?"}, 455 false, 456 }, 457 458 // map of lists is okay 459 { 460 `${map("hello", list("world"), "what's", list("up?"))}`, 461 map[string]interface{}{ 462 "hello": []interface{}{"world"}, 463 "what's": []interface{}{"up?"}, 464 }, 465 false, 466 }, 467 468 // map of maps is okay 469 { 470 `${map("hello", map("there", "world"), "what's", map("really", "up?"))}`, 471 map[string]interface{}{ 472 "hello": map[string]interface{}{"there": "world"}, 473 "what's": map[string]interface{}{"really": "up?"}, 474 }, 475 false, 476 }, 477 478 // keys have to be strings 479 { 480 `${map(list("listkey"), "val")}`, 481 nil, 482 true, 483 }, 484 485 // types have to match 486 { 487 `${map("some", "strings", "also", list("lists"))}`, 488 nil, 489 true, 490 }, 491 492 // duplicate keys are an error 493 { 494 `${map("key", "val", "key", "again")}`, 495 nil, 496 true, 497 }, 498 }, 499 }) 500 } 501 502 func TestInterpolateFuncCompact(t *testing.T) { 503 testFunction(t, testFunctionConfig{ 504 Cases: []testFunctionCase{ 505 // empty string within array 506 { 507 `${compact(split(",", "a,,b"))}`, 508 []interface{}{"a", "b"}, 509 false, 510 }, 511 512 // empty string at the end of array 513 { 514 `${compact(split(",", "a,b,"))}`, 515 []interface{}{"a", "b"}, 516 false, 517 }, 518 519 // single empty string 520 { 521 `${compact(split(",", ""))}`, 522 []interface{}{}, 523 false, 524 }, 525 526 // errrors on list of lists 527 { 528 `${compact(list(list("a"), list("b")))}`, 529 nil, 530 true, 531 }, 532 }, 533 }) 534 } 535 536 func TestInterpolateFuncCidrHost(t *testing.T) { 537 testFunction(t, testFunctionConfig{ 538 Cases: []testFunctionCase{ 539 { 540 `${cidrhost("192.168.1.0/24", 5)}`, 541 "192.168.1.5", 542 false, 543 }, 544 { 545 `${cidrhost("192.168.1.0/24", -5)}`, 546 "192.168.1.251", 547 false, 548 }, 549 { 550 `${cidrhost("192.168.1.0/24", -256)}`, 551 "192.168.1.0", 552 false, 553 }, 554 { 555 `${cidrhost("192.168.1.0/30", 255)}`, 556 nil, 557 true, // 255 doesn't fit in two bits 558 }, 559 { 560 `${cidrhost("192.168.1.0/30", -255)}`, 561 nil, 562 true, // 255 doesn't fit in two bits 563 }, 564 { 565 `${cidrhost("not-a-cidr", 6)}`, 566 nil, 567 true, // not a valid CIDR mask 568 }, 569 { 570 `${cidrhost("10.256.0.0/8", 6)}`, 571 nil, 572 true, // can't have an octet >255 573 }, 574 }, 575 }) 576 } 577 578 func TestInterpolateFuncCidrNetmask(t *testing.T) { 579 testFunction(t, testFunctionConfig{ 580 Cases: []testFunctionCase{ 581 { 582 `${cidrnetmask("192.168.1.0/24")}`, 583 "255.255.255.0", 584 false, 585 }, 586 { 587 `${cidrnetmask("192.168.1.0/32")}`, 588 "255.255.255.255", 589 false, 590 }, 591 { 592 `${cidrnetmask("0.0.0.0/0")}`, 593 "0.0.0.0", 594 false, 595 }, 596 { 597 // This doesn't really make sense for IPv6 networks 598 // but it ought to do something sensible anyway. 599 `${cidrnetmask("1::/64")}`, 600 "ffff:ffff:ffff:ffff::", 601 false, 602 }, 603 { 604 `${cidrnetmask("not-a-cidr")}`, 605 nil, 606 true, // not a valid CIDR mask 607 }, 608 { 609 `${cidrnetmask("10.256.0.0/8")}`, 610 nil, 611 true, // can't have an octet >255 612 }, 613 }, 614 }) 615 } 616 617 func TestInterpolateFuncCidrSubnet(t *testing.T) { 618 testFunction(t, testFunctionConfig{ 619 Cases: []testFunctionCase{ 620 { 621 `${cidrsubnet("192.168.2.0/20", 4, 6)}`, 622 "192.168.6.0/24", 623 false, 624 }, 625 { 626 `${cidrsubnet("fe80::/48", 16, 6)}`, 627 "fe80:0:0:6::/64", 628 false, 629 }, 630 { 631 // IPv4 address encoded in IPv6 syntax gets normalized 632 `${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`, 633 "192.168.6.0/24", 634 false, 635 }, 636 { 637 `${cidrsubnet("192.168.0.0/30", 4, 6)}`, 638 nil, 639 true, // not enough bits left 640 }, 641 { 642 `${cidrsubnet("192.168.0.0/16", 2, 16)}`, 643 nil, 644 true, // can't encode 16 in 2 bits 645 }, 646 { 647 `${cidrsubnet("not-a-cidr", 4, 6)}`, 648 nil, 649 true, // not a valid CIDR mask 650 }, 651 { 652 `${cidrsubnet("10.256.0.0/8", 4, 6)}`, 653 nil, 654 true, // can't have an octet >255 655 }, 656 }, 657 }) 658 } 659 660 func TestInterpolateFuncCoalesce(t *testing.T) { 661 testFunction(t, testFunctionConfig{ 662 Cases: []testFunctionCase{ 663 { 664 `${coalesce("first", "second", "third")}`, 665 "first", 666 false, 667 }, 668 { 669 `${coalesce("", "second", "third")}`, 670 "second", 671 false, 672 }, 673 { 674 `${coalesce("", "", "")}`, 675 "", 676 false, 677 }, 678 { 679 `${coalesce("foo")}`, 680 nil, 681 true, 682 }, 683 }, 684 }) 685 } 686 687 func TestInterpolateFuncCoalesceList(t *testing.T) { 688 testFunction(t, testFunctionConfig{ 689 Cases: []testFunctionCase{ 690 { 691 `${coalescelist(list("first"), list("second"), list("third"))}`, 692 []interface{}{"first"}, 693 false, 694 }, 695 { 696 `${coalescelist(list(), list("second"), list("third"))}`, 697 []interface{}{"second"}, 698 false, 699 }, 700 { 701 `${coalescelist(list(), list(), list())}`, 702 []interface{}{}, 703 false, 704 }, 705 { 706 `${coalescelist(list("foo"))}`, 707 nil, 708 true, 709 }, 710 }, 711 }) 712 } 713 714 func TestInterpolateFuncConcat(t *testing.T) { 715 testFunction(t, testFunctionConfig{ 716 Cases: []testFunctionCase{ 717 // String + list 718 // no longer supported, now returns an error 719 { 720 `${concat("a", split(",", "b,c"))}`, 721 nil, 722 true, 723 }, 724 725 // List + string 726 // no longer supported, now returns an error 727 { 728 `${concat(split(",", "a,b"), "c")}`, 729 nil, 730 true, 731 }, 732 733 // Single list 734 { 735 `${concat(split(",", ",foo,"))}`, 736 []interface{}{"", "foo", ""}, 737 false, 738 }, 739 { 740 `${concat(split(",", "a,b,c"))}`, 741 []interface{}{"a", "b", "c"}, 742 false, 743 }, 744 745 // Two lists 746 { 747 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 748 []interface{}{"a", "b", "c", "d", "e"}, 749 false, 750 }, 751 // Two lists with different separators 752 { 753 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 754 []interface{}{"a", "b", "c", "d", "e"}, 755 false, 756 }, 757 758 // More lists 759 { 760 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 761 []interface{}{"a", "b", "c", "d", "e", "f", "0", "1"}, 762 false, 763 }, 764 765 // list vars 766 { 767 `${concat("${var.list}", "${var.list}")}`, 768 []interface{}{"a", "b", "a", "b"}, 769 false, 770 }, 771 // lists of lists 772 { 773 `${concat("${var.lists}", "${var.lists}")}`, 774 []interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}}, 775 false, 776 }, 777 778 // lists of maps 779 { 780 `${concat("${var.maps}", "${var.maps}")}`, 781 []interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}}, 782 false, 783 }, 784 785 // multiple strings 786 // no longer supported, now returns an error 787 { 788 `${concat("string1", "string2")}`, 789 nil, 790 true, 791 }, 792 793 // mismatched types 794 { 795 `${concat("${var.lists}", "${var.maps}")}`, 796 nil, 797 true, 798 }, 799 }, 800 Vars: map[string]ast.Variable{ 801 "var.list": { 802 Type: ast.TypeList, 803 Value: []ast.Variable{ 804 { 805 Type: ast.TypeString, 806 Value: "a", 807 }, 808 { 809 Type: ast.TypeString, 810 Value: "b", 811 }, 812 }, 813 }, 814 "var.lists": { 815 Type: ast.TypeList, 816 Value: []ast.Variable{ 817 { 818 Type: ast.TypeList, 819 Value: []ast.Variable{ 820 { 821 Type: ast.TypeString, 822 Value: "c", 823 }, 824 { 825 Type: ast.TypeString, 826 Value: "d", 827 }, 828 }, 829 }, 830 }, 831 }, 832 "var.maps": { 833 Type: ast.TypeList, 834 Value: []ast.Variable{ 835 { 836 Type: ast.TypeMap, 837 Value: map[string]ast.Variable{ 838 "key1": { 839 Type: ast.TypeString, 840 Value: "a", 841 }, 842 "key2": { 843 Type: ast.TypeString, 844 Value: "b", 845 }, 846 }, 847 }, 848 }, 849 }, 850 }, 851 }) 852 } 853 854 func TestInterpolateFuncMerge(t *testing.T) { 855 testFunction(t, testFunctionConfig{ 856 Cases: []testFunctionCase{ 857 // basic merge 858 { 859 `${merge(map("a", "b"), map("c", "d"))}`, 860 map[string]interface{}{"a": "b", "c": "d"}, 861 false, 862 }, 863 864 // merge with conflicts is ok, last in wins. 865 { 866 `${merge(map("a", "b", "c", "X"), map("c", "d"))}`, 867 map[string]interface{}{"a": "b", "c": "d"}, 868 false, 869 }, 870 871 // merge variadic 872 { 873 `${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`, 874 map[string]interface{}{"a": "b", "c": "d", "e": "f"}, 875 false, 876 }, 877 878 // merge with variables 879 { 880 `${merge(var.maps[0], map("c", "d"))}`, 881 map[string]interface{}{"key1": "a", "key2": "b", "c": "d"}, 882 false, 883 }, 884 885 // only accept maps 886 { 887 `${merge(map("a", "b"), list("c", "d"))}`, 888 nil, 889 true, 890 }, 891 892 // merge maps of maps 893 { 894 `${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`, 895 map[string]interface{}{ 896 "b": map[string]interface{}{"key3": "d", "key4": "c"}, 897 "a": map[string]interface{}{"key1": "a", "key2": "b"}, 898 }, 899 false, 900 }, 901 // merge maps of lists 902 { 903 `${merge(map("a", list("b")), map("c", list("d", "e")))}`, 904 map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}}, 905 false, 906 }, 907 // merge map of various kinds 908 { 909 `${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`, 910 map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}}, 911 false, 912 }, 913 }, 914 Vars: map[string]ast.Variable{ 915 "var.maps": { 916 Type: ast.TypeList, 917 Value: []ast.Variable{ 918 { 919 Type: ast.TypeMap, 920 Value: map[string]ast.Variable{ 921 "key1": { 922 Type: ast.TypeString, 923 Value: "a", 924 }, 925 "key2": { 926 Type: ast.TypeString, 927 Value: "b", 928 }, 929 }, 930 }, 931 { 932 Type: ast.TypeMap, 933 Value: map[string]ast.Variable{ 934 "key3": { 935 Type: ast.TypeString, 936 Value: "d", 937 }, 938 "key4": { 939 Type: ast.TypeString, 940 Value: "c", 941 }, 942 }, 943 }, 944 }, 945 }, 946 }, 947 }) 948 949 } 950 951 func TestInterpolateFuncDirname(t *testing.T) { 952 testFunction(t, testFunctionConfig{ 953 Cases: []testFunctionCase{ 954 { 955 `${dirname("/foo/bar/baz")}`, 956 "/foo/bar", 957 false, 958 }, 959 }, 960 }) 961 } 962 963 func TestInterpolateFuncDistinct(t *testing.T) { 964 testFunction(t, testFunctionConfig{ 965 Cases: []testFunctionCase{ 966 // 3 duplicates 967 { 968 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, 969 []interface{}{"user1", "user2", "user3"}, 970 false, 971 }, 972 // 1 duplicate 973 { 974 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, 975 []interface{}{"user1", "user2", "user3", "user4"}, 976 false, 977 }, 978 // too many args 979 { 980 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, 981 nil, 982 true, 983 }, 984 // non-flat list is an error 985 { 986 `${distinct(list(list("a"), list("a")))}`, 987 nil, 988 true, 989 }, 990 }, 991 }) 992 } 993 994 func TestInterpolateFuncMatchKeys(t *testing.T) { 995 testFunction(t, testFunctionConfig{ 996 Cases: []testFunctionCase{ 997 // normal usage 998 { 999 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2"))}`, 1000 []interface{}{"b"}, 1001 false, 1002 }, 1003 // normal usage 2, check the order 1004 { 1005 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref1"))}`, 1006 []interface{}{"a", "b"}, 1007 false, 1008 }, 1009 // duplicate item in searchset 1010 { 1011 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref2"))}`, 1012 []interface{}{"b"}, 1013 false, 1014 }, 1015 // no matches 1016 { 1017 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref4"))}`, 1018 []interface{}{}, 1019 false, 1020 }, 1021 // no matches 2 1022 { 1023 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list())}`, 1024 []interface{}{}, 1025 false, 1026 }, 1027 // zero case 1028 { 1029 `${matchkeys(list(), list(), list("nope"))}`, 1030 []interface{}{}, 1031 false, 1032 }, 1033 // complex values 1034 { 1035 `${matchkeys(list(list("a", "a")), list("a"), list("a"))}`, 1036 []interface{}{[]interface{}{"a", "a"}}, 1037 false, 1038 }, 1039 // errors 1040 // different types 1041 { 1042 `${matchkeys(list("a"), list(1), list("a"))}`, 1043 nil, 1044 true, 1045 }, 1046 // different types 1047 { 1048 `${matchkeys(list("a"), list(list("a"), list("a")), list("a"))}`, 1049 nil, 1050 true, 1051 }, 1052 // lists of different length is an error 1053 { 1054 `${matchkeys(list("a"), list("a", "b"), list("a"))}`, 1055 nil, 1056 true, 1057 }, 1058 }, 1059 }) 1060 } 1061 1062 func TestInterpolateFuncFile(t *testing.T) { 1063 tf, err := ioutil.TempFile("", "tf") 1064 if err != nil { 1065 t.Fatalf("err: %s", err) 1066 } 1067 path := tf.Name() 1068 tf.Write([]byte("foo")) 1069 tf.Close() 1070 defer os.Remove(path) 1071 1072 testFunction(t, testFunctionConfig{ 1073 Cases: []testFunctionCase{ 1074 { 1075 fmt.Sprintf(`${file("%s")}`, path), 1076 "foo", 1077 false, 1078 }, 1079 1080 // Invalid path 1081 { 1082 `${file("/i/dont/exist")}`, 1083 nil, 1084 true, 1085 }, 1086 1087 // Too many args 1088 { 1089 `${file("foo", "bar")}`, 1090 nil, 1091 true, 1092 }, 1093 }, 1094 }) 1095 } 1096 1097 func TestInterpolateFuncFormat(t *testing.T) { 1098 testFunction(t, testFunctionConfig{ 1099 Cases: []testFunctionCase{ 1100 { 1101 `${format("hello")}`, 1102 "hello", 1103 false, 1104 }, 1105 1106 { 1107 `${format("hello %s", "world")}`, 1108 "hello world", 1109 false, 1110 }, 1111 1112 { 1113 `${format("hello %d", 42)}`, 1114 "hello 42", 1115 false, 1116 }, 1117 1118 { 1119 `${format("hello %05d", 42)}`, 1120 "hello 00042", 1121 false, 1122 }, 1123 1124 { 1125 `${format("hello %05d", 12345)}`, 1126 "hello 12345", 1127 false, 1128 }, 1129 }, 1130 }) 1131 } 1132 1133 func TestInterpolateFuncFormatList(t *testing.T) { 1134 testFunction(t, testFunctionConfig{ 1135 Cases: []testFunctionCase{ 1136 // formatlist requires at least one list 1137 { 1138 `${formatlist("hello")}`, 1139 nil, 1140 true, 1141 }, 1142 { 1143 `${formatlist("hello %s", "world")}`, 1144 nil, 1145 true, 1146 }, 1147 // formatlist applies to each list element in turn 1148 { 1149 `${formatlist("<%s>", split(",", "A,B"))}`, 1150 []interface{}{"<A>", "<B>"}, 1151 false, 1152 }, 1153 // formatlist repeats scalar elements 1154 { 1155 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 1156 "x=A, x=B, x=C", 1157 false, 1158 }, 1159 // Multiple lists are walked in parallel 1160 { 1161 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 1162 "A=1, B=2, C=3", 1163 false, 1164 }, 1165 // Mismatched list lengths generate an error 1166 { 1167 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 1168 nil, 1169 true, 1170 }, 1171 // Works with lists of length 1 [GH-2240] 1172 { 1173 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 1174 []interface{}{"demo-rest-elb.id"}, 1175 false, 1176 }, 1177 // Works with empty lists [GH-7607] 1178 { 1179 `${formatlist("%s", var.emptylist)}`, 1180 []interface{}{}, 1181 false, 1182 }, 1183 }, 1184 Vars: map[string]ast.Variable{ 1185 "var.emptylist": { 1186 Type: ast.TypeList, 1187 Value: []ast.Variable{}, 1188 }, 1189 }, 1190 }) 1191 } 1192 1193 func TestInterpolateFuncIndex(t *testing.T) { 1194 testFunction(t, testFunctionConfig{ 1195 Vars: map[string]ast.Variable{ 1196 "var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}), 1197 "var.list2": interfaceToVariableSwallowError([]string{"foo"}), 1198 "var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}), 1199 }, 1200 Cases: []testFunctionCase{ 1201 { 1202 `${index("test", "")}`, 1203 nil, 1204 true, 1205 }, 1206 1207 { 1208 `${index(var.list1, "foo")}`, 1209 nil, 1210 true, 1211 }, 1212 1213 { 1214 `${index(var.list2, "foo")}`, 1215 "0", 1216 false, 1217 }, 1218 1219 { 1220 `${index(var.list3, "bar")}`, 1221 "2", 1222 false, 1223 }, 1224 }, 1225 }) 1226 } 1227 1228 func TestInterpolateFuncJoin(t *testing.T) { 1229 testFunction(t, testFunctionConfig{ 1230 Vars: map[string]ast.Variable{ 1231 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 1232 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 1233 "var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}), 1234 }, 1235 Cases: []testFunctionCase{ 1236 { 1237 `${join(",")}`, 1238 nil, 1239 true, 1240 }, 1241 1242 { 1243 `${join(",", var.a_list)}`, 1244 "foo", 1245 false, 1246 }, 1247 1248 { 1249 `${join(".", var.a_longer_list)}`, 1250 "foo.bar.baz", 1251 false, 1252 }, 1253 1254 { 1255 `${join(".", var.list_of_lists)}`, 1256 nil, 1257 true, 1258 }, 1259 { 1260 `${join(".", list(list("nested")))}`, 1261 nil, 1262 true, 1263 }, 1264 }, 1265 }) 1266 } 1267 1268 func TestInterpolateFuncJSONEncode(t *testing.T) { 1269 testFunction(t, testFunctionConfig{ 1270 Vars: map[string]ast.Variable{ 1271 "easy": ast.Variable{ 1272 Value: "test", 1273 Type: ast.TypeString, 1274 }, 1275 "hard": ast.Variable{ 1276 Value: " foo \\ \n \t \" bar ", 1277 Type: ast.TypeString, 1278 }, 1279 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 1280 // XXX can't use InterfaceToVariable as it converts empty slice into empty 1281 // map. 1282 "emptylist": ast.Variable{ 1283 Value: []ast.Variable{}, 1284 Type: ast.TypeList, 1285 }, 1286 "map": interfaceToVariableSwallowError(map[string]string{ 1287 "foo": "bar", 1288 "ba \n z": "q\\x", 1289 }), 1290 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 1291 1292 // Not yet supported (but it would be nice) 1293 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 1294 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 1295 }, 1296 Cases: []testFunctionCase{ 1297 { 1298 `${jsonencode("test")}`, 1299 `"test"`, 1300 false, 1301 }, 1302 { 1303 `${jsonencode(easy)}`, 1304 `"test"`, 1305 false, 1306 }, 1307 { 1308 `${jsonencode(hard)}`, 1309 `" foo \\ \n \t \" bar "`, 1310 false, 1311 }, 1312 { 1313 `${jsonencode("")}`, 1314 `""`, 1315 false, 1316 }, 1317 { 1318 `${jsonencode()}`, 1319 nil, 1320 true, 1321 }, 1322 { 1323 `${jsonencode(list)}`, 1324 `["foo","bar\tbaz"]`, 1325 false, 1326 }, 1327 { 1328 `${jsonencode(emptylist)}`, 1329 `[]`, 1330 false, 1331 }, 1332 { 1333 `${jsonencode(map)}`, 1334 `{"ba \n z":"q\\x","foo":"bar"}`, 1335 false, 1336 }, 1337 { 1338 `${jsonencode(emptymap)}`, 1339 `{}`, 1340 false, 1341 }, 1342 { 1343 `${jsonencode(nestedlist)}`, 1344 nil, 1345 true, 1346 }, 1347 { 1348 `${jsonencode(nestedmap)}`, 1349 nil, 1350 true, 1351 }, 1352 }, 1353 }) 1354 } 1355 1356 func TestInterpolateFuncReplace(t *testing.T) { 1357 testFunction(t, testFunctionConfig{ 1358 Cases: []testFunctionCase{ 1359 // Regular search and replace 1360 { 1361 `${replace("hello", "hel", "bel")}`, 1362 "bello", 1363 false, 1364 }, 1365 1366 // Search string doesn't match 1367 { 1368 `${replace("hello", "nope", "bel")}`, 1369 "hello", 1370 false, 1371 }, 1372 1373 // Regular expression 1374 { 1375 `${replace("hello", "/l/", "L")}`, 1376 "heLLo", 1377 false, 1378 }, 1379 1380 { 1381 `${replace("helo", "/(l)/", "$1$1")}`, 1382 "hello", 1383 false, 1384 }, 1385 1386 // Bad regexp 1387 { 1388 `${replace("helo", "/(l/", "$1$1")}`, 1389 nil, 1390 true, 1391 }, 1392 }, 1393 }) 1394 } 1395 1396 func TestInterpolateFuncLength(t *testing.T) { 1397 testFunction(t, testFunctionConfig{ 1398 Cases: []testFunctionCase{ 1399 // Raw strings 1400 { 1401 `${length("")}`, 1402 "0", 1403 false, 1404 }, 1405 { 1406 `${length("a")}`, 1407 "1", 1408 false, 1409 }, 1410 { 1411 `${length(" ")}`, 1412 "1", 1413 false, 1414 }, 1415 { 1416 `${length(" a ,")}`, 1417 "4", 1418 false, 1419 }, 1420 { 1421 `${length("aaa")}`, 1422 "3", 1423 false, 1424 }, 1425 1426 // Lists 1427 { 1428 `${length(split(",", "a"))}`, 1429 "1", 1430 false, 1431 }, 1432 { 1433 `${length(split(",", "foo,"))}`, 1434 "2", 1435 false, 1436 }, 1437 { 1438 `${length(split(",", ",foo,"))}`, 1439 "3", 1440 false, 1441 }, 1442 { 1443 `${length(split(",", "foo,bar"))}`, 1444 "2", 1445 false, 1446 }, 1447 { 1448 `${length(split(".", "one.two.three.four.five"))}`, 1449 "5", 1450 false, 1451 }, 1452 // Want length 0 if we split an empty string then compact 1453 { 1454 `${length(compact(split(",", "")))}`, 1455 "0", 1456 false, 1457 }, 1458 // Works for maps 1459 { 1460 `${length(map("k", "v"))}`, 1461 "1", 1462 false, 1463 }, 1464 { 1465 `${length(map("k1", "v1", "k2", "v2"))}`, 1466 "2", 1467 false, 1468 }, 1469 }, 1470 }) 1471 } 1472 1473 func TestInterpolateFuncSignum(t *testing.T) { 1474 testFunction(t, testFunctionConfig{ 1475 Cases: []testFunctionCase{ 1476 { 1477 `${signum()}`, 1478 nil, 1479 true, 1480 }, 1481 1482 { 1483 `${signum("")}`, 1484 nil, 1485 true, 1486 }, 1487 1488 { 1489 `${signum(0)}`, 1490 "0", 1491 false, 1492 }, 1493 1494 { 1495 `${signum(15)}`, 1496 "1", 1497 false, 1498 }, 1499 1500 { 1501 `${signum(-29)}`, 1502 "-1", 1503 false, 1504 }, 1505 }, 1506 }) 1507 } 1508 1509 func TestInterpolateFuncSlice(t *testing.T) { 1510 testFunction(t, testFunctionConfig{ 1511 Cases: []testFunctionCase{ 1512 // Negative from index 1513 { 1514 `${slice(list("a"), -1, 0)}`, 1515 nil, 1516 true, 1517 }, 1518 // From index > to index 1519 { 1520 `${slice(list("a", "b", "c"), 2, 1)}`, 1521 nil, 1522 true, 1523 }, 1524 // To index too large 1525 { 1526 `${slice(var.list_of_strings, 1, 4)}`, 1527 nil, 1528 true, 1529 }, 1530 // Empty slice 1531 { 1532 `${slice(var.list_of_strings, 1, 1)}`, 1533 []interface{}{}, 1534 false, 1535 }, 1536 { 1537 `${slice(var.list_of_strings, 1, 2)}`, 1538 []interface{}{"b"}, 1539 false, 1540 }, 1541 { 1542 `${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`, 1543 []interface{}{"a", "b"}, 1544 false, 1545 }, 1546 }, 1547 Vars: map[string]ast.Variable{ 1548 "var.list_of_strings": { 1549 Type: ast.TypeList, 1550 Value: []ast.Variable{ 1551 { 1552 Type: ast.TypeString, 1553 Value: "a", 1554 }, 1555 { 1556 Type: ast.TypeString, 1557 Value: "b", 1558 }, 1559 { 1560 Type: ast.TypeString, 1561 Value: "c", 1562 }, 1563 }, 1564 }, 1565 }, 1566 }) 1567 } 1568 1569 func TestInterpolateFuncSort(t *testing.T) { 1570 testFunction(t, testFunctionConfig{ 1571 Vars: map[string]ast.Variable{ 1572 "var.strings": ast.Variable{ 1573 Type: ast.TypeList, 1574 Value: []ast.Variable{ 1575 {Type: ast.TypeString, Value: "c"}, 1576 {Type: ast.TypeString, Value: "a"}, 1577 {Type: ast.TypeString, Value: "b"}, 1578 }, 1579 }, 1580 "var.notstrings": ast.Variable{ 1581 Type: ast.TypeList, 1582 Value: []ast.Variable{ 1583 {Type: ast.TypeList, Value: []ast.Variable{}}, 1584 {Type: ast.TypeString, Value: "b"}, 1585 }, 1586 }, 1587 }, 1588 Cases: []testFunctionCase{ 1589 { 1590 `${sort(var.strings)}`, 1591 []interface{}{"a", "b", "c"}, 1592 false, 1593 }, 1594 { 1595 `${sort(var.notstrings)}`, 1596 nil, 1597 true, 1598 }, 1599 }, 1600 }) 1601 } 1602 1603 func TestInterpolateFuncSplit(t *testing.T) { 1604 testFunction(t, testFunctionConfig{ 1605 Cases: []testFunctionCase{ 1606 { 1607 `${split(",")}`, 1608 nil, 1609 true, 1610 }, 1611 1612 { 1613 `${split(",", "")}`, 1614 []interface{}{""}, 1615 false, 1616 }, 1617 1618 { 1619 `${split(",", "foo")}`, 1620 []interface{}{"foo"}, 1621 false, 1622 }, 1623 1624 { 1625 `${split(",", ",,,")}`, 1626 []interface{}{"", "", "", ""}, 1627 false, 1628 }, 1629 1630 { 1631 `${split(",", "foo,")}`, 1632 []interface{}{"foo", ""}, 1633 false, 1634 }, 1635 1636 { 1637 `${split(",", ",foo,")}`, 1638 []interface{}{"", "foo", ""}, 1639 false, 1640 }, 1641 1642 { 1643 `${split(".", "foo.bar.baz")}`, 1644 []interface{}{"foo", "bar", "baz"}, 1645 false, 1646 }, 1647 }, 1648 }) 1649 } 1650 1651 func TestInterpolateFuncLookup(t *testing.T) { 1652 testFunction(t, testFunctionConfig{ 1653 Vars: map[string]ast.Variable{ 1654 "var.foo": { 1655 Type: ast.TypeMap, 1656 Value: map[string]ast.Variable{ 1657 "bar": { 1658 Type: ast.TypeString, 1659 Value: "baz", 1660 }, 1661 }, 1662 }, 1663 "var.map_of_lists": ast.Variable{ 1664 Type: ast.TypeMap, 1665 Value: map[string]ast.Variable{ 1666 "bar": { 1667 Type: ast.TypeList, 1668 Value: []ast.Variable{ 1669 { 1670 Type: ast.TypeString, 1671 Value: "baz", 1672 }, 1673 }, 1674 }, 1675 }, 1676 }, 1677 }, 1678 Cases: []testFunctionCase{ 1679 { 1680 `${lookup(var.foo, "bar")}`, 1681 "baz", 1682 false, 1683 }, 1684 1685 // Invalid key 1686 { 1687 `${lookup(var.foo, "baz")}`, 1688 nil, 1689 true, 1690 }, 1691 1692 // Supplied default with valid key 1693 { 1694 `${lookup(var.foo, "bar", "")}`, 1695 "baz", 1696 false, 1697 }, 1698 1699 // Supplied default with invalid key 1700 { 1701 `${lookup(var.foo, "zip", "")}`, 1702 "", 1703 false, 1704 }, 1705 1706 // Too many args 1707 { 1708 `${lookup(var.foo, "bar", "", "abc")}`, 1709 nil, 1710 true, 1711 }, 1712 1713 // Cannot lookup into map of lists 1714 { 1715 `${lookup(var.map_of_lists, "bar")}`, 1716 nil, 1717 true, 1718 }, 1719 1720 // Non-empty default 1721 { 1722 `${lookup(var.foo, "zap", "xyz")}`, 1723 "xyz", 1724 false, 1725 }, 1726 }, 1727 }) 1728 } 1729 1730 func TestInterpolateFuncKeys(t *testing.T) { 1731 testFunction(t, testFunctionConfig{ 1732 Vars: map[string]ast.Variable{ 1733 "var.foo": ast.Variable{ 1734 Type: ast.TypeMap, 1735 Value: map[string]ast.Variable{ 1736 "bar": ast.Variable{ 1737 Value: "baz", 1738 Type: ast.TypeString, 1739 }, 1740 "qux": ast.Variable{ 1741 Value: "quack", 1742 Type: ast.TypeString, 1743 }, 1744 }, 1745 }, 1746 "var.str": ast.Variable{ 1747 Value: "astring", 1748 Type: ast.TypeString, 1749 }, 1750 }, 1751 Cases: []testFunctionCase{ 1752 { 1753 `${keys(var.foo)}`, 1754 []interface{}{"bar", "qux"}, 1755 false, 1756 }, 1757 1758 // Invalid key 1759 { 1760 `${keys(var.not)}`, 1761 nil, 1762 true, 1763 }, 1764 1765 // Too many args 1766 { 1767 `${keys(var.foo, "bar")}`, 1768 nil, 1769 true, 1770 }, 1771 1772 // Not a map 1773 { 1774 `${keys(var.str)}`, 1775 nil, 1776 true, 1777 }, 1778 }, 1779 }) 1780 } 1781 1782 // Confirm that keys return in sorted order, and values return in the order of 1783 // their sorted keys. 1784 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1785 testFunction(t, testFunctionConfig{ 1786 Vars: map[string]ast.Variable{ 1787 "var.foo": ast.Variable{ 1788 Type: ast.TypeMap, 1789 Value: map[string]ast.Variable{ 1790 "D": ast.Variable{ 1791 Value: "2", 1792 Type: ast.TypeString, 1793 }, 1794 "C": ast.Variable{ 1795 Value: "Y", 1796 Type: ast.TypeString, 1797 }, 1798 "A": ast.Variable{ 1799 Value: "X", 1800 Type: ast.TypeString, 1801 }, 1802 "10": ast.Variable{ 1803 Value: "Z", 1804 Type: ast.TypeString, 1805 }, 1806 "1": ast.Variable{ 1807 Value: "4", 1808 Type: ast.TypeString, 1809 }, 1810 "3": ast.Variable{ 1811 Value: "W", 1812 Type: ast.TypeString, 1813 }, 1814 }, 1815 }, 1816 }, 1817 Cases: []testFunctionCase{ 1818 { 1819 `${keys(var.foo)}`, 1820 []interface{}{"1", "10", "3", "A", "C", "D"}, 1821 false, 1822 }, 1823 1824 { 1825 `${values(var.foo)}`, 1826 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1827 false, 1828 }, 1829 }, 1830 }) 1831 } 1832 1833 func TestInterpolateFuncValues(t *testing.T) { 1834 testFunction(t, testFunctionConfig{ 1835 Vars: map[string]ast.Variable{ 1836 "var.foo": ast.Variable{ 1837 Type: ast.TypeMap, 1838 Value: map[string]ast.Variable{ 1839 "bar": ast.Variable{ 1840 Value: "quack", 1841 Type: ast.TypeString, 1842 }, 1843 "qux": ast.Variable{ 1844 Value: "baz", 1845 Type: ast.TypeString, 1846 }, 1847 }, 1848 }, 1849 "var.str": ast.Variable{ 1850 Value: "astring", 1851 Type: ast.TypeString, 1852 }, 1853 }, 1854 Cases: []testFunctionCase{ 1855 { 1856 `${values(var.foo)}`, 1857 []interface{}{"quack", "baz"}, 1858 false, 1859 }, 1860 1861 // Invalid key 1862 { 1863 `${values(var.not)}`, 1864 nil, 1865 true, 1866 }, 1867 1868 // Too many args 1869 { 1870 `${values(var.foo, "bar")}`, 1871 nil, 1872 true, 1873 }, 1874 1875 // Not a map 1876 { 1877 `${values(var.str)}`, 1878 nil, 1879 true, 1880 }, 1881 1882 // Map of lists 1883 { 1884 `${values(map("one", list()))}`, 1885 nil, 1886 true, 1887 }, 1888 }, 1889 }) 1890 } 1891 1892 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 1893 variable, _ := hil.InterfaceToVariable(input) 1894 return variable 1895 } 1896 1897 func TestInterpolateFuncElement(t *testing.T) { 1898 testFunction(t, testFunctionConfig{ 1899 Vars: map[string]ast.Variable{ 1900 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1901 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1902 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1903 "var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}), 1904 }, 1905 Cases: []testFunctionCase{ 1906 { 1907 `${element(var.a_list, "1")}`, 1908 "baz", 1909 false, 1910 }, 1911 1912 { 1913 `${element(var.a_short_list, "0")}`, 1914 "foo", 1915 false, 1916 }, 1917 1918 // Invalid index should wrap vs. out-of-bounds 1919 { 1920 `${element(var.a_list, "2")}`, 1921 "foo", 1922 false, 1923 }, 1924 1925 // Negative number should fail 1926 { 1927 `${element(var.a_short_list, "-1")}`, 1928 nil, 1929 true, 1930 }, 1931 1932 // Empty list should fail 1933 { 1934 `${element(var.empty_list, 0)}`, 1935 nil, 1936 true, 1937 }, 1938 1939 // Too many args 1940 { 1941 `${element(var.a_list, "0", "2")}`, 1942 nil, 1943 true, 1944 }, 1945 1946 // Only works on single-level lists 1947 { 1948 `${element(var.a_nested_list, "0")}`, 1949 nil, 1950 true, 1951 }, 1952 }, 1953 }) 1954 } 1955 1956 func TestInterpolateFuncBasename(t *testing.T) { 1957 testFunction(t, testFunctionConfig{ 1958 Cases: []testFunctionCase{ 1959 { 1960 `${basename("/foo/bar/baz")}`, 1961 "baz", 1962 false, 1963 }, 1964 }, 1965 }) 1966 } 1967 1968 func TestInterpolateFuncBase64Encode(t *testing.T) { 1969 testFunction(t, testFunctionConfig{ 1970 Cases: []testFunctionCase{ 1971 // Regular base64 encoding 1972 { 1973 `${base64encode("abc123!?$*&()'-=@~")}`, 1974 "YWJjMTIzIT8kKiYoKSctPUB+", 1975 false, 1976 }, 1977 }, 1978 }) 1979 } 1980 1981 func TestInterpolateFuncBase64Decode(t *testing.T) { 1982 testFunction(t, testFunctionConfig{ 1983 Cases: []testFunctionCase{ 1984 // Regular base64 decoding 1985 { 1986 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 1987 "abc123!?$*&()'-=@~", 1988 false, 1989 }, 1990 1991 // Invalid base64 data decoding 1992 { 1993 `${base64decode("this-is-an-invalid-base64-data")}`, 1994 nil, 1995 true, 1996 }, 1997 }, 1998 }) 1999 } 2000 2001 func TestInterpolateFuncLower(t *testing.T) { 2002 testFunction(t, testFunctionConfig{ 2003 Cases: []testFunctionCase{ 2004 { 2005 `${lower("HELLO")}`, 2006 "hello", 2007 false, 2008 }, 2009 2010 { 2011 `${lower("")}`, 2012 "", 2013 false, 2014 }, 2015 2016 { 2017 `${lower()}`, 2018 nil, 2019 true, 2020 }, 2021 }, 2022 }) 2023 } 2024 2025 func TestInterpolateFuncUpper(t *testing.T) { 2026 testFunction(t, testFunctionConfig{ 2027 Cases: []testFunctionCase{ 2028 { 2029 `${upper("hello")}`, 2030 "HELLO", 2031 false, 2032 }, 2033 2034 { 2035 `${upper("")}`, 2036 "", 2037 false, 2038 }, 2039 2040 { 2041 `${upper()}`, 2042 nil, 2043 true, 2044 }, 2045 }, 2046 }) 2047 } 2048 2049 func TestInterpolateFuncSha1(t *testing.T) { 2050 testFunction(t, testFunctionConfig{ 2051 Cases: []testFunctionCase{ 2052 { 2053 `${sha1("test")}`, 2054 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 2055 false, 2056 }, 2057 }, 2058 }) 2059 } 2060 2061 func TestInterpolateFuncSha256(t *testing.T) { 2062 testFunction(t, testFunctionConfig{ 2063 Cases: []testFunctionCase{ 2064 { // hexadecimal representation of sha256 sum 2065 `${sha256("test")}`, 2066 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 2067 false, 2068 }, 2069 }, 2070 }) 2071 } 2072 2073 func TestInterpolateFuncTitle(t *testing.T) { 2074 testFunction(t, testFunctionConfig{ 2075 Cases: []testFunctionCase{ 2076 { 2077 `${title("hello")}`, 2078 "Hello", 2079 false, 2080 }, 2081 2082 { 2083 `${title("hello world")}`, 2084 "Hello World", 2085 false, 2086 }, 2087 2088 { 2089 `${title("")}`, 2090 "", 2091 false, 2092 }, 2093 2094 { 2095 `${title()}`, 2096 nil, 2097 true, 2098 }, 2099 }, 2100 }) 2101 } 2102 2103 func TestInterpolateFuncTrimSpace(t *testing.T) { 2104 testFunction(t, testFunctionConfig{ 2105 Cases: []testFunctionCase{ 2106 { 2107 `${trimspace(" test ")}`, 2108 "test", 2109 false, 2110 }, 2111 }, 2112 }) 2113 } 2114 2115 func TestInterpolateFuncBase64Sha256(t *testing.T) { 2116 testFunction(t, testFunctionConfig{ 2117 Cases: []testFunctionCase{ 2118 { 2119 `${base64sha256("test")}`, 2120 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 2121 false, 2122 }, 2123 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2124 `${base64encode(sha256("test"))}`, 2125 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 2126 false, 2127 }, 2128 }, 2129 }) 2130 } 2131 2132 func TestInterpolateFuncMd5(t *testing.T) { 2133 testFunction(t, testFunctionConfig{ 2134 Cases: []testFunctionCase{ 2135 { 2136 `${md5("tada")}`, 2137 "ce47d07243bb6eaf5e1322c81baf9bbf", 2138 false, 2139 }, 2140 { // Confirm that we're not trimming any whitespaces 2141 `${md5(" tada ")}`, 2142 "aadf191a583e53062de2d02c008141c4", 2143 false, 2144 }, 2145 { // We accept empty string too 2146 `${md5("")}`, 2147 "d41d8cd98f00b204e9800998ecf8427e", 2148 false, 2149 }, 2150 }, 2151 }) 2152 } 2153 2154 func TestInterpolateFuncUUID(t *testing.T) { 2155 results := make(map[string]bool) 2156 2157 for i := 0; i < 100; i++ { 2158 ast, err := hil.Parse("${uuid()}") 2159 if err != nil { 2160 t.Fatalf("err: %s", err) 2161 } 2162 2163 result, err := hil.Eval(ast, langEvalConfig(nil)) 2164 if err != nil { 2165 t.Fatalf("err: %s", err) 2166 } 2167 2168 if results[result.Value.(string)] { 2169 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 2170 } 2171 2172 results[result.Value.(string)] = true 2173 } 2174 } 2175 2176 func TestInterpolateFuncTimestamp(t *testing.T) { 2177 currentTime := time.Now().UTC() 2178 ast, err := hil.Parse("${timestamp()}") 2179 if err != nil { 2180 t.Fatalf("err: %s", err) 2181 } 2182 2183 result, err := hil.Eval(ast, langEvalConfig(nil)) 2184 if err != nil { 2185 t.Fatalf("err: %s", err) 2186 } 2187 resultTime, err := time.Parse(time.RFC3339, result.Value.(string)) 2188 if err != nil { 2189 t.Fatalf("Error parsing timestamp: %s", err) 2190 } 2191 2192 if resultTime.Sub(currentTime).Seconds() > 10.0 { 2193 t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string)) 2194 } 2195 } 2196 2197 type testFunctionConfig struct { 2198 Cases []testFunctionCase 2199 Vars map[string]ast.Variable 2200 } 2201 2202 type testFunctionCase struct { 2203 Input string 2204 Result interface{} 2205 Error bool 2206 } 2207 2208 func testFunction(t *testing.T, config testFunctionConfig) { 2209 for i, tc := range config.Cases { 2210 ast, err := hil.Parse(tc.Input) 2211 if err != nil { 2212 t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err) 2213 } 2214 2215 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 2216 if err != nil != tc.Error { 2217 t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err) 2218 } 2219 2220 if !reflect.DeepEqual(result.Value, tc.Result) { 2221 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 2222 i, tc.Input, result.Value, tc.Result) 2223 } 2224 } 2225 } 2226 2227 func TestInterpolateFuncPathExpand(t *testing.T) { 2228 homePath, err := homedir.Dir() 2229 if err != nil { 2230 t.Fatalf("Error getting home directory: %v", err) 2231 } 2232 testFunction(t, testFunctionConfig{ 2233 Cases: []testFunctionCase{ 2234 { 2235 `${pathexpand("~/test-file")}`, 2236 filepath.Join(homePath, "test-file"), 2237 false, 2238 }, 2239 { 2240 `${pathexpand("~/another/test/file")}`, 2241 filepath.Join(homePath, "another/test/file"), 2242 false, 2243 }, 2244 { 2245 `${pathexpand("/root/file")}`, 2246 "/root/file", 2247 false, 2248 }, 2249 { 2250 `${pathexpand("/")}`, 2251 "/", 2252 false, 2253 }, 2254 { 2255 `${pathexpand()}`, 2256 nil, 2257 true, 2258 }, 2259 }, 2260 }) 2261 } 2262 2263 func TestInterpolateFuncSubstr(t *testing.T) { 2264 testFunction(t, testFunctionConfig{ 2265 Cases: []testFunctionCase{ 2266 { 2267 `${substr("foobar", 0, 0)}`, 2268 "", 2269 false, 2270 }, 2271 { 2272 `${substr("foobar", 0, -1)}`, 2273 "foobar", 2274 false, 2275 }, 2276 { 2277 `${substr("foobar", 0, 3)}`, 2278 "foo", 2279 false, 2280 }, 2281 { 2282 `${substr("foobar", 3, 3)}`, 2283 "bar", 2284 false, 2285 }, 2286 { 2287 `${substr("foobar", -3, 3)}`, 2288 "bar", 2289 false, 2290 }, 2291 2292 // empty string 2293 { 2294 `${substr("", 0, 0)}`, 2295 "", 2296 false, 2297 }, 2298 2299 // invalid offset 2300 { 2301 `${substr("", 1, 0)}`, 2302 nil, 2303 true, 2304 }, 2305 2306 // invalid length 2307 { 2308 `${substr("", 0, 1)}`, 2309 nil, 2310 true, 2311 }, 2312 { 2313 `${substr("", 0, -2)}`, 2314 nil, 2315 true, 2316 }, 2317 }, 2318 }) 2319 }