diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 4ef70598a164..23cc9dfb0210 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -19,6 +19,7 @@ var Funcs map[string]ast.Function func init() { Funcs = map[string]ast.Function{ + "concat": interpolationFuncConcat(), "element": interpolationFuncElement(), "file": interpolationFuncFile(), "format": interpolationFuncFormat(), @@ -27,10 +28,6 @@ func init() { "length": interpolationFuncLength(), "replace": interpolationFuncReplace(), "split": interpolationFuncSplit(), - - // Concat is a little useless now since we supported embeddded - // interpolations but we keep it around for backwards compat reasons. - "concat": interpolationFuncConcat(), } } @@ -46,11 +43,32 @@ func interpolationFuncConcat() ast.Function { VariadicType: ast.TypeString, Callback: func(args []interface{}) (interface{}, error) { var b bytes.Buffer - for _, v := range args { - b.WriteString(v.(string)) + var finalList []string + + var isDeprecated = true + + for _, arg := range args { + argument := arg.(string) + + if len(argument) == 0 { + continue + } + + if strings.Contains(argument, InterpSplitDelim) { + isDeprecated = false + } + + finalList = append(finalList, argument) + + // Deprecated concat behaviour + b.WriteString(argument) + } + + if isDeprecated { + return b.String(), nil } - return b.String(), nil + return strings.Join(finalList, InterpSplitDelim), nil }, } } diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 0460d287629e..830a0154cfcd 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -5,13 +5,14 @@ import ( "io/ioutil" "os" "reflect" + "strings" "testing" "github.com/hashicorp/terraform/config/lang" "github.com/hashicorp/terraform/config/lang/ast" ) -func TestInterpolateFuncConcat(t *testing.T) { +func TestInterpolateFuncDeprecatedConcat(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ { @@ -35,6 +36,86 @@ func TestInterpolateFuncConcat(t *testing.T) { }) } +func TestInterpolateFuncConcat(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + // String + list + { + `${concat("a", split(",", "b,c"))}`, + fmt.Sprintf( + "%s%s%s%s%s", + "a", + InterpSplitDelim, + "b", + InterpSplitDelim, + "c"), + false, + }, + + // List + string + { + `${concat(split(",", "a,b"), "c")}`, + fmt.Sprintf( + "%s%s%s%s%s", + "a", + InterpSplitDelim, + "b", + InterpSplitDelim, + "c"), + false, + }, + + // Single list + { + `${concat(split(",", ",foo,"))}`, + fmt.Sprintf( + "%s%s%s", + InterpSplitDelim, + "foo", + InterpSplitDelim), + false, + }, + { + `${concat(split(",", "a,b,c"))}`, + fmt.Sprintf( + "%s%s%s%s%s", + "a", + InterpSplitDelim, + "b", + InterpSplitDelim, + "c"), + false, + }, + + // Two lists + { + `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, + strings.Join([]string{ + "a", "b", "c", "d", "e", + }, InterpSplitDelim), + false, + }, + // Two lists with different separators + { + `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, + strings.Join([]string{ + "a", "b", "c", "d", "e", + }, InterpSplitDelim), + false, + }, + + // More lists + { + `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, + strings.Join([]string{ + "a", "b", "c", "d", "e", "f", "0", "1", + }, InterpSplitDelim), + false, + }, + }, + }) +} + func TestInterpolateFuncFile(t *testing.T) { tf, err := ioutil.TempFile("", "tf") if err != nil { diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index 0dbff526b005..8235ce18d618 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -72,8 +72,8 @@ are documented below. The supported built-in functions are: - * `concat(args...)` - Concatenates the values of multiple arguments into - a single string. + * `concat(list1, list2)` - Combines two or more lists into a single list. + Example: `combine(aws_instance.db.*.tags.Name, aws_instance.web.*.tags.Name)` * `element(list, index)` - Returns a single element from a list at the given index. If the index is greater than the number of