From 09c33fa449512820bd267ec6b0fbe354003a0258 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 9 Jun 2021 17:57:14 -0400 Subject: [PATCH] account for noop deposed instances in json plan When rendering a json plan, we need to account for deposed instances that have become a noop rather than a destroy. --- internal/command/jsonplan/values.go | 7 +++- internal/command/jsonplan/values_test.go | 48 +++++++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/internal/command/jsonplan/values.go b/internal/command/jsonplan/values.go index ac3abee5f..0e3c8ba45 100644 --- a/internal/command/jsonplan/values.go +++ b/internal/command/jsonplan/values.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/plans" + "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/terraform" ) @@ -93,8 +94,10 @@ func marshalPlannedValues(changes *plans.Changes, schemas *terraform.Schemas) (m seenModules := make(map[string]bool) for _, resource := range changes.Resources { - // if the resource is being deleted, skip over it. - if resource.Action != plans.Delete { + // If the resource is being deleted, skip over it. + // Deposed instances are always conceptually a destroy, but if they + // were gone during refresh then the change becomes a noop. + if resource.Action != plans.Delete && resource.DeposedKey == states.NotDeposed { containingModule := resource.Addr.Module.String() moduleResourceMap[containingModule] = append(moduleResourceMap[containingModule], resource.Addr) diff --git a/internal/command/jsonplan/values_test.go b/internal/command/jsonplan/values_test.go index 55b01312e..fded98fcd 100644 --- a/internal/command/jsonplan/values_test.go +++ b/internal/command/jsonplan/values_test.go @@ -204,13 +204,26 @@ func TestMarshalPlanResources(t *testing.T) { }}, Err: false, }, - "delete": { + "delete with null and nil": { Action: plans.Delete, Before: cty.NullVal(cty.EmptyObject), After: cty.NilVal, Want: nil, Err: false, }, + "delete": { + Action: plans.Delete, + Before: cty.ObjectVal(map[string]cty.Value{ + "woozles": cty.StringVal("foo"), + "foozles": cty.StringVal("bar"), + }), + After: cty.NullVal(cty.Object(map[string]cty.Type{ + "woozles": cty.String, + "foozles": cty.String, + })), + Want: nil, + Err: false, + }, "update without unknowns": { Action: plans.Update, Before: cty.ObjectVal(map[string]cty.Value{ @@ -291,6 +304,39 @@ func TestMarshalPlanResources(t *testing.T) { } } +func TestMarshalPlanValuesNoopDeposed(t *testing.T) { + dynamicNull, err := plans.NewDynamicValue(cty.NullVal(cty.DynamicPseudoType), cty.DynamicPseudoType) + if err != nil { + t.Fatal(err) + } + testChange := &plans.Changes{ + Resources: []*plans.ResourceInstanceChangeSrc{ + { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_thing", + Name: "example", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + DeposedKey: "12345678", + ProviderAddr: addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ChangeSrc: plans.ChangeSrc{ + Action: plans.NoOp, + Before: dynamicNull, + After: dynamicNull, + }, + }, + }, + } + + _, err = marshalPlannedValues(testChange, testSchemas()) + if err != nil { + t.Fatal(err) + } +} + func testSchemas() *terraform.Schemas { return &terraform.Schemas{ Providers: map[addrs.Provider]*terraform.ProviderSchema{