Get rid of the list when parsing HCL maps for vars

When we parse a map from HCL, it's decoded into a list of maps because
HCL allows declaring a key multiple times to implicitly add it to a
list.  Since there's no terraform variable configuration that supports
this structure, we can always flatten a []map[string]interface{} value
to a map[string]interface{} and remove any type rrors trying to apply
that value.
This commit is contained in:
James Bardin 2016-10-06 19:40:04 -04:00
parent 3e3854ed65
commit 9fc50a76c3
2 changed files with 51 additions and 10 deletions

View File

@ -111,6 +111,10 @@ func loadKVFile(rawPath string) (map[string]interface{}, error) {
"Decoding errors are usually caused by an invalid format.", "Decoding errors are usually caused by an invalid format.",
err) err)
} }
err = flattenMultiMaps(result)
if err != nil {
return nil, err
}
return result, nil return result, nil
} }
@ -185,10 +189,34 @@ func parseVarFlagAsHCL(input string) (string, interface{}, error) {
return "", nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", probablyName, input) return "", nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", probablyName, input)
} }
for k, v := range decoded { err = flattenMultiMaps(decoded)
return k, v, nil if err != nil {
return probablyName, "", err
} }
// Should be unreachable var k string
return "", nil, fmt.Errorf("No value for variable: %s", input) var v interface{}
for k, v = range decoded {
break
}
return k, v, nil
}
// Variables don't support any type that can be configured via multiple
// declarations of the same HCL map, so any instances of
// []map[string]interface{} are either a single map that can be flattened, or
// are invalid config.
func flattenMultiMaps(m map[string]interface{}) error {
for k, v := range m {
switch v := v.(type) {
case []map[string]interface{}:
switch {
case len(v) > 1:
return fmt.Errorf("multiple map declarations not supported for variables")
case len(v) == 1:
m[k] = v[0]
}
}
}
return nil
} }

View File

@ -2,10 +2,11 @@ package command
import ( import (
"flag" "flag"
"github.com/davecgh/go-spew/spew"
"io/ioutil" "io/ioutil"
"reflect" "reflect"
"testing" "testing"
"github.com/davecgh/go-spew/spew"
) )
func TestFlagStringKV_impl(t *testing.T) { func TestFlagStringKV_impl(t *testing.T) {
@ -118,11 +119,9 @@ func TestFlagTypedKV(t *testing.T) {
{ {
`key={"hello" = "world", "foo" = "bar"}`, `key={"hello" = "world", "foo" = "bar"}`,
map[string]interface{}{ map[string]interface{}{
"key": []map[string]interface{}{ "key": map[string]interface{}{
map[string]interface{}{ "hello": "world",
"hello": "world", "foo": "bar",
"foo": "bar",
},
}, },
}, },
false, false,
@ -169,6 +168,10 @@ func TestFlagKVFile(t *testing.T) {
inputLibucl := ` inputLibucl := `
foo = "bar" foo = "bar"
` `
inputMap := `
foo = {
k = "v"
}`
inputJson := `{ inputJson := `{
"foo": "bar"}` "foo": "bar"}`
@ -195,6 +198,16 @@ foo = "bar"
map[string]interface{}{"map.key": "foo"}, map[string]interface{}{"map.key": "foo"},
false, false,
}, },
{
inputMap,
map[string]interface{}{
"foo": map[string]interface{}{
"k": "v",
},
},
false,
},
} }
path := testTempFile(t) path := testTempFile(t)