From de7b834de783763a98660ce05c400a32c272a6ff Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 30 May 2018 13:16:39 -0700 Subject: [PATCH] core: Local and output values must reference destroy nodes too Because we currently rely on the ReferenceTransformer to introduce the necessary edges between local/output values and resource destroy nodes, we must include the destroy phase of any resource we depend on in the references of these. This works in conjunction with the changes in the prior commit to restore correct handling of dependencies for local and output values during destroy. With the current design, several seemingly-separate parts of the code must all coincidentally agree with one another for destroy edges to be created properly, which makes this code very hard to maintain. In future we should refactor this so that ReferenceTransformer doesn't create edges for destroy nodes at all, and have _all_ destroy edges (including create_before_destroy) be dealt with in the single DestroyEdgeTransformer, where they can be maintained and unit tested together. --- terraform/node_local.go | 2 +- terraform/node_output.go | 2 +- terraform/transform_reference.go | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/terraform/node_local.go b/terraform/node_local.go index de41fc5fc9..591eb305a6 100644 --- a/terraform/node_local.go +++ b/terraform/node_local.go @@ -47,7 +47,7 @@ func (n *NodeLocal) ReferenceableAddrs() []addrs.Referenceable { // GraphNodeReferencer func (n *NodeLocal) References() []*addrs.Reference { refs, _ := lang.ReferencesInExpr(n.Config.Expr) - return refs + return appendResourceDestroyReferences(refs) } // GraphNodeEvalable diff --git a/terraform/node_output.go b/terraform/node_output.go index cd686a33a6..c42dd4860f 100644 --- a/terraform/node_output.go +++ b/terraform/node_output.go @@ -108,7 +108,7 @@ func referencesForOutput(c *configs.Output) []*addrs.Reference { // GraphNodeReferencer func (n *NodeApplyableOutput) References() []*addrs.Reference { - return referencesForOutput(n.Config) + return appendResourceDestroyReferences(referencesForOutput(n.Config)) } // GraphNodeEvalable diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 4e8cae06da..4e2c629b56 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -453,6 +453,29 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string { } } +// appendResourceDestroyReferences identifies resource and resource instance +// references in the given slice and appends to it the "destroy-phase" +// equivalents of those references, returning the result. +// +// This can be used in the References implementation for a node which must also +// depend on the destruction of anything it references. +func appendResourceDestroyReferences(refs []*addrs.Reference) []*addrs.Reference { + given := refs + for _, ref := range given { + switch tr := ref.Subject.(type) { + case addrs.Resource: + newRef := *ref // shallow copy + newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) + refs = append(refs, &newRef) + case addrs.ResourceInstance: + newRef := *ref // shallow copy + newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) + refs = append(refs, &newRef) + } + } + return refs +} + func modulePrefixStr(p addrs.ModuleInstance) string { return p.String() }