diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 4859624f7..a91fb3fd7 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -75,6 +75,7 @@ func Funcs() map[string]ast.Function { "sha1": interpolationFuncSha1(), "sha256": interpolationFuncSha256(), "signum": interpolationFuncSignum(), + "sort": interpolationFuncSort(), "split": interpolationFuncSplit(), "trimspace": interpolationFuncTrimSpace(), "upper": interpolationFuncUpper(), @@ -529,6 +530,34 @@ func interpolationFuncSignum() ast.Function { } } +// interpolationFuncSort sorts a list of a strings lexographically +func interpolationFuncSort() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeList}, + ReturnType: ast.TypeList, + Variadic: false, + Callback: func(args []interface{}) (interface{}, error) { + inputList := args[0].([]ast.Variable) + + // Ensure that all the list members are strings and + // create a string slice from them + members := make([]string, len(inputList)) + for i, val := range inputList { + if val.Type != ast.TypeString { + return nil, fmt.Errorf( + "sort() may only be used with lists of strings - %s at index %d", + val.Type.String(), i) + } + + members[i] = val.Value.(string) + } + + sort.Strings(members) + return stringSliceToVariableValue(members), nil + }, + } +} + // interpolationFuncSplit implements the "split" function that allows // strings to split into multi-variable values func interpolationFuncSplit() ast.Function { diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index da12c22f5..3b2ea17cf 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -638,6 +638,40 @@ func TestInterpolateFuncSignum(t *testing.T) { }) } +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, + }, + }, + }) +} + func TestInterpolateFuncSplit(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index 3b5c57f79..640637af3 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -195,6 +195,11 @@ The supported built-in functions are: Example: `element(split(",", var.r53_failover_policy), signum(count.index))` where the 0th index points to `PRIMARY` and 1st to `FAILOVER` + * `sort(list)` - Returns a lexographically sorted list of the strings contained in + the list passed as an argument. Sort may only be used with lists which contain only + strings. + Examples: `sort(aws_instance.foo.*.id)`, `sort(var.list_of_strings)` + * `split(delim, string)` - Splits the string previously created by `join` back into a list. This is useful for pushing lists through module outputs since they currently only support string values. Depending on the