From 2bd7cfd5fe7b9e3cccbef8b96fd49f59e419cc7b Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 19 Jul 2016 13:39:40 -0400 Subject: [PATCH] Expand list interpolation to lists and maps Allow lists and maps within the list interpolation function via variable interpolation. Since this requires setting the variadic type to TypeAny, we check for non-heterogeneous lists in the callback. --- config/interpolate_funcs.go | 29 ++++++++-- config/interpolate_funcs_test.go | 55 ++++++++++++++++++- .../docs/configuration/interpolation.html.md | 2 +- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index f73df85e6c..a3cdf828ba 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -91,15 +91,34 @@ func interpolationFuncList() ast.Function { ArgTypes: []ast.Type{}, ReturnType: ast.TypeList, Variadic: true, - VariadicType: ast.TypeString, + VariadicType: ast.TypeAny, Callback: func(args []interface{}) (interface{}, error) { - var outputList []string + var outputList []ast.Variable - for _, val := range args { - outputList = append(outputList, val.(string)) + for i, val := range args { + switch v := val.(type) { + case string: + outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v}) + case []ast.Variable: + outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v}) + case map[string]ast.Variable: + outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v}) + default: + return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i) + } } - return stringSliceToVariableValue(outputList), nil + // we don't support heterogeneous types, so make sure all types match the first + if len(outputList) > 0 { + firstType := outputList[0].Type + for i, v := range outputList[1:] { + if v.Type != firstType { + return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1) + } + } + } + + return outputList, nil }, } } diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 7ee10372f2..5b9800c14c 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -38,7 +38,28 @@ func TestInterpolateFuncList(t *testing.T) { // not a string input gives error { - `${list("hello", "${var.list}")}`, + `${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, }, @@ -57,6 +78,38 @@ func TestInterpolateFuncList(t *testing.T) { }, }, }, + "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", + }, + }, + }, }, }) } diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index b2f66efd2b..338550377f 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -172,7 +172,7 @@ The supported built-in functions are: This function provides a way of representing list literals in interpolation. * `${list("a", "b", "c")}` returns a list of `"a", "b", "c"`. * `${list()}` returns an empty list. - + * `lookup(map, key [, default])` - Performs a dynamic lookup into a mapping variable. The `map` parameter should be another variable, such as `var.amis`. If `key` does not exist in `map`, the interpolation will