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