diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 23cc9dfb0..03bf070b2 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -131,8 +131,8 @@ func interpolationFuncFormatList() ast.Function { if !ok { continue } - parts := strings.Split(s, InterpSplitDelim) - if len(parts) == 1 { + parts := StringList(s).Slice() + if len(parts) <= 1 { continue } varargs[i-1] = parts @@ -167,7 +167,7 @@ func interpolationFuncFormatList() ast.Function { } list[i] = fmt.Sprintf(format, fmtargs...) } - return strings.Join(list, InterpSplitDelim), nil + return NewStringList(list).String(), nil }, } } @@ -181,7 +181,7 @@ func interpolationFuncJoin() ast.Function { Callback: func(args []interface{}) (interface{}, error) { var list []string for _, arg := range args[1:] { - parts := strings.Split(arg.(string), InterpSplitDelim) + parts := StringList(arg.(string)).Slice() list = append(list, parts...) } @@ -223,18 +223,15 @@ func interpolationFuncLength() ast.Function { ReturnType: ast.TypeInt, Variadic: false, Callback: func(args []interface{}) (interface{}, error) { - if !strings.Contains(args[0].(string), InterpSplitDelim) { + if !IsStringList(args[0].(string)) { return len(args[0].(string)), nil } - var list []string + length := 0 for _, arg := range args { - parts := strings.Split(arg.(string), InterpSplitDelim) - for _, part := range parts { - list = append(list, part) - } + length += StringList(arg.(string)).Length() } - return len(list), nil + return length, nil }, } } @@ -246,7 +243,9 @@ func interpolationFuncSplit() ast.Function { ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, ReturnType: ast.TypeString, Callback: func(args []interface{}) (interface{}, error) { - return strings.Replace(args[1].(string), args[0].(string), InterpSplitDelim, -1), nil + sep := args[0].(string) + s := args[1].(string) + return NewStringList(strings.Split(s, sep)).String(), nil }, } } @@ -284,7 +283,7 @@ func interpolationFuncElement() ast.Function { ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, ReturnType: ast.TypeString, Callback: func(args []interface{}) (interface{}, error) { - list := strings.Split(args[0].(string), InterpSplitDelim) + list := StringList(args[0].(string)) index, err := strconv.Atoi(args[1].(string)) if err != nil { @@ -292,7 +291,7 @@ func interpolationFuncElement() ast.Function { "invalid number for index, got %s", args[1]) } - v := list[index%len(list)] + v := list.Element(index) return v, nil }, } @@ -323,7 +322,7 @@ func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { sort.Strings(keys) - return strings.Join(keys, InterpSplitDelim), nil + return NewStringList(keys).String(), nil }, } } @@ -363,7 +362,7 @@ func interpolationFuncValues(vs map[string]ast.Variable) ast.Function { vals = append(vals, vs[k].Value.(string)) } - return strings.Join(vals, InterpSplitDelim), nil + return NewStringList(vals).String(), nil }, } } diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 830a0154c..488aed91c 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -204,7 +204,7 @@ func TestInterpolateFuncFormatList(t *testing.T) { // formatlist applies to each list element in turn { `${formatlist("<%s>", split(",", "A,B"))}`, - "" + InterpSplitDelim + "", + "" + StringListDelim + "", false, }, // formatlist repeats scalar elements @@ -268,8 +268,8 @@ func TestInterpolateFuncJoin(t *testing.T) { fmt.Sprintf(`${join(".", "%s")}`, fmt.Sprintf( "foo%sbar%sbaz", - InterpSplitDelim, - InterpSplitDelim)), + StringListDelim, + StringListDelim)), "foo.bar.baz", false, }, @@ -396,9 +396,9 @@ func TestInterpolateFuncSplit(t *testing.T) { `${split(",", ",,,")}`, fmt.Sprintf( "%s%s%s", - InterpSplitDelim, - InterpSplitDelim, - InterpSplitDelim), + StringListDelim, + StringListDelim, + StringListDelim), false, }, @@ -407,7 +407,7 @@ func TestInterpolateFuncSplit(t *testing.T) { fmt.Sprintf( "%s%s", "foo", - InterpSplitDelim), + StringListDelim), false, }, @@ -415,9 +415,9 @@ func TestInterpolateFuncSplit(t *testing.T) { `${split(",", ",foo,")}`, fmt.Sprintf( "%s%s%s", - InterpSplitDelim, + StringListDelim, "foo", - InterpSplitDelim), + StringListDelim), false, }, @@ -425,8 +425,8 @@ func TestInterpolateFuncSplit(t *testing.T) { `${split(".", "foo.bar.baz")}`, fmt.Sprintf( "foo%sbar%sbaz", - InterpSplitDelim, - InterpSplitDelim), + StringListDelim, + StringListDelim), false, }, }, @@ -486,7 +486,7 @@ func TestInterpolateFuncKeys(t *testing.T) { `${keys("foo")}`, fmt.Sprintf( "bar%squx", - InterpSplitDelim), + StringListDelim), false, }, @@ -535,7 +535,7 @@ func TestInterpolateFuncValues(t *testing.T) { `${values("foo")}`, fmt.Sprintf( "quack%sbaz", - InterpSplitDelim), + StringListDelim), false, }, @@ -568,7 +568,7 @@ func TestInterpolateFuncElement(t *testing.T) { Cases: []testFunctionCase{ { fmt.Sprintf(`${element("%s", "1")}`, - "foo"+InterpSplitDelim+"baz"), + "foo"+StringListDelim+"baz"), "baz", false, }, @@ -582,7 +582,7 @@ func TestInterpolateFuncElement(t *testing.T) { // Invalid index should wrap vs. out-of-bounds { fmt.Sprintf(`${element("%s", "2")}`, - "foo"+InterpSplitDelim+"baz"), + "foo"+StringListDelim+"baz"), "foo", false, }, @@ -590,7 +590,7 @@ func TestInterpolateFuncElement(t *testing.T) { // Too many args { fmt.Sprintf(`${element("%s", "0", "2")}`, - "foo"+InterpSplitDelim+"baz"), + "foo"+StringListDelim+"baz"), nil, true, }, diff --git a/config/interpolate_walk.go b/config/interpolate_walk.go index faacb5726..a8d8ac7f0 100644 --- a/config/interpolate_walk.go +++ b/config/interpolate_walk.go @@ -10,10 +10,6 @@ import ( "github.com/mitchellh/reflectwalk" ) -// InterpSplitDelim is the delimeter that is looked for to split when -// it is returned. -const InterpSplitDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6` - // interpolationWalker implements interfaces for the reflectwalk package // (github.com/mitchellh/reflectwalk) that can be used to automatically // execute a callback for an interpolation. @@ -149,7 +145,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error { // splitting (in a SliceElem) or not. remove := false if w.loc == reflectwalk.SliceElem { - parts := strings.Split(replaceVal, InterpSplitDelim) + parts := StringList(replaceVal).Slice() for _, p := range parts { if p == UnknownVariableValue { remove = true @@ -247,7 +243,7 @@ func (w *interpolationWalker) splitSlice() { if !ok { continue } - if idx := strings.Index(sv, InterpSplitDelim); idx >= 0 { + if IsStringList(sv) { split = true break } @@ -270,7 +266,7 @@ func (w *interpolationWalker) splitSlice() { } // Split on the delimiter - for _, p := range strings.Split(sv, InterpSplitDelim) { + for _, p := range StringList(sv).Slice() { result = append(result, p) } } diff --git a/config/interpolate_walk_test.go b/config/interpolate_walk_test.go index 9b2c34133..5155001e0 100644 --- a/config/interpolate_walk_test.go +++ b/config/interpolate_walk_test.go @@ -157,7 +157,7 @@ func TestInterpolationWalker_replace(t *testing.T) { "bing", }, }, - Value: "bar" + InterpSplitDelim + "baz", + Value: "bar" + StringListDelim + "baz", }, { @@ -168,7 +168,7 @@ func TestInterpolationWalker_replace(t *testing.T) { }, }, Output: map[string]interface{}{}, - Value: UnknownVariableValue + InterpSplitDelim + "baz", + Value: UnknownVariableValue + StringListDelim + "baz", }, } diff --git a/config/string_list.go b/config/string_list.go new file mode 100644 index 000000000..1a4ac6a7d --- /dev/null +++ b/config/string_list.go @@ -0,0 +1,65 @@ +package config + +import "strings" + +// StringList represents the "poor man's list" that terraform uses +// internally +type StringList string + +// This is the delimiter used to recognize and split StringLists +const StringListDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6` + +// Build a StringList from a slice +func NewStringList(parts []string) StringList { + // FOR NOW: + return StringList(strings.Join(parts, StringListDelim)) + // EVENTUALLY: + // var sl StringList + // for _, p := range parts { + // sl = sl.Append(p) + // } + // return sl +} + +// Returns a new StringList with the item appended +func (sl StringList) Append(s string) StringList { + // FOR NOW: + return StringList(strings.Join(append(sl.Slice(), s), StringListDelim)) + // EVENTUALLY: + // return StringList(fmt.Sprintf("%s%s%s", sl, s, StringListDelim)) +} + +// Returns an element at the index, wrapping around the length of the string +// when index > list length +func (sl StringList) Element(index int) string { + return sl.Slice()[index%sl.Length()] +} + +// Returns the length of the StringList +func (sl StringList) Length() int { + return len(sl.Slice()) +} + +// Returns a slice of strings as represented by this StringList +func (sl StringList) Slice() []string { + parts := strings.Split(string(sl), StringListDelim) + + // FOR NOW: + if sl.String() == "" { + return []string{} + } else { + return parts + } + // EVENTUALLY: + // StringLists always have a trailing StringListDelim + // return parts[:len(parts)-1] +} + +func (sl StringList) String() string { + return string(sl) +} + +// Determines if a given string represents a StringList +func IsStringList(s string) bool { + return strings.Contains(s, StringListDelim) +} diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 3fb368cca..2f7c259a5 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -582,7 +582,7 @@ func TestSchemaMap_Diff(t *testing.T) { }, ConfigVariables: map[string]string{ - "var.foo": "2" + config.InterpSplitDelim + "5", + "var.foo": "2" + config.StringListDelim + "5", }, Diff: &terraform.InstanceDiff{ @@ -627,7 +627,7 @@ func TestSchemaMap_Diff(t *testing.T) { ConfigVariables: map[string]string{ "var.foo": config.UnknownVariableValue + - config.InterpSplitDelim + "5", + config.StringListDelim + "5", }, Diff: &terraform.InstanceDiff{ @@ -905,7 +905,7 @@ func TestSchemaMap_Diff(t *testing.T) { }, ConfigVariables: map[string]string{ - "var.foo": "2" + config.InterpSplitDelim + "5", + "var.foo": "2" + config.StringListDelim + "5", }, Diff: &terraform.InstanceDiff{ @@ -953,7 +953,7 @@ func TestSchemaMap_Diff(t *testing.T) { ConfigVariables: map[string]string{ "var.foo": config.UnknownVariableValue + - config.InterpSplitDelim + "5", + config.StringListDelim + "5", }, Diff: &terraform.InstanceDiff{ diff --git a/terraform/interpolate.go b/terraform/interpolate.go index a066c0a5f..83f6265e1 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -453,7 +453,7 @@ func (i *Interpolater) computeResourceMultiVariable( v.FullKey()) } - return strings.Join(values, config.InterpSplitDelim), nil + return config.NewStringList(values).String(), nil } func (i *Interpolater) resourceVariableInfo(