From df3e017f6cfe1aeba68a279e6fca379eaa7da4fa Mon Sep 17 00:00:00 2001 From: Jan Schumann Date: Fri, 10 Jun 2016 17:11:51 +0200 Subject: [PATCH 1/2] fix #7106 --- config/interpolate_funcs.go | 37 ++++++++++++++++++++++++++++++++ config/interpolate_funcs_test.go | 25 +++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index c48152648..4054e61f1 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -71,6 +71,7 @@ func Funcs() map[string]ast.Function { "lower": interpolationFuncLower(), "md5": interpolationFuncMd5(), "uuid": interpolationFuncUUID(), + "uniq": interpolationFuncUniq(), "replace": interpolationFuncReplace(), "sha1": interpolationFuncSha1(), "sha256": interpolationFuncSha256(), @@ -382,6 +383,42 @@ func interpolationFuncIndex() ast.Function { } } +// interpolationFuncUniq implements the "uniq" function that +// removes duplicate elements from a list. +func interpolationFuncUniq() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeList}, + ReturnType: ast.TypeList, + Variadic: true, + VariadicType: ast.TypeList, + Callback: func(args []interface{}) (interface{}, error) { + var list []string + + if len(args) != 1 { + return nil, fmt.Errorf("uniq() excepts only one argument.") + } + + if argument, ok := args[0].([]ast.Variable); ok { + for _, element := range argument { + list = appendIfMissing(list, element.Value.(string)) + } + } + + return stringSliceToVariableValue(list), nil + }, + } +} + +// helper function to add an element to a list, if it does not already exsit +func appendIfMissing(slice []string, element string) []string { + for _, ele := range slice { + if ele == element { + return slice + } + } + return append(slice, element) +} + // interpolationFuncJoin implements the "join" function that allows // multi-variable values to be joined by some character. func interpolationFuncJoin() ast.Function { diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index f4c527ebf..f6e05c82d 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -261,6 +261,31 @@ func TestInterpolationFuncConcatListOfMaps(t *testing.T) { } } +func TestInterpolateFuncUniq(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + // 3 duplicates + { + `${uniq(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, + []interface{}{"user1", "user2", "user3"}, + false, + }, + // 1 duplicate + { + `${uniq(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, + []interface{}{"user1", "user2", "user3", "user4"}, + false, + }, + // too many args + { + `${uniq(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, + nil, + true, + }, + }, + }) +} + func TestInterpolateFuncFile(t *testing.T) { tf, err := ioutil.TempFile("", "tf") if err != nil { From 4b6a632246fba62827393816bacab49973ca1221 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 15 Jun 2016 13:24:33 +0200 Subject: [PATCH 2/2] core: Rename uniq -> distinct and add docs --- config/interpolate_funcs.go | 8 ++++---- config/interpolate_funcs_test.go | 8 ++++---- website/source/docs/configuration/interpolation.html.md | 4 ++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 4054e61f1..0ab4ce56b 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -60,6 +60,7 @@ func Funcs() map[string]ast.Function { "coalesce": interpolationFuncCoalesce(), "compact": interpolationFuncCompact(), "concat": interpolationFuncConcat(), + "distinct": interpolationFuncDistinct(), "element": interpolationFuncElement(), "file": interpolationFuncFile(), "format": interpolationFuncFormat(), @@ -71,7 +72,6 @@ func Funcs() map[string]ast.Function { "lower": interpolationFuncLower(), "md5": interpolationFuncMd5(), "uuid": interpolationFuncUUID(), - "uniq": interpolationFuncUniq(), "replace": interpolationFuncReplace(), "sha1": interpolationFuncSha1(), "sha256": interpolationFuncSha256(), @@ -383,9 +383,9 @@ func interpolationFuncIndex() ast.Function { } } -// interpolationFuncUniq implements the "uniq" function that +// interpolationFuncDistinct implements the "distinct" function that // removes duplicate elements from a list. -func interpolationFuncUniq() ast.Function { +func interpolationFuncDistinct() ast.Function { return ast.Function{ ArgTypes: []ast.Type{ast.TypeList}, ReturnType: ast.TypeList, @@ -395,7 +395,7 @@ func interpolationFuncUniq() ast.Function { var list []string if len(args) != 1 { - return nil, fmt.Errorf("uniq() excepts only one argument.") + return nil, fmt.Errorf("distinct() excepts only one argument.") } if argument, ok := args[0].([]ast.Variable); ok { diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index f6e05c82d..6a69d533a 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -261,24 +261,24 @@ func TestInterpolationFuncConcatListOfMaps(t *testing.T) { } } -func TestInterpolateFuncUniq(t *testing.T) { +func TestInterpolateFuncDistinct(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ // 3 duplicates { - `${uniq(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, + `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, []interface{}{"user1", "user2", "user3"}, false, }, // 1 duplicate { - `${uniq(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, + `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, []interface{}{"user1", "user2", "user3", "user4"}, false, }, // too many args { - `${uniq(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, + `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, nil, true, }, diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index 640637af3..6b38c7807 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -114,6 +114,10 @@ The supported built-in functions are: * `concat(list1, list2)` - Combines two or more lists into a single list. Example: `concat(aws_instance.db.*.tags.Name, aws_instance.web.*.tags.Name)` + * `distinct(list)` - Removes duplicate items from a list. Keeps the first + occurrence of each element, and removes subsequent occurences. + Example: `distinct(var.usernames)` + * `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.