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