fix CoerceValue to handle changing dynamic types

Objects with DynamicPseudoType attributes can't be coerced within a map
if a concrete type is set. Change the Value type used to an Object when
there is a type mismatch.
This commit is contained in:
James Bardin 2019-02-08 16:33:05 -05:00
parent e3618f915b
commit c20164ab31
2 changed files with 118 additions and 1 deletions

View File

@ -225,7 +225,29 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
elems[key.AsString()] = val elems[key.AsString()] = val
} }
} }
attrs[typeName] = cty.MapVal(elems)
// If the attribute values here contain any DynamicPseudoTypes,
// the concrete type must be an object.
useObject := false
switch {
case coll.Type().IsObjectType():
useObject = true
default:
// It's possible that we were given a map, and need to coerce it to an object
ety := coll.Type().ElementType()
for _, v := range elems {
if !v.Type().Equals(ety) {
useObject = true
break
}
}
}
if useObject {
attrs[typeName] = cty.ObjectVal(elems)
} else {
attrs[typeName] = cty.MapVal(elems)
}
default: default:
attrs[typeName] = cty.MapValEmpty(blockS.ImpliedType()) attrs[typeName] = cty.MapValEmpty(blockS.ImpliedType())
} }

View File

@ -436,6 +436,101 @@ func TestCoerceValue(t *testing.T) {
}), }),
``, ``,
}, },
"dynamic value attributes": {
&Block{
BlockTypes: map[string]*NestedBlock{
"foo": {
Nesting: NestingMap,
Block: Block{
Attributes: map[string]*Attribute{
"bar": {
Type: cty.String,
Optional: true,
Computed: true,
},
"baz": {
Type: cty.DynamicPseudoType,
Optional: true,
Computed: true,
},
},
},
},
},
},
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("beep"),
}),
"b": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("boop"),
"baz": cty.NumberIntVal(8),
}),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("beep"),
"baz": cty.NullVal(cty.DynamicPseudoType),
}),
"b": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("boop"),
"baz": cty.NumberIntVal(8),
}),
}),
}),
``,
},
"dynamic attributes in map": {
// Convert a block represented as a map to an object if a
// DynamicPseudoType causes the element types to mismatch.
&Block{
BlockTypes: map[string]*NestedBlock{
"foo": {
Nesting: NestingMap,
Block: Block{
Attributes: map[string]*Attribute{
"bar": {
Type: cty.String,
Optional: true,
Computed: true,
},
"baz": {
Type: cty.DynamicPseudoType,
Optional: true,
Computed: true,
},
},
},
},
},
},
cty.ObjectVal(map[string]cty.Value{
"foo": cty.MapVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("beep"),
}),
"b": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("boop"),
}),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("beep"),
"baz": cty.NullVal(cty.DynamicPseudoType),
}),
"b": cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("boop"),
"baz": cty.NullVal(cty.DynamicPseudoType),
}),
}),
}),
``,
},
} }
for name, test := range tests { for name, test := range tests {