]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / interpolate_funcs.go
CommitLineData
bae9f6d2
JC
1package config
2
3import (
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"
9b12e4fe 27 "golang.org/x/crypto/bcrypt"
bae9f6d2
JC
28)
29
30// stringSliceToVariableValue converts a string slice into the value
31// required to be returned from interpolation functions which return
32// TypeList.
33func 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
44func 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.
56func 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(),
9b12e4fe 63 "bcrypt": interpolationFuncBcrypt(),
bae9f6d2
JC
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(),
c680a8e1 73 "contains": interpolationFuncContains(),
bae9f6d2
JC
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(),
9b12e4fe 95 "pow": interpolationFuncPow(),
bae9f6d2
JC
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.
116func 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.
155func 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.
199func 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
229func 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.
256func 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.
277func 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
314func 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
338func 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
c680a8e1
RS
360// interpolationFuncContains returns true if an element is in the list
361// and return false otherwise
362func 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
bae9f6d2
JC
376// interpolationFuncConcat implements the "concat" function that concatenates
377// multiple lists.
378func 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
9b12e4fe
JC
417// interpolationFuncPow returns base x exponential of y.
418func 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
bae9f6d2
JC
428// interpolationFuncFile implements the "file" function that allows
429// loading contents from a file.
430func 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.
451func 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
465func 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
484func 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
503func 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
514func 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.
525func 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
536func 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
548func 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
558func 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.
593func 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
674func 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.
692func 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.
704func 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
734func 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`.
745func 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
778func 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.
796func 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.
828func 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.
882func 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
907func 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
929func 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.
949func 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
985func 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
1014func 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.
1029func 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.
1072func 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.
1103func 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.
1125func 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.
1160func 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.
1172func 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.
1185func 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.
1202func 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
1213func 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
1227func 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.
1249func 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
1260func 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
1275func 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
1289func 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
1303func 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
1314func 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
1329func 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
9b12e4fe
JC
1344func 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
bae9f6d2
JC
1378func 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
1389func 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.
1401func 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.
1414func 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}