diff --git a/terraform/graph_builder_apply_test.go b/terraform/graph_builder_apply_test.go index c0f987423..444aad8f4 100644 --- a/terraform/graph_builder_apply_test.go +++ b/terraform/graph_builder_apply_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" ) func TestApplyGraphBuilder_impl(t *testing.T) { @@ -474,6 +475,108 @@ func TestApplyGraphBuilder_targetModule(t *testing.T) { testGraphNotContains(t, g, "module.child1.output.instance_id") } +// Ensure that an update resulting from the removal of a resource happens after +// that resource is destroyed. +func TestApplyGraphBuilder_updateFromOrphan(t *testing.T) { + schemas := simpleTestSchemas() + instanceSchema := schemas.Providers["test"].ResourceTypes["test_object"] + + bBefore, _ := plans.NewDynamicValue( + cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("b_id"), + "test_string": cty.StringVal("a_id"), + }), instanceSchema.ImpliedType()) + bAfter, _ := plans.NewDynamicValue( + cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("b_id"), + "test_string": cty.StringVal("changed"), + }), instanceSchema.ImpliedType()) + + changes := &plans.Changes{ + Resources: []*plans.ResourceInstanceChangeSrc{ + { + Addr: mustResourceInstanceAddr("test_object.a"), + ChangeSrc: plans.ChangeSrc{ + Action: plans.Delete, + }, + }, + { + Addr: mustResourceInstanceAddr("test_object.b"), + ChangeSrc: plans.ChangeSrc{ + Action: plans.Update, + Before: bBefore, + After: bAfter, + }, + }, + }, + } + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "a", + }.Instance(addrs.NoKey), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a_id"}`), + }, + addrs.ProviderConfig{ + Type: "test", + }.Absolute(addrs.RootModuleInstance), + ) + root.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "b", + }.Instance(addrs.NoKey), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`), + Dependencies: []addrs.AbsResource{ + addrs.AbsResource{ + Resource: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "a", + }, + Module: root.Addr, + }, + }, + }, + addrs.ProviderConfig{ + Type: "test", + }.Absolute(addrs.RootModuleInstance), + ) + + b := &ApplyGraphBuilder{ + Config: testModule(t, "graph-builder-apply-orphan-update"), + Changes: changes, + Components: simpleMockComponentFactory(), + Schemas: schemas, + State: state, + } + + g, err := b.Build(addrs.RootModuleInstance) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := strings.TrimSpace(` +test_object.a (destroy) +test_object.b + test_object.a (destroy) +`) + + instanceGraph := filterInstances(g) + if instanceGraph.String() != expected { + t.Fatalf("expected:\n%s\ngot:\n%s", expected, instanceGraph) + } +} + const testApplyGraphBuilderStr = ` meta.count-boundary (EachMode fixup) module.child.test_object.other diff --git a/terraform/testdata/graph-builder-apply-orphan-update/main.tf b/terraform/testdata/graph-builder-apply-orphan-update/main.tf new file mode 100644 index 000000000..22e7ae0f1 --- /dev/null +++ b/terraform/testdata/graph-builder-apply-orphan-update/main.tf @@ -0,0 +1,3 @@ +resource "test_object" "b" { + test_string = "changed" +}