github.com/medzin/terraform@v0.11.11/config/interpolate_funcs.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "crypto/md5" 7 "crypto/rsa" 8 "crypto/sha1" 9 "crypto/sha256" 10 "crypto/sha512" 11 "crypto/x509" 12 "encoding/base64" 13 "encoding/hex" 14 "encoding/json" 15 "encoding/pem" 16 "fmt" 17 "io/ioutil" 18 "math" 19 "net" 20 "net/url" 21 "path/filepath" 22 "regexp" 23 "sort" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/apparentlymart/go-cidr/cidr" 29 "github.com/hashicorp/go-uuid" 30 "github.com/hashicorp/hil" 31 "github.com/hashicorp/hil/ast" 32 "github.com/mitchellh/go-homedir" 33 "golang.org/x/crypto/bcrypt" 34 ) 35 36 // stringSliceToVariableValue converts a string slice into the value 37 // required to be returned from interpolation functions which return 38 // TypeList. 39 func stringSliceToVariableValue(values []string) []ast.Variable { 40 output := make([]ast.Variable, len(values)) 41 for index, value := range values { 42 output[index] = ast.Variable{ 43 Type: ast.TypeString, 44 Value: value, 45 } 46 } 47 return output 48 } 49 50 func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { 51 output := make([]string, len(values)) 52 for index, value := range values { 53 if value.Type != ast.TypeString { 54 return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String()) 55 } 56 output[index] = value.Value.(string) 57 } 58 return output, nil 59 } 60 61 // Funcs is the mapping of built-in functions for configuration. 62 func Funcs() map[string]ast.Function { 63 return map[string]ast.Function{ 64 "abs": interpolationFuncAbs(), 65 "basename": interpolationFuncBasename(), 66 "base64decode": interpolationFuncBase64Decode(), 67 "base64encode": interpolationFuncBase64Encode(), 68 "base64gzip": interpolationFuncBase64Gzip(), 69 "base64sha256": interpolationFuncBase64Sha256(), 70 "base64sha512": interpolationFuncBase64Sha512(), 71 "bcrypt": interpolationFuncBcrypt(), 72 "ceil": interpolationFuncCeil(), 73 "chomp": interpolationFuncChomp(), 74 "cidrhost": interpolationFuncCidrHost(), 75 "cidrnetmask": interpolationFuncCidrNetmask(), 76 "cidrsubnet": interpolationFuncCidrSubnet(), 77 "coalesce": interpolationFuncCoalesce(), 78 "coalescelist": interpolationFuncCoalesceList(), 79 "compact": interpolationFuncCompact(), 80 "concat": interpolationFuncConcat(), 81 "contains": interpolationFuncContains(), 82 "dirname": interpolationFuncDirname(), 83 "distinct": interpolationFuncDistinct(), 84 "element": interpolationFuncElement(), 85 "chunklist": interpolationFuncChunklist(), 86 "file": interpolationFuncFile(), 87 "matchkeys": interpolationFuncMatchKeys(), 88 "flatten": interpolationFuncFlatten(), 89 "floor": interpolationFuncFloor(), 90 "format": interpolationFuncFormat(), 91 "formatlist": interpolationFuncFormatList(), 92 "indent": interpolationFuncIndent(), 93 "index": interpolationFuncIndex(), 94 "join": interpolationFuncJoin(), 95 "jsonencode": interpolationFuncJSONEncode(), 96 "length": interpolationFuncLength(), 97 "list": interpolationFuncList(), 98 "log": interpolationFuncLog(), 99 "lower": interpolationFuncLower(), 100 "map": interpolationFuncMap(), 101 "max": interpolationFuncMax(), 102 "md5": interpolationFuncMd5(), 103 "merge": interpolationFuncMerge(), 104 "min": interpolationFuncMin(), 105 "pathexpand": interpolationFuncPathExpand(), 106 "pow": interpolationFuncPow(), 107 "uuid": interpolationFuncUUID(), 108 "replace": interpolationFuncReplace(), 109 "rsadecrypt": interpolationFuncRsaDecrypt(), 110 "sha1": interpolationFuncSha1(), 111 "sha256": interpolationFuncSha256(), 112 "sha512": interpolationFuncSha512(), 113 "signum": interpolationFuncSignum(), 114 "slice": interpolationFuncSlice(), 115 "sort": interpolationFuncSort(), 116 "split": interpolationFuncSplit(), 117 "substr": interpolationFuncSubstr(), 118 "timestamp": interpolationFuncTimestamp(), 119 "timeadd": interpolationFuncTimeAdd(), 120 "title": interpolationFuncTitle(), 121 "transpose": interpolationFuncTranspose(), 122 "trimspace": interpolationFuncTrimSpace(), 123 "upper": interpolationFuncUpper(), 124 "urlencode": interpolationFuncURLEncode(), 125 "zipmap": interpolationFuncZipMap(), 126 } 127 } 128 129 // interpolationFuncList creates a list from the parameters passed 130 // to it. 131 func interpolationFuncList() ast.Function { 132 return ast.Function{ 133 ArgTypes: []ast.Type{}, 134 ReturnType: ast.TypeList, 135 Variadic: true, 136 VariadicType: ast.TypeAny, 137 Callback: func(args []interface{}) (interface{}, error) { 138 var outputList []ast.Variable 139 140 for i, val := range args { 141 switch v := val.(type) { 142 case string: 143 outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v}) 144 case []ast.Variable: 145 outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v}) 146 case map[string]ast.Variable: 147 outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v}) 148 default: 149 return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i) 150 } 151 } 152 153 // we don't support heterogeneous types, so make sure all types match the first 154 if len(outputList) > 0 { 155 firstType := outputList[0].Type 156 for i, v := range outputList[1:] { 157 if v.Type != firstType { 158 return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1) 159 } 160 } 161 } 162 163 return outputList, nil 164 }, 165 } 166 } 167 168 // interpolationFuncMap creates a map from the parameters passed 169 // to it. 170 func interpolationFuncMap() ast.Function { 171 return ast.Function{ 172 ArgTypes: []ast.Type{}, 173 ReturnType: ast.TypeMap, 174 Variadic: true, 175 VariadicType: ast.TypeAny, 176 Callback: func(args []interface{}) (interface{}, error) { 177 outputMap := make(map[string]ast.Variable) 178 179 if len(args)%2 != 0 { 180 return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args)) 181 } 182 183 var firstType *ast.Type 184 for i := 0; i < len(args); i += 2 { 185 key, ok := args[i].(string) 186 if !ok { 187 return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1) 188 } 189 val := args[i+1] 190 variable, err := hil.InterfaceToVariable(val) 191 if err != nil { 192 return nil, err 193 } 194 // Enforce map type homogeneity 195 if firstType == nil { 196 firstType = &variable.Type 197 } else if variable.Type != *firstType { 198 return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable()) 199 } 200 // Check for duplicate keys 201 if _, ok := outputMap[key]; ok { 202 return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) 203 } 204 outputMap[key] = variable 205 } 206 207 return outputMap, nil 208 }, 209 } 210 } 211 212 // interpolationFuncCompact strips a list of multi-variable values 213 // (e.g. as returned by "split") of any empty strings. 214 func interpolationFuncCompact() ast.Function { 215 return ast.Function{ 216 ArgTypes: []ast.Type{ast.TypeList}, 217 ReturnType: ast.TypeList, 218 Variadic: false, 219 Callback: func(args []interface{}) (interface{}, error) { 220 inputList := args[0].([]ast.Variable) 221 222 var outputList []string 223 for _, val := range inputList { 224 strVal, ok := val.Value.(string) 225 if !ok { 226 return nil, fmt.Errorf( 227 "compact() may only be used with flat lists, this list contains elements of %s", 228 val.Type.Printable()) 229 } 230 if strVal == "" { 231 continue 232 } 233 234 outputList = append(outputList, strVal) 235 } 236 return stringSliceToVariableValue(outputList), nil 237 }, 238 } 239 } 240 241 // interpolationFuncCidrHost implements the "cidrhost" function that 242 // fills in the host part of a CIDR range address to create a single 243 // host address 244 func interpolationFuncCidrHost() ast.Function { 245 return ast.Function{ 246 ArgTypes: []ast.Type{ 247 ast.TypeString, // starting CIDR mask 248 ast.TypeInt, // host number to insert 249 }, 250 ReturnType: ast.TypeString, 251 Variadic: false, 252 Callback: func(args []interface{}) (interface{}, error) { 253 hostNum := args[1].(int) 254 _, network, err := net.ParseCIDR(args[0].(string)) 255 if err != nil { 256 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 257 } 258 259 ip, err := cidr.Host(network, hostNum) 260 if err != nil { 261 return nil, err 262 } 263 264 return ip.String(), nil 265 }, 266 } 267 } 268 269 // interpolationFuncCidrNetmask implements the "cidrnetmask" function 270 // that returns the subnet mask in IP address notation. 271 func interpolationFuncCidrNetmask() ast.Function { 272 return ast.Function{ 273 ArgTypes: []ast.Type{ 274 ast.TypeString, // CIDR mask 275 }, 276 ReturnType: ast.TypeString, 277 Variadic: false, 278 Callback: func(args []interface{}) (interface{}, error) { 279 _, network, err := net.ParseCIDR(args[0].(string)) 280 if err != nil { 281 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 282 } 283 284 return net.IP(network.Mask).String(), nil 285 }, 286 } 287 } 288 289 // interpolationFuncCidrSubnet implements the "cidrsubnet" function that 290 // adds an additional subnet of the given length onto an existing 291 // IP block expressed in CIDR notation. 292 func interpolationFuncCidrSubnet() ast.Function { 293 return ast.Function{ 294 ArgTypes: []ast.Type{ 295 ast.TypeString, // starting CIDR mask 296 ast.TypeInt, // number of bits to extend the prefix 297 ast.TypeInt, // network number to append to the prefix 298 }, 299 ReturnType: ast.TypeString, 300 Variadic: false, 301 Callback: func(args []interface{}) (interface{}, error) { 302 extraBits := args[1].(int) 303 subnetNum := args[2].(int) 304 _, network, err := net.ParseCIDR(args[0].(string)) 305 if err != nil { 306 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 307 } 308 309 // For portability with 32-bit systems where the subnet number 310 // will be a 32-bit int, we only allow extension of 32 bits in 311 // one call even if we're running on a 64-bit machine. 312 // (Of course, this is significant only for IPv6.) 313 if extraBits > 32 { 314 return nil, fmt.Errorf("may not extend prefix by more than 32 bits") 315 } 316 317 newNetwork, err := cidr.Subnet(network, extraBits, subnetNum) 318 if err != nil { 319 return nil, err 320 } 321 322 return newNetwork.String(), nil 323 }, 324 } 325 } 326 327 // interpolationFuncCoalesce implements the "coalesce" function that 328 // returns the first non null / empty string from the provided input 329 func interpolationFuncCoalesce() ast.Function { 330 return ast.Function{ 331 ArgTypes: []ast.Type{ast.TypeString}, 332 ReturnType: ast.TypeString, 333 Variadic: true, 334 VariadicType: ast.TypeString, 335 Callback: func(args []interface{}) (interface{}, error) { 336 if len(args) < 2 { 337 return nil, fmt.Errorf("must provide at least two arguments") 338 } 339 for _, arg := range args { 340 argument := arg.(string) 341 342 if argument != "" { 343 return argument, nil 344 } 345 } 346 return "", nil 347 }, 348 } 349 } 350 351 // interpolationFuncCoalesceList implements the "coalescelist" function that 352 // returns the first non empty list from the provided input 353 func interpolationFuncCoalesceList() ast.Function { 354 return ast.Function{ 355 ArgTypes: []ast.Type{ast.TypeList}, 356 ReturnType: ast.TypeList, 357 Variadic: true, 358 VariadicType: ast.TypeList, 359 Callback: func(args []interface{}) (interface{}, error) { 360 if len(args) < 2 { 361 return nil, fmt.Errorf("must provide at least two arguments") 362 } 363 for _, arg := range args { 364 argument := arg.([]ast.Variable) 365 366 if len(argument) > 0 { 367 return argument, nil 368 } 369 } 370 return make([]ast.Variable, 0), nil 371 }, 372 } 373 } 374 375 // interpolationFuncContains returns true if an element is in the list 376 // and return false otherwise 377 func interpolationFuncContains() ast.Function { 378 return ast.Function{ 379 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 380 ReturnType: ast.TypeBool, 381 Callback: func(args []interface{}) (interface{}, error) { 382 _, err := interpolationFuncIndex().Callback(args) 383 if err != nil { 384 return false, nil 385 } 386 return true, nil 387 }, 388 } 389 } 390 391 // interpolationFuncConcat implements the "concat" function that concatenates 392 // multiple lists. 393 func interpolationFuncConcat() ast.Function { 394 return ast.Function{ 395 ArgTypes: []ast.Type{ast.TypeList}, 396 ReturnType: ast.TypeList, 397 Variadic: true, 398 VariadicType: ast.TypeList, 399 Callback: func(args []interface{}) (interface{}, error) { 400 var outputList []ast.Variable 401 402 for _, arg := range args { 403 for _, v := range arg.([]ast.Variable) { 404 switch v.Type { 405 case ast.TypeString: 406 outputList = append(outputList, v) 407 case ast.TypeList: 408 outputList = append(outputList, v) 409 case ast.TypeMap: 410 outputList = append(outputList, v) 411 default: 412 return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable()) 413 } 414 } 415 } 416 417 // we don't support heterogeneous types, so make sure all types match the first 418 if len(outputList) > 0 { 419 firstType := outputList[0].Type 420 for _, v := range outputList[1:] { 421 if v.Type != firstType { 422 return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable()) 423 } 424 } 425 } 426 427 return outputList, nil 428 }, 429 } 430 } 431 432 // interpolationFuncPow returns base x exponential of y. 433 func interpolationFuncPow() ast.Function { 434 return ast.Function{ 435 ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat}, 436 ReturnType: ast.TypeFloat, 437 Callback: func(args []interface{}) (interface{}, error) { 438 return math.Pow(args[0].(float64), args[1].(float64)), nil 439 }, 440 } 441 } 442 443 // interpolationFuncFile implements the "file" function that allows 444 // loading contents from a file. 445 func interpolationFuncFile() ast.Function { 446 return ast.Function{ 447 ArgTypes: []ast.Type{ast.TypeString}, 448 ReturnType: ast.TypeString, 449 Callback: func(args []interface{}) (interface{}, error) { 450 path, err := homedir.Expand(args[0].(string)) 451 if err != nil { 452 return "", err 453 } 454 data, err := ioutil.ReadFile(path) 455 if err != nil { 456 return "", err 457 } 458 459 return string(data), nil 460 }, 461 } 462 } 463 464 // interpolationFuncFormat implements the "format" function that does 465 // string formatting. 466 func interpolationFuncFormat() ast.Function { 467 return ast.Function{ 468 ArgTypes: []ast.Type{ast.TypeString}, 469 Variadic: true, 470 VariadicType: ast.TypeAny, 471 ReturnType: ast.TypeString, 472 Callback: func(args []interface{}) (interface{}, error) { 473 format := args[0].(string) 474 return fmt.Sprintf(format, args[1:]...), nil 475 }, 476 } 477 } 478 479 // interpolationFuncMax returns the maximum of the numeric arguments 480 func interpolationFuncMax() ast.Function { 481 return ast.Function{ 482 ArgTypes: []ast.Type{ast.TypeFloat}, 483 ReturnType: ast.TypeFloat, 484 Variadic: true, 485 VariadicType: ast.TypeFloat, 486 Callback: func(args []interface{}) (interface{}, error) { 487 max := args[0].(float64) 488 489 for i := 1; i < len(args); i++ { 490 max = math.Max(max, args[i].(float64)) 491 } 492 493 return max, nil 494 }, 495 } 496 } 497 498 // interpolationFuncMin returns the minimum of the numeric arguments 499 func interpolationFuncMin() ast.Function { 500 return ast.Function{ 501 ArgTypes: []ast.Type{ast.TypeFloat}, 502 ReturnType: ast.TypeFloat, 503 Variadic: true, 504 VariadicType: ast.TypeFloat, 505 Callback: func(args []interface{}) (interface{}, error) { 506 min := args[0].(float64) 507 508 for i := 1; i < len(args); i++ { 509 min = math.Min(min, args[i].(float64)) 510 } 511 512 return min, nil 513 }, 514 } 515 } 516 517 // interpolationFuncPathExpand will expand any `~`'s found with the full file path 518 func interpolationFuncPathExpand() ast.Function { 519 return ast.Function{ 520 ArgTypes: []ast.Type{ast.TypeString}, 521 ReturnType: ast.TypeString, 522 Callback: func(args []interface{}) (interface{}, error) { 523 return homedir.Expand(args[0].(string)) 524 }, 525 } 526 } 527 528 // interpolationFuncCeil returns the the least integer value greater than or equal to the argument 529 func interpolationFuncCeil() ast.Function { 530 return ast.Function{ 531 ArgTypes: []ast.Type{ast.TypeFloat}, 532 ReturnType: ast.TypeInt, 533 Callback: func(args []interface{}) (interface{}, error) { 534 return int(math.Ceil(args[0].(float64))), nil 535 }, 536 } 537 } 538 539 // interpolationFuncLog returns the logarithnm. 540 func interpolationFuncLog() ast.Function { 541 return ast.Function{ 542 ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat}, 543 ReturnType: ast.TypeFloat, 544 Callback: func(args []interface{}) (interface{}, error) { 545 return math.Log(args[0].(float64)) / math.Log(args[1].(float64)), nil 546 }, 547 } 548 } 549 550 // interpolationFuncChomp removes trailing newlines from the given string 551 func interpolationFuncChomp() ast.Function { 552 newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`) 553 return ast.Function{ 554 ArgTypes: []ast.Type{ast.TypeString}, 555 ReturnType: ast.TypeString, 556 Callback: func(args []interface{}) (interface{}, error) { 557 return newlines.ReplaceAllString(args[0].(string), ""), nil 558 }, 559 } 560 } 561 562 // interpolationFuncFloorreturns returns the greatest integer value less than or equal to the argument 563 func interpolationFuncFloor() ast.Function { 564 return ast.Function{ 565 ArgTypes: []ast.Type{ast.TypeFloat}, 566 ReturnType: ast.TypeInt, 567 Callback: func(args []interface{}) (interface{}, error) { 568 return int(math.Floor(args[0].(float64))), nil 569 }, 570 } 571 } 572 573 func interpolationFuncZipMap() ast.Function { 574 return ast.Function{ 575 ArgTypes: []ast.Type{ 576 ast.TypeList, // Keys 577 ast.TypeList, // Values 578 }, 579 ReturnType: ast.TypeMap, 580 Callback: func(args []interface{}) (interface{}, error) { 581 keys := args[0].([]ast.Variable) 582 values := args[1].([]ast.Variable) 583 584 if len(keys) != len(values) { 585 return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)", 586 len(keys), len(values)) 587 } 588 589 for i, val := range keys { 590 if val.Type != ast.TypeString { 591 return nil, fmt.Errorf("keys must be strings. value at position %d is %s", 592 i, val.Type.Printable()) 593 } 594 } 595 596 result := map[string]ast.Variable{} 597 for i := 0; i < len(keys); i++ { 598 result[keys[i].Value.(string)] = values[i] 599 } 600 601 return result, nil 602 }, 603 } 604 } 605 606 // interpolationFuncFormatList implements the "formatlist" function that does 607 // string formatting on lists. 608 func interpolationFuncFormatList() ast.Function { 609 return ast.Function{ 610 ArgTypes: []ast.Type{ast.TypeAny}, 611 Variadic: true, 612 VariadicType: ast.TypeAny, 613 ReturnType: ast.TypeList, 614 Callback: func(args []interface{}) (interface{}, error) { 615 // Make a copy of the variadic part of args 616 // to avoid modifying the original. 617 varargs := make([]interface{}, len(args)-1) 618 copy(varargs, args[1:]) 619 620 // Verify we have some arguments 621 if len(varargs) == 0 { 622 return nil, fmt.Errorf("no arguments to formatlist") 623 } 624 625 // Convert arguments that are lists into slices. 626 // Confirm along the way that all lists have the same length (n). 627 var n int 628 listSeen := false 629 for i := 1; i < len(args); i++ { 630 s, ok := args[i].([]ast.Variable) 631 if !ok { 632 continue 633 } 634 635 // Mark that we've seen at least one list 636 listSeen = true 637 638 // Convert the ast.Variable to a slice of strings 639 parts, err := listVariableValueToStringSlice(s) 640 if err != nil { 641 return nil, err 642 } 643 644 // otherwise the list is sent down to be indexed 645 varargs[i-1] = parts 646 647 // Check length 648 if n == 0 { 649 // first list we've seen 650 n = len(parts) 651 continue 652 } 653 if n != len(parts) { 654 return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts)) 655 } 656 } 657 658 // If we didn't see a list this is an error because we 659 // can't determine the return value length. 660 if !listSeen { 661 return nil, fmt.Errorf( 662 "formatlist requires at least one list argument") 663 } 664 665 // Do the formatting. 666 format := args[0].(string) 667 668 // Generate a list of formatted strings. 669 list := make([]string, n) 670 fmtargs := make([]interface{}, len(varargs)) 671 for i := 0; i < n; i++ { 672 for j, arg := range varargs { 673 switch arg := arg.(type) { 674 default: 675 fmtargs[j] = arg 676 case []string: 677 fmtargs[j] = arg[i] 678 } 679 } 680 list[i] = fmt.Sprintf(format, fmtargs...) 681 } 682 return stringSliceToVariableValue(list), nil 683 }, 684 } 685 } 686 687 // interpolationFuncIndent indents a multi-line string with the 688 // specified number of spaces 689 func interpolationFuncIndent() ast.Function { 690 return ast.Function{ 691 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString}, 692 ReturnType: ast.TypeString, 693 Callback: func(args []interface{}) (interface{}, error) { 694 spaces := args[0].(int) 695 data := args[1].(string) 696 pad := strings.Repeat(" ", spaces) 697 return strings.Replace(data, "\n", "\n"+pad, -1), nil 698 }, 699 } 700 } 701 702 // interpolationFuncIndex implements the "index" function that allows one to 703 // find the index of a specific element in a list 704 func interpolationFuncIndex() ast.Function { 705 return ast.Function{ 706 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 707 ReturnType: ast.TypeInt, 708 Callback: func(args []interface{}) (interface{}, error) { 709 haystack := args[0].([]ast.Variable) 710 needle := args[1].(string) 711 for index, element := range haystack { 712 if needle == element.Value { 713 return index, nil 714 } 715 } 716 return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack) 717 }, 718 } 719 } 720 721 // interpolationFuncBasename implements the "dirname" function. 722 func interpolationFuncDirname() ast.Function { 723 return ast.Function{ 724 ArgTypes: []ast.Type{ast.TypeString}, 725 ReturnType: ast.TypeString, 726 Callback: func(args []interface{}) (interface{}, error) { 727 return filepath.Dir(args[0].(string)), nil 728 }, 729 } 730 } 731 732 // interpolationFuncDistinct implements the "distinct" function that 733 // removes duplicate elements from a list. 734 func interpolationFuncDistinct() ast.Function { 735 return ast.Function{ 736 ArgTypes: []ast.Type{ast.TypeList}, 737 ReturnType: ast.TypeList, 738 Variadic: true, 739 VariadicType: ast.TypeList, 740 Callback: func(args []interface{}) (interface{}, error) { 741 var list []string 742 743 if len(args) != 1 { 744 return nil, fmt.Errorf("accepts only one argument.") 745 } 746 747 if argument, ok := args[0].([]ast.Variable); ok { 748 for _, element := range argument { 749 if element.Type != ast.TypeString { 750 return nil, fmt.Errorf( 751 "only works for flat lists, this list contains elements of %s", 752 element.Type.Printable()) 753 } 754 list = appendIfMissing(list, element.Value.(string)) 755 } 756 } 757 758 return stringSliceToVariableValue(list), nil 759 }, 760 } 761 } 762 763 // helper function to add an element to a list, if it does not already exsit 764 func appendIfMissing(slice []string, element string) []string { 765 for _, ele := range slice { 766 if ele == element { 767 return slice 768 } 769 } 770 return append(slice, element) 771 } 772 773 // for two lists `keys` and `values` of equal length, returns all elements 774 // from `values` where the corresponding element from `keys` is in `searchset`. 775 func interpolationFuncMatchKeys() ast.Function { 776 return ast.Function{ 777 ArgTypes: []ast.Type{ast.TypeList, ast.TypeList, ast.TypeList}, 778 ReturnType: ast.TypeList, 779 Callback: func(args []interface{}) (interface{}, error) { 780 output := make([]ast.Variable, 0) 781 782 values, _ := args[0].([]ast.Variable) 783 keys, _ := args[1].([]ast.Variable) 784 searchset, _ := args[2].([]ast.Variable) 785 786 if len(keys) != len(values) { 787 return nil, fmt.Errorf("length of keys and values should be equal") 788 } 789 790 for i, key := range keys { 791 for _, search := range searchset { 792 if res, err := compareSimpleVariables(key, search); err != nil { 793 return nil, err 794 } else if res == true { 795 output = append(output, values[i]) 796 break 797 } 798 } 799 } 800 // if searchset is empty, then output is an empty list as well. 801 // if we haven't matched any key, then output is an empty list. 802 return output, nil 803 }, 804 } 805 } 806 807 // compare two variables of the same type, i.e. non complex one, such as TypeList or TypeMap 808 func compareSimpleVariables(a, b ast.Variable) (bool, error) { 809 if a.Type != b.Type { 810 return false, fmt.Errorf( 811 "won't compare items of different types %s and %s", 812 a.Type.Printable(), b.Type.Printable()) 813 } 814 switch a.Type { 815 case ast.TypeString: 816 return a.Value.(string) == b.Value.(string), nil 817 default: 818 return false, fmt.Errorf( 819 "can't compare items of type %s", 820 a.Type.Printable()) 821 } 822 } 823 824 // interpolationFuncJoin implements the "join" function that allows 825 // multi-variable values to be joined by some character. 826 func interpolationFuncJoin() ast.Function { 827 return ast.Function{ 828 ArgTypes: []ast.Type{ast.TypeString}, 829 Variadic: true, 830 VariadicType: ast.TypeList, 831 ReturnType: ast.TypeString, 832 Callback: func(args []interface{}) (interface{}, error) { 833 var list []string 834 835 if len(args) < 2 { 836 return nil, fmt.Errorf("not enough arguments to join()") 837 } 838 839 for _, arg := range args[1:] { 840 for _, part := range arg.([]ast.Variable) { 841 if part.Type != ast.TypeString { 842 return nil, fmt.Errorf( 843 "only works on flat lists, this list contains elements of %s", 844 part.Type.Printable()) 845 } 846 list = append(list, part.Value.(string)) 847 } 848 } 849 850 return strings.Join(list, args[0].(string)), nil 851 }, 852 } 853 } 854 855 // interpolationFuncJSONEncode implements the "jsonencode" function that encodes 856 // a string, list, or map as its JSON representation. 857 func interpolationFuncJSONEncode() ast.Function { 858 return ast.Function{ 859 ArgTypes: []ast.Type{ast.TypeAny}, 860 ReturnType: ast.TypeString, 861 Callback: func(args []interface{}) (interface{}, error) { 862 var toEncode interface{} 863 864 switch typedArg := args[0].(type) { 865 case string: 866 toEncode = typedArg 867 868 case []ast.Variable: 869 strings := make([]string, len(typedArg)) 870 871 for i, v := range typedArg { 872 if v.Type != ast.TypeString { 873 variable, _ := hil.InterfaceToVariable(typedArg) 874 toEncode, _ = hil.VariableToInterface(variable) 875 876 jEnc, err := json.Marshal(toEncode) 877 if err != nil { 878 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) 879 } 880 return string(jEnc), nil 881 882 } 883 strings[i] = v.Value.(string) 884 } 885 toEncode = strings 886 887 case map[string]ast.Variable: 888 stringMap := make(map[string]string) 889 for k, v := range typedArg { 890 if v.Type != ast.TypeString { 891 variable, _ := hil.InterfaceToVariable(typedArg) 892 toEncode, _ = hil.VariableToInterface(variable) 893 894 jEnc, err := json.Marshal(toEncode) 895 if err != nil { 896 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) 897 } 898 return string(jEnc), nil 899 } 900 stringMap[k] = v.Value.(string) 901 } 902 toEncode = stringMap 903 904 default: 905 return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0]) 906 } 907 908 jEnc, err := json.Marshal(toEncode) 909 if err != nil { 910 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) 911 } 912 return string(jEnc), nil 913 }, 914 } 915 } 916 917 // interpolationFuncReplace implements the "replace" function that does 918 // string replacement. 919 func interpolationFuncReplace() ast.Function { 920 return ast.Function{ 921 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString}, 922 ReturnType: ast.TypeString, 923 Callback: func(args []interface{}) (interface{}, error) { 924 s := args[0].(string) 925 search := args[1].(string) 926 replace := args[2].(string) 927 928 // We search/replace using a regexp if the string is surrounded 929 // in forward slashes. 930 if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' { 931 re, err := regexp.Compile(search[1 : len(search)-1]) 932 if err != nil { 933 return nil, err 934 } 935 936 return re.ReplaceAllString(s, replace), nil 937 } 938 939 return strings.Replace(s, search, replace, -1), nil 940 }, 941 } 942 } 943 944 func interpolationFuncLength() ast.Function { 945 return ast.Function{ 946 ArgTypes: []ast.Type{ast.TypeAny}, 947 ReturnType: ast.TypeInt, 948 Variadic: false, 949 Callback: func(args []interface{}) (interface{}, error) { 950 subject := args[0] 951 952 switch typedSubject := subject.(type) { 953 case string: 954 return len(typedSubject), nil 955 case []ast.Variable: 956 return len(typedSubject), nil 957 case map[string]ast.Variable: 958 return len(typedSubject), nil 959 } 960 961 return 0, fmt.Errorf("arguments to length() must be a string, list, or map") 962 }, 963 } 964 } 965 966 func interpolationFuncSignum() ast.Function { 967 return ast.Function{ 968 ArgTypes: []ast.Type{ast.TypeInt}, 969 ReturnType: ast.TypeInt, 970 Variadic: false, 971 Callback: func(args []interface{}) (interface{}, error) { 972 num := args[0].(int) 973 switch { 974 case num < 0: 975 return -1, nil 976 case num > 0: 977 return +1, nil 978 default: 979 return 0, nil 980 } 981 }, 982 } 983 } 984 985 // interpolationFuncSlice returns a portion of the input list between from, inclusive and to, exclusive. 986 func interpolationFuncSlice() ast.Function { 987 return ast.Function{ 988 ArgTypes: []ast.Type{ 989 ast.TypeList, // inputList 990 ast.TypeInt, // from 991 ast.TypeInt, // to 992 }, 993 ReturnType: ast.TypeList, 994 Variadic: false, 995 Callback: func(args []interface{}) (interface{}, error) { 996 inputList := args[0].([]ast.Variable) 997 from := args[1].(int) 998 to := args[2].(int) 999 1000 if from < 0 { 1001 return nil, fmt.Errorf("from index must be >= 0") 1002 } 1003 if to > len(inputList) { 1004 return nil, fmt.Errorf("to index must be <= length of the input list") 1005 } 1006 if from > to { 1007 return nil, fmt.Errorf("from index must be <= to index") 1008 } 1009 1010 var outputList []ast.Variable 1011 for i, val := range inputList { 1012 if i >= from && i < to { 1013 outputList = append(outputList, val) 1014 } 1015 } 1016 return outputList, nil 1017 }, 1018 } 1019 } 1020 1021 // interpolationFuncSort sorts a list of a strings lexographically 1022 func interpolationFuncSort() ast.Function { 1023 return ast.Function{ 1024 ArgTypes: []ast.Type{ast.TypeList}, 1025 ReturnType: ast.TypeList, 1026 Variadic: false, 1027 Callback: func(args []interface{}) (interface{}, error) { 1028 inputList := args[0].([]ast.Variable) 1029 1030 // Ensure that all the list members are strings and 1031 // create a string slice from them 1032 members := make([]string, len(inputList)) 1033 for i, val := range inputList { 1034 if val.Type != ast.TypeString { 1035 return nil, fmt.Errorf( 1036 "sort() may only be used with lists of strings - %s at index %d", 1037 val.Type.String(), i) 1038 } 1039 1040 members[i] = val.Value.(string) 1041 } 1042 1043 sort.Strings(members) 1044 return stringSliceToVariableValue(members), nil 1045 }, 1046 } 1047 } 1048 1049 // interpolationFuncSplit implements the "split" function that allows 1050 // strings to split into multi-variable values 1051 func interpolationFuncSplit() ast.Function { 1052 return ast.Function{ 1053 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, 1054 ReturnType: ast.TypeList, 1055 Callback: func(args []interface{}) (interface{}, error) { 1056 sep := args[0].(string) 1057 s := args[1].(string) 1058 elements := strings.Split(s, sep) 1059 return stringSliceToVariableValue(elements), nil 1060 }, 1061 } 1062 } 1063 1064 // interpolationFuncLookup implements the "lookup" function that allows 1065 // dynamic lookups of map types within a Terraform configuration. 1066 func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function { 1067 return ast.Function{ 1068 ArgTypes: []ast.Type{ast.TypeMap, ast.TypeString}, 1069 ReturnType: ast.TypeString, 1070 Variadic: true, 1071 VariadicType: ast.TypeString, 1072 Callback: func(args []interface{}) (interface{}, error) { 1073 defaultValue := "" 1074 defaultValueSet := false 1075 if len(args) > 2 { 1076 defaultValue = args[2].(string) 1077 defaultValueSet = true 1078 } 1079 if len(args) > 3 { 1080 return "", fmt.Errorf("lookup() takes no more than three arguments") 1081 } 1082 index := args[1].(string) 1083 mapVar := args[0].(map[string]ast.Variable) 1084 1085 v, ok := mapVar[index] 1086 if !ok { 1087 if defaultValueSet { 1088 return defaultValue, nil 1089 } else { 1090 return "", fmt.Errorf( 1091 "lookup failed to find '%s'", 1092 args[1].(string)) 1093 } 1094 } 1095 if v.Type != ast.TypeString { 1096 return nil, fmt.Errorf( 1097 "lookup() may only be used with flat maps, this map contains elements of %s", 1098 v.Type.Printable()) 1099 } 1100 1101 return v.Value.(string), nil 1102 }, 1103 } 1104 } 1105 1106 // interpolationFuncElement implements the "element" function that allows 1107 // a specific index to be looked up in a multi-variable value. Note that this will 1108 // wrap if the index is larger than the number of elements in the multi-variable value. 1109 func interpolationFuncElement() ast.Function { 1110 return ast.Function{ 1111 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 1112 ReturnType: ast.TypeString, 1113 Callback: func(args []interface{}) (interface{}, error) { 1114 list := args[0].([]ast.Variable) 1115 if len(list) == 0 { 1116 return nil, fmt.Errorf("element() may not be used with an empty list") 1117 } 1118 1119 index, err := strconv.Atoi(args[1].(string)) 1120 if err != nil || index < 0 { 1121 return "", fmt.Errorf( 1122 "invalid number for index, got %s", args[1]) 1123 } 1124 1125 resolvedIndex := index % len(list) 1126 1127 v := list[resolvedIndex] 1128 if v.Type != ast.TypeString { 1129 return nil, fmt.Errorf( 1130 "element() may only be used with flat lists, this list contains elements of %s", 1131 v.Type.Printable()) 1132 } 1133 return v.Value, nil 1134 }, 1135 } 1136 } 1137 1138 // returns the `list` items chunked by `size`. 1139 func interpolationFuncChunklist() ast.Function { 1140 return ast.Function{ 1141 ArgTypes: []ast.Type{ 1142 ast.TypeList, // inputList 1143 ast.TypeInt, // size 1144 }, 1145 ReturnType: ast.TypeList, 1146 Callback: func(args []interface{}) (interface{}, error) { 1147 output := make([]ast.Variable, 0) 1148 1149 values, _ := args[0].([]ast.Variable) 1150 size, _ := args[1].(int) 1151 1152 // errors if size is negative 1153 if size < 0 { 1154 return nil, fmt.Errorf("The size argument must be positive") 1155 } 1156 1157 // if size is 0, returns a list made of the initial list 1158 if size == 0 { 1159 output = append(output, ast.Variable{ 1160 Type: ast.TypeList, 1161 Value: values, 1162 }) 1163 return output, nil 1164 } 1165 1166 variables := make([]ast.Variable, 0) 1167 chunk := ast.Variable{ 1168 Type: ast.TypeList, 1169 Value: variables, 1170 } 1171 l := len(values) 1172 for i, v := range values { 1173 variables = append(variables, v) 1174 1175 // Chunk when index isn't 0, or when reaching the values's length 1176 if (i+1)%size == 0 || (i+1) == l { 1177 chunk.Value = variables 1178 output = append(output, chunk) 1179 variables = make([]ast.Variable, 0) 1180 } 1181 } 1182 1183 return output, nil 1184 }, 1185 } 1186 } 1187 1188 // interpolationFuncKeys implements the "keys" function that yields a list of 1189 // keys of map types within a Terraform configuration. 1190 func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { 1191 return ast.Function{ 1192 ArgTypes: []ast.Type{ast.TypeMap}, 1193 ReturnType: ast.TypeList, 1194 Callback: func(args []interface{}) (interface{}, error) { 1195 mapVar := args[0].(map[string]ast.Variable) 1196 keys := make([]string, 0) 1197 1198 for k, _ := range mapVar { 1199 keys = append(keys, k) 1200 } 1201 1202 sort.Strings(keys) 1203 1204 // Keys are guaranteed to be strings 1205 return stringSliceToVariableValue(keys), nil 1206 }, 1207 } 1208 } 1209 1210 // interpolationFuncValues implements the "values" function that yields a list of 1211 // keys of map types within a Terraform configuration. 1212 func interpolationFuncValues(vs map[string]ast.Variable) ast.Function { 1213 return ast.Function{ 1214 ArgTypes: []ast.Type{ast.TypeMap}, 1215 ReturnType: ast.TypeList, 1216 Callback: func(args []interface{}) (interface{}, error) { 1217 mapVar := args[0].(map[string]ast.Variable) 1218 keys := make([]string, 0) 1219 1220 for k, _ := range mapVar { 1221 keys = append(keys, k) 1222 } 1223 1224 sort.Strings(keys) 1225 1226 values := make([]string, len(keys)) 1227 for index, key := range keys { 1228 if value, ok := mapVar[key].Value.(string); ok { 1229 values[index] = value 1230 } else { 1231 return "", fmt.Errorf("values(): %q has element with bad type %s", 1232 key, mapVar[key].Type) 1233 } 1234 } 1235 1236 variable, err := hil.InterfaceToVariable(values) 1237 if err != nil { 1238 return nil, err 1239 } 1240 1241 return variable.Value, nil 1242 }, 1243 } 1244 } 1245 1246 // interpolationFuncBasename implements the "basename" function. 1247 func interpolationFuncBasename() ast.Function { 1248 return ast.Function{ 1249 ArgTypes: []ast.Type{ast.TypeString}, 1250 ReturnType: ast.TypeString, 1251 Callback: func(args []interface{}) (interface{}, error) { 1252 return filepath.Base(args[0].(string)), nil 1253 }, 1254 } 1255 } 1256 1257 // interpolationFuncBase64Encode implements the "base64encode" function that 1258 // allows Base64 encoding. 1259 func interpolationFuncBase64Encode() ast.Function { 1260 return ast.Function{ 1261 ArgTypes: []ast.Type{ast.TypeString}, 1262 ReturnType: ast.TypeString, 1263 Callback: func(args []interface{}) (interface{}, error) { 1264 s := args[0].(string) 1265 return base64.StdEncoding.EncodeToString([]byte(s)), nil 1266 }, 1267 } 1268 } 1269 1270 // interpolationFuncBase64Decode implements the "base64decode" function that 1271 // allows Base64 decoding. 1272 func interpolationFuncBase64Decode() ast.Function { 1273 return ast.Function{ 1274 ArgTypes: []ast.Type{ast.TypeString}, 1275 ReturnType: ast.TypeString, 1276 Callback: func(args []interface{}) (interface{}, error) { 1277 s := args[0].(string) 1278 sDec, err := base64.StdEncoding.DecodeString(s) 1279 if err != nil { 1280 return "", fmt.Errorf("failed to decode base64 data '%s'", s) 1281 } 1282 return string(sDec), nil 1283 }, 1284 } 1285 } 1286 1287 // interpolationFuncBase64Gzip implements the "gzip" function that allows gzip 1288 // compression encoding the result using base64 1289 func interpolationFuncBase64Gzip() ast.Function { 1290 return ast.Function{ 1291 ArgTypes: []ast.Type{ast.TypeString}, 1292 ReturnType: ast.TypeString, 1293 Callback: func(args []interface{}) (interface{}, error) { 1294 s := args[0].(string) 1295 1296 var b bytes.Buffer 1297 gz := gzip.NewWriter(&b) 1298 if _, err := gz.Write([]byte(s)); err != nil { 1299 return "", fmt.Errorf("failed to write gzip raw data: '%s'", s) 1300 } 1301 if err := gz.Flush(); err != nil { 1302 return "", fmt.Errorf("failed to flush gzip writer: '%s'", s) 1303 } 1304 if err := gz.Close(); err != nil { 1305 return "", fmt.Errorf("failed to close gzip writer: '%s'", s) 1306 } 1307 1308 return base64.StdEncoding.EncodeToString(b.Bytes()), nil 1309 }, 1310 } 1311 } 1312 1313 // interpolationFuncLower implements the "lower" function that does 1314 // string lower casing. 1315 func interpolationFuncLower() ast.Function { 1316 return ast.Function{ 1317 ArgTypes: []ast.Type{ast.TypeString}, 1318 ReturnType: ast.TypeString, 1319 Callback: func(args []interface{}) (interface{}, error) { 1320 toLower := args[0].(string) 1321 return strings.ToLower(toLower), nil 1322 }, 1323 } 1324 } 1325 1326 func interpolationFuncMd5() ast.Function { 1327 return ast.Function{ 1328 ArgTypes: []ast.Type{ast.TypeString}, 1329 ReturnType: ast.TypeString, 1330 Callback: func(args []interface{}) (interface{}, error) { 1331 s := args[0].(string) 1332 h := md5.New() 1333 h.Write([]byte(s)) 1334 hash := hex.EncodeToString(h.Sum(nil)) 1335 return hash, nil 1336 }, 1337 } 1338 } 1339 1340 func interpolationFuncMerge() ast.Function { 1341 return ast.Function{ 1342 ArgTypes: []ast.Type{ast.TypeMap}, 1343 ReturnType: ast.TypeMap, 1344 Variadic: true, 1345 VariadicType: ast.TypeMap, 1346 Callback: func(args []interface{}) (interface{}, error) { 1347 outputMap := make(map[string]ast.Variable) 1348 1349 for _, arg := range args { 1350 for k, v := range arg.(map[string]ast.Variable) { 1351 outputMap[k] = v 1352 } 1353 } 1354 1355 return outputMap, nil 1356 }, 1357 } 1358 } 1359 1360 // interpolationFuncUpper implements the "upper" function that does 1361 // string upper casing. 1362 func interpolationFuncUpper() ast.Function { 1363 return ast.Function{ 1364 ArgTypes: []ast.Type{ast.TypeString}, 1365 ReturnType: ast.TypeString, 1366 Callback: func(args []interface{}) (interface{}, error) { 1367 toUpper := args[0].(string) 1368 return strings.ToUpper(toUpper), nil 1369 }, 1370 } 1371 } 1372 1373 func interpolationFuncSha1() ast.Function { 1374 return ast.Function{ 1375 ArgTypes: []ast.Type{ast.TypeString}, 1376 ReturnType: ast.TypeString, 1377 Callback: func(args []interface{}) (interface{}, error) { 1378 s := args[0].(string) 1379 h := sha1.New() 1380 h.Write([]byte(s)) 1381 hash := hex.EncodeToString(h.Sum(nil)) 1382 return hash, nil 1383 }, 1384 } 1385 } 1386 1387 // hexadecimal representation of sha256 sum 1388 func interpolationFuncSha256() ast.Function { 1389 return ast.Function{ 1390 ArgTypes: []ast.Type{ast.TypeString}, 1391 ReturnType: ast.TypeString, 1392 Callback: func(args []interface{}) (interface{}, error) { 1393 s := args[0].(string) 1394 h := sha256.New() 1395 h.Write([]byte(s)) 1396 hash := hex.EncodeToString(h.Sum(nil)) 1397 return hash, nil 1398 }, 1399 } 1400 } 1401 1402 func interpolationFuncSha512() ast.Function { 1403 return ast.Function{ 1404 ArgTypes: []ast.Type{ast.TypeString}, 1405 ReturnType: ast.TypeString, 1406 Callback: func(args []interface{}) (interface{}, error) { 1407 s := args[0].(string) 1408 h := sha512.New() 1409 h.Write([]byte(s)) 1410 hash := hex.EncodeToString(h.Sum(nil)) 1411 return hash, nil 1412 }, 1413 } 1414 } 1415 1416 func interpolationFuncTrimSpace() ast.Function { 1417 return ast.Function{ 1418 ArgTypes: []ast.Type{ast.TypeString}, 1419 ReturnType: ast.TypeString, 1420 Callback: func(args []interface{}) (interface{}, error) { 1421 trimSpace := args[0].(string) 1422 return strings.TrimSpace(trimSpace), nil 1423 }, 1424 } 1425 } 1426 1427 func interpolationFuncBase64Sha256() ast.Function { 1428 return ast.Function{ 1429 ArgTypes: []ast.Type{ast.TypeString}, 1430 ReturnType: ast.TypeString, 1431 Callback: func(args []interface{}) (interface{}, error) { 1432 s := args[0].(string) 1433 h := sha256.New() 1434 h.Write([]byte(s)) 1435 shaSum := h.Sum(nil) 1436 encoded := base64.StdEncoding.EncodeToString(shaSum[:]) 1437 return encoded, nil 1438 }, 1439 } 1440 } 1441 1442 func interpolationFuncBase64Sha512() ast.Function { 1443 return ast.Function{ 1444 ArgTypes: []ast.Type{ast.TypeString}, 1445 ReturnType: ast.TypeString, 1446 Callback: func(args []interface{}) (interface{}, error) { 1447 s := args[0].(string) 1448 h := sha512.New() 1449 h.Write([]byte(s)) 1450 shaSum := h.Sum(nil) 1451 encoded := base64.StdEncoding.EncodeToString(shaSum[:]) 1452 return encoded, nil 1453 }, 1454 } 1455 } 1456 1457 func interpolationFuncBcrypt() ast.Function { 1458 return ast.Function{ 1459 ArgTypes: []ast.Type{ast.TypeString}, 1460 Variadic: true, 1461 VariadicType: ast.TypeString, 1462 ReturnType: ast.TypeString, 1463 Callback: func(args []interface{}) (interface{}, error) { 1464 defaultCost := 10 1465 1466 if len(args) > 1 { 1467 costStr := args[1].(string) 1468 cost, err := strconv.Atoi(costStr) 1469 if err != nil { 1470 return "", err 1471 } 1472 1473 defaultCost = cost 1474 } 1475 1476 if len(args) > 2 { 1477 return "", fmt.Errorf("bcrypt() takes no more than two arguments") 1478 } 1479 1480 input := args[0].(string) 1481 out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost) 1482 if err != nil { 1483 return "", fmt.Errorf("error occured generating password %s", err.Error()) 1484 } 1485 1486 return string(out), nil 1487 }, 1488 } 1489 } 1490 1491 func interpolationFuncUUID() ast.Function { 1492 return ast.Function{ 1493 ArgTypes: []ast.Type{}, 1494 ReturnType: ast.TypeString, 1495 Callback: func(args []interface{}) (interface{}, error) { 1496 return uuid.GenerateUUID() 1497 }, 1498 } 1499 } 1500 1501 // interpolationFuncTimestamp 1502 func interpolationFuncTimestamp() ast.Function { 1503 return ast.Function{ 1504 ArgTypes: []ast.Type{}, 1505 ReturnType: ast.TypeString, 1506 Callback: func(args []interface{}) (interface{}, error) { 1507 return time.Now().UTC().Format(time.RFC3339), nil 1508 }, 1509 } 1510 } 1511 1512 func interpolationFuncTimeAdd() ast.Function { 1513 return ast.Function{ 1514 ArgTypes: []ast.Type{ 1515 ast.TypeString, // input timestamp string in RFC3339 format 1516 ast.TypeString, // duration to add to input timestamp that should be parsable by time.ParseDuration 1517 }, 1518 ReturnType: ast.TypeString, 1519 Callback: func(args []interface{}) (interface{}, error) { 1520 1521 ts, err := time.Parse(time.RFC3339, args[0].(string)) 1522 if err != nil { 1523 return nil, err 1524 } 1525 duration, err := time.ParseDuration(args[1].(string)) 1526 if err != nil { 1527 return nil, err 1528 } 1529 1530 return ts.Add(duration).Format(time.RFC3339), nil 1531 }, 1532 } 1533 } 1534 1535 // interpolationFuncTitle implements the "title" function that returns a copy of the 1536 // string in which first characters of all the words are capitalized. 1537 func interpolationFuncTitle() ast.Function { 1538 return ast.Function{ 1539 ArgTypes: []ast.Type{ast.TypeString}, 1540 ReturnType: ast.TypeString, 1541 Callback: func(args []interface{}) (interface{}, error) { 1542 toTitle := args[0].(string) 1543 return strings.Title(toTitle), nil 1544 }, 1545 } 1546 } 1547 1548 // interpolationFuncSubstr implements the "substr" function that allows strings 1549 // to be truncated. 1550 func interpolationFuncSubstr() ast.Function { 1551 return ast.Function{ 1552 ArgTypes: []ast.Type{ 1553 ast.TypeString, // input string 1554 ast.TypeInt, // offset 1555 ast.TypeInt, // length 1556 }, 1557 ReturnType: ast.TypeString, 1558 Callback: func(args []interface{}) (interface{}, error) { 1559 str := args[0].(string) 1560 offset := args[1].(int) 1561 length := args[2].(int) 1562 1563 // Interpret a negative offset as being equivalent to a positive 1564 // offset taken from the end of the string. 1565 if offset < 0 { 1566 offset += len(str) 1567 } 1568 1569 // Interpret a length of `-1` as indicating that the substring 1570 // should start at `offset` and continue until the end of the 1571 // string. Any other negative length (other than `-1`) is invalid. 1572 if length == -1 { 1573 length = len(str) 1574 } else if length >= 0 { 1575 length += offset 1576 } else { 1577 return nil, fmt.Errorf("length should be a non-negative integer") 1578 } 1579 1580 if offset > len(str) || offset < 0 { 1581 return nil, fmt.Errorf("offset cannot be larger than the length of the string") 1582 } 1583 1584 if length > len(str) { 1585 return nil, fmt.Errorf("'offset + length' cannot be larger than the length of the string") 1586 } 1587 1588 return str[offset:length], nil 1589 }, 1590 } 1591 } 1592 1593 // Flatten until it's not ast.TypeList 1594 func flattener(finalList []ast.Variable, flattenList []ast.Variable) []ast.Variable { 1595 for _, val := range flattenList { 1596 if val.Type == ast.TypeList { 1597 finalList = flattener(finalList, val.Value.([]ast.Variable)) 1598 } else { 1599 finalList = append(finalList, val) 1600 } 1601 } 1602 return finalList 1603 } 1604 1605 // Flatten to single list 1606 func interpolationFuncFlatten() ast.Function { 1607 return ast.Function{ 1608 ArgTypes: []ast.Type{ast.TypeList}, 1609 ReturnType: ast.TypeList, 1610 Variadic: false, 1611 Callback: func(args []interface{}) (interface{}, error) { 1612 inputList := args[0].([]ast.Variable) 1613 1614 var outputList []ast.Variable 1615 return flattener(outputList, inputList), nil 1616 }, 1617 } 1618 } 1619 1620 func interpolationFuncURLEncode() ast.Function { 1621 return ast.Function{ 1622 ArgTypes: []ast.Type{ast.TypeString}, 1623 ReturnType: ast.TypeString, 1624 Callback: func(args []interface{}) (interface{}, error) { 1625 s := args[0].(string) 1626 return url.QueryEscape(s), nil 1627 }, 1628 } 1629 } 1630 1631 // interpolationFuncTranspose implements the "transpose" function 1632 // that converts a map (string,list) to a map (string,list) where 1633 // the unique values of the original lists become the keys of the 1634 // new map and the keys of the original map become values for the 1635 // corresponding new keys. 1636 func interpolationFuncTranspose() ast.Function { 1637 return ast.Function{ 1638 ArgTypes: []ast.Type{ast.TypeMap}, 1639 ReturnType: ast.TypeMap, 1640 Callback: func(args []interface{}) (interface{}, error) { 1641 1642 inputMap := args[0].(map[string]ast.Variable) 1643 outputMap := make(map[string]ast.Variable) 1644 tmpMap := make(map[string][]string) 1645 1646 for inKey, inVal := range inputMap { 1647 if inVal.Type != ast.TypeList { 1648 return nil, fmt.Errorf("transpose requires a map of lists of strings") 1649 } 1650 values := inVal.Value.([]ast.Variable) 1651 for _, listVal := range values { 1652 if listVal.Type != ast.TypeString { 1653 return nil, fmt.Errorf("transpose requires the given map values to be lists of strings") 1654 } 1655 outKey := listVal.Value.(string) 1656 if _, ok := tmpMap[outKey]; !ok { 1657 tmpMap[outKey] = make([]string, 0) 1658 } 1659 outVal := tmpMap[outKey] 1660 outVal = append(outVal, inKey) 1661 sort.Strings(outVal) 1662 tmpMap[outKey] = outVal 1663 } 1664 } 1665 1666 for outKey, outVal := range tmpMap { 1667 values := make([]ast.Variable, 0) 1668 for _, v := range outVal { 1669 values = append(values, ast.Variable{Type: ast.TypeString, Value: v}) 1670 } 1671 outputMap[outKey] = ast.Variable{Type: ast.TypeList, Value: values} 1672 } 1673 return outputMap, nil 1674 }, 1675 } 1676 } 1677 1678 // interpolationFuncAbs returns the absolute value of a given float. 1679 func interpolationFuncAbs() ast.Function { 1680 return ast.Function{ 1681 ArgTypes: []ast.Type{ast.TypeFloat}, 1682 ReturnType: ast.TypeFloat, 1683 Callback: func(args []interface{}) (interface{}, error) { 1684 return math.Abs(args[0].(float64)), nil 1685 }, 1686 } 1687 } 1688 1689 // interpolationFuncRsaDecrypt implements the "rsadecrypt" function that does 1690 // RSA decryption. 1691 func interpolationFuncRsaDecrypt() ast.Function { 1692 return ast.Function{ 1693 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, 1694 ReturnType: ast.TypeString, 1695 Callback: func(args []interface{}) (interface{}, error) { 1696 s := args[0].(string) 1697 key := args[1].(string) 1698 1699 b, err := base64.StdEncoding.DecodeString(s) 1700 if err != nil { 1701 return "", fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", s) 1702 } 1703 1704 block, _ := pem.Decode([]byte(key)) 1705 if block == nil { 1706 return "", fmt.Errorf("Failed to read key %q: no key found", key) 1707 } 1708 if block.Headers["Proc-Type"] == "4,ENCRYPTED" { 1709 return "", fmt.Errorf( 1710 "Failed to read key %q: password protected keys are\n"+ 1711 "not supported. Please decrypt the key prior to use.", key) 1712 } 1713 1714 x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes) 1715 if err != nil { 1716 return "", err 1717 } 1718 1719 out, err := rsa.DecryptPKCS1v15(nil, x509Key, b) 1720 if err != nil { 1721 return "", err 1722 } 1723 1724 return string(out), nil 1725 }, 1726 } 1727 }