diff --git a/terraform/node_resource_apply.go b/terraform/node_resource_apply.go index c72865e9f..52c9af40b 100644 --- a/terraform/node_resource_apply.go +++ b/terraform/node_resource_apply.go @@ -12,6 +12,8 @@ import ( // it is ready to be applied and is represented by a diff. type NodeApplyableResourceInstance struct { *NodeAbstractResourceInstance + + destroyNode GraphNodeDestroyerCBD } var ( @@ -22,6 +24,27 @@ var ( _ GraphNodeEvalable = (*NodeApplyableResourceInstance)(nil) ) +// GraphNodeAttachDestroyer +func (n *NodeApplyableResourceInstance) AttachDestroyNode(d GraphNodeDestroyerCBD) { + n.destroyNode = d +} + +// createBeforeDestroy checks this nodes config status and the status af any +// companion destroy node for CreateBeforeDestroy. +func (n *NodeApplyableResourceInstance) createBeforeDestroy() bool { + cbd := false + + if n.Config != nil && n.Config.Managed != nil { + cbd = n.Config.Managed.CreateBeforeDestroy + } + + if n.destroyNode != nil { + cbd = cbd || n.destroyNode.CreateBeforeDestroy() + } + + return cbd +} + // GraphNodeCreator func (n *NodeApplyableResourceInstance) CreateAddr() *addrs.AbsResourceInstance { addr := n.ResourceInstanceAddr() @@ -42,8 +65,7 @@ func (n *NodeApplyableResourceInstance) References() []*addrs.Reference { // would create a dependency cycle. We make a compromise here of requiring // changes to be updated across two applies in this case, since the first // plan will use the old values. - cbd := n.Config != nil && n.Config.Managed != nil && n.Config.Managed.CreateBeforeDestroy - if !cbd { + if !n.createBeforeDestroy() { for _, ref := range ret { switch tr := ref.Subject.(type) { case addrs.ResourceInstance: @@ -214,7 +236,7 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe destroy = diffApply.GetDestroy() || diffApply.RequiresNew() } - if destroy && n.Config.Managed != nil && n.Config.Managed.CreateBeforeDestroy { + if destroy && n.createBeforeDestroy() { createBeforeDestroyEnabled = true } diff --git a/terraform/transform_destroy_cbd.go b/terraform/transform_destroy_cbd.go index 3470e1804..f4cc75ced 100644 --- a/terraform/transform_destroy_cbd.go +++ b/terraform/transform_destroy_cbd.go @@ -23,6 +23,20 @@ type GraphNodeDestroyerCBD interface { ModifyCreateBeforeDestroy(bool) error } +// GraphNodeAttachDestroyer is implemented by applyable nodes that have a +// companion destroy node. This allows the creation node to look up the status +// of the destroy node and determine if it needs to depose the existing state, +// or replace it. +// If a node is not marked as create-before-destroy in the configuration, but a +// dependency forces that status, only the destroy node will be aware of that +// status. +type GraphNodeAttachDestroyer interface { + // AttachDestroyNode takes a destroy node and saves a reference to that + // node in the receiver, so it can later check the status of + // CreateBeforeDestroy(). + AttachDestroyNode(n GraphNodeDestroyerCBD) +} + // CBDEdgeTransformer modifies the edges of CBD nodes that went through // the DestroyEdgeTransformer to have the right dependencies. There are // two real tasks here: diff --git a/terraform/transform_destroy_edge.go b/terraform/transform_destroy_edge.go index 80adf666a..bda93bec7 100644 --- a/terraform/transform_destroy_edge.go +++ b/terraform/transform_destroy_edge.go @@ -110,6 +110,16 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error { dag.VertexName(a), dag.VertexName(a_d)) g.Connect(&DestroyEdge{S: a, T: a_d}) + + // Attach the destroy node to the creator + // There really shouldn't be more than one destroyer, but even if + // there are, any of them will represent the correct + // CreateBeforeDestroy status. + if n, ok := cn.(GraphNodeAttachDestroyer); ok { + if d, ok := d.(GraphNodeDestroyerCBD); ok { + n.AttachDestroyNode(d) + } + } } }