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