2014-07-01 22:25:54 +02:00
|
|
|
package flatmap
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
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.
2016-12-23 23:59:18 +01:00
|
|
|
"sort"
|
2014-07-01 22:25:54 +02:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2017-02-23 19:03:59 +01:00
|
|
|
|
|
|
|
"github.com/hashicorp/hil"
|
2014-07-01 22:25:54 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Expand takes a map and a key (prefix) and expands that value into
|
|
|
|
// a more complex structure. This is the reverse of the Flatten operation.
|
|
|
|
func Expand(m map[string]string, key string) interface{} {
|
|
|
|
// If the key is exactly a key in the map, just return it
|
|
|
|
if v, ok := m[key]; ok {
|
2014-07-24 20:40:46 +02:00
|
|
|
if v == "true" {
|
2014-07-01 22:25:54 +02:00
|
|
|
return true
|
|
|
|
} else if v == "false" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the key is an array, and if so, expand the array
|
2017-02-23 19:03:59 +01:00
|
|
|
if v, ok := m[key+".#"]; ok {
|
|
|
|
// If the count of the key is unknown, then just put the unknown
|
|
|
|
// value in the value itself. This will be detected by Terraform
|
|
|
|
// core later.
|
|
|
|
if v == hil.UnknownValue {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2014-07-01 22:25:54 +02:00
|
|
|
return expandArray(m, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this is a prefix in the map
|
|
|
|
prefix := key + "."
|
2017-04-14 22:32:30 +02:00
|
|
|
for k := range m {
|
2014-07-01 22:25:54 +02:00
|
|
|
if strings.HasPrefix(k, prefix) {
|
|
|
|
return expandMap(m, prefix)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandArray(m map[string]string, prefix string) []interface{} {
|
|
|
|
num, err := strconv.ParseInt(m[prefix+".#"], 0, 0)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2017-04-14 22:32:30 +02:00
|
|
|
// If the number of elements in this array is 0, then return an
|
|
|
|
// empty slice as there is nothing to expand. Trying to expand it
|
|
|
|
// anyway could lead to crashes as any child maps, arrays or sets
|
|
|
|
// that no longer exist are still shown as empty with a count of 0.
|
|
|
|
if num == 0 {
|
|
|
|
return []interface{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Schema "Set" type stores its values in an array format, but
|
|
|
|
// using numeric hash values instead of ordinal keys. Take the set
|
|
|
|
// of keys regardless of value, and expand them in numeric order.
|
2017-01-09 15:43:45 +01:00
|
|
|
// See GH-11042 for more details.
|
2017-01-04 22:03:24 +01:00
|
|
|
keySet := map[int]bool{}
|
2017-03-01 19:28:02 +01:00
|
|
|
computed := map[string]bool{}
|
2017-01-04 22:03:24 +01:00
|
|
|
for k := range m {
|
|
|
|
if !strings.HasPrefix(k, prefix+".") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
key := k[len(prefix)+1:]
|
|
|
|
idx := strings.Index(key, ".")
|
|
|
|
if idx != -1 {
|
|
|
|
key = key[:idx]
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip the count value
|
|
|
|
if key == "#" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2017-03-01 19:28:02 +01:00
|
|
|
// strip the computed flag if there is one
|
|
|
|
if strings.HasPrefix(key, "~") {
|
|
|
|
key = key[1:]
|
|
|
|
computed[key] = true
|
|
|
|
}
|
|
|
|
|
2017-01-04 22:03:24 +01:00
|
|
|
k, err := strconv.Atoi(key)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
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.
2016-12-23 23:59:18 +01:00
|
|
|
}
|
2017-01-04 22:03:24 +01:00
|
|
|
keySet[int(k)] = true
|
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.
2016-12-23 23:59:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
keysList := make([]int, 0, num)
|
|
|
|
for key := range keySet {
|
|
|
|
keysList = append(keysList, key)
|
|
|
|
}
|
|
|
|
sort.Ints(keysList)
|
|
|
|
|
2014-07-01 22:25:54 +02:00
|
|
|
result := make([]interface{}, num)
|
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.
2016-12-23 23:59:18 +01:00
|
|
|
for i, key := range keysList {
|
2017-03-01 19:28:02 +01:00
|
|
|
keyString := strconv.Itoa(key)
|
|
|
|
if computed[keyString] {
|
|
|
|
keyString = "~" + keyString
|
|
|
|
}
|
|
|
|
result[i] = Expand(m, fmt.Sprintf("%s.%s", prefix, keyString))
|
2014-07-01 22:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandMap(m map[string]string, prefix string) map[string]interface{} {
|
2017-04-13 17:11:41 +02:00
|
|
|
// Submaps may not have a '%' key, so we can't count on this value being
|
2017-04-26 16:10:04 +02:00
|
|
|
// here. If we don't have a count, just proceed as if we have have a map.
|
2017-04-13 17:11:41 +02:00
|
|
|
if count, ok := m[prefix+"%"]; ok && count == "0" {
|
|
|
|
return map[string]interface{}{}
|
|
|
|
}
|
|
|
|
|
2014-07-01 22:25:54 +02:00
|
|
|
result := make(map[string]interface{})
|
2017-04-14 22:32:30 +02:00
|
|
|
for k := range m {
|
2014-07-01 22:25:54 +02:00
|
|
|
if !strings.HasPrefix(k, prefix) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
key := k[len(prefix):]
|
|
|
|
idx := strings.Index(key, ".")
|
2014-07-08 22:57:30 +02:00
|
|
|
if idx != -1 {
|
|
|
|
key = key[:idx]
|
|
|
|
}
|
|
|
|
if _, ok := result[key]; ok {
|
|
|
|
continue
|
2014-07-01 22:25:54 +02:00
|
|
|
}
|
|
|
|
|
2016-12-16 15:35:58 +01:00
|
|
|
// skip the map count value
|
|
|
|
if key == "%" {
|
|
|
|
continue
|
|
|
|
}
|
2017-04-14 22:32:30 +02:00
|
|
|
|
2014-07-08 22:57:30 +02:00
|
|
|
result[key] = Expand(m, k[:len(prefix)+len(key)])
|
2014-07-01 22:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|