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