github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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/terraform/config/lang" 11 "github.com/hashicorp/terraform/config/lang/ast" 12 ) 13 14 func TestInterpolateFuncCompact(t *testing.T) { 15 testFunction(t, testFunctionConfig{ 16 Cases: []testFunctionCase{ 17 // empty string within array 18 { 19 `${compact(split(",", "a,,b"))}`, 20 NewStringList([]string{"a", "b"}).String(), 21 false, 22 }, 23 24 // empty string at the end of array 25 { 26 `${compact(split(",", "a,b,"))}`, 27 NewStringList([]string{"a", "b"}).String(), 28 false, 29 }, 30 31 // single empty string 32 { 33 `${compact(split(",", ""))}`, 34 NewStringList([]string{}).String(), 35 false, 36 }, 37 }, 38 }) 39 } 40 41 func TestInterpolateFuncCidrHost(t *testing.T) { 42 testFunction(t, testFunctionConfig{ 43 Cases: []testFunctionCase{ 44 { 45 `${cidrhost("192.168.1.0/24", 5)}`, 46 "192.168.1.5", 47 false, 48 }, 49 { 50 `${cidrhost("192.168.1.0/30", 255)}`, 51 nil, 52 true, // 255 doesn't fit in two bits 53 }, 54 { 55 `${cidrhost("not-a-cidr", 6)}`, 56 nil, 57 true, // not a valid CIDR mask 58 }, 59 { 60 `${cidrhost("10.256.0.0/8", 6)}`, 61 nil, 62 true, // can't have an octet >255 63 }, 64 }, 65 }) 66 } 67 68 func TestInterpolateFuncCidrNetmask(t *testing.T) { 69 testFunction(t, testFunctionConfig{ 70 Cases: []testFunctionCase{ 71 { 72 `${cidrnetmask("192.168.1.0/24")}`, 73 "255.255.255.0", 74 false, 75 }, 76 { 77 `${cidrnetmask("192.168.1.0/32")}`, 78 "255.255.255.255", 79 false, 80 }, 81 { 82 `${cidrnetmask("0.0.0.0/0")}`, 83 "0.0.0.0", 84 false, 85 }, 86 { 87 // This doesn't really make sense for IPv6 networks 88 // but it ought to do something sensible anyway. 89 `${cidrnetmask("1::/64")}`, 90 "ffff:ffff:ffff:ffff::", 91 false, 92 }, 93 { 94 `${cidrnetmask("not-a-cidr")}`, 95 nil, 96 true, // not a valid CIDR mask 97 }, 98 { 99 `${cidrnetmask("10.256.0.0/8")}`, 100 nil, 101 true, // can't have an octet >255 102 }, 103 }, 104 }) 105 } 106 107 func TestInterpolateFuncCidrSubnet(t *testing.T) { 108 testFunction(t, testFunctionConfig{ 109 Cases: []testFunctionCase{ 110 { 111 `${cidrsubnet("192.168.2.0/20", 4, 6)}`, 112 "192.168.6.0/24", 113 false, 114 }, 115 { 116 `${cidrsubnet("fe80::/48", 16, 6)}`, 117 "fe80:0:0:6::/64", 118 false, 119 }, 120 { 121 // IPv4 address encoded in IPv6 syntax gets normalized 122 `${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`, 123 "192.168.6.0/24", 124 false, 125 }, 126 { 127 `${cidrsubnet("192.168.0.0/30", 4, 6)}`, 128 nil, 129 true, // not enough bits left 130 }, 131 { 132 `${cidrsubnet("192.168.0.0/16", 2, 16)}`, 133 nil, 134 true, // can't encode 16 in 2 bits 135 }, 136 { 137 `${cidrsubnet("not-a-cidr", 4, 6)}`, 138 nil, 139 true, // not a valid CIDR mask 140 }, 141 { 142 `${cidrsubnet("10.256.0.0/8", 4, 6)}`, 143 nil, 144 true, // can't have an octet >255 145 }, 146 }, 147 }) 148 } 149 150 func TestInterpolateFuncCoalesce(t *testing.T) { 151 testFunction(t, testFunctionConfig{ 152 Cases: []testFunctionCase{ 153 { 154 `${coalesce("first", "second", "third")}`, 155 "first", 156 false, 157 }, 158 { 159 `${coalesce("", "second", "third")}`, 160 "second", 161 false, 162 }, 163 { 164 `${coalesce("", "", "")}`, 165 "", 166 false, 167 }, 168 { 169 `${coalesce("foo")}`, 170 nil, 171 true, 172 }, 173 }, 174 }) 175 } 176 177 func TestInterpolateFuncDeprecatedConcat(t *testing.T) { 178 testFunction(t, testFunctionConfig{ 179 Cases: []testFunctionCase{ 180 { 181 `${concat("foo", "bar")}`, 182 "foobar", 183 false, 184 }, 185 186 { 187 `${concat("foo")}`, 188 "foo", 189 false, 190 }, 191 192 { 193 `${concat()}`, 194 nil, 195 true, 196 }, 197 }, 198 }) 199 } 200 201 func TestInterpolateFuncConcat(t *testing.T) { 202 testFunction(t, testFunctionConfig{ 203 Cases: []testFunctionCase{ 204 // String + list 205 { 206 `${concat("a", split(",", "b,c"))}`, 207 NewStringList([]string{"a", "b", "c"}).String(), 208 false, 209 }, 210 211 // List + string 212 { 213 `${concat(split(",", "a,b"), "c")}`, 214 NewStringList([]string{"a", "b", "c"}).String(), 215 false, 216 }, 217 218 // Single list 219 { 220 `${concat(split(",", ",foo,"))}`, 221 NewStringList([]string{"", "foo", ""}).String(), 222 false, 223 }, 224 { 225 `${concat(split(",", "a,b,c"))}`, 226 NewStringList([]string{"a", "b", "c"}).String(), 227 false, 228 }, 229 230 // Two lists 231 { 232 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 233 NewStringList([]string{"a", "b", "c", "d", "e"}).String(), 234 false, 235 }, 236 // Two lists with different separators 237 { 238 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 239 NewStringList([]string{"a", "b", "c", "d", "e"}).String(), 240 false, 241 }, 242 243 // More lists 244 { 245 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 246 NewStringList([]string{"a", "b", "c", "d", "e", "f", "0", "1"}).String(), 247 false, 248 }, 249 }, 250 }) 251 } 252 253 func TestInterpolateFuncFile(t *testing.T) { 254 tf, err := ioutil.TempFile("", "tf") 255 if err != nil { 256 t.Fatalf("err: %s", err) 257 } 258 path := tf.Name() 259 tf.Write([]byte("foo")) 260 tf.Close() 261 defer os.Remove(path) 262 263 testFunction(t, testFunctionConfig{ 264 Cases: []testFunctionCase{ 265 { 266 fmt.Sprintf(`${file("%s")}`, path), 267 "foo", 268 false, 269 }, 270 271 // Invalid path 272 { 273 `${file("/i/dont/exist")}`, 274 nil, 275 true, 276 }, 277 278 // Too many args 279 { 280 `${file("foo", "bar")}`, 281 nil, 282 true, 283 }, 284 }, 285 }) 286 } 287 288 func TestInterpolateFuncFormat(t *testing.T) { 289 testFunction(t, testFunctionConfig{ 290 Cases: []testFunctionCase{ 291 { 292 `${format("hello")}`, 293 "hello", 294 false, 295 }, 296 297 { 298 `${format("hello %s", "world")}`, 299 "hello world", 300 false, 301 }, 302 303 { 304 `${format("hello %d", 42)}`, 305 "hello 42", 306 false, 307 }, 308 309 { 310 `${format("hello %05d", 42)}`, 311 "hello 00042", 312 false, 313 }, 314 315 { 316 `${format("hello %05d", 12345)}`, 317 "hello 12345", 318 false, 319 }, 320 }, 321 }) 322 } 323 324 func TestInterpolateFuncFormatList(t *testing.T) { 325 testFunction(t, testFunctionConfig{ 326 Cases: []testFunctionCase{ 327 // formatlist requires at least one list 328 { 329 `${formatlist("hello")}`, 330 nil, 331 true, 332 }, 333 { 334 `${formatlist("hello %s", "world")}`, 335 nil, 336 true, 337 }, 338 // formatlist applies to each list element in turn 339 { 340 `${formatlist("<%s>", split(",", "A,B"))}`, 341 NewStringList([]string{"<A>", "<B>"}).String(), 342 false, 343 }, 344 // formatlist repeats scalar elements 345 { 346 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 347 "x=A, x=B, x=C", 348 false, 349 }, 350 // Multiple lists are walked in parallel 351 { 352 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 353 "A=1, B=2, C=3", 354 false, 355 }, 356 // Mismatched list lengths generate an error 357 { 358 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 359 nil, 360 true, 361 }, 362 // Works with lists of length 1 [GH-2240] 363 { 364 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 365 NewStringList([]string{"demo-rest-elb.id"}).String(), 366 false, 367 }, 368 }, 369 }) 370 } 371 372 func TestInterpolateFuncIndex(t *testing.T) { 373 testFunction(t, testFunctionConfig{ 374 Cases: []testFunctionCase{ 375 { 376 `${index("test", "")}`, 377 nil, 378 true, 379 }, 380 381 { 382 fmt.Sprintf(`${index("%s", "foo")}`, 383 NewStringList([]string{"notfoo", "stillnotfoo", "bar"}).String()), 384 nil, 385 true, 386 }, 387 388 { 389 fmt.Sprintf(`${index("%s", "foo")}`, 390 NewStringList([]string{"foo"}).String()), 391 "0", 392 false, 393 }, 394 395 { 396 fmt.Sprintf(`${index("%s", "bar")}`, 397 NewStringList([]string{"foo", "spam", "bar", "eggs"}).String()), 398 "2", 399 false, 400 }, 401 }, 402 }) 403 } 404 405 func TestInterpolateFuncJoin(t *testing.T) { 406 testFunction(t, testFunctionConfig{ 407 Cases: []testFunctionCase{ 408 { 409 `${join(",")}`, 410 nil, 411 true, 412 }, 413 414 { 415 fmt.Sprintf(`${join(",", "%s")}`, 416 NewStringList([]string{"foo"}).String()), 417 "foo", 418 false, 419 }, 420 421 /* 422 TODO 423 { 424 `${join(",", "foo", "bar")}`, 425 "foo,bar", 426 false, 427 }, 428 */ 429 430 { 431 fmt.Sprintf(`${join(".", "%s")}`, 432 NewStringList([]string{"foo", "bar", "baz"}).String()), 433 "foo.bar.baz", 434 false, 435 }, 436 }, 437 }) 438 } 439 440 func TestInterpolateFuncReplace(t *testing.T) { 441 testFunction(t, testFunctionConfig{ 442 Cases: []testFunctionCase{ 443 // Regular search and replace 444 { 445 `${replace("hello", "hel", "bel")}`, 446 "bello", 447 false, 448 }, 449 450 // Search string doesn't match 451 { 452 `${replace("hello", "nope", "bel")}`, 453 "hello", 454 false, 455 }, 456 457 // Regular expression 458 { 459 `${replace("hello", "/l/", "L")}`, 460 "heLLo", 461 false, 462 }, 463 464 { 465 `${replace("helo", "/(l)/", "$1$1")}`, 466 "hello", 467 false, 468 }, 469 470 // Bad regexp 471 { 472 `${replace("helo", "/(l/", "$1$1")}`, 473 nil, 474 true, 475 }, 476 }, 477 }) 478 } 479 480 func TestInterpolateFuncLength(t *testing.T) { 481 testFunction(t, testFunctionConfig{ 482 Cases: []testFunctionCase{ 483 // Raw strings 484 { 485 `${length("")}`, 486 "0", 487 false, 488 }, 489 { 490 `${length("a")}`, 491 "1", 492 false, 493 }, 494 { 495 `${length(" ")}`, 496 "1", 497 false, 498 }, 499 { 500 `${length(" a ,")}`, 501 "4", 502 false, 503 }, 504 { 505 `${length("aaa")}`, 506 "3", 507 false, 508 }, 509 510 // Lists 511 { 512 `${length(split(",", "a"))}`, 513 "1", 514 false, 515 }, 516 { 517 `${length(split(",", "foo,"))}`, 518 "2", 519 false, 520 }, 521 { 522 `${length(split(",", ",foo,"))}`, 523 "3", 524 false, 525 }, 526 { 527 `${length(split(",", "foo,bar"))}`, 528 "2", 529 false, 530 }, 531 { 532 `${length(split(".", "one.two.three.four.five"))}`, 533 "5", 534 false, 535 }, 536 // Want length 0 if we split an empty string then compact 537 { 538 `${length(compact(split(",", "")))}`, 539 "0", 540 false, 541 }, 542 }, 543 }) 544 } 545 546 func TestInterpolateFuncSplit(t *testing.T) { 547 testFunction(t, testFunctionConfig{ 548 Cases: []testFunctionCase{ 549 { 550 `${split(",")}`, 551 nil, 552 true, 553 }, 554 555 { 556 `${split(",", "")}`, 557 NewStringList([]string{""}).String(), 558 false, 559 }, 560 561 { 562 `${split(",", "foo")}`, 563 NewStringList([]string{"foo"}).String(), 564 false, 565 }, 566 567 { 568 `${split(",", ",,,")}`, 569 NewStringList([]string{"", "", "", ""}).String(), 570 false, 571 }, 572 573 { 574 `${split(",", "foo,")}`, 575 NewStringList([]string{"foo", ""}).String(), 576 false, 577 }, 578 579 { 580 `${split(",", ",foo,")}`, 581 NewStringList([]string{"", "foo", ""}).String(), 582 false, 583 }, 584 585 { 586 `${split(".", "foo.bar.baz")}`, 587 NewStringList([]string{"foo", "bar", "baz"}).String(), 588 false, 589 }, 590 }, 591 }) 592 } 593 594 func TestInterpolateFuncLookup(t *testing.T) { 595 testFunction(t, testFunctionConfig{ 596 Vars: map[string]ast.Variable{ 597 "var.foo.bar": ast.Variable{ 598 Value: "baz", 599 Type: ast.TypeString, 600 }, 601 }, 602 Cases: []testFunctionCase{ 603 { 604 `${lookup("foo", "bar")}`, 605 "baz", 606 false, 607 }, 608 609 // Invalid key 610 { 611 `${lookup("foo", "baz")}`, 612 nil, 613 true, 614 }, 615 616 // Too many args 617 { 618 `${lookup("foo", "bar", "baz")}`, 619 nil, 620 true, 621 }, 622 }, 623 }) 624 } 625 626 func TestInterpolateFuncKeys(t *testing.T) { 627 testFunction(t, testFunctionConfig{ 628 Vars: map[string]ast.Variable{ 629 "var.foo.bar": ast.Variable{ 630 Value: "baz", 631 Type: ast.TypeString, 632 }, 633 "var.foo.qux": ast.Variable{ 634 Value: "quack", 635 Type: ast.TypeString, 636 }, 637 "var.str": ast.Variable{ 638 Value: "astring", 639 Type: ast.TypeString, 640 }, 641 }, 642 Cases: []testFunctionCase{ 643 { 644 `${keys("foo")}`, 645 NewStringList([]string{"bar", "qux"}).String(), 646 false, 647 }, 648 649 // Invalid key 650 { 651 `${keys("not")}`, 652 nil, 653 true, 654 }, 655 656 // Too many args 657 { 658 `${keys("foo", "bar")}`, 659 nil, 660 true, 661 }, 662 663 // Not a map 664 { 665 `${keys("str")}`, 666 nil, 667 true, 668 }, 669 }, 670 }) 671 } 672 673 func TestInterpolateFuncValues(t *testing.T) { 674 testFunction(t, testFunctionConfig{ 675 Vars: map[string]ast.Variable{ 676 "var.foo.bar": ast.Variable{ 677 Value: "quack", 678 Type: ast.TypeString, 679 }, 680 "var.foo.qux": ast.Variable{ 681 Value: "baz", 682 Type: ast.TypeString, 683 }, 684 "var.str": ast.Variable{ 685 Value: "astring", 686 Type: ast.TypeString, 687 }, 688 }, 689 Cases: []testFunctionCase{ 690 { 691 `${values("foo")}`, 692 NewStringList([]string{"quack", "baz"}).String(), 693 false, 694 }, 695 696 // Invalid key 697 { 698 `${values("not")}`, 699 nil, 700 true, 701 }, 702 703 // Too many args 704 { 705 `${values("foo", "bar")}`, 706 nil, 707 true, 708 }, 709 710 // Not a map 711 { 712 `${values("str")}`, 713 nil, 714 true, 715 }, 716 }, 717 }) 718 } 719 720 func TestInterpolateFuncElement(t *testing.T) { 721 testFunction(t, testFunctionConfig{ 722 Cases: []testFunctionCase{ 723 { 724 fmt.Sprintf(`${element("%s", "1")}`, 725 NewStringList([]string{"foo", "baz"}).String()), 726 "baz", 727 false, 728 }, 729 730 { 731 fmt.Sprintf(`${element("%s", "0")}`, 732 NewStringList([]string{"foo"}).String()), 733 "foo", 734 false, 735 }, 736 737 // Invalid index should wrap vs. out-of-bounds 738 { 739 fmt.Sprintf(`${element("%s", "2")}`, 740 NewStringList([]string{"foo", "baz"}).String()), 741 "foo", 742 false, 743 }, 744 745 // Too many args 746 { 747 fmt.Sprintf(`${element("%s", "0", "2")}`, 748 NewStringList([]string{"foo", "baz"}).String()), 749 nil, 750 true, 751 }, 752 }, 753 }) 754 } 755 756 func TestInterpolateFuncBase64Encode(t *testing.T) { 757 testFunction(t, testFunctionConfig{ 758 Cases: []testFunctionCase{ 759 // Regular base64 encoding 760 { 761 `${base64encode("abc123!?$*&()'-=@~")}`, 762 "YWJjMTIzIT8kKiYoKSctPUB+", 763 false, 764 }, 765 }, 766 }) 767 } 768 769 func TestInterpolateFuncBase64Decode(t *testing.T) { 770 testFunction(t, testFunctionConfig{ 771 Cases: []testFunctionCase{ 772 // Regular base64 decoding 773 { 774 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 775 "abc123!?$*&()'-=@~", 776 false, 777 }, 778 779 // Invalid base64 data decoding 780 { 781 `${base64decode("this-is-an-invalid-base64-data")}`, 782 nil, 783 true, 784 }, 785 }, 786 }) 787 } 788 789 func TestInterpolateFuncLower(t *testing.T) { 790 testFunction(t, testFunctionConfig{ 791 Cases: []testFunctionCase{ 792 { 793 `${lower("HELLO")}`, 794 "hello", 795 false, 796 }, 797 798 { 799 `${lower("")}`, 800 "", 801 false, 802 }, 803 804 { 805 `${lower()}`, 806 nil, 807 true, 808 }, 809 }, 810 }) 811 } 812 813 func TestInterpolateFuncUpper(t *testing.T) { 814 testFunction(t, testFunctionConfig{ 815 Cases: []testFunctionCase{ 816 { 817 `${upper("hello")}`, 818 "HELLO", 819 false, 820 }, 821 822 { 823 `${upper("")}`, 824 "", 825 false, 826 }, 827 828 { 829 `${upper()}`, 830 nil, 831 true, 832 }, 833 }, 834 }) 835 } 836 837 type testFunctionConfig struct { 838 Cases []testFunctionCase 839 Vars map[string]ast.Variable 840 } 841 842 type testFunctionCase struct { 843 Input string 844 Result interface{} 845 Error bool 846 } 847 848 func testFunction(t *testing.T, config testFunctionConfig) { 849 for i, tc := range config.Cases { 850 ast, err := lang.Parse(tc.Input) 851 if err != nil { 852 t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) 853 } 854 855 out, _, err := lang.Eval(ast, langEvalConfig(config.Vars)) 856 if err != nil != tc.Error { 857 t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) 858 } 859 860 if !reflect.DeepEqual(out, tc.Result) { 861 t.Fatalf( 862 "%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 863 i, tc.Input, out, tc.Result) 864 } 865 } 866 }