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