diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 20014aa2f3..2a94a8ad6e 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -14,6 +14,7 @@ func init() { Funcs = map[string]InterpolationFunc{ "concat": interpolationFuncConcat, "file": interpolationFuncFile, + "join": interpolationFuncJoin, "lookup": interpolationFuncLookup, } } @@ -50,6 +51,23 @@ func interpolationFuncFile( return string(data), nil } +// interpolationFuncJoin implements the "join" function that allows +// multi-variable values to be joined by some character. +func interpolationFuncJoin( + vs map[string]string, args ...string) (string, error) { + if len(args) < 2 { + return "", fmt.Errorf("join expects 2 arguments") + } + + var list []string + for _, arg := range args[1:] { + parts := strings.Split(arg, InterpSplitDelim) + list = append(list, parts...) + } + + return strings.Join(list, args[0]), nil +} + // interpolationFuncLookup implements the "lookup" function that allows // dynamic lookups of map types within a Terraform configuration. func interpolationFuncLookup( diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 0bb230e0a5..93bb979c9b 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "io/ioutil" "os" "testing" @@ -91,6 +92,55 @@ func TestInterpolateFuncFile(t *testing.T) { } } +func TestInterpolateFuncJoin(t *testing.T) { + cases := []struct { + Args []string + Result string + Error bool + }{ + { + []string{","}, + "", + true, + }, + + { + []string{",", "foo"}, + "foo", + false, + }, + + { + []string{",", "foo", "bar"}, + "foo,bar", + false, + }, + + { + []string{ + ".", + fmt.Sprintf( + "foo%sbar%sbaz", + InterpSplitDelim, + InterpSplitDelim), + }, + "foo.bar.baz", + false, + }, + } + + for i, tc := range cases { + actual, err := interpolationFuncJoin(nil, tc.Args...) + if (err != nil) != tc.Error { + t.Fatalf("%d: err: %s", i, err) + } + + if actual != tc.Result { + t.Fatalf("%d: bad: %#v", i, actual) + } + } +} + func TestInterpolateFuncLookup(t *testing.T) { cases := []struct { M map[string]string diff --git a/config/interpolate_walk_test.go b/config/interpolate_walk_test.go index f14e3e6bda..bfa42c705b 100644 --- a/config/interpolate_walk_test.go +++ b/config/interpolate_walk_test.go @@ -103,6 +103,32 @@ func TestInterpolationWalker_detect(t *testing.T) { }, }, }, + + { + Input: map[string]interface{}{ + "foo": `${join(",", foo.bar.*.id)}`, + }, + Result: []Interpolation{ + &FunctionInterpolation{ + Func: nil, + Args: []Interpolation{ + &LiteralInterpolation{ + Literal: ",", + }, + &VariableInterpolation{ + Variable: &ResourceVariable{ + Type: "foo", + Name: "bar", + Field: "id", + Multi: true, + Index: -1, + key: "foo.bar.*.id", + }, + }, + }, + }, + }, + }, } for i, tc := range cases { @@ -197,7 +223,7 @@ func TestInterpolationWalker_replace(t *testing.T) { }, }, Output: map[string]interface{}{}, - Value: UnknownVariableValue + InterpSplitDelim + "baz", + Value: UnknownVariableValue + InterpSplitDelim + "baz", }, }