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