Fix string representation of sets during interpolation
The change in #10787 used flatmap.Expand to fix interpolation of nested maps, but it broke interpolation of sets such that their elements were not represented. For example, the expected string representation of a splatted aws_network_interface.whatever.*.private_ips should be: ``` [{Variable (TypeList): [{Variable (TypeString): 10.41.17.25}]} {Variable (TypeList): [{Variable (TypeString): 10.41.22.236}]}] ``` But instead it became: ``` [{Variable (TypeList): [{Variable (TypeString): }]} {Variable (TypeList): [{Variable (TypeString): }]}] ``` This is because the expandArray function of expand.go treated arrays to exclusively be lists, e.g. not sets. The old code used to match for numeric keys, so it would work for sets, whereas expandArray just assumed keys started at 0 and ascended incrementally. Remember that sets' keys are numeric, but since they are hashes, they can be any integer. The result of assuming that the keys start at 0 led to the recursive call to flatmap.Expand not matching any keys of the set, and returning nil, which is why the above example has nothing where the IP addresses used to be. So we bring back that matching behavior, but we move it to expandArray instead. We've modified it to not reconstruct the data structures like it used to when it was in the Interpolator, and to use the standard int sorter rather than implementing a custom sorter since a custom one is no longer necessary thanks to the use of flatmap.Expand. Fixes #10908, and restores the viability of the workaround I posted in #8696. Big thanks to @jszwedko for helping me with this fix. I was able to diagnose the problem along, but couldn't fix it without his help.
This commit is contained in:
parent
2d894bae48
commit
497010ce42
|
@ -2,6 +2,8 @@ package flatmap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -42,9 +44,27 @@ func expandArray(m map[string]string, prefix string) []interface{} {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keySet := make(map[int]struct{})
|
||||||
|
listElementKey := regexp.MustCompile("^" + prefix + "\\.([0-9]+)(?:\\..*)?$")
|
||||||
|
for key := range m {
|
||||||
|
if matches := listElementKey.FindStringSubmatch(key); matches != nil {
|
||||||
|
k, err := strconv.ParseInt(matches[1], 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
keySet[int(k)] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keysList := make([]int, 0, num)
|
||||||
|
for key := range keySet {
|
||||||
|
keysList = append(keysList, key)
|
||||||
|
}
|
||||||
|
sort.Ints(keysList)
|
||||||
|
|
||||||
result := make([]interface{}, num)
|
result := make([]interface{}, num)
|
||||||
for i := 0; i < int(num); i++ {
|
for i, key := range keysList {
|
||||||
result[i] = Expand(m, fmt.Sprintf("%s.%d", prefix, i))
|
result[i] = Expand(m, fmt.Sprintf("%s.%d", prefix, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -838,6 +838,44 @@ func TestInterpolator_nestedMapsAndLists(t *testing.T) {
|
||||||
interfaceToVariableSwallowError(mapOfList))
|
interfaceToVariableSwallowError(mapOfList))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInterpolator_sets(t *testing.T) {
|
||||||
|
state := &State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"aws_network_interface.set": &ResourceState{
|
||||||
|
Type: "aws_network_interface",
|
||||||
|
Dependencies: []string{},
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "null",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"private_ips.#": "1",
|
||||||
|
"private_ips.3977356764": "10.42.16.179",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
i := &Interpolater{
|
||||||
|
Module: testModule(t, "interpolate-multi-vars"),
|
||||||
|
StateLock: new(sync.RWMutex),
|
||||||
|
State: state,
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := &InterpolationScope{
|
||||||
|
Path: rootModulePath,
|
||||||
|
}
|
||||||
|
|
||||||
|
set := []interface{}{"10.42.16.179"}
|
||||||
|
|
||||||
|
testInterpolate(t, i, scope, "aws_network_interface.set.private_ips",
|
||||||
|
interfaceToVariableSwallowError(set))
|
||||||
|
}
|
||||||
|
|
||||||
func testInterpolate(
|
func testInterpolate(
|
||||||
t *testing.T, i *Interpolater,
|
t *testing.T, i *Interpolater,
|
||||||
scope *InterpolationScope,
|
scope *InterpolationScope,
|
||||||
|
|
Loading…
Reference in New Issue