github.com/grange74/terraform@v0.7.0-rc3.0.20160722171430-8c8803864753/config/interpolate_funcs_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "reflect" 8 "testing" 9 10 "github.com/hashicorp/hil" 11 "github.com/hashicorp/hil/ast" 12 ) 13 14 func TestInterpolateFuncList(t *testing.T) { 15 testFunction(t, testFunctionConfig{ 16 Cases: []testFunctionCase{ 17 // empty input returns empty list 18 { 19 `${list()}`, 20 []interface{}{}, 21 false, 22 }, 23 24 // single input returns list of length 1 25 { 26 `${list("hello")}`, 27 []interface{}{"hello"}, 28 false, 29 }, 30 31 // two inputs returns list of length 2 32 { 33 `${list("hello", "world")}`, 34 []interface{}{"hello", "world"}, 35 false, 36 }, 37 38 // not a string input gives error 39 { 40 `${list("hello", 42)}`, 41 nil, 42 true, 43 }, 44 45 // list of lists 46 { 47 `${list("${var.list}", "${var.list2}")}`, 48 []interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}}, 49 false, 50 }, 51 52 // list of maps 53 { 54 `${list("${var.map}", "${var.map2}")}`, 55 []interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}}, 56 false, 57 }, 58 59 // error on a heterogeneous list 60 { 61 `${list("first", "${var.list}")}`, 62 nil, 63 true, 64 }, 65 }, 66 Vars: map[string]ast.Variable{ 67 "var.list": { 68 Type: ast.TypeList, 69 Value: []ast.Variable{ 70 { 71 Type: ast.TypeString, 72 Value: "Hello", 73 }, 74 { 75 Type: ast.TypeString, 76 Value: "World", 77 }, 78 }, 79 }, 80 "var.list2": { 81 Type: ast.TypeList, 82 Value: []ast.Variable{ 83 { 84 Type: ast.TypeString, 85 Value: "bar", 86 }, 87 { 88 Type: ast.TypeString, 89 Value: "baz", 90 }, 91 }, 92 }, 93 94 "var.map": { 95 Type: ast.TypeMap, 96 Value: map[string]ast.Variable{ 97 "key": { 98 Type: ast.TypeString, 99 Value: "bar", 100 }, 101 }, 102 }, 103 "var.map2": { 104 Type: ast.TypeMap, 105 Value: map[string]ast.Variable{ 106 "key2": { 107 Type: ast.TypeString, 108 Value: "baz", 109 }, 110 }, 111 }, 112 }, 113 }) 114 } 115 116 func TestInterpolateFuncCompact(t *testing.T) { 117 testFunction(t, testFunctionConfig{ 118 Cases: []testFunctionCase{ 119 // empty string within array 120 { 121 `${compact(split(",", "a,,b"))}`, 122 []interface{}{"a", "b"}, 123 false, 124 }, 125 126 // empty string at the end of array 127 { 128 `${compact(split(",", "a,b,"))}`, 129 []interface{}{"a", "b"}, 130 false, 131 }, 132 133 // single empty string 134 { 135 `${compact(split(",", ""))}`, 136 []interface{}{}, 137 false, 138 }, 139 }, 140 }) 141 } 142 143 func TestInterpolateFuncCidrHost(t *testing.T) { 144 testFunction(t, testFunctionConfig{ 145 Cases: []testFunctionCase{ 146 { 147 `${cidrhost("192.168.1.0/24", 5)}`, 148 "192.168.1.5", 149 false, 150 }, 151 { 152 `${cidrhost("192.168.1.0/30", 255)}`, 153 nil, 154 true, // 255 doesn't fit in two bits 155 }, 156 { 157 `${cidrhost("not-a-cidr", 6)}`, 158 nil, 159 true, // not a valid CIDR mask 160 }, 161 { 162 `${cidrhost("10.256.0.0/8", 6)}`, 163 nil, 164 true, // can't have an octet >255 165 }, 166 }, 167 }) 168 } 169 170 func TestInterpolateFuncCidrNetmask(t *testing.T) { 171 testFunction(t, testFunctionConfig{ 172 Cases: []testFunctionCase{ 173 { 174 `${cidrnetmask("192.168.1.0/24")}`, 175 "255.255.255.0", 176 false, 177 }, 178 { 179 `${cidrnetmask("192.168.1.0/32")}`, 180 "255.255.255.255", 181 false, 182 }, 183 { 184 `${cidrnetmask("0.0.0.0/0")}`, 185 "0.0.0.0", 186 false, 187 }, 188 { 189 // This doesn't really make sense for IPv6 networks 190 // but it ought to do something sensible anyway. 191 `${cidrnetmask("1::/64")}`, 192 "ffff:ffff:ffff:ffff::", 193 false, 194 }, 195 { 196 `${cidrnetmask("not-a-cidr")}`, 197 nil, 198 true, // not a valid CIDR mask 199 }, 200 { 201 `${cidrnetmask("10.256.0.0/8")}`, 202 nil, 203 true, // can't have an octet >255 204 }, 205 }, 206 }) 207 } 208 209 func TestInterpolateFuncCidrSubnet(t *testing.T) { 210 testFunction(t, testFunctionConfig{ 211 Cases: []testFunctionCase{ 212 { 213 `${cidrsubnet("192.168.2.0/20", 4, 6)}`, 214 "192.168.6.0/24", 215 false, 216 }, 217 { 218 `${cidrsubnet("fe80::/48", 16, 6)}`, 219 "fe80:0:0:6::/64", 220 false, 221 }, 222 { 223 // IPv4 address encoded in IPv6 syntax gets normalized 224 `${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`, 225 "192.168.6.0/24", 226 false, 227 }, 228 { 229 `${cidrsubnet("192.168.0.0/30", 4, 6)}`, 230 nil, 231 true, // not enough bits left 232 }, 233 { 234 `${cidrsubnet("192.168.0.0/16", 2, 16)}`, 235 nil, 236 true, // can't encode 16 in 2 bits 237 }, 238 { 239 `${cidrsubnet("not-a-cidr", 4, 6)}`, 240 nil, 241 true, // not a valid CIDR mask 242 }, 243 { 244 `${cidrsubnet("10.256.0.0/8", 4, 6)}`, 245 nil, 246 true, // can't have an octet >255 247 }, 248 }, 249 }) 250 } 251 252 func TestInterpolateFuncCoalesce(t *testing.T) { 253 testFunction(t, testFunctionConfig{ 254 Cases: []testFunctionCase{ 255 { 256 `${coalesce("first", "second", "third")}`, 257 "first", 258 false, 259 }, 260 { 261 `${coalesce("", "second", "third")}`, 262 "second", 263 false, 264 }, 265 { 266 `${coalesce("", "", "")}`, 267 "", 268 false, 269 }, 270 { 271 `${coalesce("foo")}`, 272 nil, 273 true, 274 }, 275 }, 276 }) 277 } 278 279 func TestInterpolateFuncConcat(t *testing.T) { 280 testFunction(t, testFunctionConfig{ 281 Cases: []testFunctionCase{ 282 // String + list 283 { 284 `${concat("a", split(",", "b,c"))}`, 285 []interface{}{"a", "b", "c"}, 286 false, 287 }, 288 289 // List + string 290 { 291 `${concat(split(",", "a,b"), "c")}`, 292 []interface{}{"a", "b", "c"}, 293 false, 294 }, 295 296 // Single list 297 { 298 `${concat(split(",", ",foo,"))}`, 299 []interface{}{"", "foo", ""}, 300 false, 301 }, 302 { 303 `${concat(split(",", "a,b,c"))}`, 304 []interface{}{"a", "b", "c"}, 305 false, 306 }, 307 308 // Two lists 309 { 310 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 311 []interface{}{"a", "b", "c", "d", "e"}, 312 false, 313 }, 314 // Two lists with different separators 315 { 316 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 317 []interface{}{"a", "b", "c", "d", "e"}, 318 false, 319 }, 320 321 // More lists 322 { 323 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 324 []interface{}{"a", "b", "c", "d", "e", "f", "0", "1"}, 325 false, 326 }, 327 328 // list vars 329 { 330 `${concat("${var.list}", "${var.list}")}`, 331 []interface{}{"a", "b", "a", "b"}, 332 false, 333 }, 334 // lists of lists 335 { 336 `${concat("${var.lists}", "${var.lists}")}`, 337 []interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}}, 338 false, 339 }, 340 341 // lists of maps 342 { 343 `${concat("${var.maps}", "${var.maps}")}`, 344 []interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}}, 345 false, 346 }, 347 348 // mismatched types 349 { 350 `${concat("${var.lists}", "${var.maps}")}`, 351 nil, 352 true, 353 }, 354 }, 355 Vars: map[string]ast.Variable{ 356 "var.list": { 357 Type: ast.TypeList, 358 Value: []ast.Variable{ 359 { 360 Type: ast.TypeString, 361 Value: "a", 362 }, 363 { 364 Type: ast.TypeString, 365 Value: "b", 366 }, 367 }, 368 }, 369 "var.lists": { 370 Type: ast.TypeList, 371 Value: []ast.Variable{ 372 { 373 Type: ast.TypeList, 374 Value: []ast.Variable{ 375 { 376 Type: ast.TypeString, 377 Value: "c", 378 }, 379 { 380 Type: ast.TypeString, 381 Value: "d", 382 }, 383 }, 384 }, 385 }, 386 }, 387 "var.maps": { 388 Type: ast.TypeList, 389 Value: []ast.Variable{ 390 { 391 Type: ast.TypeMap, 392 Value: map[string]ast.Variable{ 393 "key1": { 394 Type: ast.TypeString, 395 Value: "a", 396 }, 397 "key2": { 398 Type: ast.TypeString, 399 Value: "b", 400 }, 401 }, 402 }, 403 }, 404 }, 405 }, 406 }) 407 } 408 409 func TestInterpolateFuncDistinct(t *testing.T) { 410 testFunction(t, testFunctionConfig{ 411 Cases: []testFunctionCase{ 412 // 3 duplicates 413 { 414 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, 415 []interface{}{"user1", "user2", "user3"}, 416 false, 417 }, 418 // 1 duplicate 419 { 420 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, 421 []interface{}{"user1", "user2", "user3", "user4"}, 422 false, 423 }, 424 // too many args 425 { 426 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, 427 nil, 428 true, 429 }, 430 }, 431 }) 432 } 433 434 func TestInterpolateFuncFile(t *testing.T) { 435 tf, err := ioutil.TempFile("", "tf") 436 if err != nil { 437 t.Fatalf("err: %s", err) 438 } 439 path := tf.Name() 440 tf.Write([]byte("foo")) 441 tf.Close() 442 defer os.Remove(path) 443 444 testFunction(t, testFunctionConfig{ 445 Cases: []testFunctionCase{ 446 { 447 fmt.Sprintf(`${file("%s")}`, path), 448 "foo", 449 false, 450 }, 451 452 // Invalid path 453 { 454 `${file("/i/dont/exist")}`, 455 nil, 456 true, 457 }, 458 459 // Too many args 460 { 461 `${file("foo", "bar")}`, 462 nil, 463 true, 464 }, 465 }, 466 }) 467 } 468 469 func TestInterpolateFuncFormat(t *testing.T) { 470 testFunction(t, testFunctionConfig{ 471 Cases: []testFunctionCase{ 472 { 473 `${format("hello")}`, 474 "hello", 475 false, 476 }, 477 478 { 479 `${format("hello %s", "world")}`, 480 "hello world", 481 false, 482 }, 483 484 { 485 `${format("hello %d", 42)}`, 486 "hello 42", 487 false, 488 }, 489 490 { 491 `${format("hello %05d", 42)}`, 492 "hello 00042", 493 false, 494 }, 495 496 { 497 `${format("hello %05d", 12345)}`, 498 "hello 12345", 499 false, 500 }, 501 }, 502 }) 503 } 504 505 func TestInterpolateFuncFormatList(t *testing.T) { 506 testFunction(t, testFunctionConfig{ 507 Cases: []testFunctionCase{ 508 // formatlist requires at least one list 509 { 510 `${formatlist("hello")}`, 511 nil, 512 true, 513 }, 514 { 515 `${formatlist("hello %s", "world")}`, 516 nil, 517 true, 518 }, 519 // formatlist applies to each list element in turn 520 { 521 `${formatlist("<%s>", split(",", "A,B"))}`, 522 []interface{}{"<A>", "<B>"}, 523 false, 524 }, 525 // formatlist repeats scalar elements 526 { 527 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 528 "x=A, x=B, x=C", 529 false, 530 }, 531 // Multiple lists are walked in parallel 532 { 533 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 534 "A=1, B=2, C=3", 535 false, 536 }, 537 // Mismatched list lengths generate an error 538 { 539 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 540 nil, 541 true, 542 }, 543 // Works with lists of length 1 [GH-2240] 544 { 545 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 546 []interface{}{"demo-rest-elb.id"}, 547 false, 548 }, 549 }, 550 }) 551 } 552 553 func TestInterpolateFuncIndex(t *testing.T) { 554 testFunction(t, testFunctionConfig{ 555 Vars: map[string]ast.Variable{ 556 "var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}), 557 "var.list2": interfaceToVariableSwallowError([]string{"foo"}), 558 "var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}), 559 }, 560 Cases: []testFunctionCase{ 561 { 562 `${index("test", "")}`, 563 nil, 564 true, 565 }, 566 567 { 568 `${index(var.list1, "foo")}`, 569 nil, 570 true, 571 }, 572 573 { 574 `${index(var.list2, "foo")}`, 575 "0", 576 false, 577 }, 578 579 { 580 `${index(var.list3, "bar")}`, 581 "2", 582 false, 583 }, 584 }, 585 }) 586 } 587 588 func TestInterpolateFuncJoin(t *testing.T) { 589 testFunction(t, testFunctionConfig{ 590 Vars: map[string]ast.Variable{ 591 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 592 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 593 }, 594 Cases: []testFunctionCase{ 595 { 596 `${join(",")}`, 597 nil, 598 true, 599 }, 600 601 { 602 `${join(",", var.a_list)}`, 603 "foo", 604 false, 605 }, 606 607 { 608 `${join(".", var.a_longer_list)}`, 609 "foo.bar.baz", 610 false, 611 }, 612 }, 613 }) 614 } 615 616 func TestInterpolateFuncJSONEncode(t *testing.T) { 617 testFunction(t, testFunctionConfig{ 618 Vars: map[string]ast.Variable{ 619 "easy": ast.Variable{ 620 Value: "test", 621 Type: ast.TypeString, 622 }, 623 "hard": ast.Variable{ 624 Value: " foo \\ \n \t \" bar ", 625 Type: ast.TypeString, 626 }, 627 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 628 // XXX can't use InterfaceToVariable as it converts empty slice into empty 629 // map. 630 "emptylist": ast.Variable{ 631 Value: []ast.Variable{}, 632 Type: ast.TypeList, 633 }, 634 "map": interfaceToVariableSwallowError(map[string]string{ 635 "foo": "bar", 636 "ba \n z": "q\\x", 637 }), 638 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 639 640 // Not yet supported (but it would be nice) 641 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 642 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 643 }, 644 Cases: []testFunctionCase{ 645 { 646 `${jsonencode("test")}`, 647 `"test"`, 648 false, 649 }, 650 { 651 `${jsonencode(easy)}`, 652 `"test"`, 653 false, 654 }, 655 { 656 `${jsonencode(hard)}`, 657 `" foo \\ \n \t \" bar "`, 658 false, 659 }, 660 { 661 `${jsonencode("")}`, 662 `""`, 663 false, 664 }, 665 { 666 `${jsonencode()}`, 667 nil, 668 true, 669 }, 670 { 671 `${jsonencode(list)}`, 672 `["foo","bar\tbaz"]`, 673 false, 674 }, 675 { 676 `${jsonencode(emptylist)}`, 677 `[]`, 678 false, 679 }, 680 { 681 `${jsonencode(map)}`, 682 `{"ba \n z":"q\\x","foo":"bar"}`, 683 false, 684 }, 685 { 686 `${jsonencode(emptymap)}`, 687 `{}`, 688 false, 689 }, 690 { 691 `${jsonencode(nestedlist)}`, 692 nil, 693 true, 694 }, 695 { 696 `${jsonencode(nestedmap)}`, 697 nil, 698 true, 699 }, 700 }, 701 }) 702 } 703 704 func TestInterpolateFuncReplace(t *testing.T) { 705 testFunction(t, testFunctionConfig{ 706 Cases: []testFunctionCase{ 707 // Regular search and replace 708 { 709 `${replace("hello", "hel", "bel")}`, 710 "bello", 711 false, 712 }, 713 714 // Search string doesn't match 715 { 716 `${replace("hello", "nope", "bel")}`, 717 "hello", 718 false, 719 }, 720 721 // Regular expression 722 { 723 `${replace("hello", "/l/", "L")}`, 724 "heLLo", 725 false, 726 }, 727 728 { 729 `${replace("helo", "/(l)/", "$1$1")}`, 730 "hello", 731 false, 732 }, 733 734 // Bad regexp 735 { 736 `${replace("helo", "/(l/", "$1$1")}`, 737 nil, 738 true, 739 }, 740 }, 741 }) 742 } 743 744 func TestInterpolateFuncLength(t *testing.T) { 745 testFunction(t, testFunctionConfig{ 746 Cases: []testFunctionCase{ 747 // Raw strings 748 { 749 `${length("")}`, 750 "0", 751 false, 752 }, 753 { 754 `${length("a")}`, 755 "1", 756 false, 757 }, 758 { 759 `${length(" ")}`, 760 "1", 761 false, 762 }, 763 { 764 `${length(" a ,")}`, 765 "4", 766 false, 767 }, 768 { 769 `${length("aaa")}`, 770 "3", 771 false, 772 }, 773 774 // Lists 775 { 776 `${length(split(",", "a"))}`, 777 "1", 778 false, 779 }, 780 { 781 `${length(split(",", "foo,"))}`, 782 "2", 783 false, 784 }, 785 { 786 `${length(split(",", ",foo,"))}`, 787 "3", 788 false, 789 }, 790 { 791 `${length(split(",", "foo,bar"))}`, 792 "2", 793 false, 794 }, 795 { 796 `${length(split(".", "one.two.three.four.five"))}`, 797 "5", 798 false, 799 }, 800 // Want length 0 if we split an empty string then compact 801 { 802 `${length(compact(split(",", "")))}`, 803 "0", 804 false, 805 }, 806 }, 807 }) 808 } 809 810 func TestInterpolateFuncSignum(t *testing.T) { 811 testFunction(t, testFunctionConfig{ 812 Cases: []testFunctionCase{ 813 { 814 `${signum()}`, 815 nil, 816 true, 817 }, 818 819 { 820 `${signum("")}`, 821 nil, 822 true, 823 }, 824 825 { 826 `${signum(0)}`, 827 "0", 828 false, 829 }, 830 831 { 832 `${signum(15)}`, 833 "1", 834 false, 835 }, 836 837 { 838 `${signum(-29)}`, 839 "-1", 840 false, 841 }, 842 }, 843 }) 844 } 845 846 func TestInterpolateFuncSort(t *testing.T) { 847 testFunction(t, testFunctionConfig{ 848 Vars: map[string]ast.Variable{ 849 "var.strings": ast.Variable{ 850 Type: ast.TypeList, 851 Value: []ast.Variable{ 852 {Type: ast.TypeString, Value: "c"}, 853 {Type: ast.TypeString, Value: "a"}, 854 {Type: ast.TypeString, Value: "b"}, 855 }, 856 }, 857 "var.notstrings": ast.Variable{ 858 Type: ast.TypeList, 859 Value: []ast.Variable{ 860 {Type: ast.TypeList, Value: []ast.Variable{}}, 861 {Type: ast.TypeString, Value: "b"}, 862 }, 863 }, 864 }, 865 Cases: []testFunctionCase{ 866 { 867 `${sort(var.strings)}`, 868 []interface{}{"a", "b", "c"}, 869 false, 870 }, 871 { 872 `${sort(var.notstrings)}`, 873 nil, 874 true, 875 }, 876 }, 877 }) 878 } 879 880 func TestInterpolateFuncSplit(t *testing.T) { 881 testFunction(t, testFunctionConfig{ 882 Cases: []testFunctionCase{ 883 { 884 `${split(",")}`, 885 nil, 886 true, 887 }, 888 889 { 890 `${split(",", "")}`, 891 []interface{}{""}, 892 false, 893 }, 894 895 { 896 `${split(",", "foo")}`, 897 []interface{}{"foo"}, 898 false, 899 }, 900 901 { 902 `${split(",", ",,,")}`, 903 []interface{}{"", "", "", ""}, 904 false, 905 }, 906 907 { 908 `${split(",", "foo,")}`, 909 []interface{}{"foo", ""}, 910 false, 911 }, 912 913 { 914 `${split(",", ",foo,")}`, 915 []interface{}{"", "foo", ""}, 916 false, 917 }, 918 919 { 920 `${split(".", "foo.bar.baz")}`, 921 []interface{}{"foo", "bar", "baz"}, 922 false, 923 }, 924 }, 925 }) 926 } 927 928 func TestInterpolateFuncLookup(t *testing.T) { 929 testFunction(t, testFunctionConfig{ 930 Vars: map[string]ast.Variable{ 931 "var.foo": ast.Variable{ 932 Type: ast.TypeMap, 933 Value: map[string]ast.Variable{ 934 "bar": ast.Variable{ 935 Type: ast.TypeString, 936 Value: "baz", 937 }, 938 }, 939 }, 940 }, 941 Cases: []testFunctionCase{ 942 { 943 `${lookup(var.foo, "bar")}`, 944 "baz", 945 false, 946 }, 947 948 // Invalid key 949 { 950 `${lookup(var.foo, "baz")}`, 951 nil, 952 true, 953 }, 954 955 // Supplied default with valid key 956 { 957 `${lookup(var.foo, "bar", "")}`, 958 "baz", 959 false, 960 }, 961 962 // Supplied default with invalid key 963 { 964 `${lookup(var.foo, "zip", "")}`, 965 "", 966 false, 967 }, 968 969 // Too many args 970 { 971 `${lookup(var.foo, "bar", "", "abc")}`, 972 nil, 973 true, 974 }, 975 976 // Non-empty default 977 { 978 `${lookup(var.foo, "zap", "xyz")}`, 979 "xyz", 980 false, 981 }, 982 }, 983 }) 984 } 985 986 func TestInterpolateFuncKeys(t *testing.T) { 987 testFunction(t, testFunctionConfig{ 988 Vars: map[string]ast.Variable{ 989 "var.foo": ast.Variable{ 990 Type: ast.TypeMap, 991 Value: map[string]ast.Variable{ 992 "bar": ast.Variable{ 993 Value: "baz", 994 Type: ast.TypeString, 995 }, 996 "qux": ast.Variable{ 997 Value: "quack", 998 Type: ast.TypeString, 999 }, 1000 }, 1001 }, 1002 "var.str": ast.Variable{ 1003 Value: "astring", 1004 Type: ast.TypeString, 1005 }, 1006 }, 1007 Cases: []testFunctionCase{ 1008 { 1009 `${keys(var.foo)}`, 1010 []interface{}{"bar", "qux"}, 1011 false, 1012 }, 1013 1014 // Invalid key 1015 { 1016 `${keys(var.not)}`, 1017 nil, 1018 true, 1019 }, 1020 1021 // Too many args 1022 { 1023 `${keys(var.foo, "bar")}`, 1024 nil, 1025 true, 1026 }, 1027 1028 // Not a map 1029 { 1030 `${keys(var.str)}`, 1031 nil, 1032 true, 1033 }, 1034 }, 1035 }) 1036 } 1037 1038 // Confirm that keys return in sorted order, and values return in the order of 1039 // their sorted keys. 1040 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1041 testFunction(t, testFunctionConfig{ 1042 Vars: map[string]ast.Variable{ 1043 "var.foo": ast.Variable{ 1044 Type: ast.TypeMap, 1045 Value: map[string]ast.Variable{ 1046 "D": ast.Variable{ 1047 Value: "2", 1048 Type: ast.TypeString, 1049 }, 1050 "C": ast.Variable{ 1051 Value: "Y", 1052 Type: ast.TypeString, 1053 }, 1054 "A": ast.Variable{ 1055 Value: "X", 1056 Type: ast.TypeString, 1057 }, 1058 "10": ast.Variable{ 1059 Value: "Z", 1060 Type: ast.TypeString, 1061 }, 1062 "1": ast.Variable{ 1063 Value: "4", 1064 Type: ast.TypeString, 1065 }, 1066 "3": ast.Variable{ 1067 Value: "W", 1068 Type: ast.TypeString, 1069 }, 1070 }, 1071 }, 1072 }, 1073 Cases: []testFunctionCase{ 1074 { 1075 `${keys(var.foo)}`, 1076 []interface{}{"1", "10", "3", "A", "C", "D"}, 1077 false, 1078 }, 1079 1080 { 1081 `${values(var.foo)}`, 1082 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1083 false, 1084 }, 1085 }, 1086 }) 1087 } 1088 1089 func TestInterpolateFuncValues(t *testing.T) { 1090 testFunction(t, testFunctionConfig{ 1091 Vars: map[string]ast.Variable{ 1092 "var.foo": ast.Variable{ 1093 Type: ast.TypeMap, 1094 Value: map[string]ast.Variable{ 1095 "bar": ast.Variable{ 1096 Value: "quack", 1097 Type: ast.TypeString, 1098 }, 1099 "qux": ast.Variable{ 1100 Value: "baz", 1101 Type: ast.TypeString, 1102 }, 1103 }, 1104 }, 1105 "var.str": ast.Variable{ 1106 Value: "astring", 1107 Type: ast.TypeString, 1108 }, 1109 }, 1110 Cases: []testFunctionCase{ 1111 { 1112 `${values(var.foo)}`, 1113 []interface{}{"quack", "baz"}, 1114 false, 1115 }, 1116 1117 // Invalid key 1118 { 1119 `${values(var.not)}`, 1120 nil, 1121 true, 1122 }, 1123 1124 // Too many args 1125 { 1126 `${values(var.foo, "bar")}`, 1127 nil, 1128 true, 1129 }, 1130 1131 // Not a map 1132 { 1133 `${values(var.str)}`, 1134 nil, 1135 true, 1136 }, 1137 }, 1138 }) 1139 } 1140 1141 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 1142 variable, _ := hil.InterfaceToVariable(input) 1143 return variable 1144 } 1145 1146 func TestInterpolateFuncElement(t *testing.T) { 1147 testFunction(t, testFunctionConfig{ 1148 Vars: map[string]ast.Variable{ 1149 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1150 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1151 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1152 }, 1153 Cases: []testFunctionCase{ 1154 { 1155 `${element(var.a_list, "1")}`, 1156 "baz", 1157 false, 1158 }, 1159 1160 { 1161 `${element(var.a_short_list, "0")}`, 1162 "foo", 1163 false, 1164 }, 1165 1166 // Invalid index should wrap vs. out-of-bounds 1167 { 1168 `${element(var.a_list, "2")}`, 1169 "foo", 1170 false, 1171 }, 1172 1173 // Negative number should fail 1174 { 1175 `${element(var.a_short_list, "-1")}`, 1176 nil, 1177 true, 1178 }, 1179 1180 // Empty list should fail 1181 { 1182 `${element(var.empty_list, 0)}`, 1183 nil, 1184 true, 1185 }, 1186 1187 // Too many args 1188 { 1189 `${element(var.a_list, "0", "2")}`, 1190 nil, 1191 true, 1192 }, 1193 }, 1194 }) 1195 } 1196 1197 func TestInterpolateFuncBase64Encode(t *testing.T) { 1198 testFunction(t, testFunctionConfig{ 1199 Cases: []testFunctionCase{ 1200 // Regular base64 encoding 1201 { 1202 `${base64encode("abc123!?$*&()'-=@~")}`, 1203 "YWJjMTIzIT8kKiYoKSctPUB+", 1204 false, 1205 }, 1206 }, 1207 }) 1208 } 1209 1210 func TestInterpolateFuncBase64Decode(t *testing.T) { 1211 testFunction(t, testFunctionConfig{ 1212 Cases: []testFunctionCase{ 1213 // Regular base64 decoding 1214 { 1215 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 1216 "abc123!?$*&()'-=@~", 1217 false, 1218 }, 1219 1220 // Invalid base64 data decoding 1221 { 1222 `${base64decode("this-is-an-invalid-base64-data")}`, 1223 nil, 1224 true, 1225 }, 1226 }, 1227 }) 1228 } 1229 1230 func TestInterpolateFuncLower(t *testing.T) { 1231 testFunction(t, testFunctionConfig{ 1232 Cases: []testFunctionCase{ 1233 { 1234 `${lower("HELLO")}`, 1235 "hello", 1236 false, 1237 }, 1238 1239 { 1240 `${lower("")}`, 1241 "", 1242 false, 1243 }, 1244 1245 { 1246 `${lower()}`, 1247 nil, 1248 true, 1249 }, 1250 }, 1251 }) 1252 } 1253 1254 func TestInterpolateFuncUpper(t *testing.T) { 1255 testFunction(t, testFunctionConfig{ 1256 Cases: []testFunctionCase{ 1257 { 1258 `${upper("hello")}`, 1259 "HELLO", 1260 false, 1261 }, 1262 1263 { 1264 `${upper("")}`, 1265 "", 1266 false, 1267 }, 1268 1269 { 1270 `${upper()}`, 1271 nil, 1272 true, 1273 }, 1274 }, 1275 }) 1276 } 1277 1278 func TestInterpolateFuncSha1(t *testing.T) { 1279 testFunction(t, testFunctionConfig{ 1280 Cases: []testFunctionCase{ 1281 { 1282 `${sha1("test")}`, 1283 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 1284 false, 1285 }, 1286 }, 1287 }) 1288 } 1289 1290 func TestInterpolateFuncSha256(t *testing.T) { 1291 testFunction(t, testFunctionConfig{ 1292 Cases: []testFunctionCase{ 1293 { // hexadecimal representation of sha256 sum 1294 `${sha256("test")}`, 1295 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 1296 false, 1297 }, 1298 }, 1299 }) 1300 } 1301 1302 func TestInterpolateFuncTrimSpace(t *testing.T) { 1303 testFunction(t, testFunctionConfig{ 1304 Cases: []testFunctionCase{ 1305 { 1306 `${trimspace(" test ")}`, 1307 "test", 1308 false, 1309 }, 1310 }, 1311 }) 1312 } 1313 1314 func TestInterpolateFuncBase64Sha256(t *testing.T) { 1315 testFunction(t, testFunctionConfig{ 1316 Cases: []testFunctionCase{ 1317 { 1318 `${base64sha256("test")}`, 1319 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 1320 false, 1321 }, 1322 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 1323 `${base64encode(sha256("test"))}`, 1324 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 1325 false, 1326 }, 1327 }, 1328 }) 1329 } 1330 1331 func TestInterpolateFuncMd5(t *testing.T) { 1332 testFunction(t, testFunctionConfig{ 1333 Cases: []testFunctionCase{ 1334 { 1335 `${md5("tada")}`, 1336 "ce47d07243bb6eaf5e1322c81baf9bbf", 1337 false, 1338 }, 1339 { // Confirm that we're not trimming any whitespaces 1340 `${md5(" tada ")}`, 1341 "aadf191a583e53062de2d02c008141c4", 1342 false, 1343 }, 1344 { // We accept empty string too 1345 `${md5("")}`, 1346 "d41d8cd98f00b204e9800998ecf8427e", 1347 false, 1348 }, 1349 }, 1350 }) 1351 } 1352 1353 func TestInterpolateFuncUUID(t *testing.T) { 1354 results := make(map[string]bool) 1355 1356 for i := 0; i < 100; i++ { 1357 ast, err := hil.Parse("${uuid()}") 1358 if err != nil { 1359 t.Fatalf("err: %s", err) 1360 } 1361 1362 result, err := hil.Eval(ast, langEvalConfig(nil)) 1363 if err != nil { 1364 t.Fatalf("err: %s", err) 1365 } 1366 1367 if results[result.Value.(string)] { 1368 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 1369 } 1370 1371 results[result.Value.(string)] = true 1372 } 1373 } 1374 1375 type testFunctionConfig struct { 1376 Cases []testFunctionCase 1377 Vars map[string]ast.Variable 1378 } 1379 1380 type testFunctionCase struct { 1381 Input string 1382 Result interface{} 1383 Error bool 1384 } 1385 1386 func testFunction(t *testing.T, config testFunctionConfig) { 1387 for i, tc := range config.Cases { 1388 ast, err := hil.Parse(tc.Input) 1389 if err != nil { 1390 t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) 1391 } 1392 1393 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 1394 if err != nil != tc.Error { 1395 t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) 1396 } 1397 1398 if !reflect.DeepEqual(result.Value, tc.Result) { 1399 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 1400 i, tc.Input, result.Value, tc.Result) 1401 } 1402 } 1403 }