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