2014-07-21 14:56:03 -05:00
|
|
|
package config
|
|
|
|
|
2014-07-21 15:12:43 -05:00
|
|
|
import (
|
2015-11-11 02:51:30 -06:00
|
|
|
"bytes"
|
|
|
|
"compress/gzip"
|
2016-02-23 05:29:11 -06:00
|
|
|
"crypto/md5"
|
2017-12-05 13:06:32 -06:00
|
|
|
"crypto/rsa"
|
2015-12-27 16:28:56 -06:00
|
|
|
"crypto/sha1"
|
2016-01-16 17:54:04 -06:00
|
|
|
"crypto/sha256"
|
2017-05-02 18:35:23 -05:00
|
|
|
"crypto/sha512"
|
2017-12-05 13:06:32 -06:00
|
|
|
"crypto/x509"
|
2015-09-25 02:23:36 -05:00
|
|
|
"encoding/base64"
|
2015-12-27 16:28:56 -06:00
|
|
|
"encoding/hex"
|
2016-03-28 20:02:06 -05:00
|
|
|
"encoding/json"
|
2017-12-05 13:06:32 -06:00
|
|
|
"encoding/pem"
|
2014-07-21 15:12:43 -05:00
|
|
|
"fmt"
|
2014-07-22 20:35:36 -05:00
|
|
|
"io/ioutil"
|
2016-10-28 12:49:31 -05:00
|
|
|
"math"
|
2015-10-22 10:10:42 -05:00
|
|
|
"net"
|
2017-08-22 13:26:09 -05:00
|
|
|
"net/url"
|
2017-03-26 20:21:48 -05:00
|
|
|
"path/filepath"
|
2015-03-02 11:37:40 -06:00
|
|
|
"regexp"
|
2015-06-02 16:48:38 -05:00
|
|
|
"sort"
|
2014-11-07 12:23:02 -06:00
|
|
|
"strconv"
|
2014-07-21 15:12:43 -05:00
|
|
|
"strings"
|
2016-12-01 17:02:39 -06:00
|
|
|
"time"
|
2015-01-13 13:50:44 -06:00
|
|
|
|
2015-10-22 10:10:42 -05:00
|
|
|
"github.com/apparentlymart/go-cidr/cidr"
|
2016-03-21 15:14:30 -05:00
|
|
|
"github.com/hashicorp/go-uuid"
|
2016-04-11 12:40:06 -05:00
|
|
|
"github.com/hashicorp/hil"
|
2016-01-31 01:38:37 -06:00
|
|
|
"github.com/hashicorp/hil/ast"
|
2015-03-30 18:52:39 -05:00
|
|
|
"github.com/mitchellh/go-homedir"
|
2017-05-22 05:02:32 -05:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2014-07-21 15:12:43 -05:00
|
|
|
)
|
|
|
|
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
// stringSliceToVariableValue converts a string slice into the value
|
|
|
|
// required to be returned from interpolation functions which return
|
|
|
|
// TypeList.
|
|
|
|
func stringSliceToVariableValue(values []string) []ast.Variable {
|
|
|
|
output := make([]ast.Variable, len(values))
|
|
|
|
for index, value := range values {
|
|
|
|
output[index] = ast.Variable{
|
|
|
|
Type: ast.TypeString,
|
|
|
|
Value: value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
|
|
|
func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
|
|
|
|
output := make([]string, len(values))
|
|
|
|
for index, value := range values {
|
|
|
|
if value.Type != ast.TypeString {
|
|
|
|
return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String())
|
|
|
|
}
|
|
|
|
output[index] = value.Value.(string)
|
|
|
|
}
|
|
|
|
return output, nil
|
|
|
|
}
|
|
|
|
|
2014-07-21 14:56:03 -05:00
|
|
|
// Funcs is the mapping of built-in functions for configuration.
|
2016-01-15 15:28:47 -06:00
|
|
|
func Funcs() map[string]ast.Function {
|
|
|
|
return map[string]ast.Function{
|
2017-09-25 16:19:18 -05:00
|
|
|
"abs": interpolationFuncAbs(),
|
2017-03-26 20:21:48 -05:00
|
|
|
"basename": interpolationFuncBasename(),
|
2016-02-24 03:16:34 -06:00
|
|
|
"base64decode": interpolationFuncBase64Decode(),
|
|
|
|
"base64encode": interpolationFuncBase64Encode(),
|
2015-11-11 02:51:30 -06:00
|
|
|
"base64gzip": interpolationFuncBase64Gzip(),
|
2016-01-29 06:09:57 -06:00
|
|
|
"base64sha256": interpolationFuncBase64Sha256(),
|
2017-05-02 18:35:23 -05:00
|
|
|
"base64sha512": interpolationFuncBase64Sha512(),
|
2017-05-22 05:02:32 -05:00
|
|
|
"bcrypt": interpolationFuncBcrypt(),
|
2016-10-28 12:49:31 -05:00
|
|
|
"ceil": interpolationFuncCeil(),
|
2017-04-06 06:14:09 -05:00
|
|
|
"chomp": interpolationFuncChomp(),
|
2015-10-22 10:10:42 -05:00
|
|
|
"cidrhost": interpolationFuncCidrHost(),
|
|
|
|
"cidrnetmask": interpolationFuncCidrNetmask(),
|
|
|
|
"cidrsubnet": interpolationFuncCidrSubnet(),
|
2015-11-08 00:34:56 -06:00
|
|
|
"coalesce": interpolationFuncCoalesce(),
|
2017-03-08 14:36:01 -06:00
|
|
|
"coalescelist": interpolationFuncCoalesceList(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"compact": interpolationFuncCompact(),
|
|
|
|
"concat": interpolationFuncConcat(),
|
2017-06-16 13:38:01 -05:00
|
|
|
"contains": interpolationFuncContains(),
|
2017-03-26 20:21:48 -05:00
|
|
|
"dirname": interpolationFuncDirname(),
|
2016-06-15 06:24:33 -05:00
|
|
|
"distinct": interpolationFuncDistinct(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"element": interpolationFuncElement(),
|
2017-10-10 13:56:13 -05:00
|
|
|
"chunklist": interpolationFuncChunklist(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"file": interpolationFuncFile(),
|
2017-04-21 12:04:49 -05:00
|
|
|
"matchkeys": interpolationFuncMatchKeys(),
|
2017-06-13 14:27:45 -05:00
|
|
|
"flatten": interpolationFuncFlatten(),
|
2016-10-28 12:49:31 -05:00
|
|
|
"floor": interpolationFuncFloor(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"format": interpolationFuncFormat(),
|
|
|
|
"formatlist": interpolationFuncFormatList(),
|
2017-06-15 15:47:54 -05:00
|
|
|
"indent": interpolationFuncIndent(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"index": interpolationFuncIndex(),
|
|
|
|
"join": interpolationFuncJoin(),
|
2016-03-28 20:02:06 -05:00
|
|
|
"jsonencode": interpolationFuncJSONEncode(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"length": interpolationFuncLength(),
|
2016-07-07 07:19:41 -05:00
|
|
|
"list": interpolationFuncList(),
|
2017-04-25 16:41:33 -05:00
|
|
|
"log": interpolationFuncLog(),
|
2015-10-19 20:49:51 -05:00
|
|
|
"lower": interpolationFuncLower(),
|
config: Add map() interpolation function
* `map(key, value, ...)` - Returns a map consisting of the key/value pairs
specified as arguments. Every odd argument must be a string key, and every
even argument must have the same type as the other values specified.
Duplicate keys are not allowed. Examples:
* `map("hello", "world")`
* `map("us-east", list("a", "b", "c"), "us-west", list("b", "c", "d"))`
2016-07-27 12:42:46 -05:00
|
|
|
"map": interpolationFuncMap(),
|
2016-10-28 12:49:31 -05:00
|
|
|
"max": interpolationFuncMax(),
|
2016-02-23 05:29:11 -06:00
|
|
|
"md5": interpolationFuncMd5(),
|
2016-08-01 17:10:53 -05:00
|
|
|
"merge": interpolationFuncMerge(),
|
2016-10-28 12:49:31 -05:00
|
|
|
"min": interpolationFuncMin(),
|
2017-01-18 15:11:19 -06:00
|
|
|
"pathexpand": interpolationFuncPathExpand(),
|
2017-05-17 16:46:33 -05:00
|
|
|
"pow": interpolationFuncPow(),
|
2016-03-21 15:14:30 -05:00
|
|
|
"uuid": interpolationFuncUUID(),
|
2015-10-10 17:17:25 -05:00
|
|
|
"replace": interpolationFuncReplace(),
|
2017-12-05 13:06:32 -06:00
|
|
|
"rsadecrypt": interpolationFuncRsaDecrypt(),
|
2015-12-27 16:28:56 -06:00
|
|
|
"sha1": interpolationFuncSha1(),
|
2016-01-16 17:54:04 -06:00
|
|
|
"sha256": interpolationFuncSha256(),
|
2017-05-02 18:35:23 -05:00
|
|
|
"sha512": interpolationFuncSha512(),
|
2016-02-24 03:16:34 -06:00
|
|
|
"signum": interpolationFuncSignum(),
|
2017-02-13 15:20:02 -06:00
|
|
|
"slice": interpolationFuncSlice(),
|
2016-06-11 11:36:29 -05:00
|
|
|
"sort": interpolationFuncSort(),
|
2016-02-24 03:16:34 -06:00
|
|
|
"split": interpolationFuncSplit(),
|
2017-03-22 10:30:39 -05:00
|
|
|
"substr": interpolationFuncSubstr(),
|
2016-12-01 13:51:01 -06:00
|
|
|
"timestamp": interpolationFuncTimestamp(),
|
2017-12-05 12:46:12 -06:00
|
|
|
"timeadd": interpolationFuncTimeAdd(),
|
2016-10-26 07:21:32 -05:00
|
|
|
"title": interpolationFuncTitle(),
|
2017-09-01 23:19:47 -05:00
|
|
|
"transpose": interpolationFuncTranspose(),
|
2016-01-30 03:51:28 -06:00
|
|
|
"trimspace": interpolationFuncTrimSpace(),
|
2015-10-19 20:49:51 -05:00
|
|
|
"upper": interpolationFuncUpper(),
|
2017-08-22 13:26:09 -05:00
|
|
|
"urlencode": interpolationFuncURLEncode(),
|
2016-10-26 09:33:54 -05:00
|
|
|
"zipmap": interpolationFuncZipMap(),
|
2014-07-21 14:56:03 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-07 07:19:41 -05:00
|
|
|
// interpolationFuncList creates a list from the parameters passed
|
|
|
|
// to it.
|
|
|
|
func interpolationFuncList() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Variadic: true,
|
2016-07-19 12:39:40 -05:00
|
|
|
VariadicType: ast.TypeAny,
|
2016-07-07 07:19:41 -05:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-07-19 12:39:40 -05:00
|
|
|
var outputList []ast.Variable
|
2016-07-07 07:19:41 -05:00
|
|
|
|
2016-07-19 12:39:40 -05:00
|
|
|
for i, val := range args {
|
|
|
|
switch v := val.(type) {
|
|
|
|
case string:
|
|
|
|
outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v})
|
|
|
|
case []ast.Variable:
|
|
|
|
outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v})
|
|
|
|
case map[string]ast.Variable:
|
|
|
|
outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v})
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i)
|
|
|
|
}
|
2016-07-07 07:19:41 -05:00
|
|
|
}
|
|
|
|
|
2016-07-19 12:39:40 -05:00
|
|
|
// we don't support heterogeneous types, so make sure all types match the first
|
|
|
|
if len(outputList) > 0 {
|
|
|
|
firstType := outputList[0].Type
|
|
|
|
for i, v := range outputList[1:] {
|
|
|
|
if v.Type != firstType {
|
|
|
|
return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return outputList, nil
|
2016-07-07 07:19:41 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
config: Add map() interpolation function
* `map(key, value, ...)` - Returns a map consisting of the key/value pairs
specified as arguments. Every odd argument must be a string key, and every
even argument must have the same type as the other values specified.
Duplicate keys are not allowed. Examples:
* `map("hello", "world")`
* `map("us-east", list("a", "b", "c"), "us-west", list("b", "c", "d"))`
2016-07-27 12:42:46 -05:00
|
|
|
// interpolationFuncMap creates a map from the parameters passed
|
|
|
|
// to it.
|
|
|
|
func interpolationFuncMap() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{},
|
|
|
|
ReturnType: ast.TypeMap,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeAny,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
outputMap := make(map[string]ast.Variable)
|
|
|
|
|
|
|
|
if len(args)%2 != 0 {
|
|
|
|
return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args))
|
|
|
|
}
|
|
|
|
|
|
|
|
var firstType *ast.Type
|
|
|
|
for i := 0; i < len(args); i += 2 {
|
|
|
|
key, ok := args[i].(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1)
|
|
|
|
}
|
|
|
|
val := args[i+1]
|
|
|
|
variable, err := hil.InterfaceToVariable(val)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Enforce map type homogeneity
|
|
|
|
if firstType == nil {
|
|
|
|
firstType = &variable.Type
|
|
|
|
} else if variable.Type != *firstType {
|
|
|
|
return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable())
|
|
|
|
}
|
|
|
|
// Check for duplicate keys
|
|
|
|
if _, ok := outputMap[key]; ok {
|
|
|
|
return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key)
|
|
|
|
}
|
|
|
|
outputMap[key] = variable
|
|
|
|
}
|
|
|
|
|
|
|
|
return outputMap, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-16 04:08:58 -05:00
|
|
|
// interpolationFuncCompact strips a list of multi-variable values
|
|
|
|
// (e.g. as returned by "split") of any empty strings.
|
|
|
|
func interpolationFuncCompact() ast.Function {
|
|
|
|
return ast.Function{
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeList},
|
|
|
|
ReturnType: ast.TypeList,
|
2015-09-16 04:08:58 -05:00
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
inputList := args[0].([]ast.Variable)
|
|
|
|
|
|
|
|
var outputList []string
|
|
|
|
for _, val := range inputList {
|
2016-07-27 12:47:44 -05:00
|
|
|
strVal, ok := val.Value.(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"compact() may only be used with flat lists, this list contains elements of %s",
|
|
|
|
val.Type.Printable())
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
}
|
2016-07-27 12:47:44 -05:00
|
|
|
if strVal == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
outputList = append(outputList, strVal)
|
2015-09-16 04:08:58 -05:00
|
|
|
}
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
return stringSliceToVariableValue(outputList), nil
|
2015-09-16 04:08:58 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-22 10:10:42 -05:00
|
|
|
// interpolationFuncCidrHost implements the "cidrhost" function that
|
|
|
|
// fills in the host part of a CIDR range address to create a single
|
|
|
|
// host address
|
|
|
|
func interpolationFuncCidrHost() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeString, // starting CIDR mask
|
|
|
|
ast.TypeInt, // host number to insert
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
hostNum := args[1].(int)
|
|
|
|
_, network, err := net.ParseCIDR(args[0].(string))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid CIDR expression: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ip, err := cidr.Host(network, hostNum)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ip.String(), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// interpolationFuncCidrNetmask implements the "cidrnetmask" function
|
|
|
|
// that returns the subnet mask in IP address notation.
|
|
|
|
func interpolationFuncCidrNetmask() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeString, // CIDR mask
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
_, network, err := net.ParseCIDR(args[0].(string))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid CIDR expression: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return net.IP(network.Mask).String(), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// interpolationFuncCidrSubnet implements the "cidrsubnet" function that
|
|
|
|
// adds an additional subnet of the given length onto an existing
|
|
|
|
// IP block expressed in CIDR notation.
|
|
|
|
func interpolationFuncCidrSubnet() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeString, // starting CIDR mask
|
|
|
|
ast.TypeInt, // number of bits to extend the prefix
|
|
|
|
ast.TypeInt, // network number to append to the prefix
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
extraBits := args[1].(int)
|
|
|
|
subnetNum := args[2].(int)
|
|
|
|
_, network, err := net.ParseCIDR(args[0].(string))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid CIDR expression: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// For portability with 32-bit systems where the subnet number
|
|
|
|
// will be a 32-bit int, we only allow extension of 32 bits in
|
|
|
|
// one call even if we're running on a 64-bit machine.
|
|
|
|
// (Of course, this is significant only for IPv6.)
|
|
|
|
if extraBits > 32 {
|
|
|
|
return nil, fmt.Errorf("may not extend prefix by more than 32 bits")
|
|
|
|
}
|
|
|
|
|
|
|
|
newNetwork, err := cidr.Subnet(network, extraBits, subnetNum)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return newNetwork.String(), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-08 00:34:56 -06:00
|
|
|
// interpolationFuncCoalesce implements the "coalesce" function that
|
|
|
|
// returns the first non null / empty string from the provided input
|
|
|
|
func interpolationFuncCoalesce() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
if len(args) < 2 {
|
|
|
|
return nil, fmt.Errorf("must provide at least two arguments")
|
|
|
|
}
|
|
|
|
for _, arg := range args {
|
|
|
|
argument := arg.(string)
|
|
|
|
|
|
|
|
if argument != "" {
|
|
|
|
return argument, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 14:36:01 -06:00
|
|
|
// interpolationFuncCoalesceList implements the "coalescelist" function that
|
|
|
|
// returns the first non empty list from the provided input
|
|
|
|
func interpolationFuncCoalesceList() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeList},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeList,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
if len(args) < 2 {
|
|
|
|
return nil, fmt.Errorf("must provide at least two arguments")
|
|
|
|
}
|
|
|
|
for _, arg := range args {
|
|
|
|
argument := arg.([]ast.Variable)
|
|
|
|
|
|
|
|
if len(argument) > 0 {
|
|
|
|
return argument, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return make([]ast.Variable, 0), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 13:38:01 -05:00
|
|
|
// interpolationFuncContains returns true if an element is in the list
|
|
|
|
// and return false otherwise
|
|
|
|
func interpolationFuncContains() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeList, ast.TypeString},
|
|
|
|
ReturnType: ast.TypeBool,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
_, err := interpolationFuncIndex().Callback(args)
|
|
|
|
if err != nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-19 16:21:14 -05:00
|
|
|
// interpolationFuncConcat implements the "concat" function that concatenates
|
|
|
|
// multiple lists.
|
2015-01-15 00:01:42 -06:00
|
|
|
func interpolationFuncConcat() ast.Function {
|
|
|
|
return ast.Function{
|
2016-08-01 14:24:18 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeList},
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ReturnType: ast.TypeList,
|
2015-01-13 14:47:54 -06:00
|
|
|
Variadic: true,
|
2016-08-01 14:24:18 -05:00
|
|
|
VariadicType: ast.TypeList,
|
2015-01-13 14:47:54 -06:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-07-19 16:21:14 -05:00
|
|
|
var outputList []ast.Variable
|
2015-05-03 09:53:33 -05:00
|
|
|
|
|
|
|
for _, arg := range args {
|
2016-08-01 14:24:18 -05:00
|
|
|
for _, v := range arg.([]ast.Variable) {
|
|
|
|
switch v.Type {
|
|
|
|
case ast.TypeString:
|
|
|
|
outputList = append(outputList, v)
|
|
|
|
case ast.TypeList:
|
|
|
|
outputList = append(outputList, v)
|
|
|
|
case ast.TypeMap:
|
|
|
|
outputList = append(outputList, v)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable())
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
}
|
2015-05-03 09:53:33 -05:00
|
|
|
}
|
2016-07-19 16:21:14 -05:00
|
|
|
}
|
2015-05-03 09:53:33 -05:00
|
|
|
|
2016-07-19 16:21:14 -05:00
|
|
|
// we don't support heterogeneous types, so make sure all types match the first
|
|
|
|
if len(outputList) > 0 {
|
|
|
|
firstType := outputList[0].Type
|
|
|
|
for _, v := range outputList[1:] {
|
|
|
|
if v.Type != firstType {
|
|
|
|
return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable())
|
|
|
|
}
|
|
|
|
}
|
2015-05-03 09:53:33 -05:00
|
|
|
}
|
|
|
|
|
2016-07-19 16:21:14 -05:00
|
|
|
return outputList, nil
|
2015-01-13 14:47:54 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 16:46:33 -05:00
|
|
|
// interpolationFuncPow returns base x exponential of y.
|
|
|
|
func interpolationFuncPow() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeFloat,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return math.Pow(args[0].(float64), args[1].(float64)), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-22 20:35:36 -05:00
|
|
|
// interpolationFuncFile implements the "file" function that allows
|
|
|
|
// loading contents from a file.
|
2015-01-15 00:01:42 -06:00
|
|
|
func interpolationFuncFile() ast.Function {
|
|
|
|
return ast.Function{
|
2015-01-13 13:50:44 -06:00
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2015-03-30 18:52:39 -05:00
|
|
|
path, err := homedir.Expand(args[0].(string))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
data, err := ioutil.ReadFile(path)
|
2015-01-13 13:50:44 -06:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(data), nil
|
|
|
|
},
|
2014-07-22 20:35:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:29:50 -05:00
|
|
|
// interpolationFuncFormat implements the "format" function that does
|
|
|
|
// string formatting.
|
2015-03-02 12:26:06 -06:00
|
|
|
func interpolationFuncFormat() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeAny,
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
format := args[0].(string)
|
|
|
|
return fmt.Sprintf(format, args[1:]...), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 12:49:31 -05:00
|
|
|
// interpolationFuncMax returns the maximum of the numeric arguments
|
|
|
|
func interpolationFuncMax() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeFloat,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeFloat,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
max := args[0].(float64)
|
|
|
|
|
|
|
|
for i := 1; i < len(args); i++ {
|
|
|
|
max = math.Max(max, args[i].(float64))
|
|
|
|
}
|
|
|
|
|
|
|
|
return max, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// interpolationFuncMin returns the minimum of the numeric arguments
|
|
|
|
func interpolationFuncMin() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeFloat,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeFloat,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
min := args[0].(float64)
|
|
|
|
|
|
|
|
for i := 1; i < len(args); i++ {
|
|
|
|
min = math.Min(min, args[i].(float64))
|
|
|
|
}
|
|
|
|
|
|
|
|
return min, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 15:11:19 -06:00
|
|
|
// interpolationFuncPathExpand will expand any `~`'s found with the full file path
|
|
|
|
func interpolationFuncPathExpand() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return homedir.Expand(args[0].(string))
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 12:49:31 -05:00
|
|
|
// interpolationFuncCeil returns the the least integer value greater than or equal to the argument
|
|
|
|
func interpolationFuncCeil() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeInt,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return int(math.Ceil(args[0].(float64))), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-25 16:41:33 -05:00
|
|
|
// interpolationFuncLog returns the logarithnm.
|
|
|
|
func interpolationFuncLog() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat, ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeFloat,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return math.Log(args[0].(float64)) / math.Log(args[1].(float64)), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-06 06:14:09 -05:00
|
|
|
// interpolationFuncChomp removes trailing newlines from the given string
|
|
|
|
func interpolationFuncChomp() ast.Function {
|
2017-04-07 03:41:55 -05:00
|
|
|
newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`)
|
2017-04-06 06:14:09 -05:00
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2017-04-07 03:41:55 -05:00
|
|
|
return newlines.ReplaceAllString(args[0].(string), ""), nil
|
2017-04-06 06:14:09 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 12:49:31 -05:00
|
|
|
// interpolationFuncFloorreturns returns the greatest integer value less than or equal to the argument
|
|
|
|
func interpolationFuncFloor() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeInt,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return int(math.Floor(args[0].(float64))), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 09:33:54 -05:00
|
|
|
func interpolationFuncZipMap() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeList, // Keys
|
|
|
|
ast.TypeList, // Values
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeMap,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
keys := args[0].([]ast.Variable)
|
|
|
|
values := args[1].([]ast.Variable)
|
|
|
|
|
|
|
|
if len(keys) != len(values) {
|
|
|
|
return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)",
|
|
|
|
len(keys), len(values))
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, val := range keys {
|
|
|
|
if val.Type != ast.TypeString {
|
|
|
|
return nil, fmt.Errorf("keys must be strings. value at position %d is %s",
|
|
|
|
i, val.Type.Printable())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result := map[string]ast.Variable{}
|
|
|
|
for i := 0; i < len(keys); i++ {
|
|
|
|
result[keys[i].Value.(string)] = values[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:34:40 -05:00
|
|
|
// interpolationFuncFormatList implements the "formatlist" function that does
|
|
|
|
// string formatting on lists.
|
|
|
|
func interpolationFuncFormatList() ast.Function {
|
|
|
|
return ast.Function{
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeAny},
|
2015-05-05 22:34:40 -05:00
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeAny,
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ReturnType: ast.TypeList,
|
2015-05-05 22:34:40 -05:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
// Make a copy of the variadic part of args
|
|
|
|
// to avoid modifying the original.
|
|
|
|
varargs := make([]interface{}, len(args)-1)
|
|
|
|
copy(varargs, args[1:])
|
|
|
|
|
2016-11-02 00:57:11 -05:00
|
|
|
// Verify we have some arguments
|
|
|
|
if len(varargs) == 0 {
|
|
|
|
return nil, fmt.Errorf("no arguments to formatlist")
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:34:40 -05:00
|
|
|
// Convert arguments that are lists into slices.
|
|
|
|
// Confirm along the way that all lists have the same length (n).
|
|
|
|
var n int
|
2016-11-02 00:57:11 -05:00
|
|
|
listSeen := false
|
2015-05-05 22:34:40 -05:00
|
|
|
for i := 1; i < len(args); i++ {
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
s, ok := args[i].([]ast.Variable)
|
2015-05-05 22:34:40 -05:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2015-06-25 17:55:51 -05:00
|
|
|
|
2016-11-02 00:57:11 -05:00
|
|
|
// Mark that we've seen at least one list
|
|
|
|
listSeen = true
|
|
|
|
|
|
|
|
// Convert the ast.Variable to a slice of strings
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
parts, err := listVariableValueToStringSlice(s)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-06-25 17:55:51 -05:00
|
|
|
|
|
|
|
// otherwise the list is sent down to be indexed
|
2015-05-05 22:34:40 -05:00
|
|
|
varargs[i-1] = parts
|
2015-06-25 17:55:51 -05:00
|
|
|
|
|
|
|
// Check length
|
2015-05-05 22:34:40 -05:00
|
|
|
if n == 0 {
|
|
|
|
// first list we've seen
|
|
|
|
n = len(parts)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if n != len(parts) {
|
|
|
|
return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-02 00:57:11 -05:00
|
|
|
// If we didn't see a list this is an error because we
|
|
|
|
// can't determine the return value length.
|
|
|
|
if !listSeen {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"formatlist requires at least one list argument")
|
2015-05-05 22:34:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Do the formatting.
|
|
|
|
format := args[0].(string)
|
|
|
|
|
|
|
|
// Generate a list of formatted strings.
|
|
|
|
list := make([]string, n)
|
|
|
|
fmtargs := make([]interface{}, len(varargs))
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
for j, arg := range varargs {
|
|
|
|
switch arg := arg.(type) {
|
|
|
|
default:
|
|
|
|
fmtargs[j] = arg
|
|
|
|
case []string:
|
|
|
|
fmtargs[j] = arg[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
list[i] = fmt.Sprintf(format, fmtargs...)
|
|
|
|
}
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
return stringSliceToVariableValue(list), nil
|
2015-05-05 22:34:40 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-15 15:47:54 -05:00
|
|
|
// interpolationFuncIndent indents a multi-line string with the
|
|
|
|
// specified number of spaces
|
|
|
|
func interpolationFuncIndent() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
spaces := args[0].(int)
|
|
|
|
data := args[1].(string)
|
|
|
|
pad := strings.Repeat(" ", spaces)
|
|
|
|
return strings.Replace(data, "\n", "\n"+pad, -1), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-12 14:55:19 -05:00
|
|
|
// interpolationFuncIndex implements the "index" function that allows one to
|
|
|
|
// find the index of a specific element in a list
|
|
|
|
func interpolationFuncIndex() ast.Function {
|
|
|
|
return ast.Function{
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeList, ast.TypeString},
|
2015-07-12 14:55:19 -05:00
|
|
|
ReturnType: ast.TypeInt,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
haystack := args[0].([]ast.Variable)
|
2015-07-12 14:55:19 -05:00
|
|
|
needle := args[1].(string)
|
|
|
|
for index, element := range haystack {
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
if needle == element.Value {
|
2015-07-12 14:55:19 -05:00
|
|
|
return index, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 20:21:48 -05:00
|
|
|
// interpolationFuncBasename implements the "dirname" function.
|
|
|
|
func interpolationFuncDirname() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return filepath.Dir(args[0].(string)), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-15 06:24:33 -05:00
|
|
|
// interpolationFuncDistinct implements the "distinct" function that
|
2016-06-10 10:11:51 -05:00
|
|
|
// removes duplicate elements from a list.
|
2016-06-15 06:24:33 -05:00
|
|
|
func interpolationFuncDistinct() ast.Function {
|
2016-06-10 10:11:51 -05:00
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeList},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeList,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
var list []string
|
|
|
|
|
|
|
|
if len(args) != 1 {
|
2016-07-27 12:47:44 -05:00
|
|
|
return nil, fmt.Errorf("accepts only one argument.")
|
2016-06-10 10:11:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if argument, ok := args[0].([]ast.Variable); ok {
|
|
|
|
for _, element := range argument {
|
2016-07-27 12:47:44 -05:00
|
|
|
if element.Type != ast.TypeString {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"only works for flat lists, this list contains elements of %s",
|
|
|
|
element.Type.Printable())
|
|
|
|
}
|
2016-06-10 10:11:51 -05:00
|
|
|
list = appendIfMissing(list, element.Value.(string))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stringSliceToVariableValue(list), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function to add an element to a list, if it does not already exsit
|
|
|
|
func appendIfMissing(slice []string, element string) []string {
|
|
|
|
for _, ele := range slice {
|
|
|
|
if ele == element {
|
|
|
|
return slice
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return append(slice, element)
|
|
|
|
}
|
|
|
|
|
2017-04-21 12:04:49 -05:00
|
|
|
// for two lists `keys` and `values` of equal length, returns all elements
|
|
|
|
// from `values` where the corresponding element from `keys` is in `searchset`.
|
|
|
|
func interpolationFuncMatchKeys() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeList, ast.TypeList, ast.TypeList},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
output := make([]ast.Variable, 0)
|
|
|
|
|
|
|
|
values, _ := args[0].([]ast.Variable)
|
|
|
|
keys, _ := args[1].([]ast.Variable)
|
|
|
|
searchset, _ := args[2].([]ast.Variable)
|
|
|
|
|
|
|
|
if len(keys) != len(values) {
|
|
|
|
return nil, fmt.Errorf("length of keys and values should be equal")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, key := range keys {
|
|
|
|
for _, search := range searchset {
|
|
|
|
if res, err := compareSimpleVariables(key, search); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if res == true {
|
|
|
|
output = append(output, values[i])
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if searchset is empty, then output is an empty list as well.
|
|
|
|
// if we haven't matched any key, then output is an empty list.
|
|
|
|
return output, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// compare two variables of the same type, i.e. non complex one, such as TypeList or TypeMap
|
|
|
|
func compareSimpleVariables(a, b ast.Variable) (bool, error) {
|
|
|
|
if a.Type != b.Type {
|
|
|
|
return false, fmt.Errorf(
|
|
|
|
"won't compare items of different types %s and %s",
|
|
|
|
a.Type.Printable(), b.Type.Printable())
|
|
|
|
}
|
|
|
|
switch a.Type {
|
|
|
|
case ast.TypeString:
|
|
|
|
return a.Value.(string) == b.Value.(string), nil
|
|
|
|
default:
|
|
|
|
return false, fmt.Errorf(
|
|
|
|
"can't compare items of type %s",
|
|
|
|
a.Type.Printable())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-09 23:22:35 -05:00
|
|
|
// interpolationFuncJoin implements the "join" function that allows
|
|
|
|
// multi-variable values to be joined by some character.
|
2015-01-15 00:01:42 -06:00
|
|
|
func interpolationFuncJoin() ast.Function {
|
|
|
|
return ast.Function{
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeList,
|
|
|
|
ReturnType: ast.TypeString,
|
2015-01-13 13:50:44 -06:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
var list []string
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
|
|
|
|
if len(args) < 2 {
|
|
|
|
return nil, fmt.Errorf("not enough arguments to join()")
|
|
|
|
}
|
|
|
|
|
2015-01-13 13:50:44 -06:00
|
|
|
for _, arg := range args[1:] {
|
2016-07-27 12:47:44 -05:00
|
|
|
for _, part := range arg.([]ast.Variable) {
|
|
|
|
if part.Type != ast.TypeString {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"only works on flat lists, this list contains elements of %s",
|
|
|
|
part.Type.Printable())
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
}
|
2016-07-27 12:47:44 -05:00
|
|
|
list = append(list, part.Value.(string))
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
}
|
2015-01-13 13:50:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(list, args[0].(string)), nil
|
|
|
|
},
|
2014-10-09 23:22:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-28 20:02:06 -05:00
|
|
|
// interpolationFuncJSONEncode implements the "jsonencode" function that encodes
|
2017-05-27 18:58:40 -05:00
|
|
|
// a string, list, or map as its JSON representation.
|
2016-03-28 20:02:06 -05:00
|
|
|
func interpolationFuncJSONEncode() ast.Function {
|
|
|
|
return ast.Function{
|
2016-05-18 12:45:22 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeAny},
|
2016-03-28 20:02:06 -05:00
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-05-18 12:45:22 -05:00
|
|
|
var toEncode interface{}
|
|
|
|
|
|
|
|
switch typedArg := args[0].(type) {
|
|
|
|
case string:
|
|
|
|
toEncode = typedArg
|
|
|
|
|
|
|
|
case []ast.Variable:
|
|
|
|
strings := make([]string, len(typedArg))
|
|
|
|
|
|
|
|
for i, v := range typedArg {
|
|
|
|
if v.Type != ast.TypeString {
|
2017-05-27 18:58:40 -05:00
|
|
|
variable, _ := hil.InterfaceToVariable(typedArg)
|
|
|
|
toEncode, _ = hil.VariableToInterface(variable)
|
|
|
|
|
|
|
|
jEnc, err := json.Marshal(toEncode)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
|
|
|
|
}
|
|
|
|
return string(jEnc), nil
|
|
|
|
|
2016-05-18 12:45:22 -05:00
|
|
|
}
|
|
|
|
strings[i] = v.Value.(string)
|
|
|
|
}
|
|
|
|
toEncode = strings
|
|
|
|
|
|
|
|
case map[string]ast.Variable:
|
|
|
|
stringMap := make(map[string]string)
|
|
|
|
for k, v := range typedArg {
|
|
|
|
if v.Type != ast.TypeString {
|
2017-05-27 18:58:40 -05:00
|
|
|
variable, _ := hil.InterfaceToVariable(typedArg)
|
|
|
|
toEncode, _ = hil.VariableToInterface(variable)
|
|
|
|
|
|
|
|
jEnc, err := json.Marshal(toEncode)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
|
|
|
|
}
|
|
|
|
return string(jEnc), nil
|
2016-05-18 12:45:22 -05:00
|
|
|
}
|
|
|
|
stringMap[k] = v.Value.(string)
|
|
|
|
}
|
|
|
|
toEncode = stringMap
|
|
|
|
|
|
|
|
default:
|
|
|
|
return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
jEnc, err := json.Marshal(toEncode)
|
2016-03-28 20:02:06 -05:00
|
|
|
if err != nil {
|
2016-05-18 12:45:22 -05:00
|
|
|
return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
|
2016-03-28 20:02:06 -05:00
|
|
|
}
|
|
|
|
return string(jEnc), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:37:40 -06:00
|
|
|
// interpolationFuncReplace implements the "replace" function that does
|
|
|
|
// string replacement.
|
|
|
|
func interpolationFuncReplace() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
search := args[1].(string)
|
|
|
|
replace := args[2].(string)
|
|
|
|
|
|
|
|
// We search/replace using a regexp if the string is surrounded
|
|
|
|
// in forward slashes.
|
2015-03-02 11:46:46 -06:00
|
|
|
if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' {
|
2015-03-02 11:37:40 -06:00
|
|
|
re, err := regexp.Compile(search[1 : len(search)-1])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return re.ReplaceAllString(s, replace), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Replace(s, search, replace, -1), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-12 09:17:26 -05:00
|
|
|
func interpolationFuncLength() ast.Function {
|
|
|
|
return ast.Function{
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeAny},
|
2015-04-12 09:17:26 -05:00
|
|
|
ReturnType: ast.TypeInt,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
subject := args[0]
|
2015-04-12 09:17:26 -05:00
|
|
|
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
switch typedSubject := subject.(type) {
|
|
|
|
case string:
|
|
|
|
return len(typedSubject), nil
|
|
|
|
case []ast.Variable:
|
|
|
|
return len(typedSubject), nil
|
2016-07-27 12:47:44 -05:00
|
|
|
case map[string]ast.Variable:
|
|
|
|
return len(typedSubject), nil
|
2015-04-12 09:17:26 -05:00
|
|
|
}
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
|
2016-07-27 12:47:44 -05:00
|
|
|
return 0, fmt.Errorf("arguments to length() must be a string, list, or map")
|
2015-04-12 09:17:26 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-27 09:36:31 -06:00
|
|
|
func interpolationFuncSignum() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeInt},
|
|
|
|
ReturnType: ast.TypeInt,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
num := args[0].(int)
|
|
|
|
switch {
|
|
|
|
case num < 0:
|
|
|
|
return -1, nil
|
|
|
|
case num > 0:
|
|
|
|
return +1, nil
|
|
|
|
default:
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 15:20:02 -06:00
|
|
|
// interpolationFuncSlice returns a portion of the input list between from, inclusive and to, exclusive.
|
|
|
|
func interpolationFuncSlice() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeList, // inputList
|
|
|
|
ast.TypeInt, // from
|
|
|
|
ast.TypeInt, // to
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
inputList := args[0].([]ast.Variable)
|
|
|
|
from := args[1].(int)
|
|
|
|
to := args[2].(int)
|
|
|
|
|
|
|
|
if from < 0 {
|
|
|
|
return nil, fmt.Errorf("from index must be >= 0")
|
|
|
|
}
|
|
|
|
if to > len(inputList) {
|
|
|
|
return nil, fmt.Errorf("to index must be <= length of the input list")
|
|
|
|
}
|
|
|
|
if from > to {
|
|
|
|
return nil, fmt.Errorf("from index must be <= to index")
|
|
|
|
}
|
|
|
|
|
|
|
|
var outputList []ast.Variable
|
|
|
|
for i, val := range inputList {
|
|
|
|
if i >= from && i < to {
|
|
|
|
outputList = append(outputList, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return outputList, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-11 11:36:29 -05:00
|
|
|
// interpolationFuncSort sorts a list of a strings lexographically
|
|
|
|
func interpolationFuncSort() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeList},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
inputList := args[0].([]ast.Variable)
|
|
|
|
|
|
|
|
// Ensure that all the list members are strings and
|
|
|
|
// create a string slice from them
|
|
|
|
members := make([]string, len(inputList))
|
|
|
|
for i, val := range inputList {
|
|
|
|
if val.Type != ast.TypeString {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"sort() may only be used with lists of strings - %s at index %d",
|
|
|
|
val.Type.String(), i)
|
|
|
|
}
|
|
|
|
|
|
|
|
members[i] = val.Value.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(members)
|
|
|
|
return stringSliceToVariableValue(members), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 15:59:16 -06:00
|
|
|
// interpolationFuncSplit implements the "split" function that allows
|
|
|
|
// strings to split into multi-variable values
|
|
|
|
func interpolationFuncSplit() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ReturnType: ast.TypeList,
|
2015-01-28 15:59:16 -06:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2015-06-10 11:29:44 -05:00
|
|
|
sep := args[0].(string)
|
|
|
|
s := args[1].(string)
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
elements := strings.Split(s, sep)
|
|
|
|
return stringSliceToVariableValue(elements), nil
|
2015-01-28 15:59:16 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-21 14:56:03 -05:00
|
|
|
// interpolationFuncLookup implements the "lookup" function that allows
|
|
|
|
// dynamic lookups of map types within a Terraform configuration.
|
2015-01-15 00:01:42 -06:00
|
|
|
func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function {
|
|
|
|
return ast.Function{
|
2016-05-25 19:25:15 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeMap, ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeString,
|
2015-01-13 14:06:04 -06:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-05-25 19:25:15 -05:00
|
|
|
defaultValue := ""
|
|
|
|
defaultValueSet := false
|
|
|
|
if len(args) > 2 {
|
|
|
|
defaultValue = args[2].(string)
|
|
|
|
defaultValueSet = true
|
|
|
|
}
|
|
|
|
if len(args) > 3 {
|
|
|
|
return "", fmt.Errorf("lookup() takes no more than three arguments")
|
|
|
|
}
|
2016-04-11 12:40:06 -05:00
|
|
|
index := args[1].(string)
|
|
|
|
mapVar := args[0].(map[string]ast.Variable)
|
|
|
|
|
|
|
|
v, ok := mapVar[index]
|
2015-01-13 14:06:04 -06:00
|
|
|
if !ok {
|
2016-05-25 19:25:15 -05:00
|
|
|
if defaultValueSet {
|
|
|
|
return defaultValue, nil
|
|
|
|
} else {
|
|
|
|
return "", fmt.Errorf(
|
|
|
|
"lookup failed to find '%s'",
|
|
|
|
args[1].(string))
|
|
|
|
}
|
2015-01-13 14:06:04 -06:00
|
|
|
}
|
2015-01-14 12:40:43 -06:00
|
|
|
if v.Type != ast.TypeString {
|
2016-07-27 12:47:44 -05:00
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"lookup() may only be used with flat maps, this map contains elements of %s",
|
|
|
|
v.Type.Printable())
|
2015-01-14 12:40:43 -06:00
|
|
|
}
|
2014-07-21 15:12:43 -05:00
|
|
|
|
2015-01-14 12:40:43 -06:00
|
|
|
return v.Value.(string), nil
|
2015-01-13 14:06:04 -06:00
|
|
|
},
|
2014-07-21 15:12:43 -05:00
|
|
|
}
|
2014-07-21 14:56:03 -05:00
|
|
|
}
|
2014-11-07 12:23:02 -06:00
|
|
|
|
|
|
|
// interpolationFuncElement implements the "element" function that allows
|
|
|
|
// a specific index to be looked up in a multi-variable value. Note that this will
|
|
|
|
// wrap if the index is larger than the number of elements in the multi-variable value.
|
2015-01-15 00:01:42 -06:00
|
|
|
func interpolationFuncElement() ast.Function {
|
|
|
|
return ast.Function{
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeList, ast.TypeString},
|
2015-01-13 13:50:44 -06:00
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
list := args[0].([]ast.Variable)
|
2016-06-23 06:29:13 -05:00
|
|
|
if len(list) == 0 {
|
|
|
|
return nil, fmt.Errorf("element() may not be used with an empty list")
|
|
|
|
}
|
2015-01-13 13:50:44 -06:00
|
|
|
|
|
|
|
index, err := strconv.Atoi(args[1].(string))
|
2016-02-22 16:47:17 -06:00
|
|
|
if err != nil || index < 0 {
|
2015-01-13 13:50:44 -06:00
|
|
|
return "", fmt.Errorf(
|
|
|
|
"invalid number for index, got %s", args[1])
|
|
|
|
}
|
|
|
|
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
resolvedIndex := index % len(list)
|
|
|
|
|
2016-07-27 12:47:44 -05:00
|
|
|
v := list[resolvedIndex]
|
|
|
|
if v.Type != ast.TypeString {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"element() may only be used with flat lists, this list contains elements of %s",
|
|
|
|
v.Type.Printable())
|
|
|
|
}
|
|
|
|
return v.Value, nil
|
2015-01-13 13:50:44 -06:00
|
|
|
},
|
2014-11-07 12:23:02 -06:00
|
|
|
}
|
|
|
|
}
|
2015-06-02 16:48:38 -05:00
|
|
|
|
2017-10-10 13:56:13 -05:00
|
|
|
// returns the `list` items chunked by `size`.
|
|
|
|
func interpolationFuncChunklist() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeList, // inputList
|
|
|
|
ast.TypeInt, // size
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
output := make([]ast.Variable, 0)
|
|
|
|
|
|
|
|
values, _ := args[0].([]ast.Variable)
|
|
|
|
size, _ := args[1].(int)
|
|
|
|
|
|
|
|
// errors if size is negative
|
|
|
|
if size < 0 {
|
|
|
|
return nil, fmt.Errorf("The size argument must be positive")
|
|
|
|
}
|
|
|
|
|
|
|
|
// if size is 0, returns a list made of the initial list
|
|
|
|
if size == 0 {
|
|
|
|
output = append(output, ast.Variable{
|
|
|
|
Type: ast.TypeList,
|
|
|
|
Value: values,
|
|
|
|
})
|
|
|
|
return output, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
variables := make([]ast.Variable, 0)
|
|
|
|
chunk := ast.Variable{
|
|
|
|
Type: ast.TypeList,
|
|
|
|
Value: variables,
|
|
|
|
}
|
|
|
|
l := len(values)
|
|
|
|
for i, v := range values {
|
|
|
|
variables = append(variables, v)
|
|
|
|
|
|
|
|
// Chunk when index isn't 0, or when reaching the values's length
|
|
|
|
if (i+1)%size == 0 || (i+1) == l {
|
|
|
|
chunk.Value = variables
|
|
|
|
output = append(output, chunk)
|
|
|
|
variables = make([]ast.Variable, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return output, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-02 16:48:38 -05:00
|
|
|
// interpolationFuncKeys implements the "keys" function that yields a list of
|
|
|
|
// keys of map types within a Terraform configuration.
|
|
|
|
func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
|
|
|
|
return ast.Function{
|
2016-04-11 12:40:06 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeMap},
|
|
|
|
ReturnType: ast.TypeList,
|
2015-06-02 16:48:38 -05:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-04-11 12:40:06 -05:00
|
|
|
mapVar := args[0].(map[string]ast.Variable)
|
|
|
|
keys := make([]string, 0)
|
2015-06-02 16:48:38 -05:00
|
|
|
|
2016-04-11 12:40:06 -05:00
|
|
|
for k, _ := range mapVar {
|
|
|
|
keys = append(keys, k)
|
2015-06-02 16:48:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
2016-07-27 12:47:44 -05:00
|
|
|
// Keys are guaranteed to be strings
|
core: support native list variables in config
This commit adds support for native list variables and outputs, building
up on the previous change to state. Interpolation functions now return
native lists in preference to StringList.
List variables are defined like this:
variable "test" {
# This can also be inferred
type = "list"
default = ["Hello", "World"]
}
output "test_out" {
value = "${var.a_list}"
}
This results in the following state:
```
...
"outputs": {
"test_out": [
"hello",
"world"
]
},
...
```
And the result of terraform output is as follows:
```
$ terraform output
test_out = [
hello
world
]
```
Using the output name, an xargs-friendly representation is output:
```
$ terraform output test_out
hello
world
```
The output command also supports indexing into the list (with
appropriate range checking and no wrapping):
```
$ terraform output test_out 1
world
```
Along with maps, list outputs from one module may be passed as variables
into another, removing the need for the `join(",", var.list_as_string)`
and `split(",", var.list_as_string)` which was previously necessary in
Terraform configuration.
This commit also updates the tests and implementations of built-in
interpolation functions to take and return native lists where
appropriate.
A backwards compatibility note: previously the concat interpolation
function was capable of concatenating either strings or lists. The
strings use case was deprectated a long time ago but still remained.
Because we cannot return `ast.TypeAny` from an interpolation function,
this use case is no longer supported for strings - `concat` is only
capable of concatenating lists. This should not be a huge issue - the
type checker picks up incorrect parameters, and the native HIL string
concatenation - or the `join` function - can be used to replicate the
missing behaviour.
2016-04-21 19:03:24 -05:00
|
|
|
return stringSliceToVariableValue(keys), nil
|
2015-06-02 16:48:38 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// interpolationFuncValues implements the "values" function that yields a list of
|
|
|
|
// keys of map types within a Terraform configuration.
|
|
|
|
func interpolationFuncValues(vs map[string]ast.Variable) ast.Function {
|
|
|
|
return ast.Function{
|
2016-04-11 12:40:06 -05:00
|
|
|
ArgTypes: []ast.Type{ast.TypeMap},
|
|
|
|
ReturnType: ast.TypeList,
|
2015-06-02 16:48:38 -05:00
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-04-11 12:40:06 -05:00
|
|
|
mapVar := args[0].(map[string]ast.Variable)
|
|
|
|
keys := make([]string, 0)
|
2015-06-02 16:48:38 -05:00
|
|
|
|
2016-04-11 12:40:06 -05:00
|
|
|
for k, _ := range mapVar {
|
|
|
|
keys = append(keys, k)
|
2015-06-02 16:48:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
2016-04-11 12:40:06 -05:00
|
|
|
values := make([]string, len(keys))
|
|
|
|
for index, key := range keys {
|
|
|
|
if value, ok := mapVar[key].Value.(string); ok {
|
|
|
|
values[index] = value
|
|
|
|
} else {
|
|
|
|
return "", fmt.Errorf("values(): %q has element with bad type %s",
|
|
|
|
key, mapVar[key].Type)
|
2015-06-02 16:48:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-11 12:40:06 -05:00
|
|
|
variable, err := hil.InterfaceToVariable(values)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return variable.Value, nil
|
2015-06-02 16:48:38 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2015-09-25 02:23:36 -05:00
|
|
|
|
2017-03-26 20:21:48 -05:00
|
|
|
// interpolationFuncBasename implements the "basename" function.
|
|
|
|
func interpolationFuncBasename() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return filepath.Base(args[0].(string)), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-03 17:12:51 -05:00
|
|
|
// interpolationFuncBase64Encode implements the "base64encode" function that
|
|
|
|
// allows Base64 encoding.
|
2015-09-25 02:23:36 -05:00
|
|
|
func interpolationFuncBase64Encode() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
return base64.StdEncoding.EncodeToString([]byte(s)), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-03 17:12:51 -05:00
|
|
|
// interpolationFuncBase64Decode implements the "base64decode" function that
|
|
|
|
// allows Base64 decoding.
|
2015-09-25 02:23:36 -05:00
|
|
|
func interpolationFuncBase64Decode() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
sDec, err := base64.StdEncoding.DecodeString(s)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to decode base64 data '%s'", s)
|
|
|
|
}
|
|
|
|
return string(sDec), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2015-10-19 20:49:51 -05:00
|
|
|
|
2015-11-11 02:51:30 -06:00
|
|
|
// interpolationFuncBase64Gzip implements the "gzip" function that allows gzip
|
|
|
|
// compression encoding the result using base64
|
|
|
|
func interpolationFuncBase64Gzip() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
gz := gzip.NewWriter(&b)
|
|
|
|
if _, err := gz.Write([]byte(s)); err != nil {
|
|
|
|
return "", fmt.Errorf("failed to write gzip raw data: '%s'", s)
|
|
|
|
}
|
|
|
|
if err := gz.Flush(); err != nil {
|
|
|
|
return "", fmt.Errorf("failed to flush gzip writer: '%s'", s)
|
|
|
|
}
|
|
|
|
if err := gz.Close(); err != nil {
|
|
|
|
return "", fmt.Errorf("failed to close gzip writer: '%s'", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-19 20:49:51 -05:00
|
|
|
// interpolationFuncLower implements the "lower" function that does
|
|
|
|
// string lower casing.
|
|
|
|
func interpolationFuncLower() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
toLower := args[0].(string)
|
|
|
|
return strings.ToLower(toLower), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-23 05:29:11 -06:00
|
|
|
func interpolationFuncMd5() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
h := md5.New()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
hash := hex.EncodeToString(h.Sum(nil))
|
|
|
|
return hash, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-01 17:10:53 -05:00
|
|
|
func interpolationFuncMerge() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeMap},
|
|
|
|
ReturnType: ast.TypeMap,
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeMap,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
outputMap := make(map[string]ast.Variable)
|
|
|
|
|
|
|
|
for _, arg := range args {
|
|
|
|
for k, v := range arg.(map[string]ast.Variable) {
|
|
|
|
outputMap[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return outputMap, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-19 20:49:51 -05:00
|
|
|
// interpolationFuncUpper implements the "upper" function that does
|
|
|
|
// string upper casing.
|
|
|
|
func interpolationFuncUpper() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
toUpper := args[0].(string)
|
|
|
|
return strings.ToUpper(toUpper), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2015-12-27 16:28:56 -06:00
|
|
|
|
|
|
|
func interpolationFuncSha1() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
h := sha1.New()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
hash := hex.EncodeToString(h.Sum(nil))
|
|
|
|
return hash, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2016-01-16 17:54:04 -06:00
|
|
|
|
2016-01-29 06:09:57 -06:00
|
|
|
// hexadecimal representation of sha256 sum
|
2016-01-16 17:54:04 -06:00
|
|
|
func interpolationFuncSha256() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
h := sha256.New()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
hash := hex.EncodeToString(h.Sum(nil))
|
|
|
|
return hash, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2016-01-29 17:28:04 -06:00
|
|
|
|
2017-05-02 18:35:23 -05:00
|
|
|
func interpolationFuncSha512() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
h := sha512.New()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
hash := hex.EncodeToString(h.Sum(nil))
|
|
|
|
return hash, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-30 03:51:28 -06:00
|
|
|
func interpolationFuncTrimSpace() ast.Function {
|
2016-01-29 17:28:04 -06:00
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
2016-01-30 03:51:28 -06:00
|
|
|
trimSpace := args[0].(string)
|
|
|
|
return strings.TrimSpace(trimSpace), nil
|
2016-01-29 17:28:04 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2016-01-29 06:09:57 -06:00
|
|
|
|
|
|
|
func interpolationFuncBase64Sha256() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
h := sha256.New()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
shaSum := h.Sum(nil)
|
|
|
|
encoded := base64.StdEncoding.EncodeToString(shaSum[:])
|
|
|
|
return encoded, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2016-03-21 15:14:30 -05:00
|
|
|
|
2017-05-02 18:35:23 -05:00
|
|
|
func interpolationFuncBase64Sha512() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
h := sha512.New()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
shaSum := h.Sum(nil)
|
|
|
|
encoded := base64.StdEncoding.EncodeToString(shaSum[:])
|
|
|
|
return encoded, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-22 05:02:32 -05:00
|
|
|
func interpolationFuncBcrypt() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
Variadic: true,
|
|
|
|
VariadicType: ast.TypeString,
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
defaultCost := 10
|
|
|
|
|
|
|
|
if len(args) > 1 {
|
|
|
|
costStr := args[1].(string)
|
|
|
|
cost, err := strconv.Atoi(costStr)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultCost = cost
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(args) > 2 {
|
|
|
|
return "", fmt.Errorf("bcrypt() takes no more than two arguments")
|
|
|
|
}
|
|
|
|
|
|
|
|
input := args[0].(string)
|
|
|
|
out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("error occured generating password %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(out), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-21 15:14:30 -05:00
|
|
|
func interpolationFuncUUID() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return uuid.GenerateUUID()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2016-10-26 07:21:32 -05:00
|
|
|
|
2016-12-01 13:51:01 -06:00
|
|
|
// interpolationFuncTimestamp
|
|
|
|
func interpolationFuncTimestamp() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return time.Now().UTC().Format(time.RFC3339), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:46:12 -06:00
|
|
|
func interpolationFuncTimeAdd() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeString, // input timestamp string in RFC3339 format
|
|
|
|
ast.TypeString, // duration to add to input timestamp that should be parsable by time.ParseDuration
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
|
|
|
|
ts, err := time.Parse(time.RFC3339, args[0].(string))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
duration, err := time.ParseDuration(args[1].(string))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ts.Add(duration).Format(time.RFC3339), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 07:21:32 -05:00
|
|
|
// interpolationFuncTitle implements the "title" function that returns a copy of the
|
|
|
|
// string in which first characters of all the words are capitalized.
|
|
|
|
func interpolationFuncTitle() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
toTitle := args[0].(string)
|
|
|
|
return strings.Title(toTitle), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-03-22 10:30:39 -05:00
|
|
|
|
|
|
|
// interpolationFuncSubstr implements the "substr" function that allows strings
|
|
|
|
// to be truncated.
|
|
|
|
func interpolationFuncSubstr() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{
|
|
|
|
ast.TypeString, // input string
|
|
|
|
ast.TypeInt, // offset
|
|
|
|
ast.TypeInt, // length
|
|
|
|
},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
str := args[0].(string)
|
|
|
|
offset := args[1].(int)
|
|
|
|
length := args[2].(int)
|
|
|
|
|
|
|
|
// Interpret a negative offset as being equivalent to a positive
|
|
|
|
// offset taken from the end of the string.
|
|
|
|
if offset < 0 {
|
|
|
|
offset += len(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpret a length of `-1` as indicating that the substring
|
|
|
|
// should start at `offset` and continue until the end of the
|
|
|
|
// string. Any other negative length (other than `-1`) is invalid.
|
|
|
|
if length == -1 {
|
|
|
|
length = len(str)
|
|
|
|
} else if length >= 0 {
|
|
|
|
length += offset
|
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf("length should be a non-negative integer")
|
|
|
|
}
|
|
|
|
|
2018-01-05 09:40:48 -06:00
|
|
|
if offset > len(str) || offset < 0 {
|
2017-03-22 10:30:39 -05:00
|
|
|
return nil, fmt.Errorf("offset cannot be larger than the length of the string")
|
|
|
|
}
|
|
|
|
|
|
|
|
if length > len(str) {
|
|
|
|
return nil, fmt.Errorf("'offset + length' cannot be larger than the length of the string")
|
|
|
|
}
|
|
|
|
|
|
|
|
return str[offset:length], nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-06-13 14:27:45 -05:00
|
|
|
|
|
|
|
// Flatten until it's not ast.TypeList
|
|
|
|
func flattener(finalList []ast.Variable, flattenList []ast.Variable) []ast.Variable {
|
|
|
|
for _, val := range flattenList {
|
|
|
|
if val.Type == ast.TypeList {
|
|
|
|
finalList = flattener(finalList, val.Value.([]ast.Variable))
|
|
|
|
} else {
|
|
|
|
finalList = append(finalList, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return finalList
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flatten to single list
|
|
|
|
func interpolationFuncFlatten() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeList},
|
|
|
|
ReturnType: ast.TypeList,
|
|
|
|
Variadic: false,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
inputList := args[0].([]ast.Variable)
|
|
|
|
|
|
|
|
var outputList []ast.Variable
|
|
|
|
return flattener(outputList, inputList), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-08-22 13:26:09 -05:00
|
|
|
|
|
|
|
func interpolationFuncURLEncode() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
return url.QueryEscape(s), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-09-25 16:19:18 -05:00
|
|
|
|
2017-09-01 23:19:47 -05:00
|
|
|
// interpolationFuncTranspose implements the "transpose" function
|
|
|
|
// that converts a map (string,list) to a map (string,list) where
|
|
|
|
// the unique values of the original lists become the keys of the
|
|
|
|
// new map and the keys of the original map become values for the
|
|
|
|
// corresponding new keys.
|
|
|
|
func interpolationFuncTranspose() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeMap},
|
|
|
|
ReturnType: ast.TypeMap,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
|
|
|
|
inputMap := args[0].(map[string]ast.Variable)
|
|
|
|
outputMap := make(map[string]ast.Variable)
|
|
|
|
tmpMap := make(map[string][]string)
|
|
|
|
|
|
|
|
for inKey, inVal := range inputMap {
|
|
|
|
if inVal.Type != ast.TypeList {
|
|
|
|
return nil, fmt.Errorf("transpose requires a map of lists of strings")
|
|
|
|
}
|
|
|
|
values := inVal.Value.([]ast.Variable)
|
|
|
|
for _, listVal := range values {
|
|
|
|
if listVal.Type != ast.TypeString {
|
|
|
|
return nil, fmt.Errorf("transpose requires the given map values to be lists of strings")
|
|
|
|
}
|
|
|
|
outKey := listVal.Value.(string)
|
|
|
|
if _, ok := tmpMap[outKey]; !ok {
|
|
|
|
tmpMap[outKey] = make([]string, 0)
|
|
|
|
}
|
|
|
|
outVal := tmpMap[outKey]
|
|
|
|
outVal = append(outVal, inKey)
|
|
|
|
sort.Strings(outVal)
|
|
|
|
tmpMap[outKey] = outVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for outKey, outVal := range tmpMap {
|
|
|
|
values := make([]ast.Variable, 0)
|
|
|
|
for _, v := range outVal {
|
|
|
|
values = append(values, ast.Variable{Type: ast.TypeString, Value: v})
|
|
|
|
}
|
|
|
|
outputMap[outKey] = ast.Variable{Type: ast.TypeList, Value: values}
|
|
|
|
}
|
|
|
|
return outputMap, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-25 16:19:18 -05:00
|
|
|
// interpolationFuncAbs returns the absolute value of a given float.
|
|
|
|
func interpolationFuncAbs() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
|
|
|
ReturnType: ast.TypeFloat,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
return math.Abs(args[0].(float64)), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-12-05 13:06:32 -06:00
|
|
|
|
|
|
|
// interpolationFuncRsaDecrypt implements the "rsadecrypt" function that does
|
|
|
|
// RSA decryption.
|
|
|
|
func interpolationFuncRsaDecrypt() ast.Function {
|
|
|
|
return ast.Function{
|
|
|
|
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
|
|
|
ReturnType: ast.TypeString,
|
|
|
|
Callback: func(args []interface{}) (interface{}, error) {
|
|
|
|
s := args[0].(string)
|
|
|
|
key := args[1].(string)
|
|
|
|
|
|
|
|
b, err := base64.StdEncoding.DecodeString(s)
|
|
|
|
if err != nil {
|
2018-06-26 17:19:37 -05:00
|
|
|
return "", fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", s)
|
2017-12-05 13:06:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
block, _ := pem.Decode([]byte(key))
|
|
|
|
if block == nil {
|
|
|
|
return "", fmt.Errorf("Failed to read key %q: no key found", key)
|
|
|
|
}
|
|
|
|
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
|
|
|
|
return "", fmt.Errorf(
|
|
|
|
"Failed to read key %q: password protected keys are\n"+
|
|
|
|
"not supported. Please decrypt the key prior to use.", key)
|
|
|
|
}
|
|
|
|
|
|
|
|
x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(out), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|