diff --git a/command/format/diff.go b/command/format/diff.go index b697b1726..4e9fab552 100644 --- a/command/format/diff.go +++ b/command/format/diff.go @@ -477,7 +477,7 @@ func (p *blockBodyDiffPrinter) writeValue(val cty.Value, action plans.Action, in p.buf.WriteString(strings.Repeat(" ", indent)) p.buf.WriteString("]") case ty.IsMapType(): - p.buf.WriteString("{\n") + p.buf.WriteString("{") keyLen := 0 for it := val.ElementIterator(); it.Next(); { @@ -489,16 +489,20 @@ func (p *blockBodyDiffPrinter) writeValue(val cty.Value, action plans.Action, in for it := val.ElementIterator(); it.Next(); { key, val := it.Element() + + p.buf.WriteString("\n") p.buf.WriteString(strings.Repeat(" ", indent+2)) p.writeActionSymbol(action) p.writeValue(key, action, indent+4) p.buf.WriteString(strings.Repeat(" ", keyLen-len(key.AsString()))) p.buf.WriteString(" = ") p.writeValue(val, action, indent+4) - p.buf.WriteString("\n") } - p.buf.WriteString(strings.Repeat(" ", indent)) + if val.LengthInt() > 0 { + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent)) + } p.buf.WriteString("}") case ty.IsObjectType(): p.buf.WriteString("{\n") diff --git a/command/format/diff_test.go b/command/format/diff_test.go index 314173fb8..928ec41be 100644 --- a/command/format/diff_test.go +++ b/command/format/diff_test.go @@ -743,6 +743,39 @@ func TestResourceChange_primitiveSet(t *testing.T) { func TestResourceChange_map(t *testing.T) { testCases := map[string]testCase{ "in-place update - creation": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-STATIC"), + "map_field": cty.NullVal(cty.Map(cty.String)), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "ami": cty.StringVal("ami-STATIC"), + "map_field": cty.MapVal(map[string]cty.Value{ + "new-key": cty.StringVal("new-element"), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "map_field": {Type: cty.Map(cty.String), Optional: true}, + }, + }, + RequiredReplace: cty.NewPathSet(), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ami = "ami-STATIC" + ~ id = "i-02ae66f368e8518a9" -> (known after apply) + + map_field = { + + "new-key" = "new-element" + } + } +`, + }, + "in-place update - first insertion": { Action: plans.Update, Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ @@ -894,6 +927,31 @@ func TestResourceChange_map(t *testing.T) { - "c" = "cccc" -> null } } +`, + }, + "creation - empty": { + Action: plans.Create, + Mode: addrs.ManagedResourceMode, + Before: cty.NullVal(cty.EmptyObject), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "ami": cty.StringVal("ami-STATIC"), + "map_field": cty.MapValEmpty(cty.String), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "map_field": {Type: cty.Map(cty.String), Optional: true}, + }, + }, + RequiredReplace: cty.NewPathSet(), + ExpectedOutput: ` # test_instance.example will be created + + resource "test_instance" "example" { + + ami = "ami-STATIC" + + id = (known after apply) + + map_field = {} + } `, }, }