diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 2a94a8ad6..cbba62d0d 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io/ioutil" + "strconv" "strings" ) @@ -16,6 +17,7 @@ func init() { "file": interpolationFuncFile, "join": interpolationFuncJoin, "lookup": interpolationFuncLookup, + "element": interpolationFuncElement, } } @@ -87,3 +89,26 @@ func interpolationFuncLookup( return v, nil } + +// interpolationFuncElement implements the "element" function that allows +// a specific index to be looked up in a multi-variable value. Note that this will +// wrap if the index is larger than the number of elements in the multi-variable value. +func interpolationFuncElement( + vs map[string]string, args ...string) (string, error) { + if len(args) != 2 { + return "", fmt.Errorf( + "element expects 2 arguments, got %d", len(args)) + } + + list := strings.Split(args[0], InterpSplitDelim) + + index, err := strconv.Atoi(args[1]) + if err != nil { + return "", fmt.Errorf( + "invalid number for index, got %s", args[1]) + } + + v := list[index % len(list)] + + return v, nil +} diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 93bb979c9..332b9af4b 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -189,3 +189,48 @@ func TestInterpolateFuncLookup(t *testing.T) { } } } + +func TestInterpolateFuncElement(t *testing.T) { + cases := []struct { + Args []string + Result string + Error bool + }{ + { + []string{"foo" + InterpSplitDelim + "baz", "1"}, + "baz", + false, + }, + + { + []string{"foo", "0"}, + "foo", + false, + }, + + // Invalid index should wrap vs. out-of-bounds + { + []string{"foo" + InterpSplitDelim + "baz", "2"}, + "foo", + false, + }, + + // Too many args + { + []string{"foo" + InterpSplitDelim + "baz", "0", "1"}, + "", + true, + }, + } + + for i, tc := range cases { + actual, err := interpolationFuncElement(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) + } + } +} diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index c0fe3372f..326d155fb 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -74,3 +74,10 @@ The supported built-in functions are: * `lookup(map, key)` - Performs a dynamic lookup into a mapping variable. + + * `element(list, index)` - Returns a single element from a list + at the given index. If the index is greater than the number of + elements, this function will wrap using a standard mod algorithm. + A list is only possible with splat variables from resources with + a count greater than one. + Example: `element(aws_subnet.foo.*.id, count.index)`