core: GraphNodeAttachDestroyer

Add a graphNodeAttachDestroy interface, so destroy nodes can be attached
to their companion create node. The creator can then reference the
CreateBeforeDestroy status of the destroyer, determining  if the current
state needs to be replaced or deposed.

This is needed when a node is forced to become CreateBeforeDestroy by a
dependency rather than the config, since because the config is
immutable, only the destroyer is aware that it has been forced
CreateBeforeDestroy.
This commit is contained in:
James Bardin 2018-06-01 09:13:14 -04:00 committed by Martin Atkins
parent fb70eaa7d1
commit fa87397f50
3 changed files with 49 additions and 3 deletions

View File

@ -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
}

View File

@ -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:

View File

@ -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)
}
}
}
}