diff --git a/internal/terraform/node_resource_abstract_instance.go b/internal/terraform/node_resource_abstract_instance.go index 9ff15b8408..27c86b48de 100644 --- a/internal/terraform/node_resource_abstract_instance.go +++ b/internal/terraform/node_resource_abstract_instance.go @@ -96,15 +96,21 @@ func (n *NodeAbstractResourceInstance) References() []*addrs.Reference { return nil } -// StateDependencies returns the dependencies saved in the state. +// StateDependencies returns the dependencies which will be saved in the state +// for managed resources, or the most current dependencies for data resources. func (n *NodeAbstractResourceInstance) StateDependencies() []addrs.ConfigResource { + // Managed resources prefer the stored dependencies, to avoid possible + // conflicts in ordering when refactoring configuration. if s := n.instanceState; s != nil { if s.Current != nil { return s.Current.Dependencies } } - return nil + // If there are no stored dependencies, this is either a newly created + // managed resource, or a data source, and we can use the most recently + // calculated dependencies. + return n.Dependencies } // GraphNodeProviderConsumer diff --git a/internal/terraform/transform_destroy_edge_test.go b/internal/terraform/transform_destroy_edge_test.go index b4fc351be2..c82d07e385 100644 --- a/internal/terraform/transform_destroy_edge_test.go +++ b/internal/terraform/transform_destroy_edge_test.go @@ -509,6 +509,54 @@ test_object.C (destroy)`) } } +func TestDestroyEdgeTransformer_dataDependsOn(t *testing.T) { + g := Graph{Path: addrs.RootModuleInstance} + + addrA := mustResourceInstanceAddr("test_object.A") + instA := NewNodeAbstractResourceInstance(addrA) + a := &NodeDestroyResourceInstance{NodeAbstractResourceInstance: instA} + g.Add(a) + + // B here represents a data sources, which is effectively an update during + // apply, but won't have dependencies stored in the state. + addrB := mustResourceInstanceAddr("test_object.B") + instB := NewNodeAbstractResourceInstance(addrB) + instB.Dependencies = append(instB.Dependencies, addrA.ConfigResource()) + b := &NodeApplyableResourceInstance{NodeAbstractResourceInstance: instB} + + g.Add(b) + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_object.A").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"A"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { + t.Fatal(err) + } + + tf := &DestroyEdgeTransformer{} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(` +test_object.A (destroy) +test_object.B + test_object.A (destroy) +`) + if actual != expected { + t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) + } +} + func testDestroyNode(addrString string) GraphNodeDestroyer { instAddr := mustResourceInstanceAddr(addrString) inst := NewNodeAbstractResourceInstance(instAddr)