github.com/icebourg/terraform@v0.6.5-0.20151015205227-263cc1b85535/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 TestInterpolateFuncDeprecatedConcat(t *testing.T) { 42 testFunction(t, testFunctionConfig{ 43 Cases: []testFunctionCase{ 44 { 45 `${concat("foo", "bar")}`, 46 "foobar", 47 false, 48 }, 49 50 { 51 `${concat("foo")}`, 52 "foo", 53 false, 54 }, 55 56 { 57 `${concat()}`, 58 nil, 59 true, 60 }, 61 }, 62 }) 63 } 64 65 func TestInterpolateFuncConcat(t *testing.T) { 66 testFunction(t, testFunctionConfig{ 67 Cases: []testFunctionCase{ 68 // String + list 69 { 70 `${concat("a", split(",", "b,c"))}`, 71 NewStringList([]string{"a", "b", "c"}).String(), 72 false, 73 }, 74 75 // List + string 76 { 77 `${concat(split(",", "a,b"), "c")}`, 78 NewStringList([]string{"a", "b", "c"}).String(), 79 false, 80 }, 81 82 // Single list 83 { 84 `${concat(split(",", ",foo,"))}`, 85 NewStringList([]string{"", "foo", ""}).String(), 86 false, 87 }, 88 { 89 `${concat(split(",", "a,b,c"))}`, 90 NewStringList([]string{"a", "b", "c"}).String(), 91 false, 92 }, 93 94 // Two lists 95 { 96 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 97 NewStringList([]string{"a", "b", "c", "d", "e"}).String(), 98 false, 99 }, 100 // Two lists with different separators 101 { 102 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 103 NewStringList([]string{"a", "b", "c", "d", "e"}).String(), 104 false, 105 }, 106 107 // More lists 108 { 109 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 110 NewStringList([]string{"a", "b", "c", "d", "e", "f", "0", "1"}).String(), 111 false, 112 }, 113 }, 114 }) 115 } 116 117 func TestInterpolateFuncFile(t *testing.T) { 118 tf, err := ioutil.TempFile("", "tf") 119 if err != nil { 120 t.Fatalf("err: %s", err) 121 } 122 path := tf.Name() 123 tf.Write([]byte("foo")) 124 tf.Close() 125 defer os.Remove(path) 126 127 testFunction(t, testFunctionConfig{ 128 Cases: []testFunctionCase{ 129 { 130 fmt.Sprintf(`${file("%s")}`, path), 131 "foo", 132 false, 133 }, 134 135 // Invalid path 136 { 137 `${file("/i/dont/exist")}`, 138 nil, 139 true, 140 }, 141 142 // Too many args 143 { 144 `${file("foo", "bar")}`, 145 nil, 146 true, 147 }, 148 }, 149 }) 150 } 151 152 func TestInterpolateFuncFormat(t *testing.T) { 153 testFunction(t, testFunctionConfig{ 154 Cases: []testFunctionCase{ 155 { 156 `${format("hello")}`, 157 "hello", 158 false, 159 }, 160 161 { 162 `${format("hello %s", "world")}`, 163 "hello world", 164 false, 165 }, 166 167 { 168 `${format("hello %d", 42)}`, 169 "hello 42", 170 false, 171 }, 172 173 { 174 `${format("hello %05d", 42)}`, 175 "hello 00042", 176 false, 177 }, 178 179 { 180 `${format("hello %05d", 12345)}`, 181 "hello 12345", 182 false, 183 }, 184 }, 185 }) 186 } 187 188 func TestInterpolateFuncFormatList(t *testing.T) { 189 testFunction(t, testFunctionConfig{ 190 Cases: []testFunctionCase{ 191 // formatlist requires at least one list 192 { 193 `${formatlist("hello")}`, 194 nil, 195 true, 196 }, 197 { 198 `${formatlist("hello %s", "world")}`, 199 nil, 200 true, 201 }, 202 // formatlist applies to each list element in turn 203 { 204 `${formatlist("<%s>", split(",", "A,B"))}`, 205 NewStringList([]string{"<A>", "<B>"}).String(), 206 false, 207 }, 208 // formatlist repeats scalar elements 209 { 210 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 211 "x=A, x=B, x=C", 212 false, 213 }, 214 // Multiple lists are walked in parallel 215 { 216 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 217 "A=1, B=2, C=3", 218 false, 219 }, 220 // Mismatched list lengths generate an error 221 { 222 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 223 nil, 224 true, 225 }, 226 // Works with lists of length 1 [GH-2240] 227 { 228 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 229 NewStringList([]string{"demo-rest-elb.id"}).String(), 230 false, 231 }, 232 }, 233 }) 234 } 235 236 func TestInterpolateFuncIndex(t *testing.T) { 237 testFunction(t, testFunctionConfig{ 238 Cases: []testFunctionCase{ 239 { 240 `${index("test", "")}`, 241 nil, 242 true, 243 }, 244 245 { 246 fmt.Sprintf(`${index("%s", "foo")}`, 247 NewStringList([]string{"notfoo", "stillnotfoo", "bar"}).String()), 248 nil, 249 true, 250 }, 251 252 { 253 fmt.Sprintf(`${index("%s", "foo")}`, 254 NewStringList([]string{"foo"}).String()), 255 "0", 256 false, 257 }, 258 259 { 260 fmt.Sprintf(`${index("%s", "bar")}`, 261 NewStringList([]string{"foo", "spam", "bar", "eggs"}).String()), 262 "2", 263 false, 264 }, 265 }, 266 }) 267 } 268 269 func TestInterpolateFuncJoin(t *testing.T) { 270 testFunction(t, testFunctionConfig{ 271 Cases: []testFunctionCase{ 272 { 273 `${join(",")}`, 274 nil, 275 true, 276 }, 277 278 { 279 fmt.Sprintf(`${join(",", "%s")}`, 280 NewStringList([]string{"foo"}).String()), 281 "foo", 282 false, 283 }, 284 285 /* 286 TODO 287 { 288 `${join(",", "foo", "bar")}`, 289 "foo,bar", 290 false, 291 }, 292 */ 293 294 { 295 fmt.Sprintf(`${join(".", "%s")}`, 296 NewStringList([]string{"foo", "bar", "baz"}).String()), 297 "foo.bar.baz", 298 false, 299 }, 300 }, 301 }) 302 } 303 304 func TestInterpolateFuncReplace(t *testing.T) { 305 testFunction(t, testFunctionConfig{ 306 Cases: []testFunctionCase{ 307 // Regular search and replace 308 { 309 `${replace("hello", "hel", "bel")}`, 310 "bello", 311 false, 312 }, 313 314 // Search string doesn't match 315 { 316 `${replace("hello", "nope", "bel")}`, 317 "hello", 318 false, 319 }, 320 321 // Regular expression 322 { 323 `${replace("hello", "/l/", "L")}`, 324 "heLLo", 325 false, 326 }, 327 328 { 329 `${replace("helo", "/(l)/", "$1$1")}`, 330 "hello", 331 false, 332 }, 333 334 // Bad regexp 335 { 336 `${replace("helo", "/(l/", "$1$1")}`, 337 nil, 338 true, 339 }, 340 }, 341 }) 342 } 343 344 func TestInterpolateFuncLength(t *testing.T) { 345 testFunction(t, testFunctionConfig{ 346 Cases: []testFunctionCase{ 347 // Raw strings 348 { 349 `${length("")}`, 350 "0", 351 false, 352 }, 353 { 354 `${length("a")}`, 355 "1", 356 false, 357 }, 358 { 359 `${length(" ")}`, 360 "1", 361 false, 362 }, 363 { 364 `${length(" a ,")}`, 365 "4", 366 false, 367 }, 368 { 369 `${length("aaa")}`, 370 "3", 371 false, 372 }, 373 374 // Lists 375 { 376 `${length(split(",", "a"))}`, 377 "1", 378 false, 379 }, 380 { 381 `${length(split(",", "foo,"))}`, 382 "2", 383 false, 384 }, 385 { 386 `${length(split(",", ",foo,"))}`, 387 "3", 388 false, 389 }, 390 { 391 `${length(split(",", "foo,bar"))}`, 392 "2", 393 false, 394 }, 395 { 396 `${length(split(".", "one.two.three.four.five"))}`, 397 "5", 398 false, 399 }, 400 }, 401 }) 402 } 403 404 func TestInterpolateFuncSplit(t *testing.T) { 405 testFunction(t, testFunctionConfig{ 406 Cases: []testFunctionCase{ 407 { 408 `${split(",")}`, 409 nil, 410 true, 411 }, 412 413 { 414 `${split(",", "")}`, 415 NewStringList([]string{""}).String(), 416 false, 417 }, 418 419 { 420 `${split(",", "foo")}`, 421 NewStringList([]string{"foo"}).String(), 422 false, 423 }, 424 425 { 426 `${split(",", ",,,")}`, 427 NewStringList([]string{"", "", "", ""}).String(), 428 false, 429 }, 430 431 { 432 `${split(",", "foo,")}`, 433 NewStringList([]string{"foo", ""}).String(), 434 false, 435 }, 436 437 { 438 `${split(",", ",foo,")}`, 439 NewStringList([]string{"", "foo", ""}).String(), 440 false, 441 }, 442 443 { 444 `${split(".", "foo.bar.baz")}`, 445 NewStringList([]string{"foo", "bar", "baz"}).String(), 446 false, 447 }, 448 }, 449 }) 450 } 451 452 func TestInterpolateFuncLookup(t *testing.T) { 453 testFunction(t, testFunctionConfig{ 454 Vars: map[string]ast.Variable{ 455 "var.foo.bar": ast.Variable{ 456 Value: "baz", 457 Type: ast.TypeString, 458 }, 459 }, 460 Cases: []testFunctionCase{ 461 { 462 `${lookup("foo", "bar")}`, 463 "baz", 464 false, 465 }, 466 467 // Invalid key 468 { 469 `${lookup("foo", "baz")}`, 470 nil, 471 true, 472 }, 473 474 // Too many args 475 { 476 `${lookup("foo", "bar", "baz")}`, 477 nil, 478 true, 479 }, 480 }, 481 }) 482 } 483 484 func TestInterpolateFuncKeys(t *testing.T) { 485 testFunction(t, testFunctionConfig{ 486 Vars: map[string]ast.Variable{ 487 "var.foo.bar": ast.Variable{ 488 Value: "baz", 489 Type: ast.TypeString, 490 }, 491 "var.foo.qux": ast.Variable{ 492 Value: "quack", 493 Type: ast.TypeString, 494 }, 495 "var.str": ast.Variable{ 496 Value: "astring", 497 Type: ast.TypeString, 498 }, 499 }, 500 Cases: []testFunctionCase{ 501 { 502 `${keys("foo")}`, 503 NewStringList([]string{"bar", "qux"}).String(), 504 false, 505 }, 506 507 // Invalid key 508 { 509 `${keys("not")}`, 510 nil, 511 true, 512 }, 513 514 // Too many args 515 { 516 `${keys("foo", "bar")}`, 517 nil, 518 true, 519 }, 520 521 // Not a map 522 { 523 `${keys("str")}`, 524 nil, 525 true, 526 }, 527 }, 528 }) 529 } 530 531 func TestInterpolateFuncValues(t *testing.T) { 532 testFunction(t, testFunctionConfig{ 533 Vars: map[string]ast.Variable{ 534 "var.foo.bar": ast.Variable{ 535 Value: "quack", 536 Type: ast.TypeString, 537 }, 538 "var.foo.qux": ast.Variable{ 539 Value: "baz", 540 Type: ast.TypeString, 541 }, 542 "var.str": ast.Variable{ 543 Value: "astring", 544 Type: ast.TypeString, 545 }, 546 }, 547 Cases: []testFunctionCase{ 548 { 549 `${values("foo")}`, 550 NewStringList([]string{"quack", "baz"}).String(), 551 false, 552 }, 553 554 // Invalid key 555 { 556 `${values("not")}`, 557 nil, 558 true, 559 }, 560 561 // Too many args 562 { 563 `${values("foo", "bar")}`, 564 nil, 565 true, 566 }, 567 568 // Not a map 569 { 570 `${values("str")}`, 571 nil, 572 true, 573 }, 574 }, 575 }) 576 } 577 578 func TestInterpolateFuncElement(t *testing.T) { 579 testFunction(t, testFunctionConfig{ 580 Cases: []testFunctionCase{ 581 { 582 fmt.Sprintf(`${element("%s", "1")}`, 583 NewStringList([]string{"foo", "baz"}).String()), 584 "baz", 585 false, 586 }, 587 588 { 589 fmt.Sprintf(`${element("%s", "0")}`, 590 NewStringList([]string{"foo"}).String()), 591 "foo", 592 false, 593 }, 594 595 // Invalid index should wrap vs. out-of-bounds 596 { 597 fmt.Sprintf(`${element("%s", "2")}`, 598 NewStringList([]string{"foo", "baz"}).String()), 599 "foo", 600 false, 601 }, 602 603 // Too many args 604 { 605 fmt.Sprintf(`${element("%s", "0", "2")}`, 606 NewStringList([]string{"foo", "baz"}).String()), 607 nil, 608 true, 609 }, 610 }, 611 }) 612 } 613 614 func TestInterpolateFuncBase64Encode(t *testing.T) { 615 testFunction(t, testFunctionConfig{ 616 Cases: []testFunctionCase{ 617 // Regular base64 encoding 618 { 619 `${base64encode("abc123!?$*&()'-=@~")}`, 620 "YWJjMTIzIT8kKiYoKSctPUB+", 621 false, 622 }, 623 }, 624 }) 625 } 626 627 func TestInterpolateFuncBase64Decode(t *testing.T) { 628 testFunction(t, testFunctionConfig{ 629 Cases: []testFunctionCase{ 630 // Regular base64 decoding 631 { 632 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 633 "abc123!?$*&()'-=@~", 634 false, 635 }, 636 637 // Invalid base64 data decoding 638 { 639 `${base64decode("this-is-an-invalid-base64-data")}`, 640 nil, 641 true, 642 }, 643 }, 644 }) 645 } 646 647 type testFunctionConfig struct { 648 Cases []testFunctionCase 649 Vars map[string]ast.Variable 650 } 651 652 type testFunctionCase struct { 653 Input string 654 Result interface{} 655 Error bool 656 } 657 658 func testFunction(t *testing.T, config testFunctionConfig) { 659 for i, tc := range config.Cases { 660 ast, err := lang.Parse(tc.Input) 661 if err != nil { 662 t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) 663 } 664 665 out, _, err := lang.Eval(ast, langEvalConfig(config.Vars)) 666 if err != nil != tc.Error { 667 t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) 668 } 669 670 if !reflect.DeepEqual(out, tc.Result) { 671 t.Fatalf( 672 "%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 673 i, tc.Input, out, tc.Result) 674 } 675 } 676 }