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