terraform/config/interpolate_funcs_test.go

2996 lines
55 KiB
Go
Raw Normal View History

2014-07-21 22:12:43 +02:00
package config
import (
2014-10-10 06:22:35 +02:00
"fmt"
"io/ioutil"
"os"
"reflect"
2014-07-21 22:12:43 +02:00
"testing"
"time"
2014-08-19 22:14:45 +02:00
"path/filepath"
2016-01-31 08:38:37 +01:00
"github.com/hashicorp/hil"
"github.com/hashicorp/hil/ast"
"github.com/mitchellh/go-homedir"
2017-05-22 12:02:32 +02:00
"golang.org/x/crypto/bcrypt"
)
2014-08-19 22:14:45 +02:00
func TestInterpolateFuncZipMap(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${zipmap(var.list, var.list2)}`,
map[string]interface{}{
"Hello": "bar",
"World": "baz",
},
false,
},
{
`${zipmap(var.list, var.nonstrings)}`,
map[string]interface{}{
"Hello": []interface{}{"bar", "baz"},
"World": []interface{}{"boo", "foo"},
},
false,
},
{
`${zipmap(var.nonstrings, var.list2)}`,
nil,
true,
},
{
`${zipmap(var.list, var.differentlengthlist)}`,
nil,
true,
},
},
Vars: map[string]ast.Variable{
"var.list": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "Hello",
},
{
Type: ast.TypeString,
Value: "World",
},
},
},
"var.list2": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
},
},
"var.differentlengthlist": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
{
Type: ast.TypeString,
Value: "boo",
},
},
},
"var.nonstrings": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
},
},
{
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "boo",
},
{
Type: ast.TypeString,
Value: "foo",
},
},
},
},
},
},
})
}
func TestInterpolateFuncList(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// empty input returns empty list
{
`${list()}`,
[]interface{}{},
false,
},
// single input returns list of length 1
{
`${list("hello")}`,
[]interface{}{"hello"},
false,
},
// two inputs returns list of length 2
{
`${list("hello", "world")}`,
[]interface{}{"hello", "world"},
false,
},
// not a string input gives error
{
`${list("hello", 42)}`,
nil,
true,
},
// list of lists
{
`${list("${var.list}", "${var.list2}")}`,
[]interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}},
false,
},
// list of maps
{
`${list("${var.map}", "${var.map2}")}`,
[]interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}},
false,
},
// error on a heterogeneous list
{
`${list("first", "${var.list}")}`,
nil,
true,
},
},
Vars: map[string]ast.Variable{
"var.list": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "Hello",
},
{
Type: ast.TypeString,
Value: "World",
},
},
},
"var.list2": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
},
},
"var.map": {
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key": {
Type: ast.TypeString,
Value: "bar",
},
},
},
"var.map2": {
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key2": {
Type: ast.TypeString,
Value: "baz",
},
},
},
},
})
}
func TestInterpolateFuncMax(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${max()}`,
nil,
true,
},
{
`${max("")}`,
nil,
true,
},
{
`${max(-1, 0, 1)}`,
"1",
false,
},
{
`${max(1, 0, -1)}`,
"1",
false,
},
{
`${max(-1, -2)}`,
"-1",
false,
},
{
`${max(-1)}`,
"-1",
false,
},
},
})
}
func TestInterpolateFuncMin(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${min()}`,
nil,
true,
},
{
`${min("")}`,
nil,
true,
},
{
`${min(-1, 0, 1)}`,
"-1",
false,
},
{
`${min(1, 0, -1)}`,
"-1",
false,
},
{
`${min(-1, -2)}`,
"-2",
false,
},
{
`${min(-1)}`,
"-1",
false,
},
},
})
}
2017-05-17 23:46:33 +02:00
func TestInterpolateFuncPow(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${pow(1, 0)}`,
"1",
false,
},
{
`${pow(1, 1)}`,
"1",
false,
},
{
`${pow(2, 0)}`,
"1",
false,
},
{
`${pow(2, 1)}`,
"2",
false,
},
{
`${pow(3, 2)}`,
"9",
false,
},
{
`${pow(-3, 2)}`,
"9",
false,
},
{
`${pow(2, -2)}`,
"0.25",
false,
},
{
`${pow(0, 2)}`,
"0",
false,
},
{
`${pow("invalid-input", 2)}`,
nil,
true,
},
{
`${pow(2, "invalid-input")}`,
nil,
true,
},
{
`${pow(2)}`,
nil,
true,
},
2017-05-17 23:46:33 +02:00
},
})
}
func TestInterpolateFuncFloor(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${floor()}`,
nil,
true,
},
{
`${floor("")}`,
nil,
true,
},
{
`${floor("-1.3")}`, // there appears to be a AST bug where the parsed argument ends up being -1 without the "s
"-2",
false,
},
{
`${floor(1.7)}`,
"1",
false,
},
},
})
}
func TestInterpolateFuncCeil(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${ceil()}`,
nil,
true,
},
{
`${ceil("")}`,
nil,
true,
},
{
`${ceil(-1.8)}`,
"-1",
false,
},
{
`${ceil(1.2)}`,
"2",
false,
},
},
})
}
func TestInterpolateFuncLog(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${log(1, 10)}`,
"0",
false,
},
{
`${log(10, 10)}`,
"1",
false,
},
{
`${log(0, 10)}`,
"-Inf",
false,
},
{
`${log(10, 0)}`,
"-0",
false,
},
},
})
}
2017-04-06 13:14:09 +02:00
func TestInterpolateFuncChomp(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${chomp()}`,
nil,
true,
},
{
`${chomp("hello world")}`,
"hello world",
false,
},
{
2017-04-07 10:41:55 +02:00
fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld"),
"goodbye\ncruel\nworld",
false,
},
{
fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld"),
"goodbye\r\nwindows\r\nworld",
false,
},
{
fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n"),
"goodbye\ncruel\nworld",
false,
},
{
fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n\n\n\n"),
"goodbye\ncruel\nworld",
false,
},
{
fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n"),
"goodbye\r\nwindows\r\nworld",
false,
},
{
fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n\r\n\r\n\r\n"),
"goodbye\r\nwindows\r\nworld",
2017-04-06 13:14:09 +02:00
false,
},
},
})
}
func TestInterpolateFuncMap(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// empty input returns empty map
{
`${map()}`,
map[string]interface{}{},
false,
},
// odd args is error
{
`${map("odd")}`,
nil,
true,
},
// two args returns map w/ one k/v
{
`${map("hello", "world")}`,
map[string]interface{}{"hello": "world"},
false,
},
// four args get two k/v
{
`${map("hello", "world", "what's", "up?")}`,
map[string]interface{}{"hello": "world", "what's": "up?"},
false,
},
// map of lists is okay
{
`${map("hello", list("world"), "what's", list("up?"))}`,
map[string]interface{}{
"hello": []interface{}{"world"},
"what's": []interface{}{"up?"},
},
false,
},
// map of maps is okay
{
`${map("hello", map("there", "world"), "what's", map("really", "up?"))}`,
map[string]interface{}{
"hello": map[string]interface{}{"there": "world"},
"what's": map[string]interface{}{"really": "up?"},
},
false,
},
// keys have to be strings
{
`${map(list("listkey"), "val")}`,
nil,
true,
},
// types have to match
{
`${map("some", "strings", "also", list("lists"))}`,
nil,
true,
},
// duplicate keys are an error
{
`${map("key", "val", "key", "again")}`,
nil,
true,
},
},
})
}
2015-09-15 15:50:31 +02:00
func TestInterpolateFuncCompact(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// empty string within array
{
`${compact(split(",", "a,,b"))}`,
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-22 02:03:24 +02:00
[]interface{}{"a", "b"},
2015-09-15 15:50:31 +02:00
false,
},
// empty string at the end of array
{
`${compact(split(",", "a,b,"))}`,
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-22 02:03:24 +02:00
[]interface{}{"a", "b"},
2015-09-15 15:50:31 +02:00
false,
},
// single empty string
{
`${compact(split(",", ""))}`,
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-22 02:03:24 +02:00
[]interface{}{},
2015-09-15 15:50:31 +02:00
false,
},
// errrors on list of lists
{
`${compact(list(list("a"), list("b")))}`,
nil,
true,
},
2015-09-15 15:50:31 +02:00
},
})
}
func TestInterpolateFuncCidrHost(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${cidrhost("192.168.1.0/24", 5)}`,
"192.168.1.5",
false,
},
{
`${cidrhost("192.168.1.0/24", -5)}`,
"192.168.1.251",
false,
},
{
`${cidrhost("192.168.1.0/24", -256)}`,
"192.168.1.0",
false,
},
{
`${cidrhost("192.168.1.0/30", 255)}`,
nil,
true, // 255 doesn't fit in two bits
},
{
`${cidrhost("192.168.1.0/30", -255)}`,
nil,
true, // 255 doesn't fit in two bits
},
{
`${cidrhost("not-a-cidr", 6)}`,
nil,
true, // not a valid CIDR mask
},
{
`${cidrhost("10.256.0.0/8", 6)}`,
nil,
true, // can't have an octet >255
},
},
})
}
func TestInterpolateFuncCidrNetmask(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${cidrnetmask("192.168.1.0/24")}`,
"255.255.255.0",
false,
},
{
`${cidrnetmask("192.168.1.0/32")}`,
"255.255.255.255",
false,
},
{
`${cidrnetmask("0.0.0.0/0")}`,
"0.0.0.0",
false,
},
{
// This doesn't really make sense for IPv6 networks
// but it ought to do something sensible anyway.
`${cidrnetmask("1::/64")}`,
"ffff:ffff:ffff:ffff::",
false,
},
{
`${cidrnetmask("not-a-cidr")}`,
nil,
true, // not a valid CIDR mask
},
{
`${cidrnetmask("10.256.0.0/8")}`,
nil,
true, // can't have an octet >255
},
},
})
}
func TestInterpolateFuncCidrSubnet(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${cidrsubnet("192.168.2.0/20", 4, 6)}`,
"192.168.6.0/24",
false,
},
{
`${cidrsubnet("fe80::/48", 16, 6)}`,
"fe80:0:0:6::/64",
false,
},
{
// IPv4 address encoded in IPv6 syntax gets normalized
`${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`,
"192.168.6.0/24",
false,
},
{
`${cidrsubnet("192.168.0.0/30", 4, 6)}`,
nil,
true, // not enough bits left
},
{
`${cidrsubnet("192.168.0.0/16", 2, 16)}`,
nil,
true, // can't encode 16 in 2 bits
},
{
`${cidrsubnet("not-a-cidr", 4, 6)}`,
nil,
true, // not a valid CIDR mask
},
{
`${cidrsubnet("10.256.0.0/8", 4, 6)}`,
nil,
true, // can't have an octet >255
},
},
})
}
2015-11-08 07:34:56 +01:00
func TestInterpolateFuncCoalesce(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${coalesce("first", "second", "third")}`,
"first",
false,
},
{
`${coalesce("", "second", "third")}`,
"second",
false,
},
{
`${coalesce("", "", "")}`,
"",
false,
},
{
`${coalesce("foo")}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncCoalesceList(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${coalescelist(list("first"), list("second"), list("third"))}`,
[]interface{}{"first"},
false,
},
{
`${coalescelist(list(), list("second"), list("third"))}`,
[]interface{}{"second"},
false,
},
{
`${coalescelist(list(), list(), list())}`,
[]interface{}{},
false,
},
{
`${coalescelist(list("foo"))}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncConcat(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// String + list
// no longer supported, now returns an error
{
`${concat("a", split(",", "b,c"))}`,
nil,
true,
},
// List + string
// no longer supported, now returns an error
{
`${concat(split(",", "a,b"), "c")}`,
nil,
true,
},
// Single list
{
`${concat(split(",", ",foo,"))}`,
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-22 02:03:24 +02:00
[]interface{}{"", "foo", ""},
false,
},
{
`${concat(split(",", "a,b,c"))}`,
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-22 02:03:24 +02:00
[]interface{}{"a", "b", "c"},
false,
},
// Two lists
{
`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
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-22 02:03:24 +02:00
[]interface{}{"a", "b", "c", "d", "e"},
false,
},
// Two lists with different separators
{
`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
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-22 02:03:24 +02:00
[]interface{}{"a", "b", "c", "d", "e"},
false,
},
// More lists
{
`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,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-22 02:03:24 +02:00
[]interface{}{"a", "b", "c", "d", "e", "f", "0", "1"},
false,
},
// list vars
{
`${concat("${var.list}", "${var.list}")}`,
[]interface{}{"a", "b", "a", "b"},
false,
},
// lists of lists
{
`${concat("${var.lists}", "${var.lists}")}`,
[]interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}},
false,
},
// lists of maps
{
`${concat("${var.maps}", "${var.maps}")}`,
[]interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}},
false,
},
// multiple strings
// no longer supported, now returns an error
{
`${concat("string1", "string2")}`,
nil,
true,
},
// mismatched types
{
`${concat("${var.lists}", "${var.maps}")}`,
nil,
true,
},
},
Vars: map[string]ast.Variable{
"var.list": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "a",
},
{
Type: ast.TypeString,
Value: "b",
},
},
},
"var.lists": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "c",
},
{
Type: ast.TypeString,
Value: "d",
},
},
},
},
},
"var.maps": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": {
Type: ast.TypeString,
Value: "a",
},
"key2": {
Type: ast.TypeString,
Value: "b",
},
},
},
},
},
},
})
}
func TestInterpolateFuncContains(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.listOfStrings": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
"var.listOfInts": interfaceToVariableSwallowError([]int{1, 2, 3}),
},
Cases: []testFunctionCase{
{
`${contains(var.listOfStrings, "bar")}`,
"true",
false,
},
{
`${contains(var.listOfStrings, "foo")}`,
"false",
false,
},
{
`${contains(var.listOfInts, 1)}`,
"true",
false,
},
{
`${contains(var.listOfInts, 10)}`,
"false",
false,
},
{
`${contains(var.listOfInts, "2")}`,
"true",
false,
},
},
})
}
func TestInterpolateFuncMerge(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// basic merge
{
`${merge(map("a", "b"), map("c", "d"))}`,
map[string]interface{}{"a": "b", "c": "d"},
false,
},
// merge with conflicts is ok, last in wins.
{
`${merge(map("a", "b", "c", "X"), map("c", "d"))}`,
map[string]interface{}{"a": "b", "c": "d"},
false,
},
// merge variadic
{
`${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`,
map[string]interface{}{"a": "b", "c": "d", "e": "f"},
false,
},
// merge with variables
{
`${merge(var.maps[0], map("c", "d"))}`,
map[string]interface{}{"key1": "a", "key2": "b", "c": "d"},
false,
},
// only accept maps
{
`${merge(map("a", "b"), list("c", "d"))}`,
nil,
true,
},
// merge maps of maps
{
`${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`,
map[string]interface{}{
"b": map[string]interface{}{"key3": "d", "key4": "c"},
"a": map[string]interface{}{"key1": "a", "key2": "b"},
},
false,
},
// merge maps of lists
{
`${merge(map("a", list("b")), map("c", list("d", "e")))}`,
map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}},
false,
},
// merge map of various kinds
{
`${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`,
map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}},
false,
},
},
Vars: map[string]ast.Variable{
"var.maps": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": {
Type: ast.TypeString,
Value: "a",
},
"key2": {
Type: ast.TypeString,
Value: "b",
},
},
},
{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key3": {
Type: ast.TypeString,
Value: "d",
},
"key4": {
Type: ast.TypeString,
Value: "c",
},
},
},
},
},
},
})
}
func TestInterpolateFuncDirname(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${dirname("/foo/bar/baz")}`,
"/foo/bar",
false,
},
},
})
}
func TestInterpolateFuncDistinct(t *testing.T) {
2016-06-10 17:11:51 +02:00
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// 3 duplicates
{
`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`,
2016-06-10 17:11:51 +02:00
[]interface{}{"user1", "user2", "user3"},
false,
},
// 1 duplicate
{
`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`,
2016-06-10 17:11:51 +02:00
[]interface{}{"user1", "user2", "user3", "user4"},
false,
},
// too many args
{
`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`,
2016-06-10 17:11:51 +02:00
nil,
true,
},
// non-flat list is an error
{
`${distinct(list(list("a"), list("a")))}`,
nil,
true,
},
2016-06-10 17:11:51 +02:00
},
})
}
func TestInterpolateFuncMatchKeys(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// normal usage
{
`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2"))}`,
[]interface{}{"b"},
false,
},
// normal usage 2, check the order
{
`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref1"))}`,
[]interface{}{"a", "b"},
false,
},
// duplicate item in searchset
{
`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref2"))}`,
[]interface{}{"b"},
false,
},
// no matches
{
`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref4"))}`,
[]interface{}{},
false,
},
// no matches 2
{
`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list())}`,
[]interface{}{},
false,
},
// zero case
{
`${matchkeys(list(), list(), list("nope"))}`,
[]interface{}{},
false,
},
// complex values
{
`${matchkeys(list(list("a", "a")), list("a"), list("a"))}`,
[]interface{}{[]interface{}{"a", "a"}},
false,
},
// errors
// different types
{
`${matchkeys(list("a"), list(1), list("a"))}`,
nil,
true,
},
// different types
{
`${matchkeys(list("a"), list(list("a"), list("a")), list("a"))}`,
nil,
true,
},
// lists of different length is an error
{
`${matchkeys(list("a"), list("a", "b"), list("a"))}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncFile(t *testing.T) {
tf, err := ioutil.TempFile("", "tf")
if err != nil {
t.Fatalf("err: %s", err)
}
path := tf.Name()
tf.Write([]byte("foo"))
tf.Close()
defer os.Remove(path)
2015-01-13 21:06:04 +01:00
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
fmt.Sprintf(`${file("%s")}`, path),
"foo",
false,
},
2015-01-13 21:06:04 +01:00
// Invalid path
{
`${file("/i/dont/exist")}`,
nil,
true,
},
2015-01-13 21:06:04 +01:00
// Too many args
{
`${file("foo", "bar")}`,
nil,
true,
},
},
})
}
2014-07-23 03:35:53 +02:00
2015-03-02 19:26:06 +01:00
func TestInterpolateFuncFormat(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${format("hello")}`,
"hello",
false,
},
{
`${format("hello %s", "world")}`,
"hello world",
false,
},
{
`${format("hello %d", 42)}`,
"hello 42",
false,
},
{
`${format("hello %05d", 42)}`,
"hello 00042",
false,
},
{
`${format("hello %05d", 12345)}`,
"hello 12345",
false,
},
},
})
}
func TestInterpolateFuncFormatList(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// formatlist requires at least one list
{
`${formatlist("hello")}`,
nil,
true,
},
{
`${formatlist("hello %s", "world")}`,
nil,
true,
},
// formatlist applies to each list element in turn
{
`${formatlist("<%s>", split(",", "A,B"))}`,
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-22 02:03:24 +02:00
[]interface{}{"<A>", "<B>"},
false,
},
// formatlist repeats scalar elements
{
`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
"x=A, x=B, x=C",
false,
},
// Multiple lists are walked in parallel
{
`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
"A=1, B=2, C=3",
false,
},
// Mismatched list lengths generate an error
{
`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
nil,
true,
},
// Works with lists of length 1 [GH-2240]
{
`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
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-22 02:03:24 +02:00
[]interface{}{"demo-rest-elb.id"},
false,
},
// Works with empty lists [GH-7607]
{
`${formatlist("%s", var.emptylist)}`,
[]interface{}{},
false,
},
},
Vars: map[string]ast.Variable{
"var.emptylist": {
Type: ast.TypeList,
Value: []ast.Variable{},
},
},
})
}
func TestInterpolateFuncIndex(t *testing.T) {
testFunction(t, testFunctionConfig{
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-22 02:03:24 +02:00
Vars: map[string]ast.Variable{
"var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
"var.list2": interfaceToVariableSwallowError([]string{"foo"}),
"var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}),
},
Cases: []testFunctionCase{
{
`${index("test", "")}`,
nil,
true,
},
{
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-22 02:03:24 +02:00
`${index(var.list1, "foo")}`,
nil,
true,
},
{
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-22 02:03:24 +02:00
`${index(var.list2, "foo")}`,
"0",
false,
},
{
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-22 02:03:24 +02:00
`${index(var.list3, "bar")}`,
"2",
false,
},
},
})
}
func TestInterpolateFuncIndent(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${indent(4, "Fleas:
Adam
Had'em
E.E. Cummings")}`,
"Fleas:\n Adam\n Had'em\n \n E.E. Cummings",
false,
},
{
`${indent(4, "oneliner")}`,
"oneliner",
false,
},
{
`${indent(4, "#!/usr/bin/env bash
date
pwd")}`,
"#!/usr/bin/env bash\n date\n pwd",
false,
},
},
})
}
2014-10-10 06:22:35 +02:00
func TestInterpolateFuncJoin(t *testing.T) {
2015-01-13 21:06:04 +01:00
testFunction(t, testFunctionConfig{
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-22 02:03:24 +02:00
Vars: map[string]ast.Variable{
"var.a_list": interfaceToVariableSwallowError([]string{"foo"}),
"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
"var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}),
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-22 02:03:24 +02:00
},
2015-01-13 21:06:04 +01:00
Cases: []testFunctionCase{
{
`${join(",")}`,
nil,
true,
},
2014-10-10 06:22:35 +02:00
2015-01-13 21:06:04 +01: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-22 02:03:24 +02:00
`${join(",", var.a_list)}`,
2015-01-13 21:06:04 +01:00
"foo",
false,
},
{
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-22 02:03:24 +02:00
`${join(".", var.a_longer_list)}`,
2015-01-13 21:06:04 +01:00
"foo.bar.baz",
false,
},
{
`${join(".", var.list_of_lists)}`,
nil,
true,
},
{
`${join(".", list(list("nested")))}`,
nil,
true,
},
2014-10-10 06:22:35 +02:00
},
})
2014-10-10 06:22:35 +02:00
}
func TestInterpolateFuncJSONEncode(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"easy": ast.Variable{
Value: "test",
Type: ast.TypeString,
},
"hard": ast.Variable{
Value: " foo \\ \n \t \" bar ",
Type: ast.TypeString,
},
"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
"emptylist": ast.Variable{
Value: []ast.Variable{},
Type: ast.TypeList,
},
"map": interfaceToVariableSwallowError(map[string]string{
"foo": "bar",
"ba \n z": "q\\x",
}),
"emptymap": interfaceToVariableSwallowError(map[string]string{}),
"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
"nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
},
Cases: []testFunctionCase{
{
`${jsonencode("test")}`,
`"test"`,
false,
},
{
`${jsonencode(easy)}`,
`"test"`,
false,
},
{
`${jsonencode(hard)}`,
`" foo \\ \n \t \" bar "`,
false,
},
{
`${jsonencode("")}`,
`""`,
false,
},
{
`${jsonencode()}`,
nil,
true,
},
{
`${jsonencode(list)}`,
`["foo","bar\tbaz"]`,
false,
},
{
`${jsonencode(emptylist)}`,
`[]`,
false,
},
{
`${jsonencode(map)}`,
`{"ba \n z":"q\\x","foo":"bar"}`,
false,
},
{
`${jsonencode(emptymap)}`,
`{}`,
false,
},
{
`${jsonencode(nestedlist)}`,
`[["foo"]]`,
false,
},
{
`${jsonencode(nestedmap)}`,
`{"foo":["bar"]}`,
false,
},
},
})
}
2015-03-02 18:37:40 +01:00
func TestInterpolateFuncReplace(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// Regular search and replace
{
`${replace("hello", "hel", "bel")}`,
"bello",
false,
},
// Search string doesn't match
{
`${replace("hello", "nope", "bel")}`,
"hello",
false,
},
// Regular expression
{
`${replace("hello", "/l/", "L")}`,
"heLLo",
false,
},
{
`${replace("helo", "/(l)/", "$1$1")}`,
"hello",
false,
},
2015-03-02 18:44:45 +01:00
// Bad regexp
{
`${replace("helo", "/(l/", "$1$1")}`,
nil,
true,
},
2015-03-02 18:37:40 +01:00
},
})
}
func TestInterpolateFuncReverse(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.inputlist": {
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "a"},
{Type: ast.TypeString, Value: "b"},
{Type: ast.TypeString, Value: "1"},
{Type: ast.TypeString, Value: "d"},
},
},
"var.emptylist": {
Type: ast.TypeList,
// Intentionally 0-lengthed list
Value: []ast.Variable{},
},
},
Cases: []testFunctionCase{
{
`${reverse(var.inputlist)}`,
[]interface{}{"d", "1", "b", "a"},
false,
},
{
`${reverse(var.emptylist)}`,
[]interface{}{},
false,
},
},
})
}
2015-04-12 16:17:26 +02:00
func TestInterpolateFuncLength(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// Raw strings
{
`${length("")}`,
"0",
false,
},
{
`${length("a")}`,
"1",
false,
},
{
`${length(" ")}`,
"1",
false,
},
{
`${length(" a ,")}`,
"4",
false,
},
{
`${length("aaa")}`,
"3",
false,
},
// Lists
{
`${length(split(",", "a"))}`,
"1",
false,
},
{
`${length(split(",", "foo,"))}`,
"2",
false,
},
{
`${length(split(",", ",foo,"))}`,
"3",
false,
},
{
`${length(split(",", "foo,bar"))}`,
"2",
false,
},
{
`${length(split(".", "one.two.three.four.five"))}`,
"5",
false,
},
2015-10-25 19:55:21 +01:00
// Want length 0 if we split an empty string then compact
{
`${length(compact(split(",", "")))}`,
"0",
false,
},
// Works for maps
{
`${length(map("k", "v"))}`,
"1",
false,
},
{
`${length(map("k1", "v1", "k2", "v2"))}`,
"2",
false,
},
2015-04-12 16:17:26 +02:00
},
})
}
func TestInterpolateFuncSignum(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${signum()}`,
nil,
true,
},
{
`${signum("")}`,
nil,
true,
},
{
`${signum(0)}`,
"0",
false,
},
{
`${signum(15)}`,
"1",
false,
},
{
`${signum(-29)}`,
"-1",
false,
},
},
})
}
func TestInterpolateFuncSlice(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// Negative from index
{
`${slice(list("a"), -1, 0)}`,
nil,
true,
},
// From index > to index
{
`${slice(list("a", "b", "c"), 2, 1)}`,
nil,
true,
},
// To index too large
{
`${slice(var.list_of_strings, 1, 4)}`,
nil,
true,
},
// Empty slice
{
`${slice(var.list_of_strings, 1, 1)}`,
[]interface{}{},
false,
},
{
`${slice(var.list_of_strings, 1, 2)}`,
[]interface{}{"b"},
false,
},
{
`${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`,
[]interface{}{"a", "b"},
false,
},
},
Vars: map[string]ast.Variable{
"var.list_of_strings": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "a",
},
{
Type: ast.TypeString,
Value: "b",
},
{
Type: ast.TypeString,
Value: "c",
},
},
},
},
})
}
func TestInterpolateFuncSort(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.strings": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "c"},
{Type: ast.TypeString, Value: "a"},
{Type: ast.TypeString, Value: "b"},
},
},
"var.notstrings": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeList, Value: []ast.Variable{}},
{Type: ast.TypeString, Value: "b"},
},
},
},
Cases: []testFunctionCase{
{
`${sort(var.strings)}`,
[]interface{}{"a", "b", "c"},
false,
},
{
`${sort(var.notstrings)}`,
nil,
true,
},
},
})
}
2015-01-28 22:59:16 +01:00
func TestInterpolateFuncSplit(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${split(",")}`,
nil,
true,
},
{
`${split(",", "")}`,
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-22 02:03:24 +02:00
[]interface{}{""},
false,
},
2015-01-28 22:59:16 +01:00
{
`${split(",", "foo")}`,
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-22 02:03:24 +02:00
[]interface{}{"foo"},
2015-01-28 22:59:16 +01:00
false,
},
2015-04-12 16:17:26 +02:00
{
`${split(",", ",,,")}`,
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-22 02:03:24 +02:00
[]interface{}{"", "", "", ""},
2015-04-12 16:17:26 +02:00
false,
},
{
`${split(",", "foo,")}`,
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-22 02:03:24 +02:00
[]interface{}{"foo", ""},
2015-04-12 16:17:26 +02:00
false,
},
{
`${split(",", ",foo,")}`,
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-22 02:03:24 +02:00
[]interface{}{"", "foo", ""},
2015-04-12 16:17:26 +02:00
false,
},
2015-01-28 22:59:16 +01:00
{
`${split(".", "foo.bar.baz")}`,
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-22 02:03:24 +02:00
[]interface{}{"foo", "bar", "baz"},
2015-01-28 22:59:16 +01:00
false,
},
},
})
}
2014-07-21 22:12:43 +02:00
func TestInterpolateFuncLookup(t *testing.T) {
2015-01-13 21:06:04 +01:00
testFunction(t, testFunctionConfig{
2015-01-15 07:01:42 +01:00
Vars: map[string]ast.Variable{
"var.foo": {
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"bar": {
Type: ast.TypeString,
Value: "baz",
},
},
2015-01-14 19:40:43 +01:00
},
"var.map_of_lists": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"bar": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "baz",
},
},
},
},
},
2015-01-14 19:40:43 +01:00
},
2015-01-13 21:06:04 +01:00
Cases: []testFunctionCase{
{
`${lookup(var.foo, "bar")}`,
2015-01-13 21:06:04 +01:00
"baz",
false,
2014-07-21 22:12:43 +02:00
},
2015-01-13 21:06:04 +01:00
// Invalid key
{
`${lookup(var.foo, "baz")}`,
2015-01-13 21:06:04 +01:00
nil,
true,
2014-07-21 22:12:43 +02:00
},
// Supplied default with valid key
{
`${lookup(var.foo, "bar", "")}`,
"baz",
false,
},
// Supplied default with invalid key
{
`${lookup(var.foo, "zip", "")}`,
"",
false,
},
2015-01-13 21:06:04 +01:00
// Too many args
{
`${lookup(var.foo, "bar", "", "abc")}`,
2015-01-13 21:06:04 +01:00
nil,
true,
2014-07-21 22:12:43 +02:00
},
// Cannot lookup into map of lists
{
`${lookup(var.map_of_lists, "bar")}`,
nil,
true,
},
// Non-empty default
{
`${lookup(var.foo, "zap", "xyz")}`,
"xyz",
false,
},
2014-07-21 22:12:43 +02:00
},
2015-01-13 21:06:04 +01:00
})
2014-07-21 22:12:43 +02:00
}
func TestInterpolateFuncKeys(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.foo": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"bar": ast.Variable{
Value: "baz",
Type: ast.TypeString,
},
"qux": ast.Variable{
Value: "quack",
Type: ast.TypeString,
},
},
},
"var.str": ast.Variable{
Value: "astring",
Type: ast.TypeString,
},
},
Cases: []testFunctionCase{
{
`${keys(var.foo)}`,
[]interface{}{"bar", "qux"},
false,
},
// Invalid key
{
`${keys(var.not)}`,
nil,
true,
},
// Too many args
{
`${keys(var.foo, "bar")}`,
nil,
true,
},
// Not a map
{
`${keys(var.str)}`,
nil,
true,
},
},
})
}
// Confirm that keys return in sorted order, and values return in the order of
// their sorted keys.
func TestInterpolateFuncKeyValOrder(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.foo": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"D": ast.Variable{
Value: "2",
Type: ast.TypeString,
},
"C": ast.Variable{
Value: "Y",
Type: ast.TypeString,
},
"A": ast.Variable{
Value: "X",
Type: ast.TypeString,
},
"10": ast.Variable{
Value: "Z",
Type: ast.TypeString,
},
"1": ast.Variable{
Value: "4",
Type: ast.TypeString,
},
"3": ast.Variable{
Value: "W",
Type: ast.TypeString,
},
},
},
},
Cases: []testFunctionCase{
{
`${keys(var.foo)}`,
[]interface{}{"1", "10", "3", "A", "C", "D"},
false,
},
{
`${values(var.foo)}`,
[]interface{}{"4", "Z", "W", "X", "Y", "2"},
false,
},
},
})
}
func TestInterpolateFuncValues(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.foo": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"bar": ast.Variable{
Value: "quack",
Type: ast.TypeString,
},
"qux": ast.Variable{
Value: "baz",
Type: ast.TypeString,
},
},
},
"var.str": ast.Variable{
Value: "astring",
Type: ast.TypeString,
},
},
Cases: []testFunctionCase{
{
`${values(var.foo)}`,
[]interface{}{"quack", "baz"},
false,
},
// Invalid key
{
`${values(var.not)}`,
nil,
true,
},
// Too many args
{
`${values(var.foo, "bar")}`,
nil,
true,
},
// Not a map
{
`${values(var.str)}`,
nil,
true,
},
// Map of lists
{
`${values(map("one", list()))}`,
nil,
true,
},
},
})
}
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-22 02:03:24 +02:00
func interfaceToVariableSwallowError(input interface{}) ast.Variable {
variable, _ := hil.InterfaceToVariable(input)
return variable
}
func TestInterpolateFuncElement(t *testing.T) {
2015-01-13 21:06:04 +01:00
testFunction(t, testFunctionConfig{
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-22 02:03:24 +02:00
Vars: map[string]ast.Variable{
"var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}),
"var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}),
"var.empty_list": interfaceToVariableSwallowError([]interface{}{}),
"var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}),
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-22 02:03:24 +02:00
},
2015-01-13 21:06:04 +01:00
Cases: []testFunctionCase{
{
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-22 02:03:24 +02:00
`${element(var.a_list, "1")}`,
2015-01-13 21:06:04 +01:00
"baz",
false,
},
2015-01-13 21:06:04 +01: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-22 02:03:24 +02:00
`${element(var.a_short_list, "0")}`,
2015-01-13 21:06:04 +01:00
"foo",
false,
},
2015-01-13 21:06:04 +01:00
// Invalid index should wrap vs. out-of-bounds
{
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-22 02:03:24 +02:00
`${element(var.a_list, "2")}`,
2015-01-13 21:06:04 +01:00
"foo",
false,
},
// Negative number should fail
{
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-22 02:03:24 +02:00
`${element(var.a_short_list, "-1")}`,
nil,
true,
},
// Empty list should fail
{
`${element(var.empty_list, 0)}`,
nil,
true,
},
2015-01-13 21:06:04 +01:00
// Too many args
{
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-22 02:03:24 +02:00
`${element(var.a_list, "0", "2")}`,
2015-01-13 21:06:04 +01:00
nil,
true,
},
// Only works on single-level lists
{
`${element(var.a_nested_list, "0")}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncChunklist(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// normal usage
{
`${chunklist(list("a", "b", "c"), 1)}`,
[]interface{}{
[]interface{}{"a"},
[]interface{}{"b"},
[]interface{}{"c"},
},
false,
},
// `size` is pair and the list has an impair number of items
{
`${chunklist(list("a", "b", "c"), 2)}`,
[]interface{}{
[]interface{}{"a", "b"},
[]interface{}{"c"},
},
false,
},
// list made of the same list, since size is 0
{
`${chunklist(list("a", "b", "c"), 0)}`,
[]interface{}{[]interface{}{"a", "b", "c"}},
false,
},
// negative size of chunks
{
`${chunklist(list("a", "b", "c"), -1)}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncBasename(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${basename("/foo/bar/baz")}`,
"baz",
false,
},
},
})
}
func TestInterpolateFuncBase64Encode(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// Regular base64 encoding
{
`${base64encode("abc123!?$*&()'-=@~")}`,
"YWJjMTIzIT8kKiYoKSctPUB+",
false,
},
},
})
}
func TestInterpolateFuncBase64Decode(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// Regular base64 decoding
{
`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
"abc123!?$*&()'-=@~",
false,
},
// Invalid base64 data decoding
{
`${base64decode("this-is-an-invalid-base64-data")}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncLower(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${lower("HELLO")}`,
"hello",
false,
},
{
`${lower("")}`,
"",
false,
},
{
`${lower()}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncUpper(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${upper("hello")}`,
"HELLO",
false,
},
{
`${upper("")}`,
"",
false,
},
{
`${upper()}`,
nil,
true,
},
},
})
}
2015-12-27 23:28:56 +01:00
func TestInterpolateFuncSha1(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${sha1("test")}`,
"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
false,
},
},
})
}
func TestInterpolateFuncSha256(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
2016-01-29 13:09:57 +01:00
{ // hexadecimal representation of sha256 sum
`${sha256("test")}`,
"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
false,
},
},
})
}
2017-05-03 01:35:23 +02:00
func TestInterpolateFuncSha512(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${sha512("test")}`,
"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
false,
},
},
})
}
func TestInterpolateFuncTitle(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${title("hello")}`,
"Hello",
false,
},
{
`${title("hello world")}`,
"Hello World",
false,
},
{
`${title("")}`,
"",
false,
},
{
`${title()}`,
nil,
true,
},
},
})
}
2016-01-30 10:52:45 +01:00
func TestInterpolateFuncTrimSpace(t *testing.T) {
2016-01-30 00:28:04 +01:00
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
2016-01-30 10:52:45 +01:00
`${trimspace(" test ")}`,
2016-01-30 00:28:04 +01:00
"test",
false,
},
},
})
}
func TestInterpolateFuncBase64Gzip(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${base64gzip("test")}`,
"H4sIAAAAAAAA/ypJLS4BAAAA//8BAAD//wx+f9gEAAAA",
false,
2016-01-30 00:28:04 +01:00
},
},
})
}
2016-01-29 13:09:57 +01:00
func TestInterpolateFuncBase64Sha256(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${base64sha256("test")}`,
"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
false,
},
{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
`${base64encode(sha256("test"))}`,
"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
false,
},
},
})
}
2017-05-03 01:35:23 +02:00
func TestInterpolateFuncBase64Sha512(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${base64sha512("test")}`,
"7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==",
false,
},
{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
`${base64encode(sha512("test"))}`,
"ZWUyNmIwZGQ0YWY3ZTc0OWFhMWE4ZWUzYzEwYWU5OTIzZjYxODk4MDc3MmU0NzNmODgxOWE1ZDQ5NDBlMGRiMjdhYzE4NWY4YTBlMWQ1Zjg0Zjg4YmM4ODdmZDY3YjE0MzczMmMzMDRjYzVmYTlhZDhlNmY1N2Y1MDAyOGE4ZmY=",
false,
},
},
})
}
func TestInterpolateFuncMd5(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${md5("tada")}`,
"ce47d07243bb6eaf5e1322c81baf9bbf",
false,
},
{ // Confirm that we're not trimming any whitespaces
`${md5(" tada ")}`,
"aadf191a583e53062de2d02c008141c4",
false,
},
{ // We accept empty string too
`${md5("")}`,
"d41d8cd98f00b204e9800998ecf8427e",
false,
},
},
})
}
func TestInterpolateFuncUUID(t *testing.T) {
results := make(map[string]bool)
for i := 0; i < 100; i++ {
ast, err := hil.Parse("${uuid()}")
if err != nil {
t.Fatalf("err: %s", err)
}
result, err := hil.Eval(ast, langEvalConfig(nil))
if err != nil {
t.Fatalf("err: %s", err)
}
if results[result.Value.(string)] {
t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
}
results[result.Value.(string)] = true
}
}
func TestInterpolateFuncTimestamp(t *testing.T) {
currentTime := time.Now().UTC()
ast, err := hil.Parse("${timestamp()}")
if err != nil {
t.Fatalf("err: %s", err)
}
result, err := hil.Eval(ast, langEvalConfig(nil))
if err != nil {
t.Fatalf("err: %s", err)
}
resultTime, err := time.Parse(time.RFC3339, result.Value.(string))
if err != nil {
t.Fatalf("Error parsing timestamp: %s", err)
}
if resultTime.Sub(currentTime).Seconds() > 10.0 {
2017-03-20 18:15:27 +01:00
t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string))
}
}
func TestInterpolateFuncTimeAdd(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${timeadd("2017-11-22T00:00:00Z", "1s")}`,
"2017-11-22T00:00:01Z",
false,
},
{
`${timeadd("2017-11-22T00:00:00Z", "10m1s")}`,
"2017-11-22T00:10:01Z",
false,
},
{ // also support subtraction
`${timeadd("2017-11-22T00:00:00Z", "-1h")}`,
"2017-11-21T23:00:00Z",
false,
},
{ // Invalid format timestamp
`${timeadd("2017-11-22", "-1h")}`,
nil,
true,
},
{ // Invalid format duration (day is not supported by ParseDuration)
`${timeadd("2017-11-22T00:00:00Z", "1d")}`,
nil,
true,
},
},
})
}
2015-01-13 21:06:04 +01:00
type testFunctionConfig struct {
Cases []testFunctionCase
2015-01-15 07:01:42 +01:00
Vars map[string]ast.Variable
2015-01-13 21:06:04 +01:00
}
type testFunctionCase struct {
Input string
Result interface{}
Error bool
}
2015-01-13 21:06:04 +01:00
func testFunction(t *testing.T, config testFunctionConfig) {
t.Helper()
for _, tc := range config.Cases {
t.Run(tc.Input, func(t *testing.T) {
ast, err := hil.Parse(tc.Input)
if err != nil {
t.Fatalf("unexpected parse error: %s", err)
}
result, err := hil.Eval(ast, langEvalConfig(config.Vars))
if err != nil != tc.Error {
t.Fatalf("unexpected eval error: %s", err)
}
if !reflect.DeepEqual(result.Value, tc.Result) {
t.Errorf("wrong result\ngiven: %s\ngot: %#v\nwant: %#v", tc.Input, result.Value, tc.Result)
}
})
}
}
func TestInterpolateFuncPathExpand(t *testing.T) {
homePath, err := homedir.Dir()
if err != nil {
t.Fatalf("Error getting home directory: %v", err)
}
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${pathexpand("~/test-file")}`,
filepath.Join(homePath, "test-file"),
false,
},
{
`${pathexpand("~/another/test/file")}`,
filepath.Join(homePath, "another/test/file"),
false,
},
{
`${pathexpand("/root/file")}`,
"/root/file",
false,
},
{
`${pathexpand("/")}`,
"/",
false,
},
{
`${pathexpand()}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncSubstr(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${substr("foobar", 0, 0)}`,
"",
false,
},
{
`${substr("foobar", 0, -1)}`,
"foobar",
false,
},
{
`${substr("foobar", 0, 3)}`,
"foo",
false,
},
{
`${substr("foobar", 3, 3)}`,
"bar",
false,
},
{
`${substr("foobar", -3, 3)}`,
"bar",
false,
},
// empty string
{
`${substr("", 0, 0)}`,
"",
false,
},
// invalid offset
{
`${substr("", 1, 0)}`,
nil,
true,
},
{
`${substr("foo", -4, -1)}`,
nil,
true,
},
// invalid length
{
`${substr("", 0, 1)}`,
nil,
true,
},
{
`${substr("", 0, -2)}`,
nil,
true,
},
},
})
}
2017-05-22 12:02:32 +02:00
func TestInterpolateFuncBcrypt(t *testing.T) {
node, err := hil.Parse(`${bcrypt("test")}`)
if err != nil {
t.Fatalf("err: %s", err)
}
result, err := hil.Eval(node, langEvalConfig(nil))
if err != nil {
t.Fatalf("err: %s", err)
}
err = bcrypt.CompareHashAndPassword([]byte(result.Value.(string)), []byte("test"))
if err != nil {
t.Fatalf("Error comparing hash and password: %s", err)
}
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
//Negative test for more than two parameters
{
`${bcrypt("test", 15, 12)}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncFlatten(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// empty string within array
{
`${flatten(split(",", "a,,b"))}`,
[]interface{}{"a", "", "b"},
false,
},
// typical array
{
`${flatten(split(",", "a,b,c"))}`,
[]interface{}{"a", "b", "c"},
false,
},
// empty array
{
`${flatten(split(",", ""))}`,
[]interface{}{""},
false,
},
// list of lists
{
`${flatten(list(list("a"), list("b")))}`,
[]interface{}{"a", "b"},
false,
},
// list of lists of lists
{
`${flatten(list(list("a"), list(list("b","c"))))}`,
[]interface{}{"a", "b", "c"},
false,
},
// list of strings
{
`${flatten(list("a", "b", "c"))}`,
[]interface{}{"a", "b", "c"},
false,
},
},
})
}
func TestInterpolateFuncURLEncode(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${urlencode("abc123-_")}`,
"abc123-_",
false,
},
{
`${urlencode("foo:bar@localhost?foo=bar&bar=baz")}`,
"foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz",
false,
},
{
`${urlencode("mailto:email?subject=this+is+my+subject")}`,
"mailto%3Aemail%3Fsubject%3Dthis%2Bis%2Bmy%2Bsubject",
false,
},
{
`${urlencode("foo/bar")}`,
"foo%2Fbar",
false,
},
},
})
}
func TestInterpolateFuncTranspose(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.map": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "a"},
{Type: ast.TypeString, Value: "b"},
},
},
"key2": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "a"},
{Type: ast.TypeString, Value: "b"},
{Type: ast.TypeString, Value: "c"},
},
},
"key3": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "c"},
},
},
"key4": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{},
},
}},
"var.badmap": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeList, Value: []ast.Variable{}},
{Type: ast.TypeList, Value: []ast.Variable{}},
},
},
}},
"var.worsemap": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": ast.Variable{
Type: ast.TypeString,
Value: "not-a-list",
},
}},
},
Cases: []testFunctionCase{
{
`${transpose(var.map)}`,
map[string]interface{}{
"a": []interface{}{"key1", "key2"},
"b": []interface{}{"key1", "key2"},
"c": []interface{}{"key2", "key3"},
},
false,
},
{
`${transpose(var.badmap)}`,
nil,
true,
},
{
`${transpose(var.worsemap)}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncAbs(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${abs()}`,
nil,
true,
},
{
`${abs("")}`,
nil,
true,
},
{
`${abs(0)}`,
"0",
false,
},
{
`${abs(1)}`,
"1",
false,
},
{
`${abs(-1)}`,
"1",
false,
},
{
`${abs(1.0)}`,
"1",
false,
},
{
`${abs(-1.0)}`,
"1",
false,
},
{
`${abs(-3.14)}`,
"3.14",
false,
},
{
`${abs(-42.001)}`,
"42.001",
false,
},
},
})
}
func TestInterpolateFuncRsaDecrypt(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.cipher_base64": ast.Variable{
Type: ast.TypeString,
Value: "eczGaDhXDbOFRZGhjx2etVzWbRqWDlmq0bvNt284JHVbwCgObiuyX9uV0LSAMY707IEgMkExJqXmsB4OWKxvB7epRB9G/3+F+pcrQpODlDuL9oDUAsa65zEpYF0Wbn7Oh7nrMQncyUPpyr9WUlALl0gRWytOA23S+y5joa4M34KFpawFgoqTu/2EEH4Xl1zo+0fy73fEto+nfkUY+meuyGZ1nUx/+DljP7ZqxHBFSlLODmtuTMdswUbHbXbWneW51D7Jm7xB8nSdiA2JQNK5+Sg5x8aNfgvFTt/m2w2+qpsyFa5Wjeu6fZmXSl840CA07aXbk9vN4I81WmJyblD/ZA==",
},
"var.private_key": ast.Variable{
Type: ast.TypeString,
Value: `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9
c1zEekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPV
Xcxae4MR0BEegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER
1v6eHQa/nchi03MBpT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7
r6v24u/vp/QTmBIAlNPgadVAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZ
pqcAM8wHRph8mD1EfL9hsz77pHewxolBATV+7QIDAQABAoIBAC1rK+kFW3vrAYm3
+8/fQnQQw5nec4o6+crng6JVQXLeH32qXShNf8kLLG/Jj0vaYcTPPDZw9JCKkTMQ
0mKj9XR/5DLbBMsV6eNXXuvJJ3x4iKW5eD9WkLD4FKlNarBRyO7j8sfPTqXW7uat
NxWdFH7YsSRvNh/9pyQHLWA5OituidMrYbc3EUx8B1GPNyJ9W8Q8znNYLfwYOjU4
Wv1SLE6qGQQH9Q0WzA2WUf8jklCYyMYTIywAjGb8kbAJlKhmj2t2Igjmqtwt1PYc
pGlqbtQBDUiWXt5S4YX/1maIQ/49yeNUajjpbJiH3DbhJbHwFTzP3pZ9P9GHOzlG
kYR+wSECgYEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS
Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvd
qcliF5vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUCgYEAqNsw
1aSl7WJa27F0DoJdlU9LWerpXcazlJcIdOz/S9QDmSK3RDQTdqfTxRmrxiYI9LEs
mkOkvzlnnOBMpnZ3ZOU5qIRfprecRIi37KDAOHWGnlC0EWGgl46YLb7/jXiWf0AG
Y+DfJJNd9i6TbIDWu8254/erAS6bKMhW/3q7f2kCgYAZ7Id/BiKJAWRpqTRBXlvw
BhXoKvjI2HjYP21z/EyZ+PFPzur/lNaZhIUlMnUfibbwE9pFggQzzf8scM7c7Sf+
mLoVSdoQ/Rujz7CqvQzi2nKSsM7t0curUIb3lJWee5/UeEaxZcmIufoNUrzohAWH
BJOIPDM4ssUTLRq7wYM9uQKBgHCBau5OP8gE6mjKuXsZXWUoahpFLKwwwmJUp2vQ
pOFPJ/6WZOlqkTVT6QPAcPUbTohKrF80hsZqZyDdSfT3peFx4ZLocBrS56m6NmHR
UYHMvJ8rQm76T1fryHVidz85g3zRmfBeWg8yqT5oFg4LYgfLsPm1gRjOhs8LfPvI
OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56
RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh
T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7
-----END RSA PRIVATE KEY-----
`,
},
"var.wrong_private_key": ast.Variable{
Type: ast.TypeString,
Value: `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAlrCgnEVgmNKCq7KPc+zUU5IrxPu1ClMNJS7RTsTPEkbwe5SB
p+6V6WtCbD/X/lDRRGbOENChh1Phulb7lViqgrdpHydgsrKoS5ah3DfSIxLFLE00
9Yo4TCYwgw6+s59j16ZAFVinaQ9l6Kmrb2ll136hMrz8QKh+qw+onOLd38WFgm+W
ZtUqSXf2LANzfzzy4OWFNyFqKaCAolSkPdTS9Nz+svtScvp002DQp8OdP1AgPO+l
o5N3M38Fftapwg0pCtJ5Zq0NRWIXEonXiTEMA6zy3gEZVOmDxoIFUWnmrqlMJLFy
5S6LDrHSdqJhCxDK6WRZj43X9j8spktk3eGhMwIDAQABAoIBAAem8ID/BOi9x+Tw
LFi2rhGQWqimH4tmrEQ3HGnjlKBY+d1MrUjZ1MMFr1nP5CgF8pqGnfA8p/c3Sz8r
K5tp5T6+EZiDZ2WrrOApxg5ox0MAsQKO6SGO40z6o3wEQ6rbbTaGOrraxaWQIpyu
AQanU4Sd6ZGqByVBaS1GnklZO+shCHqw73b7g1cpLEmFzcYnKHYHlUUIsstMe8E1
BaCY0CH7JbWBjcbiTnBVwIRZuu+EjGiQuhTilYL2OWqoMVg1WU0L2IFpR8lkf/2W
SBx5J6xhwbBGASOpM+qidiN580GdPzGhWYSqKGroHEzBm6xPSmV1tadNA26WFG4p
pthLiAECgYEA5BsPRpNYJAQLu5B0N7mj9eEp0HABVEgL/MpwiImjaKdAwp78HM64
IuPvJxs7r+xESiIz4JyjR8zrQjYOCKJsARYkmNlEuAz0SkHabCw1BdEBwUhjUGVB
efoERK6GxfAoNqmSDwsOvHFOtsmDIlbHmg7G2rUxNVpeou415BSB0B8CgYEAqR4J
YHKk2Ibr9rU+rBU33TcdTGw0aAkFNAVeqM9j0haWuFXmV3RArgoy09lH+2Ha6z/g
fTX2xSDAWV7QUlLOlBRIhurPAo2jO2yCrGHPZcWiugstrR2hTTInigaSnCmK3i7F
6sYmL3S7K01IcVNxSlWvGijtClT92Cl2WUCTfG0CgYAiEjyk4QtQTd5mxLvnOu5X
oqs5PBGmwiAwQRiv/EcRMbJFn7Oupd3xMDSflbzDmTnWDOfMy/jDl8MoH6TW+1PA
kcsjnYhbKWwvz0hN0giVdtOZSDO1ZXpzOrn6fEsbM7T9/TQY1SD9WrtUKCNTNL0Z
sM1ZC6lu+7GZCpW4HKwLJwKBgQCRT0yxQXBg1/UxwuO5ynV4rx2Oh76z0WRWIXMH
S0MyxdP1SWGkrS/SGtM3cg/GcHtA/V6vV0nUcWK0p6IJyjrTw2XZ/zGluPuTWJYi
9dvVT26Vunshrz7kbH7KuwEICy3V4IyQQHeY+QzFlR70uMS0IVFWAepCoWqHbIDT
CYhwNQKBgGPcLXmjpGtkZvggl0aZr9LsvCTckllSCFSI861kivL/rijdNoCHGxZv
dfDkLTLcz9Gk41rD9Gxn/3sqodnTAc3Z2PxFnzg1Q/u3+x6YAgBwI/g/jE2xutGW
H7CurtMwALQ/n/6LUKFmjRZjqbKX9SO2QSaC3grd6sY9Tu+bZjLe
-----END RSA PRIVATE KEY-----
`,
},
},
Cases: []testFunctionCase{
// Base-64 encoded cipher decrypts correctly
{
`${rsadecrypt(var.cipher_base64, var.private_key)}`,
"message",
false,
},
// Raw cipher
{
`${rsadecrypt(base64decode(var.cipher_base64), var.private_key)}`,
nil,
true,
},
// Wrong key
{
`${rsadecrypt(var.cipher_base64, var.wrong_private_key)}`,
nil,
true,
},
// Bad key
{
`${rsadecrypt(var.cipher_base64, "bad key")}`,
nil,
true,
},
// Empty key
{
`${rsadecrypt(var.cipher_base64, "")}`,
nil,
true,
},
// Bad cipher
{
`${rsadecrypt("bad cipher", var.private_key)}`,
nil,
true,
},
// Bad base64-encoded cipher
{
`${rsadecrypt(base64encode("bad cipher"), var.private_key)}`,
nil,
true,
},
// Empty cipher
{
`${rsadecrypt("", var.private_key)}`,
nil,
true,
},
// Too many arguments
{
`${rsadecrypt("", "", "")}`,
nil,
true,
},
// One argument
{
`${rsadecrypt("")}`,
nil,
true,
},
// No arguments
{
`${rsadecrypt()}`,
nil,
true,
},
},
})
}