diff --git a/config/hcl2shim/flatmap.go b/config/hcl2shim/flatmap.go index bcecb30df..bb4228d98 100644 --- a/config/hcl2shim/flatmap.go +++ b/config/hcl2shim/flatmap.go @@ -347,10 +347,8 @@ func hcl2ValueFromFlatmapSet(m map[string]string, prefix string, ty cty.Type) (c return cty.UnknownVal(ty), nil } - // We actually don't really care about the "count" of a set for our - // purposes here, but we do need to check if it _exists_ in order to - // recognize the difference between null (not set at all) and empty. - if strCount, exists := m[prefix+"#"]; !exists { + strCount, exists := m[prefix+"#"] + if !exists { return cty.NullVal(ty), nil } else if strCount == UnknownVariableValue { return cty.UnknownVal(ty), nil @@ -394,7 +392,31 @@ func hcl2ValueFromFlatmapSet(m map[string]string, prefix string, ty cty.Type) (c vals = append(vals, val) } - if len(vals) == 0 { + if len(vals) == 0 && strCount == "1" { + // An empty set wouldn't be represented in the flatmap, so this must be + // a single empty object since the count is actually 1. + // Add an appropriately typed null value to the set. + var val cty.Value + switch { + case ety.IsMapType(): + val = cty.MapValEmpty(ety) + case ety.IsListType(): + val = cty.ListValEmpty(ety) + case ety.IsSetType(): + val = cty.SetValEmpty(ety) + case ety.IsObjectType(): + // TODO: cty.ObjectValEmpty + objectMap := map[string]cty.Value{} + for attr, ty := range ety.AttributeTypes() { + objectMap[attr] = cty.NullVal(ty) + } + val = cty.ObjectVal(objectMap) + default: + val = cty.NullVal(ety) + } + vals = append(vals, val) + + } else if len(vals) == 0 { return cty.SetValEmpty(ety), nil } diff --git a/config/hcl2shim/flatmap_test.go b/config/hcl2shim/flatmap_test.go index 07f93ac26..e2ae8a70f 100644 --- a/config/hcl2shim/flatmap_test.go +++ b/config/hcl2shim/flatmap_test.go @@ -679,10 +679,52 @@ func TestHCL2ValueFromFlatmap(t *testing.T) { }), }), }, + { + Flatmap: map[string]string{ + "foo.#": "1", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + })), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.NullVal(cty.String), + }), + }), + }), + }, + { + Flatmap: map[string]string{ + "multi.#": "1", + "multi.2.set.#": "1", + "multi.2.set.3.required": "val", + }, + Type: cty.Object(map[string]cty.Type{ + "multi": cty.Set(cty.Object(map[string]cty.Type{ + "set": cty.Set(cty.Object(map[string]cty.Type{ + "required": cty.String, + })), + })), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "multi": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "required": cty.StringVal("val"), + }), + }), + }), + }), + }), + }, } - for _, test := range tests { - t.Run(fmt.Sprintf("%#v as %#v", test.Flatmap, test.Type), func(t *testing.T) { + for i, test := range tests { + t.Run(fmt.Sprintf("%d %#v as %#v", i, test.Flatmap, test.Type), func(t *testing.T) { got, err := HCL2ValueFromFlatmap(test.Flatmap, test.Type) if test.WantErr != "" {