mirror of https://github.com/hashicorp/terraform
The work integrated in hashicorp/terraform#6322 silently broke the ability to use remote state correctly. This commit adds a fix for that, making use of the work integrated in hashicorp/terraform#7124. In order to deal with outputs which are complex structures, we use a forked version of the flatmap package - the difference in the version this commit vs the github.com/hashicorp/terraform/flatmap package is that we add in an additional key for map counts which state requires. Because we bypass the normal helper/schema mechanism, this is not set for us. Because of the HIL type checking of maps, values must be of a homogenous type. This is unfortunate, as it means we can no longer refer to outputs as: ${terraform_remote_state.foo.output.outputname} Instead we had to bring them to the top level namespace: ${terraform_remote_state.foo.outputname} This actually does lead to better overall usability - and the BC breakage is made better by the fact that indexing would have broken the original syntax anyway. We also add a real-world test and assert against specific values. Tests which were previously acceptance tests are now run as unit tests, so regression should be identified at a much earlier stage.pull/7127/head
parent
f51c9d5efd
commit
bdc6a49ae3
@ -0,0 +1,76 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// remoteStateFlatten takes a structure and turns into a flat map[string]string.
|
||||
//
|
||||
// Within the "thing" parameter, only primitive values are allowed. Structs are
|
||||
// not supported. Therefore, it can only be slices, maps, primitives, and
|
||||
// any combination of those together.
|
||||
//
|
||||
// The difference between this version and the version in package flatmap is that
|
||||
// we add the count key for maps in this version, and return a normal
|
||||
// map[string]string instead of a flatmap.Map
|
||||
func remoteStateFlatten(thing map[string]interface{}) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
for k, raw := range thing {
|
||||
flatten(result, k, reflect.ValueOf(raw))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func flatten(result map[string]string, prefix string, v reflect.Value) {
|
||||
if v.Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
result[prefix] = "true"
|
||||
} else {
|
||||
result[prefix] = "false"
|
||||
}
|
||||
case reflect.Int:
|
||||
result[prefix] = fmt.Sprintf("%d", v.Int())
|
||||
case reflect.Map:
|
||||
flattenMap(result, prefix, v)
|
||||
case reflect.Slice:
|
||||
flattenSlice(result, prefix, v)
|
||||
case reflect.String:
|
||||
result[prefix] = v.String()
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown: %s", v))
|
||||
}
|
||||
}
|
||||
|
||||
func flattenMap(result map[string]string, prefix string, v reflect.Value) {
|
||||
mapKeys := v.MapKeys()
|
||||
|
||||
result[fmt.Sprintf("%s.%%", prefix)] = fmt.Sprintf("%d", len(mapKeys))
|
||||
for _, k := range mapKeys {
|
||||
if k.Kind() == reflect.Interface {
|
||||
k = k.Elem()
|
||||
}
|
||||
|
||||
if k.Kind() != reflect.String {
|
||||
panic(fmt.Sprintf("%s: map key is not string: %s", prefix, k))
|
||||
}
|
||||
|
||||
flatten(result, fmt.Sprintf("%s.%s", prefix, k.String()), v.MapIndex(k))
|
||||
}
|
||||
}
|
||||
|
||||
func flattenSlice(result map[string]string, prefix string, v reflect.Value) {
|
||||
prefix = prefix + "."
|
||||
|
||||
result[prefix+"#"] = fmt.Sprintf("%d", v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
flatten(result, fmt.Sprintf("%s%d", prefix, i), v.Index(i))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
{
|
||||
"version": 3,
|
||||
"terraform_version": "0.7.0",
|
||||
"serial": 3,
|
||||
"modules": [
|
||||
{
|
||||
"path": [
|
||||
"root"
|
||||
],
|
||||
"outputs": {
|
||||
"computed_map": {
|
||||
"sensitive": false,
|
||||
"type": "map",
|
||||
"value": {
|
||||
"key1": "value1"
|
||||
}
|
||||
},
|
||||
"computed_set": {
|
||||
"sensitive": false,
|
||||
"type": "list",
|
||||
"value": [
|
||||
"setval1",
|
||||
"setval2"
|
||||
]
|
||||
},
|
||||
"map": {
|
||||
"sensitive": false,
|
||||
"type": "map",
|
||||
"value": {
|
||||
"key": "test",
|
||||
"test": "test"
|
||||
}
|
||||
},
|
||||
"set": {
|
||||
"sensitive": false,
|
||||
"type": "list",
|
||||
"value": [
|
||||
"test1",
|
||||
"test2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"test_resource.main": {
|
||||
"type": "test_resource",
|
||||
"primary": {
|
||||
"id": "testId",
|
||||
"attributes": {
|
||||
"computed_list.#": "2",
|
||||
"computed_list.0": "listval1",
|
||||
"computed_list.1": "listval2",
|
||||
"computed_map.%": "1",
|
||||
"computed_map.key1": "value1",
|
||||
"computed_read_only": "value_from_api",
|
||||
"computed_read_only_force_new": "value_from_api",
|
||||
"computed_set.#": "2",
|
||||
"computed_set.2337322984": "setval1",
|
||||
"computed_set.307881554": "setval2",
|
||||
"id": "testId",
|
||||
"list_of_map.#": "2",
|
||||
"list_of_map.0.%": "2",
|
||||
"list_of_map.0.key1": "value1",
|
||||
"list_of_map.0.key2": "value2",
|
||||
"list_of_map.1.%": "2",
|
||||
"list_of_map.1.key3": "value3",
|
||||
"list_of_map.1.key4": "value4",
|
||||
"map.%": "2",
|
||||
"map.key": "test",
|
||||
"map.test": "test",
|
||||
"map_that_look_like_set.%": "2",
|
||||
"map_that_look_like_set.12352223": "hello",
|
||||
"map_that_look_like_set.36234341": "world",
|
||||
"optional_computed_map.%": "0",
|
||||
"required": "Hello World",
|
||||
"required_map.%": "3",
|
||||
"required_map.key1": "value1",
|
||||
"required_map.key2": "value2",
|
||||
"required_map.key3": "value3",
|
||||
"set.#": "2",
|
||||
"set.2326977762": "test1",
|
||||
"set.331058520": "test2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in new issue