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