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