diff --git a/terraform/graph_config_node_output.go b/terraform/graph_config_node_output.go index 232d737ea..5b2d95fdc 100644 --- a/terraform/graph_config_node_output.go +++ b/terraform/graph_config_node_output.go @@ -62,7 +62,7 @@ func (n *GraphNodeConfigOutput) Proxy() bool { } // GraphNodeDestroyEdgeInclude impl. -func (n *GraphNodeConfigOutput) DestroyEdgeInclude(bool) bool { +func (n *GraphNodeConfigOutput) DestroyEdgeInclude(dag.Vertex) bool { return false } diff --git a/terraform/graph_config_node_resource.go b/terraform/graph_config_node_resource.go index 298e90167..30667cb43 100644 --- a/terraform/graph_config_node_resource.go +++ b/terraform/graph_config_node_resource.go @@ -9,6 +9,12 @@ import ( "github.com/hashicorp/terraform/dot" ) +// GraphNodeCountDependent is implemented by resources for giving only +// the dependencies they have from the "count" field. +type GraphNodeCountDependent interface { + CountDependentOn() []string +} + // GraphNodeConfigResource represents a resource within the config graph. type GraphNodeConfigResource struct { Resource *config.Resource @@ -31,6 +37,18 @@ func (n *GraphNodeConfigResource) DependableName() []string { return []string{n.Resource.Id()} } +// GraphNodeCountDependent impl. +func (n *GraphNodeConfigResource) CountDependentOn() []string { + result := make([]string, 0, len(n.Resource.RawCount.Variables)) + for _, v := range n.Resource.RawCount.Variables { + if vn := varNameForVar(v); vn != "" { + result = append(result, vn) + } + } + + return result +} + // GraphNodeDependent impl. func (n *GraphNodeConfigResource) DependentOn() []string { result := make([]string, len(n.Resource.DependsOn), diff --git a/terraform/graph_config_node_variable.go b/terraform/graph_config_node_variable.go index 569219c4a..91f126c49 100644 --- a/terraform/graph_config_node_variable.go +++ b/terraform/graph_config_node_variable.go @@ -56,14 +56,23 @@ func (n *GraphNodeConfigVariable) VariableName() string { } // GraphNodeDestroyEdgeInclude impl. -func (n *GraphNodeConfigVariable) DestroyEdgeInclude(full bool) bool { - // Don't include variables as dependencies in destroy nodes. - // Destroy nodes don't interpolate anyways and this has a possibility - // to create cycles. See GH-1835 - // - // We include the variable on non-full destroys because it might - // be used for count interpolation. - return !full +func (n *GraphNodeConfigVariable) DestroyEdgeInclude(v dag.Vertex) bool { + // Only include this variable in a destroy edge if the source vertex + // "v" has a count dependency on this variable. + cv, ok := v.(GraphNodeCountDependent) + if !ok { + return false + } + + for _, d := range cv.CountDependentOn() { + for _, d2 := range n.DependableName() { + if d == d2 { + return true + } + } + } + + return false } // GraphNodeProxy impl. diff --git a/terraform/transform_destroy.go b/terraform/transform_destroy.go index 4fb076218..3fe585664 100644 --- a/terraform/transform_destroy.go +++ b/terraform/transform_destroy.go @@ -49,7 +49,7 @@ type GraphNodeDestroyPrunable interface { // as an edge within the destroy graph. This is usually done because it // might cause unnecessary cycles. type GraphNodeDestroyEdgeInclude interface { - DestroyEdgeInclude(bool) bool + DestroyEdgeInclude(dag.Vertex) bool } // DestroyTransformer is a GraphTransformer that creates the destruction @@ -114,7 +114,7 @@ func (t *DestroyTransformer) transform( // If this thing specifically requests to not be depended on // by destroy nodes, then don't. if i, ok := edgeRaw.(GraphNodeDestroyEdgeInclude); ok && - !i.DestroyEdgeInclude(t.FullDestroy) { + !i.DestroyEdgeInclude(v) { continue }