]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
7b7b3f2620bcb9818ce2a5fe186c66aa248a17eb
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / interpolate_funcs.go
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 }