hcl2_upgrade: fix a case where the generated type is wrong

when it encounters map[string]interface{} or []interface{} types,  hcl2_upgrade now takes the 'most complex' entry from those in order to tell wether this is going to be a body `body {}` or an attribute `attribute = {}`. Before that the hcl2_upgrade command could be a bit random there.

A way better ( but may be somewhat hard ) way to do this would be to use the actual plugins structs in order to generate the HCL2.
This commit is contained in:
Adrien Delorme 2020-08-27 16:02:05 +02:00
parent 279e44e51d
commit 0f00709fb6
1 changed files with 48 additions and 7 deletions

View File

@ -326,24 +326,65 @@ func jsonBodyToHCL2Body(out *hclwrite.Body, kvs map[string]interface{}) {
switch value := value.(type) {
case map[string]interface{}:
var first interface{}
for _, elem := range value {
first = elem
var mostComplexElem interface{}
for _, randomElem := range value {
// HACK: we take the most complex element of that map because
// in HCL2, map of objects can be bodies, for example:
// map containing object: source_ami_filter {} ( body )
// simple string/string map: tags = {} ) ( attribute )
//
// if we could not find an object in this map then it's most
// likely a plain map and so we guess it should be and
// attribute. Though now if value refers to something that is
// an object but only contains a string or a bool; we could
// generate a faulty object. For example a (somewhat invalid)
// source_ami_filter where only `most_recent` is set.
switch randomElem.(type) {
case string, int, float64, bool:
if mostComplexElem != nil {
continue
}
mostComplexElem = randomElem
default:
mostComplexElem = randomElem
}
}
switch first.(type) {
case string, int, float64:
switch mostComplexElem.(type) {
case string, int, float64, bool:
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
default:
nestedBlockBody := out.AppendNewBlock(k, nil).Body()
jsonBodyToHCL2Body(nestedBlockBody, value)
}
case map[string]string, map[string]int, map[string]float64:
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
case []interface{}:
if len(value) == 0 {
continue
}
switch value[0].(type) {
var mostComplexElem interface{}
for _, randomElem := range value {
// HACK: we take the most complex element of that slice because
// in hcl2 slices of plain types can be arrays, for example:
// simple string type: owners = ["0000000000"]
// object: launch_block_device_mappings {}
switch randomElem.(type) {
case string, int, float64, bool:
if mostComplexElem != nil {
continue
}
mostComplexElem = randomElem
default:
mostComplexElem = randomElem
}
}
switch mostComplexElem.(type) {
case map[string]interface{}:
// this is an object in a slice; so we unwrap it. We
// could try to remove any 's' suffix in the key, but
// this might not work everywhere.
for i := range value {
value := value[i].(map[string]interface{})
nestedBlockBody := out.AppendNewBlock(k, nil).Body()
@ -351,8 +392,8 @@ func jsonBodyToHCL2Body(out *hclwrite.Body, kvs map[string]interface{}) {
}
continue
default:
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
}
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
default:
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
}