github.com/serbaut/terraform@v0.6.12-0.20160607213102-ac2d195cc560/config/interpolate_funcs.go (about) 1 package config 2 3 import ( 4 "crypto/md5" 5 "crypto/sha1" 6 "crypto/sha256" 7 "encoding/base64" 8 "encoding/hex" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "net" 14 "regexp" 15 "sort" 16 "strconv" 17 "strings" 18 19 "github.com/apparentlymart/go-cidr/cidr" 20 "github.com/hashicorp/go-uuid" 21 "github.com/hashicorp/hil" 22 "github.com/hashicorp/hil/ast" 23 "github.com/mitchellh/go-homedir" 24 ) 25 26 // stringSliceToVariableValue converts a string slice into the value 27 // required to be returned from interpolation functions which return 28 // TypeList. 29 func stringSliceToVariableValue(values []string) []ast.Variable { 30 output := make([]ast.Variable, len(values)) 31 for index, value := range values { 32 output[index] = ast.Variable{ 33 Type: ast.TypeString, 34 Value: value, 35 } 36 } 37 return output 38 } 39 40 func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { 41 output := make([]string, len(values)) 42 for index, value := range values { 43 if value.Type != ast.TypeString { 44 return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String()) 45 } 46 output[index] = value.Value.(string) 47 } 48 return output, nil 49 } 50 51 // Funcs is the mapping of built-in functions for configuration. 52 func Funcs() map[string]ast.Function { 53 return map[string]ast.Function{ 54 "base64decode": interpolationFuncBase64Decode(), 55 "base64encode": interpolationFuncBase64Encode(), 56 "base64sha256": interpolationFuncBase64Sha256(), 57 "cidrhost": interpolationFuncCidrHost(), 58 "cidrnetmask": interpolationFuncCidrNetmask(), 59 "cidrsubnet": interpolationFuncCidrSubnet(), 60 "coalesce": interpolationFuncCoalesce(), 61 "compact": interpolationFuncCompact(), 62 "concat": interpolationFuncConcat(), 63 "element": interpolationFuncElement(), 64 "file": interpolationFuncFile(), 65 "format": interpolationFuncFormat(), 66 "formatlist": interpolationFuncFormatList(), 67 "index": interpolationFuncIndex(), 68 "join": interpolationFuncJoin(), 69 "jsonencode": interpolationFuncJSONEncode(), 70 "length": interpolationFuncLength(), 71 "lower": interpolationFuncLower(), 72 "md5": interpolationFuncMd5(), 73 "uuid": interpolationFuncUUID(), 74 "replace": interpolationFuncReplace(), 75 "sha1": interpolationFuncSha1(), 76 "sha256": interpolationFuncSha256(), 77 "signum": interpolationFuncSignum(), 78 "split": interpolationFuncSplit(), 79 "trimspace": interpolationFuncTrimSpace(), 80 "upper": interpolationFuncUpper(), 81 } 82 } 83 84 // interpolationFuncCompact strips a list of multi-variable values 85 // (e.g. as returned by "split") of any empty strings. 86 func interpolationFuncCompact() ast.Function { 87 return ast.Function{ 88 ArgTypes: []ast.Type{ast.TypeList}, 89 ReturnType: ast.TypeList, 90 Variadic: false, 91 Callback: func(args []interface{}) (interface{}, error) { 92 inputList := args[0].([]ast.Variable) 93 94 var outputList []string 95 for _, val := range inputList { 96 if strVal, ok := val.Value.(string); ok { 97 if strVal == "" { 98 continue 99 } 100 101 outputList = append(outputList, strVal) 102 } 103 } 104 return stringSliceToVariableValue(outputList), nil 105 }, 106 } 107 } 108 109 // interpolationFuncCidrHost implements the "cidrhost" function that 110 // fills in the host part of a CIDR range address to create a single 111 // host address 112 func interpolationFuncCidrHost() ast.Function { 113 return ast.Function{ 114 ArgTypes: []ast.Type{ 115 ast.TypeString, // starting CIDR mask 116 ast.TypeInt, // host number to insert 117 }, 118 ReturnType: ast.TypeString, 119 Variadic: false, 120 Callback: func(args []interface{}) (interface{}, error) { 121 hostNum := args[1].(int) 122 _, network, err := net.ParseCIDR(args[0].(string)) 123 if err != nil { 124 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 125 } 126 127 ip, err := cidr.Host(network, hostNum) 128 if err != nil { 129 return nil, err 130 } 131 132 return ip.String(), nil 133 }, 134 } 135 } 136 137 // interpolationFuncCidrNetmask implements the "cidrnetmask" function 138 // that returns the subnet mask in IP address notation. 139 func interpolationFuncCidrNetmask() ast.Function { 140 return ast.Function{ 141 ArgTypes: []ast.Type{ 142 ast.TypeString, // CIDR mask 143 }, 144 ReturnType: ast.TypeString, 145 Variadic: false, 146 Callback: func(args []interface{}) (interface{}, error) { 147 _, network, err := net.ParseCIDR(args[0].(string)) 148 if err != nil { 149 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 150 } 151 152 return net.IP(network.Mask).String(), nil 153 }, 154 } 155 } 156 157 // interpolationFuncCidrSubnet implements the "cidrsubnet" function that 158 // adds an additional subnet of the given length onto an existing 159 // IP block expressed in CIDR notation. 160 func interpolationFuncCidrSubnet() ast.Function { 161 return ast.Function{ 162 ArgTypes: []ast.Type{ 163 ast.TypeString, // starting CIDR mask 164 ast.TypeInt, // number of bits to extend the prefix 165 ast.TypeInt, // network number to append to the prefix 166 }, 167 ReturnType: ast.TypeString, 168 Variadic: false, 169 Callback: func(args []interface{}) (interface{}, error) { 170 extraBits := args[1].(int) 171 subnetNum := args[2].(int) 172 _, network, err := net.ParseCIDR(args[0].(string)) 173 if err != nil { 174 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 175 } 176 177 // For portability with 32-bit systems where the subnet number 178 // will be a 32-bit int, we only allow extension of 32 bits in 179 // one call even if we're running on a 64-bit machine. 180 // (Of course, this is significant only for IPv6.) 181 if extraBits > 32 { 182 return nil, fmt.Errorf("may not extend prefix by more than 32 bits") 183 } 184 185 newNetwork, err := cidr.Subnet(network, extraBits, subnetNum) 186 if err != nil { 187 return nil, err 188 } 189 190 return newNetwork.String(), nil 191 }, 192 } 193 } 194 195 // interpolationFuncCoalesce implements the "coalesce" function that 196 // returns the first non null / empty string from the provided input 197 func interpolationFuncCoalesce() ast.Function { 198 return ast.Function{ 199 ArgTypes: []ast.Type{ast.TypeString}, 200 ReturnType: ast.TypeString, 201 Variadic: true, 202 VariadicType: ast.TypeString, 203 Callback: func(args []interface{}) (interface{}, error) { 204 if len(args) < 2 { 205 return nil, fmt.Errorf("must provide at least two arguments") 206 } 207 for _, arg := range args { 208 argument := arg.(string) 209 210 if argument != "" { 211 return argument, nil 212 } 213 } 214 return "", nil 215 }, 216 } 217 } 218 219 // interpolationFuncConcat implements the "concat" function that 220 // concatenates multiple strings. This isn't actually necessary anymore 221 // since our language supports string concat natively, but for backwards 222 // compat we do this. 223 func interpolationFuncConcat() ast.Function { 224 return ast.Function{ 225 ArgTypes: []ast.Type{ast.TypeAny}, 226 ReturnType: ast.TypeList, 227 Variadic: true, 228 VariadicType: ast.TypeAny, 229 Callback: func(args []interface{}) (interface{}, error) { 230 var finalListElements []string 231 232 for _, arg := range args { 233 // Append strings for backward compatibility 234 if argument, ok := arg.(string); ok { 235 finalListElements = append(finalListElements, argument) 236 continue 237 } 238 239 // Otherwise variables 240 if argument, ok := arg.([]ast.Variable); ok { 241 for _, element := range argument { 242 finalListElements = append(finalListElements, element.Value.(string)) 243 } 244 continue 245 } 246 247 return nil, fmt.Errorf("arguments to concat() must be a string or list") 248 } 249 250 return stringSliceToVariableValue(finalListElements), nil 251 }, 252 } 253 } 254 255 // interpolationFuncFile implements the "file" function that allows 256 // loading contents from a file. 257 func interpolationFuncFile() ast.Function { 258 return ast.Function{ 259 ArgTypes: []ast.Type{ast.TypeString}, 260 ReturnType: ast.TypeString, 261 Callback: func(args []interface{}) (interface{}, error) { 262 path, err := homedir.Expand(args[0].(string)) 263 if err != nil { 264 return "", err 265 } 266 data, err := ioutil.ReadFile(path) 267 if err != nil { 268 return "", err 269 } 270 271 return string(data), nil 272 }, 273 } 274 } 275 276 // interpolationFuncFormat implements the "format" function that does 277 // string formatting. 278 func interpolationFuncFormat() ast.Function { 279 return ast.Function{ 280 ArgTypes: []ast.Type{ast.TypeString}, 281 Variadic: true, 282 VariadicType: ast.TypeAny, 283 ReturnType: ast.TypeString, 284 Callback: func(args []interface{}) (interface{}, error) { 285 format := args[0].(string) 286 return fmt.Sprintf(format, args[1:]...), nil 287 }, 288 } 289 } 290 291 // interpolationFuncFormatList implements the "formatlist" function that does 292 // string formatting on lists. 293 func interpolationFuncFormatList() ast.Function { 294 return ast.Function{ 295 ArgTypes: []ast.Type{ast.TypeAny}, 296 Variadic: true, 297 VariadicType: ast.TypeAny, 298 ReturnType: ast.TypeList, 299 Callback: func(args []interface{}) (interface{}, error) { 300 // Make a copy of the variadic part of args 301 // to avoid modifying the original. 302 varargs := make([]interface{}, len(args)-1) 303 copy(varargs, args[1:]) 304 305 // Convert arguments that are lists into slices. 306 // Confirm along the way that all lists have the same length (n). 307 var n int 308 for i := 1; i < len(args); i++ { 309 s, ok := args[i].([]ast.Variable) 310 if !ok { 311 continue 312 } 313 314 parts, err := listVariableValueToStringSlice(s) 315 if err != nil { 316 return nil, err 317 } 318 319 // otherwise the list is sent down to be indexed 320 varargs[i-1] = parts 321 322 // Check length 323 if n == 0 { 324 // first list we've seen 325 n = len(parts) 326 continue 327 } 328 if n != len(parts) { 329 return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts)) 330 } 331 } 332 333 if n == 0 { 334 return nil, errors.New("no lists in arguments to formatlist") 335 } 336 337 // Do the formatting. 338 format := args[0].(string) 339 340 // Generate a list of formatted strings. 341 list := make([]string, n) 342 fmtargs := make([]interface{}, len(varargs)) 343 for i := 0; i < n; i++ { 344 for j, arg := range varargs { 345 switch arg := arg.(type) { 346 default: 347 fmtargs[j] = arg 348 case []string: 349 fmtargs[j] = arg[i] 350 } 351 } 352 list[i] = fmt.Sprintf(format, fmtargs...) 353 } 354 return stringSliceToVariableValue(list), nil 355 }, 356 } 357 } 358 359 // interpolationFuncIndex implements the "index" function that allows one to 360 // find the index of a specific element in a list 361 func interpolationFuncIndex() ast.Function { 362 return ast.Function{ 363 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 364 ReturnType: ast.TypeInt, 365 Callback: func(args []interface{}) (interface{}, error) { 366 haystack := args[0].([]ast.Variable) 367 needle := args[1].(string) 368 for index, element := range haystack { 369 if needle == element.Value { 370 return index, nil 371 } 372 } 373 return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack) 374 }, 375 } 376 } 377 378 // interpolationFuncJoin implements the "join" function that allows 379 // multi-variable values to be joined by some character. 380 func interpolationFuncJoin() ast.Function { 381 return ast.Function{ 382 ArgTypes: []ast.Type{ast.TypeString}, 383 Variadic: true, 384 VariadicType: ast.TypeList, 385 ReturnType: ast.TypeString, 386 Callback: func(args []interface{}) (interface{}, error) { 387 var list []string 388 389 if len(args) < 2 { 390 return nil, fmt.Errorf("not enough arguments to join()") 391 } 392 393 for _, arg := range args[1:] { 394 if parts, ok := arg.(ast.Variable); ok { 395 for _, part := range parts.Value.([]ast.Variable) { 396 list = append(list, part.Value.(string)) 397 } 398 } 399 if parts, ok := arg.([]ast.Variable); ok { 400 for _, part := range parts { 401 list = append(list, part.Value.(string)) 402 } 403 } 404 } 405 406 return strings.Join(list, args[0].(string)), nil 407 }, 408 } 409 } 410 411 // interpolationFuncJSONEncode implements the "jsonencode" function that encodes 412 // a string, list, or map as its JSON representation. For now, values in the 413 // list or map may only be strings. 414 func interpolationFuncJSONEncode() ast.Function { 415 return ast.Function{ 416 ArgTypes: []ast.Type{ast.TypeAny}, 417 ReturnType: ast.TypeString, 418 Callback: func(args []interface{}) (interface{}, error) { 419 var toEncode interface{} 420 421 switch typedArg := args[0].(type) { 422 case string: 423 toEncode = typedArg 424 425 case []ast.Variable: 426 // We preallocate the list here. Note that it's important that in 427 // the length 0 case, we have an empty list rather than nil, as 428 // they encode differently. 429 // XXX It would be nice to support arbitrarily nested data here. Is 430 // there an inverse of hil.InterfaceToVariable? 431 strings := make([]string, len(typedArg)) 432 433 for i, v := range typedArg { 434 if v.Type != ast.TypeString { 435 return "", fmt.Errorf("list elements must be strings") 436 } 437 strings[i] = v.Value.(string) 438 } 439 toEncode = strings 440 441 case map[string]ast.Variable: 442 // XXX It would be nice to support arbitrarily nested data here. Is 443 // there an inverse of hil.InterfaceToVariable? 444 stringMap := make(map[string]string) 445 for k, v := range typedArg { 446 if v.Type != ast.TypeString { 447 return "", fmt.Errorf("map values must be strings") 448 } 449 stringMap[k] = v.Value.(string) 450 } 451 toEncode = stringMap 452 453 default: 454 return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0]) 455 } 456 457 jEnc, err := json.Marshal(toEncode) 458 if err != nil { 459 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) 460 } 461 return string(jEnc), nil 462 }, 463 } 464 } 465 466 // interpolationFuncReplace implements the "replace" function that does 467 // string replacement. 468 func interpolationFuncReplace() ast.Function { 469 return ast.Function{ 470 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString}, 471 ReturnType: ast.TypeString, 472 Callback: func(args []interface{}) (interface{}, error) { 473 s := args[0].(string) 474 search := args[1].(string) 475 replace := args[2].(string) 476 477 // We search/replace using a regexp if the string is surrounded 478 // in forward slashes. 479 if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' { 480 re, err := regexp.Compile(search[1 : len(search)-1]) 481 if err != nil { 482 return nil, err 483 } 484 485 return re.ReplaceAllString(s, replace), nil 486 } 487 488 return strings.Replace(s, search, replace, -1), nil 489 }, 490 } 491 } 492 493 func interpolationFuncLength() ast.Function { 494 return ast.Function{ 495 ArgTypes: []ast.Type{ast.TypeAny}, 496 ReturnType: ast.TypeInt, 497 Variadic: false, 498 Callback: func(args []interface{}) (interface{}, error) { 499 subject := args[0] 500 501 switch typedSubject := subject.(type) { 502 case string: 503 return len(typedSubject), nil 504 case []ast.Variable: 505 return len(typedSubject), nil 506 } 507 508 return 0, fmt.Errorf("arguments to length() must be a string or list") 509 }, 510 } 511 } 512 513 func interpolationFuncSignum() ast.Function { 514 return ast.Function{ 515 ArgTypes: []ast.Type{ast.TypeInt}, 516 ReturnType: ast.TypeInt, 517 Variadic: false, 518 Callback: func(args []interface{}) (interface{}, error) { 519 num := args[0].(int) 520 switch { 521 case num < 0: 522 return -1, nil 523 case num > 0: 524 return +1, nil 525 default: 526 return 0, nil 527 } 528 }, 529 } 530 } 531 532 // interpolationFuncSplit implements the "split" function that allows 533 // strings to split into multi-variable values 534 func interpolationFuncSplit() ast.Function { 535 return ast.Function{ 536 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, 537 ReturnType: ast.TypeList, 538 Callback: func(args []interface{}) (interface{}, error) { 539 sep := args[0].(string) 540 s := args[1].(string) 541 elements := strings.Split(s, sep) 542 return stringSliceToVariableValue(elements), nil 543 }, 544 } 545 } 546 547 // interpolationFuncLookup implements the "lookup" function that allows 548 // dynamic lookups of map types within a Terraform configuration. 549 func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function { 550 return ast.Function{ 551 ArgTypes: []ast.Type{ast.TypeMap, ast.TypeString}, 552 ReturnType: ast.TypeString, 553 Variadic: true, 554 VariadicType: ast.TypeString, 555 Callback: func(args []interface{}) (interface{}, error) { 556 defaultValue := "" 557 defaultValueSet := false 558 if len(args) > 2 { 559 defaultValue = args[2].(string) 560 defaultValueSet = true 561 } 562 if len(args) > 3 { 563 return "", fmt.Errorf("lookup() takes no more than three arguments") 564 } 565 index := args[1].(string) 566 mapVar := args[0].(map[string]ast.Variable) 567 568 v, ok := mapVar[index] 569 if !ok { 570 if defaultValueSet { 571 return defaultValue, nil 572 } else { 573 return "", fmt.Errorf( 574 "lookup failed to find '%s'", 575 args[1].(string)) 576 } 577 } 578 if v.Type != ast.TypeString { 579 return "", fmt.Errorf( 580 "lookup for '%s' has bad type %s", 581 args[1].(string), v.Type) 582 } 583 584 return v.Value.(string), nil 585 }, 586 } 587 } 588 589 // interpolationFuncElement implements the "element" function that allows 590 // a specific index to be looked up in a multi-variable value. Note that this will 591 // wrap if the index is larger than the number of elements in the multi-variable value. 592 func interpolationFuncElement() ast.Function { 593 return ast.Function{ 594 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 595 ReturnType: ast.TypeString, 596 Callback: func(args []interface{}) (interface{}, error) { 597 list := args[0].([]ast.Variable) 598 599 index, err := strconv.Atoi(args[1].(string)) 600 if err != nil || index < 0 { 601 return "", fmt.Errorf( 602 "invalid number for index, got %s", args[1]) 603 } 604 605 resolvedIndex := index % len(list) 606 607 v := list[resolvedIndex].Value 608 return v, nil 609 }, 610 } 611 } 612 613 // interpolationFuncKeys implements the "keys" function that yields a list of 614 // keys of map types within a Terraform configuration. 615 func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { 616 return ast.Function{ 617 ArgTypes: []ast.Type{ast.TypeMap}, 618 ReturnType: ast.TypeList, 619 Callback: func(args []interface{}) (interface{}, error) { 620 mapVar := args[0].(map[string]ast.Variable) 621 keys := make([]string, 0) 622 623 for k, _ := range mapVar { 624 keys = append(keys, k) 625 } 626 627 sort.Strings(keys) 628 629 //Keys are guaranteed to be strings 630 return stringSliceToVariableValue(keys), nil 631 }, 632 } 633 } 634 635 // interpolationFuncValues implements the "values" function that yields a list of 636 // keys of map types within a Terraform configuration. 637 func interpolationFuncValues(vs map[string]ast.Variable) ast.Function { 638 return ast.Function{ 639 ArgTypes: []ast.Type{ast.TypeMap}, 640 ReturnType: ast.TypeList, 641 Callback: func(args []interface{}) (interface{}, error) { 642 mapVar := args[0].(map[string]ast.Variable) 643 keys := make([]string, 0) 644 645 for k, _ := range mapVar { 646 keys = append(keys, k) 647 } 648 649 sort.Strings(keys) 650 651 values := make([]string, len(keys)) 652 for index, key := range keys { 653 if value, ok := mapVar[key].Value.(string); ok { 654 values[index] = value 655 } else { 656 return "", fmt.Errorf("values(): %q has element with bad type %s", 657 key, mapVar[key].Type) 658 } 659 } 660 661 variable, err := hil.InterfaceToVariable(values) 662 if err != nil { 663 return nil, err 664 } 665 666 return variable.Value, nil 667 }, 668 } 669 } 670 671 // interpolationFuncBase64Encode implements the "base64encode" function that 672 // allows Base64 encoding. 673 func interpolationFuncBase64Encode() ast.Function { 674 return ast.Function{ 675 ArgTypes: []ast.Type{ast.TypeString}, 676 ReturnType: ast.TypeString, 677 Callback: func(args []interface{}) (interface{}, error) { 678 s := args[0].(string) 679 return base64.StdEncoding.EncodeToString([]byte(s)), nil 680 }, 681 } 682 } 683 684 // interpolationFuncBase64Decode implements the "base64decode" function that 685 // allows Base64 decoding. 686 func interpolationFuncBase64Decode() ast.Function { 687 return ast.Function{ 688 ArgTypes: []ast.Type{ast.TypeString}, 689 ReturnType: ast.TypeString, 690 Callback: func(args []interface{}) (interface{}, error) { 691 s := args[0].(string) 692 sDec, err := base64.StdEncoding.DecodeString(s) 693 if err != nil { 694 return "", fmt.Errorf("failed to decode base64 data '%s'", s) 695 } 696 return string(sDec), nil 697 }, 698 } 699 } 700 701 // interpolationFuncLower implements the "lower" function that does 702 // string lower casing. 703 func interpolationFuncLower() ast.Function { 704 return ast.Function{ 705 ArgTypes: []ast.Type{ast.TypeString}, 706 ReturnType: ast.TypeString, 707 Callback: func(args []interface{}) (interface{}, error) { 708 toLower := args[0].(string) 709 return strings.ToLower(toLower), nil 710 }, 711 } 712 } 713 714 func interpolationFuncMd5() ast.Function { 715 return ast.Function{ 716 ArgTypes: []ast.Type{ast.TypeString}, 717 ReturnType: ast.TypeString, 718 Callback: func(args []interface{}) (interface{}, error) { 719 s := args[0].(string) 720 h := md5.New() 721 h.Write([]byte(s)) 722 hash := hex.EncodeToString(h.Sum(nil)) 723 return hash, nil 724 }, 725 } 726 } 727 728 // interpolationFuncUpper implements the "upper" function that does 729 // string upper casing. 730 func interpolationFuncUpper() ast.Function { 731 return ast.Function{ 732 ArgTypes: []ast.Type{ast.TypeString}, 733 ReturnType: ast.TypeString, 734 Callback: func(args []interface{}) (interface{}, error) { 735 toUpper := args[0].(string) 736 return strings.ToUpper(toUpper), nil 737 }, 738 } 739 } 740 741 func interpolationFuncSha1() ast.Function { 742 return ast.Function{ 743 ArgTypes: []ast.Type{ast.TypeString}, 744 ReturnType: ast.TypeString, 745 Callback: func(args []interface{}) (interface{}, error) { 746 s := args[0].(string) 747 h := sha1.New() 748 h.Write([]byte(s)) 749 hash := hex.EncodeToString(h.Sum(nil)) 750 return hash, nil 751 }, 752 } 753 } 754 755 // hexadecimal representation of sha256 sum 756 func interpolationFuncSha256() ast.Function { 757 return ast.Function{ 758 ArgTypes: []ast.Type{ast.TypeString}, 759 ReturnType: ast.TypeString, 760 Callback: func(args []interface{}) (interface{}, error) { 761 s := args[0].(string) 762 h := sha256.New() 763 h.Write([]byte(s)) 764 hash := hex.EncodeToString(h.Sum(nil)) 765 return hash, nil 766 }, 767 } 768 } 769 770 func interpolationFuncTrimSpace() ast.Function { 771 return ast.Function{ 772 ArgTypes: []ast.Type{ast.TypeString}, 773 ReturnType: ast.TypeString, 774 Callback: func(args []interface{}) (interface{}, error) { 775 trimSpace := args[0].(string) 776 return strings.TrimSpace(trimSpace), nil 777 }, 778 } 779 } 780 781 func interpolationFuncBase64Sha256() ast.Function { 782 return ast.Function{ 783 ArgTypes: []ast.Type{ast.TypeString}, 784 ReturnType: ast.TypeString, 785 Callback: func(args []interface{}) (interface{}, error) { 786 s := args[0].(string) 787 h := sha256.New() 788 h.Write([]byte(s)) 789 shaSum := h.Sum(nil) 790 encoded := base64.StdEncoding.EncodeToString(shaSum[:]) 791 return encoded, nil 792 }, 793 } 794 } 795 796 func interpolationFuncUUID() ast.Function { 797 return ast.Function{ 798 ArgTypes: []ast.Type{}, 799 ReturnType: ast.TypeString, 800 Callback: func(args []interface{}) (interface{}, error) { 801 return uuid.GenerateUUID() 802 }, 803 } 804 }