github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/config/interpolate_funcs.go (about) 1 package config 2 3 import ( 4 "crypto/md5" 5 "crypto/sha1" 6 "crypto/sha256" 7 "crypto/sha512" 8 "encoding/base64" 9 "encoding/hex" 10 "encoding/json" 11 "fmt" 12 "io/ioutil" 13 "math" 14 "net" 15 "path/filepath" 16 "regexp" 17 "sort" 18 "strconv" 19 "strings" 20 "time" 21 22 "github.com/apparentlymart/go-cidr/cidr" 23 "github.com/hashicorp/go-uuid" 24 "github.com/hashicorp/hil" 25 "github.com/hashicorp/hil/ast" 26 "github.com/mitchellh/go-homedir" 27 "golang.org/x/crypto/bcrypt" 28 ) 29 30 // stringSliceToVariableValue converts a string slice into the value 31 // required to be returned from interpolation functions which return 32 // TypeList. 33 func stringSliceToVariableValue(values []string) []ast.Variable { 34 output := make([]ast.Variable, len(values)) 35 for index, value := range values { 36 output[index] = ast.Variable{ 37 Type: ast.TypeString, 38 Value: value, 39 } 40 } 41 return output 42 } 43 44 func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { 45 output := make([]string, len(values)) 46 for index, value := range values { 47 if value.Type != ast.TypeString { 48 return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String()) 49 } 50 output[index] = value.Value.(string) 51 } 52 return output, nil 53 } 54 55 // Funcs is the mapping of built-in functions for configuration. 56 func Funcs() map[string]ast.Function { 57 return map[string]ast.Function{ 58 "basename": interpolationFuncBasename(), 59 "base64decode": interpolationFuncBase64Decode(), 60 "base64encode": interpolationFuncBase64Encode(), 61 "base64sha256": interpolationFuncBase64Sha256(), 62 "base64sha512": interpolationFuncBase64Sha512(), 63 "bcrypt": interpolationFuncBcrypt(), 64 "ceil": interpolationFuncCeil(), 65 "chomp": interpolationFuncChomp(), 66 "cidrhost": interpolationFuncCidrHost(), 67 "cidrnetmask": interpolationFuncCidrNetmask(), 68 "cidrsubnet": interpolationFuncCidrSubnet(), 69 "coalesce": interpolationFuncCoalesce(), 70 "coalescelist": interpolationFuncCoalesceList(), 71 "compact": interpolationFuncCompact(), 72 "concat": interpolationFuncConcat(), 73 "dirname": interpolationFuncDirname(), 74 "distinct": interpolationFuncDistinct(), 75 "element": interpolationFuncElement(), 76 "file": interpolationFuncFile(), 77 "matchkeys": interpolationFuncMatchKeys(), 78 "floor": interpolationFuncFloor(), 79 "format": interpolationFuncFormat(), 80 "formatlist": interpolationFuncFormatList(), 81 "index": interpolationFuncIndex(), 82 "join": interpolationFuncJoin(), 83 "jsonencode": interpolationFuncJSONEncode(), 84 "length": interpolationFuncLength(), 85 "list": interpolationFuncList(), 86 "log": interpolationFuncLog(), 87 "lower": interpolationFuncLower(), 88 "map": interpolationFuncMap(), 89 "max": interpolationFuncMax(), 90 "md5": interpolationFuncMd5(), 91 "merge": interpolationFuncMerge(), 92 "min": interpolationFuncMin(), 93 "pathexpand": interpolationFuncPathExpand(), 94 "pow": interpolationFuncPow(), 95 "uuid": interpolationFuncUUID(), 96 "replace": interpolationFuncReplace(), 97 "sha1": interpolationFuncSha1(), 98 "sha256": interpolationFuncSha256(), 99 "sha512": interpolationFuncSha512(), 100 "signum": interpolationFuncSignum(), 101 "slice": interpolationFuncSlice(), 102 "sort": interpolationFuncSort(), 103 "split": interpolationFuncSplit(), 104 "substr": interpolationFuncSubstr(), 105 "timestamp": interpolationFuncTimestamp(), 106 "title": interpolationFuncTitle(), 107 "trimspace": interpolationFuncTrimSpace(), 108 "upper": interpolationFuncUpper(), 109 "zipmap": interpolationFuncZipMap(), 110 } 111 } 112 113 // interpolationFuncList creates a list from the parameters passed 114 // to it. 115 func interpolationFuncList() ast.Function { 116 return ast.Function{ 117 ArgTypes: []ast.Type{}, 118 ReturnType: ast.TypeList, 119 Variadic: true, 120 VariadicType: ast.TypeAny, 121 Callback: func(args []interface{}) (interface{}, error) { 122 var outputList []ast.Variable 123 124 for i, val := range args { 125 switch v := val.(type) { 126 case string: 127 outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v}) 128 case []ast.Variable: 129 outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v}) 130 case map[string]ast.Variable: 131 outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v}) 132 default: 133 return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i) 134 } 135 } 136 137 // we don't support heterogeneous types, so make sure all types match the first 138 if len(outputList) > 0 { 139 firstType := outputList[0].Type 140 for i, v := range outputList[1:] { 141 if v.Type != firstType { 142 return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1) 143 } 144 } 145 } 146 147 return outputList, nil 148 }, 149 } 150 } 151 152 // interpolationFuncMap creates a map from the parameters passed 153 // to it. 154 func interpolationFuncMap() ast.Function { 155 return ast.Function{ 156 ArgTypes: []ast.Type{}, 157 ReturnType: ast.TypeMap, 158 Variadic: true, 159 VariadicType: ast.TypeAny, 160 Callback: func(args []interface{}) (interface{}, error) { 161 outputMap := make(map[string]ast.Variable) 162 163 if len(args)%2 != 0 { 164 return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args)) 165 } 166 167 var firstType *ast.Type 168 for i := 0; i < len(args); i += 2 { 169 key, ok := args[i].(string) 170 if !ok { 171 return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1) 172 } 173 val := args[i+1] 174 variable, err := hil.InterfaceToVariable(val) 175 if err != nil { 176 return nil, err 177 } 178 // Enforce map type homogeneity 179 if firstType == nil { 180 firstType = &variable.Type 181 } else if variable.Type != *firstType { 182 return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable()) 183 } 184 // Check for duplicate keys 185 if _, ok := outputMap[key]; ok { 186 return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) 187 } 188 outputMap[key] = variable 189 } 190 191 return outputMap, nil 192 }, 193 } 194 } 195 196 // interpolationFuncCompact strips a list of multi-variable values 197 // (e.g. as returned by "split") of any empty strings. 198 func interpolationFuncCompact() ast.Function { 199 return ast.Function{ 200 ArgTypes: []ast.Type{ast.TypeList}, 201 ReturnType: ast.TypeList, 202 Variadic: false, 203 Callback: func(args []interface{}) (interface{}, error) { 204 inputList := args[0].([]ast.Variable) 205 206 var outputList []string 207 for _, val := range inputList { 208 strVal, ok := val.Value.(string) 209 if !ok { 210 return nil, fmt.Errorf( 211 "compact() may only be used with flat lists, this list contains elements of %s", 212 val.Type.Printable()) 213 } 214 if strVal == "" { 215 continue 216 } 217 218 outputList = append(outputList, strVal) 219 } 220 return stringSliceToVariableValue(outputList), nil 221 }, 222 } 223 } 224 225 // interpolationFuncCidrHost implements the "cidrhost" function that 226 // fills in the host part of a CIDR range address to create a single 227 // host address 228 func interpolationFuncCidrHost() ast.Function { 229 return ast.Function{ 230 ArgTypes: []ast.Type{ 231 ast.TypeString, // starting CIDR mask 232 ast.TypeInt, // host number to insert 233 }, 234 ReturnType: ast.TypeString, 235 Variadic: false, 236 Callback: func(args []interface{}) (interface{}, error) { 237 hostNum := args[1].(int) 238 _, network, err := net.ParseCIDR(args[0].(string)) 239 if err != nil { 240 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 241 } 242 243 ip, err := cidr.Host(network, hostNum) 244 if err != nil { 245 return nil, err 246 } 247 248 return ip.String(), nil 249 }, 250 } 251 } 252 253 // interpolationFuncCidrNetmask implements the "cidrnetmask" function 254 // that returns the subnet mask in IP address notation. 255 func interpolationFuncCidrNetmask() ast.Function { 256 return ast.Function{ 257 ArgTypes: []ast.Type{ 258 ast.TypeString, // CIDR mask 259 }, 260 ReturnType: ast.TypeString, 261 Variadic: false, 262 Callback: func(args []interface{}) (interface{}, error) { 263 _, network, err := net.ParseCIDR(args[0].(string)) 264 if err != nil { 265 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 266 } 267 268 return net.IP(network.Mask).String(), nil 269 }, 270 } 271 } 272 273 // interpolationFuncCidrSubnet implements the "cidrsubnet" function that 274 // adds an additional subnet of the given length onto an existing 275 // IP block expressed in CIDR notation. 276 func interpolationFuncCidrSubnet() ast.Function { 277 return ast.Function{ 278 ArgTypes: []ast.Type{ 279 ast.TypeString, // starting CIDR mask 280 ast.TypeInt, // number of bits to extend the prefix 281 ast.TypeInt, // network number to append to the prefix 282 }, 283 ReturnType: ast.TypeString, 284 Variadic: false, 285 Callback: func(args []interface{}) (interface{}, error) { 286 extraBits := args[1].(int) 287 subnetNum := args[2].(int) 288 _, network, err := net.ParseCIDR(args[0].(string)) 289 if err != nil { 290 return nil, fmt.Errorf("invalid CIDR expression: %s", err) 291 } 292 293 // For portability with 32-bit systems where the subnet number 294 // will be a 32-bit int, we only allow extension of 32 bits in 295 // one call even if we're running on a 64-bit machine. 296 // (Of course, this is significant only for IPv6.) 297 if extraBits > 32 { 298 return nil, fmt.Errorf("may not extend prefix by more than 32 bits") 299 } 300 301 newNetwork, err := cidr.Subnet(network, extraBits, subnetNum) 302 if err != nil { 303 return nil, err 304 } 305 306 return newNetwork.String(), nil 307 }, 308 } 309 } 310 311 // interpolationFuncCoalesce implements the "coalesce" function that 312 // returns the first non null / empty string from the provided input 313 func interpolationFuncCoalesce() ast.Function { 314 return ast.Function{ 315 ArgTypes: []ast.Type{ast.TypeString}, 316 ReturnType: ast.TypeString, 317 Variadic: true, 318 VariadicType: ast.TypeString, 319 Callback: func(args []interface{}) (interface{}, error) { 320 if len(args) < 2 { 321 return nil, fmt.Errorf("must provide at least two arguments") 322 } 323 for _, arg := range args { 324 argument := arg.(string) 325 326 if argument != "" { 327 return argument, nil 328 } 329 } 330 return "", nil 331 }, 332 } 333 } 334 335 // interpolationFuncCoalesceList implements the "coalescelist" function that 336 // returns the first non empty list from the provided input 337 func interpolationFuncCoalesceList() ast.Function { 338 return ast.Function{ 339 ArgTypes: []ast.Type{ast.TypeList}, 340 ReturnType: ast.TypeList, 341 Variadic: true, 342 VariadicType: ast.TypeList, 343 Callback: func(args []interface{}) (interface{}, error) { 344 if len(args) < 2 { 345 return nil, fmt.Errorf("must provide at least two arguments") 346 } 347 for _, arg := range args { 348 argument := arg.([]ast.Variable) 349 350 if len(argument) > 0 { 351 return argument, nil 352 } 353 } 354 return make([]ast.Variable, 0), nil 355 }, 356 } 357 } 358 359 // interpolationFuncConcat implements the "concat" function that concatenates 360 // multiple lists. 361 func interpolationFuncConcat() ast.Function { 362 return ast.Function{ 363 ArgTypes: []ast.Type{ast.TypeList}, 364 ReturnType: ast.TypeList, 365 Variadic: true, 366 VariadicType: ast.TypeList, 367 Callback: func(args []interface{}) (interface{}, error) { 368 var outputList []ast.Variable 369 370 for _, arg := range args { 371 for _, v := range arg.([]ast.Variable) { 372 switch v.Type { 373 case ast.TypeString: 374 outputList = append(outputList, v) 375 case ast.TypeList: 376 outputList = append(outputList, v) 377 case ast.TypeMap: 378 outputList = append(outputList, v) 379 default: 380 return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable()) 381 } 382 } 383 } 384 385 // we don't support heterogeneous types, so make sure all types match the first 386 if len(outputList) > 0 { 387 firstType := outputList[0].Type 388 for _, v := range outputList[1:] { 389 if v.Type != firstType { 390 return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable()) 391 } 392 } 393 } 394 395 return outputList, nil 396 }, 397 } 398 } 399 400 // interpolationFuncPow returns base x exponential of y. 401 func interpolationFuncPow() ast.Function { 402 return ast.Function{ 403 ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat}, 404 ReturnType: ast.TypeFloat, 405 Callback: func(args []interface{}) (interface{}, error) { 406 return math.Pow(args[0].(float64), args[1].(float64)), nil 407 }, 408 } 409 } 410 411 // interpolationFuncFile implements the "file" function that allows 412 // loading contents from a file. 413 func interpolationFuncFile() ast.Function { 414 return ast.Function{ 415 ArgTypes: []ast.Type{ast.TypeString}, 416 ReturnType: ast.TypeString, 417 Callback: func(args []interface{}) (interface{}, error) { 418 path, err := homedir.Expand(args[0].(string)) 419 if err != nil { 420 return "", err 421 } 422 data, err := ioutil.ReadFile(path) 423 if err != nil { 424 return "", err 425 } 426 427 return string(data), nil 428 }, 429 } 430 } 431 432 // interpolationFuncFormat implements the "format" function that does 433 // string formatting. 434 func interpolationFuncFormat() ast.Function { 435 return ast.Function{ 436 ArgTypes: []ast.Type{ast.TypeString}, 437 Variadic: true, 438 VariadicType: ast.TypeAny, 439 ReturnType: ast.TypeString, 440 Callback: func(args []interface{}) (interface{}, error) { 441 format := args[0].(string) 442 return fmt.Sprintf(format, args[1:]...), nil 443 }, 444 } 445 } 446 447 // interpolationFuncMax returns the maximum of the numeric arguments 448 func interpolationFuncMax() ast.Function { 449 return ast.Function{ 450 ArgTypes: []ast.Type{ast.TypeFloat}, 451 ReturnType: ast.TypeFloat, 452 Variadic: true, 453 VariadicType: ast.TypeFloat, 454 Callback: func(args []interface{}) (interface{}, error) { 455 max := args[0].(float64) 456 457 for i := 1; i < len(args); i++ { 458 max = math.Max(max, args[i].(float64)) 459 } 460 461 return max, nil 462 }, 463 } 464 } 465 466 // interpolationFuncMin returns the minimum of the numeric arguments 467 func interpolationFuncMin() ast.Function { 468 return ast.Function{ 469 ArgTypes: []ast.Type{ast.TypeFloat}, 470 ReturnType: ast.TypeFloat, 471 Variadic: true, 472 VariadicType: ast.TypeFloat, 473 Callback: func(args []interface{}) (interface{}, error) { 474 min := args[0].(float64) 475 476 for i := 1; i < len(args); i++ { 477 min = math.Min(min, args[i].(float64)) 478 } 479 480 return min, nil 481 }, 482 } 483 } 484 485 // interpolationFuncPathExpand will expand any `~`'s found with the full file path 486 func interpolationFuncPathExpand() ast.Function { 487 return ast.Function{ 488 ArgTypes: []ast.Type{ast.TypeString}, 489 ReturnType: ast.TypeString, 490 Callback: func(args []interface{}) (interface{}, error) { 491 return homedir.Expand(args[0].(string)) 492 }, 493 } 494 } 495 496 // interpolationFuncCeil returns the the least integer value greater than or equal to the argument 497 func interpolationFuncCeil() ast.Function { 498 return ast.Function{ 499 ArgTypes: []ast.Type{ast.TypeFloat}, 500 ReturnType: ast.TypeInt, 501 Callback: func(args []interface{}) (interface{}, error) { 502 return int(math.Ceil(args[0].(float64))), nil 503 }, 504 } 505 } 506 507 // interpolationFuncLog returns the logarithnm. 508 func interpolationFuncLog() ast.Function { 509 return ast.Function{ 510 ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat}, 511 ReturnType: ast.TypeFloat, 512 Callback: func(args []interface{}) (interface{}, error) { 513 return math.Log(args[0].(float64)) / math.Log(args[1].(float64)), nil 514 }, 515 } 516 } 517 518 // interpolationFuncChomp removes trailing newlines from the given string 519 func interpolationFuncChomp() ast.Function { 520 newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`) 521 return ast.Function{ 522 ArgTypes: []ast.Type{ast.TypeString}, 523 ReturnType: ast.TypeString, 524 Callback: func(args []interface{}) (interface{}, error) { 525 return newlines.ReplaceAllString(args[0].(string), ""), nil 526 }, 527 } 528 } 529 530 // interpolationFuncFloorreturns returns the greatest integer value less than or equal to the argument 531 func interpolationFuncFloor() ast.Function { 532 return ast.Function{ 533 ArgTypes: []ast.Type{ast.TypeFloat}, 534 ReturnType: ast.TypeInt, 535 Callback: func(args []interface{}) (interface{}, error) { 536 return int(math.Floor(args[0].(float64))), nil 537 }, 538 } 539 } 540 541 func interpolationFuncZipMap() ast.Function { 542 return ast.Function{ 543 ArgTypes: []ast.Type{ 544 ast.TypeList, // Keys 545 ast.TypeList, // Values 546 }, 547 ReturnType: ast.TypeMap, 548 Callback: func(args []interface{}) (interface{}, error) { 549 keys := args[0].([]ast.Variable) 550 values := args[1].([]ast.Variable) 551 552 if len(keys) != len(values) { 553 return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)", 554 len(keys), len(values)) 555 } 556 557 for i, val := range keys { 558 if val.Type != ast.TypeString { 559 return nil, fmt.Errorf("keys must be strings. value at position %d is %s", 560 i, val.Type.Printable()) 561 } 562 } 563 564 result := map[string]ast.Variable{} 565 for i := 0; i < len(keys); i++ { 566 result[keys[i].Value.(string)] = values[i] 567 } 568 569 return result, nil 570 }, 571 } 572 } 573 574 // interpolationFuncFormatList implements the "formatlist" function that does 575 // string formatting on lists. 576 func interpolationFuncFormatList() ast.Function { 577 return ast.Function{ 578 ArgTypes: []ast.Type{ast.TypeAny}, 579 Variadic: true, 580 VariadicType: ast.TypeAny, 581 ReturnType: ast.TypeList, 582 Callback: func(args []interface{}) (interface{}, error) { 583 // Make a copy of the variadic part of args 584 // to avoid modifying the original. 585 varargs := make([]interface{}, len(args)-1) 586 copy(varargs, args[1:]) 587 588 // Verify we have some arguments 589 if len(varargs) == 0 { 590 return nil, fmt.Errorf("no arguments to formatlist") 591 } 592 593 // Convert arguments that are lists into slices. 594 // Confirm along the way that all lists have the same length (n). 595 var n int 596 listSeen := false 597 for i := 1; i < len(args); i++ { 598 s, ok := args[i].([]ast.Variable) 599 if !ok { 600 continue 601 } 602 603 // Mark that we've seen at least one list 604 listSeen = true 605 606 // Convert the ast.Variable to a slice of strings 607 parts, err := listVariableValueToStringSlice(s) 608 if err != nil { 609 return nil, err 610 } 611 612 // otherwise the list is sent down to be indexed 613 varargs[i-1] = parts 614 615 // Check length 616 if n == 0 { 617 // first list we've seen 618 n = len(parts) 619 continue 620 } 621 if n != len(parts) { 622 return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts)) 623 } 624 } 625 626 // If we didn't see a list this is an error because we 627 // can't determine the return value length. 628 if !listSeen { 629 return nil, fmt.Errorf( 630 "formatlist requires at least one list argument") 631 } 632 633 // Do the formatting. 634 format := args[0].(string) 635 636 // Generate a list of formatted strings. 637 list := make([]string, n) 638 fmtargs := make([]interface{}, len(varargs)) 639 for i := 0; i < n; i++ { 640 for j, arg := range varargs { 641 switch arg := arg.(type) { 642 default: 643 fmtargs[j] = arg 644 case []string: 645 fmtargs[j] = arg[i] 646 } 647 } 648 list[i] = fmt.Sprintf(format, fmtargs...) 649 } 650 return stringSliceToVariableValue(list), nil 651 }, 652 } 653 } 654 655 // interpolationFuncIndex implements the "index" function that allows one to 656 // find the index of a specific element in a list 657 func interpolationFuncIndex() ast.Function { 658 return ast.Function{ 659 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 660 ReturnType: ast.TypeInt, 661 Callback: func(args []interface{}) (interface{}, error) { 662 haystack := args[0].([]ast.Variable) 663 needle := args[1].(string) 664 for index, element := range haystack { 665 if needle == element.Value { 666 return index, nil 667 } 668 } 669 return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack) 670 }, 671 } 672 } 673 674 // interpolationFuncBasename implements the "dirname" function. 675 func interpolationFuncDirname() ast.Function { 676 return ast.Function{ 677 ArgTypes: []ast.Type{ast.TypeString}, 678 ReturnType: ast.TypeString, 679 Callback: func(args []interface{}) (interface{}, error) { 680 return filepath.Dir(args[0].(string)), nil 681 }, 682 } 683 } 684 685 // interpolationFuncDistinct implements the "distinct" function that 686 // removes duplicate elements from a list. 687 func interpolationFuncDistinct() ast.Function { 688 return ast.Function{ 689 ArgTypes: []ast.Type{ast.TypeList}, 690 ReturnType: ast.TypeList, 691 Variadic: true, 692 VariadicType: ast.TypeList, 693 Callback: func(args []interface{}) (interface{}, error) { 694 var list []string 695 696 if len(args) != 1 { 697 return nil, fmt.Errorf("accepts only one argument.") 698 } 699 700 if argument, ok := args[0].([]ast.Variable); ok { 701 for _, element := range argument { 702 if element.Type != ast.TypeString { 703 return nil, fmt.Errorf( 704 "only works for flat lists, this list contains elements of %s", 705 element.Type.Printable()) 706 } 707 list = appendIfMissing(list, element.Value.(string)) 708 } 709 } 710 711 return stringSliceToVariableValue(list), nil 712 }, 713 } 714 } 715 716 // helper function to add an element to a list, if it does not already exsit 717 func appendIfMissing(slice []string, element string) []string { 718 for _, ele := range slice { 719 if ele == element { 720 return slice 721 } 722 } 723 return append(slice, element) 724 } 725 726 // for two lists `keys` and `values` of equal length, returns all elements 727 // from `values` where the corresponding element from `keys` is in `searchset`. 728 func interpolationFuncMatchKeys() ast.Function { 729 return ast.Function{ 730 ArgTypes: []ast.Type{ast.TypeList, ast.TypeList, ast.TypeList}, 731 ReturnType: ast.TypeList, 732 Callback: func(args []interface{}) (interface{}, error) { 733 output := make([]ast.Variable, 0) 734 735 values, _ := args[0].([]ast.Variable) 736 keys, _ := args[1].([]ast.Variable) 737 searchset, _ := args[2].([]ast.Variable) 738 739 if len(keys) != len(values) { 740 return nil, fmt.Errorf("length of keys and values should be equal") 741 } 742 743 for i, key := range keys { 744 for _, search := range searchset { 745 if res, err := compareSimpleVariables(key, search); err != nil { 746 return nil, err 747 } else if res == true { 748 output = append(output, values[i]) 749 break 750 } 751 } 752 } 753 // if searchset is empty, then output is an empty list as well. 754 // if we haven't matched any key, then output is an empty list. 755 return output, nil 756 }, 757 } 758 } 759 760 // compare two variables of the same type, i.e. non complex one, such as TypeList or TypeMap 761 func compareSimpleVariables(a, b ast.Variable) (bool, error) { 762 if a.Type != b.Type { 763 return false, fmt.Errorf( 764 "won't compare items of different types %s and %s", 765 a.Type.Printable(), b.Type.Printable()) 766 } 767 switch a.Type { 768 case ast.TypeString: 769 return a.Value.(string) == b.Value.(string), nil 770 default: 771 return false, fmt.Errorf( 772 "can't compare items of type %s", 773 a.Type.Printable()) 774 } 775 } 776 777 // interpolationFuncJoin implements the "join" function that allows 778 // multi-variable values to be joined by some character. 779 func interpolationFuncJoin() ast.Function { 780 return ast.Function{ 781 ArgTypes: []ast.Type{ast.TypeString}, 782 Variadic: true, 783 VariadicType: ast.TypeList, 784 ReturnType: ast.TypeString, 785 Callback: func(args []interface{}) (interface{}, error) { 786 var list []string 787 788 if len(args) < 2 { 789 return nil, fmt.Errorf("not enough arguments to join()") 790 } 791 792 for _, arg := range args[1:] { 793 for _, part := range arg.([]ast.Variable) { 794 if part.Type != ast.TypeString { 795 return nil, fmt.Errorf( 796 "only works on flat lists, this list contains elements of %s", 797 part.Type.Printable()) 798 } 799 list = append(list, part.Value.(string)) 800 } 801 } 802 803 return strings.Join(list, args[0].(string)), nil 804 }, 805 } 806 } 807 808 // interpolationFuncJSONEncode implements the "jsonencode" function that encodes 809 // a string, list, or map as its JSON representation. For now, values in the 810 // list or map may only be strings. 811 func interpolationFuncJSONEncode() ast.Function { 812 return ast.Function{ 813 ArgTypes: []ast.Type{ast.TypeAny}, 814 ReturnType: ast.TypeString, 815 Callback: func(args []interface{}) (interface{}, error) { 816 var toEncode interface{} 817 818 switch typedArg := args[0].(type) { 819 case string: 820 toEncode = typedArg 821 822 case []ast.Variable: 823 // We preallocate the list here. Note that it's important that in 824 // the length 0 case, we have an empty list rather than nil, as 825 // they encode differently. 826 // XXX It would be nice to support arbitrarily nested data here. Is 827 // there an inverse of hil.InterfaceToVariable? 828 strings := make([]string, len(typedArg)) 829 830 for i, v := range typedArg { 831 if v.Type != ast.TypeString { 832 return "", fmt.Errorf("list elements must be strings") 833 } 834 strings[i] = v.Value.(string) 835 } 836 toEncode = strings 837 838 case map[string]ast.Variable: 839 // XXX It would be nice to support arbitrarily nested data here. Is 840 // there an inverse of hil.InterfaceToVariable? 841 stringMap := make(map[string]string) 842 for k, v := range typedArg { 843 if v.Type != ast.TypeString { 844 return "", fmt.Errorf("map values must be strings") 845 } 846 stringMap[k] = v.Value.(string) 847 } 848 toEncode = stringMap 849 850 default: 851 return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0]) 852 } 853 854 jEnc, err := json.Marshal(toEncode) 855 if err != nil { 856 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) 857 } 858 return string(jEnc), nil 859 }, 860 } 861 } 862 863 // interpolationFuncReplace implements the "replace" function that does 864 // string replacement. 865 func interpolationFuncReplace() ast.Function { 866 return ast.Function{ 867 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString}, 868 ReturnType: ast.TypeString, 869 Callback: func(args []interface{}) (interface{}, error) { 870 s := args[0].(string) 871 search := args[1].(string) 872 replace := args[2].(string) 873 874 // We search/replace using a regexp if the string is surrounded 875 // in forward slashes. 876 if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' { 877 re, err := regexp.Compile(search[1 : len(search)-1]) 878 if err != nil { 879 return nil, err 880 } 881 882 return re.ReplaceAllString(s, replace), nil 883 } 884 885 return strings.Replace(s, search, replace, -1), nil 886 }, 887 } 888 } 889 890 func interpolationFuncLength() ast.Function { 891 return ast.Function{ 892 ArgTypes: []ast.Type{ast.TypeAny}, 893 ReturnType: ast.TypeInt, 894 Variadic: false, 895 Callback: func(args []interface{}) (interface{}, error) { 896 subject := args[0] 897 898 switch typedSubject := subject.(type) { 899 case string: 900 return len(typedSubject), nil 901 case []ast.Variable: 902 return len(typedSubject), nil 903 case map[string]ast.Variable: 904 return len(typedSubject), nil 905 } 906 907 return 0, fmt.Errorf("arguments to length() must be a string, list, or map") 908 }, 909 } 910 } 911 912 func interpolationFuncSignum() ast.Function { 913 return ast.Function{ 914 ArgTypes: []ast.Type{ast.TypeInt}, 915 ReturnType: ast.TypeInt, 916 Variadic: false, 917 Callback: func(args []interface{}) (interface{}, error) { 918 num := args[0].(int) 919 switch { 920 case num < 0: 921 return -1, nil 922 case num > 0: 923 return +1, nil 924 default: 925 return 0, nil 926 } 927 }, 928 } 929 } 930 931 // interpolationFuncSlice returns a portion of the input list between from, inclusive and to, exclusive. 932 func interpolationFuncSlice() ast.Function { 933 return ast.Function{ 934 ArgTypes: []ast.Type{ 935 ast.TypeList, // inputList 936 ast.TypeInt, // from 937 ast.TypeInt, // to 938 }, 939 ReturnType: ast.TypeList, 940 Variadic: false, 941 Callback: func(args []interface{}) (interface{}, error) { 942 inputList := args[0].([]ast.Variable) 943 from := args[1].(int) 944 to := args[2].(int) 945 946 if from < 0 { 947 return nil, fmt.Errorf("from index must be >= 0") 948 } 949 if to > len(inputList) { 950 return nil, fmt.Errorf("to index must be <= length of the input list") 951 } 952 if from > to { 953 return nil, fmt.Errorf("from index must be <= to index") 954 } 955 956 var outputList []ast.Variable 957 for i, val := range inputList { 958 if i >= from && i < to { 959 outputList = append(outputList, val) 960 } 961 } 962 return outputList, nil 963 }, 964 } 965 } 966 967 // interpolationFuncSort sorts a list of a strings lexographically 968 func interpolationFuncSort() ast.Function { 969 return ast.Function{ 970 ArgTypes: []ast.Type{ast.TypeList}, 971 ReturnType: ast.TypeList, 972 Variadic: false, 973 Callback: func(args []interface{}) (interface{}, error) { 974 inputList := args[0].([]ast.Variable) 975 976 // Ensure that all the list members are strings and 977 // create a string slice from them 978 members := make([]string, len(inputList)) 979 for i, val := range inputList { 980 if val.Type != ast.TypeString { 981 return nil, fmt.Errorf( 982 "sort() may only be used with lists of strings - %s at index %d", 983 val.Type.String(), i) 984 } 985 986 members[i] = val.Value.(string) 987 } 988 989 sort.Strings(members) 990 return stringSliceToVariableValue(members), nil 991 }, 992 } 993 } 994 995 // interpolationFuncSplit implements the "split" function that allows 996 // strings to split into multi-variable values 997 func interpolationFuncSplit() ast.Function { 998 return ast.Function{ 999 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, 1000 ReturnType: ast.TypeList, 1001 Callback: func(args []interface{}) (interface{}, error) { 1002 sep := args[0].(string) 1003 s := args[1].(string) 1004 elements := strings.Split(s, sep) 1005 return stringSliceToVariableValue(elements), nil 1006 }, 1007 } 1008 } 1009 1010 // interpolationFuncLookup implements the "lookup" function that allows 1011 // dynamic lookups of map types within a Terraform configuration. 1012 func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function { 1013 return ast.Function{ 1014 ArgTypes: []ast.Type{ast.TypeMap, ast.TypeString}, 1015 ReturnType: ast.TypeString, 1016 Variadic: true, 1017 VariadicType: ast.TypeString, 1018 Callback: func(args []interface{}) (interface{}, error) { 1019 defaultValue := "" 1020 defaultValueSet := false 1021 if len(args) > 2 { 1022 defaultValue = args[2].(string) 1023 defaultValueSet = true 1024 } 1025 if len(args) > 3 { 1026 return "", fmt.Errorf("lookup() takes no more than three arguments") 1027 } 1028 index := args[1].(string) 1029 mapVar := args[0].(map[string]ast.Variable) 1030 1031 v, ok := mapVar[index] 1032 if !ok { 1033 if defaultValueSet { 1034 return defaultValue, nil 1035 } else { 1036 return "", fmt.Errorf( 1037 "lookup failed to find '%s'", 1038 args[1].(string)) 1039 } 1040 } 1041 if v.Type != ast.TypeString { 1042 return nil, fmt.Errorf( 1043 "lookup() may only be used with flat maps, this map contains elements of %s", 1044 v.Type.Printable()) 1045 } 1046 1047 return v.Value.(string), nil 1048 }, 1049 } 1050 } 1051 1052 // interpolationFuncElement implements the "element" function that allows 1053 // a specific index to be looked up in a multi-variable value. Note that this will 1054 // wrap if the index is larger than the number of elements in the multi-variable value. 1055 func interpolationFuncElement() ast.Function { 1056 return ast.Function{ 1057 ArgTypes: []ast.Type{ast.TypeList, ast.TypeString}, 1058 ReturnType: ast.TypeString, 1059 Callback: func(args []interface{}) (interface{}, error) { 1060 list := args[0].([]ast.Variable) 1061 if len(list) == 0 { 1062 return nil, fmt.Errorf("element() may not be used with an empty list") 1063 } 1064 1065 index, err := strconv.Atoi(args[1].(string)) 1066 if err != nil || index < 0 { 1067 return "", fmt.Errorf( 1068 "invalid number for index, got %s", args[1]) 1069 } 1070 1071 resolvedIndex := index % len(list) 1072 1073 v := list[resolvedIndex] 1074 if v.Type != ast.TypeString { 1075 return nil, fmt.Errorf( 1076 "element() may only be used with flat lists, this list contains elements of %s", 1077 v.Type.Printable()) 1078 } 1079 return v.Value, nil 1080 }, 1081 } 1082 } 1083 1084 // interpolationFuncKeys implements the "keys" function that yields a list of 1085 // keys of map types within a Terraform configuration. 1086 func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { 1087 return ast.Function{ 1088 ArgTypes: []ast.Type{ast.TypeMap}, 1089 ReturnType: ast.TypeList, 1090 Callback: func(args []interface{}) (interface{}, error) { 1091 mapVar := args[0].(map[string]ast.Variable) 1092 keys := make([]string, 0) 1093 1094 for k, _ := range mapVar { 1095 keys = append(keys, k) 1096 } 1097 1098 sort.Strings(keys) 1099 1100 // Keys are guaranteed to be strings 1101 return stringSliceToVariableValue(keys), nil 1102 }, 1103 } 1104 } 1105 1106 // interpolationFuncValues implements the "values" function that yields a list of 1107 // keys of map types within a Terraform configuration. 1108 func interpolationFuncValues(vs map[string]ast.Variable) ast.Function { 1109 return ast.Function{ 1110 ArgTypes: []ast.Type{ast.TypeMap}, 1111 ReturnType: ast.TypeList, 1112 Callback: func(args []interface{}) (interface{}, error) { 1113 mapVar := args[0].(map[string]ast.Variable) 1114 keys := make([]string, 0) 1115 1116 for k, _ := range mapVar { 1117 keys = append(keys, k) 1118 } 1119 1120 sort.Strings(keys) 1121 1122 values := make([]string, len(keys)) 1123 for index, key := range keys { 1124 if value, ok := mapVar[key].Value.(string); ok { 1125 values[index] = value 1126 } else { 1127 return "", fmt.Errorf("values(): %q has element with bad type %s", 1128 key, mapVar[key].Type) 1129 } 1130 } 1131 1132 variable, err := hil.InterfaceToVariable(values) 1133 if err != nil { 1134 return nil, err 1135 } 1136 1137 return variable.Value, nil 1138 }, 1139 } 1140 } 1141 1142 // interpolationFuncBasename implements the "basename" function. 1143 func interpolationFuncBasename() ast.Function { 1144 return ast.Function{ 1145 ArgTypes: []ast.Type{ast.TypeString}, 1146 ReturnType: ast.TypeString, 1147 Callback: func(args []interface{}) (interface{}, error) { 1148 return filepath.Base(args[0].(string)), nil 1149 }, 1150 } 1151 } 1152 1153 // interpolationFuncBase64Encode implements the "base64encode" function that 1154 // allows Base64 encoding. 1155 func interpolationFuncBase64Encode() ast.Function { 1156 return ast.Function{ 1157 ArgTypes: []ast.Type{ast.TypeString}, 1158 ReturnType: ast.TypeString, 1159 Callback: func(args []interface{}) (interface{}, error) { 1160 s := args[0].(string) 1161 return base64.StdEncoding.EncodeToString([]byte(s)), nil 1162 }, 1163 } 1164 } 1165 1166 // interpolationFuncBase64Decode implements the "base64decode" function that 1167 // allows Base64 decoding. 1168 func interpolationFuncBase64Decode() ast.Function { 1169 return ast.Function{ 1170 ArgTypes: []ast.Type{ast.TypeString}, 1171 ReturnType: ast.TypeString, 1172 Callback: func(args []interface{}) (interface{}, error) { 1173 s := args[0].(string) 1174 sDec, err := base64.StdEncoding.DecodeString(s) 1175 if err != nil { 1176 return "", fmt.Errorf("failed to decode base64 data '%s'", s) 1177 } 1178 return string(sDec), nil 1179 }, 1180 } 1181 } 1182 1183 // interpolationFuncLower implements the "lower" function that does 1184 // string lower casing. 1185 func interpolationFuncLower() ast.Function { 1186 return ast.Function{ 1187 ArgTypes: []ast.Type{ast.TypeString}, 1188 ReturnType: ast.TypeString, 1189 Callback: func(args []interface{}) (interface{}, error) { 1190 toLower := args[0].(string) 1191 return strings.ToLower(toLower), nil 1192 }, 1193 } 1194 } 1195 1196 func interpolationFuncMd5() ast.Function { 1197 return ast.Function{ 1198 ArgTypes: []ast.Type{ast.TypeString}, 1199 ReturnType: ast.TypeString, 1200 Callback: func(args []interface{}) (interface{}, error) { 1201 s := args[0].(string) 1202 h := md5.New() 1203 h.Write([]byte(s)) 1204 hash := hex.EncodeToString(h.Sum(nil)) 1205 return hash, nil 1206 }, 1207 } 1208 } 1209 1210 func interpolationFuncMerge() ast.Function { 1211 return ast.Function{ 1212 ArgTypes: []ast.Type{ast.TypeMap}, 1213 ReturnType: ast.TypeMap, 1214 Variadic: true, 1215 VariadicType: ast.TypeMap, 1216 Callback: func(args []interface{}) (interface{}, error) { 1217 outputMap := make(map[string]ast.Variable) 1218 1219 for _, arg := range args { 1220 for k, v := range arg.(map[string]ast.Variable) { 1221 outputMap[k] = v 1222 } 1223 } 1224 1225 return outputMap, nil 1226 }, 1227 } 1228 } 1229 1230 // interpolationFuncUpper implements the "upper" function that does 1231 // string upper casing. 1232 func interpolationFuncUpper() ast.Function { 1233 return ast.Function{ 1234 ArgTypes: []ast.Type{ast.TypeString}, 1235 ReturnType: ast.TypeString, 1236 Callback: func(args []interface{}) (interface{}, error) { 1237 toUpper := args[0].(string) 1238 return strings.ToUpper(toUpper), nil 1239 }, 1240 } 1241 } 1242 1243 func interpolationFuncSha1() ast.Function { 1244 return ast.Function{ 1245 ArgTypes: []ast.Type{ast.TypeString}, 1246 ReturnType: ast.TypeString, 1247 Callback: func(args []interface{}) (interface{}, error) { 1248 s := args[0].(string) 1249 h := sha1.New() 1250 h.Write([]byte(s)) 1251 hash := hex.EncodeToString(h.Sum(nil)) 1252 return hash, nil 1253 }, 1254 } 1255 } 1256 1257 // hexadecimal representation of sha256 sum 1258 func interpolationFuncSha256() ast.Function { 1259 return ast.Function{ 1260 ArgTypes: []ast.Type{ast.TypeString}, 1261 ReturnType: ast.TypeString, 1262 Callback: func(args []interface{}) (interface{}, error) { 1263 s := args[0].(string) 1264 h := sha256.New() 1265 h.Write([]byte(s)) 1266 hash := hex.EncodeToString(h.Sum(nil)) 1267 return hash, nil 1268 }, 1269 } 1270 } 1271 1272 func interpolationFuncSha512() ast.Function { 1273 return ast.Function{ 1274 ArgTypes: []ast.Type{ast.TypeString}, 1275 ReturnType: ast.TypeString, 1276 Callback: func(args []interface{}) (interface{}, error) { 1277 s := args[0].(string) 1278 h := sha512.New() 1279 h.Write([]byte(s)) 1280 hash := hex.EncodeToString(h.Sum(nil)) 1281 return hash, nil 1282 }, 1283 } 1284 } 1285 1286 func interpolationFuncTrimSpace() ast.Function { 1287 return ast.Function{ 1288 ArgTypes: []ast.Type{ast.TypeString}, 1289 ReturnType: ast.TypeString, 1290 Callback: func(args []interface{}) (interface{}, error) { 1291 trimSpace := args[0].(string) 1292 return strings.TrimSpace(trimSpace), nil 1293 }, 1294 } 1295 } 1296 1297 func interpolationFuncBase64Sha256() ast.Function { 1298 return ast.Function{ 1299 ArgTypes: []ast.Type{ast.TypeString}, 1300 ReturnType: ast.TypeString, 1301 Callback: func(args []interface{}) (interface{}, error) { 1302 s := args[0].(string) 1303 h := sha256.New() 1304 h.Write([]byte(s)) 1305 shaSum := h.Sum(nil) 1306 encoded := base64.StdEncoding.EncodeToString(shaSum[:]) 1307 return encoded, nil 1308 }, 1309 } 1310 } 1311 1312 func interpolationFuncBase64Sha512() ast.Function { 1313 return ast.Function{ 1314 ArgTypes: []ast.Type{ast.TypeString}, 1315 ReturnType: ast.TypeString, 1316 Callback: func(args []interface{}) (interface{}, error) { 1317 s := args[0].(string) 1318 h := sha512.New() 1319 h.Write([]byte(s)) 1320 shaSum := h.Sum(nil) 1321 encoded := base64.StdEncoding.EncodeToString(shaSum[:]) 1322 return encoded, nil 1323 }, 1324 } 1325 } 1326 1327 func interpolationFuncBcrypt() ast.Function { 1328 return ast.Function{ 1329 ArgTypes: []ast.Type{ast.TypeString}, 1330 Variadic: true, 1331 VariadicType: ast.TypeString, 1332 ReturnType: ast.TypeString, 1333 Callback: func(args []interface{}) (interface{}, error) { 1334 defaultCost := 10 1335 1336 if len(args) > 1 { 1337 costStr := args[1].(string) 1338 cost, err := strconv.Atoi(costStr) 1339 if err != nil { 1340 return "", err 1341 } 1342 1343 defaultCost = cost 1344 } 1345 1346 if len(args) > 2 { 1347 return "", fmt.Errorf("bcrypt() takes no more than two arguments") 1348 } 1349 1350 input := args[0].(string) 1351 out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost) 1352 if err != nil { 1353 return "", fmt.Errorf("error occured generating password %s", err.Error()) 1354 } 1355 1356 return string(out), nil 1357 }, 1358 } 1359 } 1360 1361 func interpolationFuncUUID() ast.Function { 1362 return ast.Function{ 1363 ArgTypes: []ast.Type{}, 1364 ReturnType: ast.TypeString, 1365 Callback: func(args []interface{}) (interface{}, error) { 1366 return uuid.GenerateUUID() 1367 }, 1368 } 1369 } 1370 1371 // interpolationFuncTimestamp 1372 func interpolationFuncTimestamp() ast.Function { 1373 return ast.Function{ 1374 ArgTypes: []ast.Type{}, 1375 ReturnType: ast.TypeString, 1376 Callback: func(args []interface{}) (interface{}, error) { 1377 return time.Now().UTC().Format(time.RFC3339), nil 1378 }, 1379 } 1380 } 1381 1382 // interpolationFuncTitle implements the "title" function that returns a copy of the 1383 // string in which first characters of all the words are capitalized. 1384 func interpolationFuncTitle() ast.Function { 1385 return ast.Function{ 1386 ArgTypes: []ast.Type{ast.TypeString}, 1387 ReturnType: ast.TypeString, 1388 Callback: func(args []interface{}) (interface{}, error) { 1389 toTitle := args[0].(string) 1390 return strings.Title(toTitle), nil 1391 }, 1392 } 1393 } 1394 1395 // interpolationFuncSubstr implements the "substr" function that allows strings 1396 // to be truncated. 1397 func interpolationFuncSubstr() ast.Function { 1398 return ast.Function{ 1399 ArgTypes: []ast.Type{ 1400 ast.TypeString, // input string 1401 ast.TypeInt, // offset 1402 ast.TypeInt, // length 1403 }, 1404 ReturnType: ast.TypeString, 1405 Callback: func(args []interface{}) (interface{}, error) { 1406 str := args[0].(string) 1407 offset := args[1].(int) 1408 length := args[2].(int) 1409 1410 // Interpret a negative offset as being equivalent to a positive 1411 // offset taken from the end of the string. 1412 if offset < 0 { 1413 offset += len(str) 1414 } 1415 1416 // Interpret a length of `-1` as indicating that the substring 1417 // should start at `offset` and continue until the end of the 1418 // string. Any other negative length (other than `-1`) is invalid. 1419 if length == -1 { 1420 length = len(str) 1421 } else if length >= 0 { 1422 length += offset 1423 } else { 1424 return nil, fmt.Errorf("length should be a non-negative integer") 1425 } 1426 1427 if offset > len(str) { 1428 return nil, fmt.Errorf("offset cannot be larger than the length of the string") 1429 } 1430 1431 if length > len(str) { 1432 return nil, fmt.Errorf("'offset + length' cannot be larger than the length of the string") 1433 } 1434 1435 return str[offset:length], nil 1436 }, 1437 } 1438 }