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