terraform: destroy edge must include resources through outputs

This fixes: `TestContext2Apply_moduleDestroyOrder`

The new destroy graph wasn't properly creating edges that happened
_through_ an output, it was only created the edges for _direct_
dependents.

To fix this, the DestroyEdgeTransformer now creates the full transitive
list of destroy edges by walking all ancestors. This will create more
edges than are necessary but also will no longer miss resources through
an output.
This commit is contained in:
Mitchell Hashimoto 2016-11-11 14:29:19 -08:00
parent 008b8b4c23
commit f6161a7dc9
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
4 changed files with 54 additions and 5 deletions

View File

@ -0,0 +1,5 @@
resource "aws_instance" "b" {
value = "foo"
}
output "output" { value = "${aws_instance.b.value}" }

View File

@ -0,0 +1,7 @@
resource "aws_instance" "a" {
value = "${module.child.output}"
}
module "child" {
source = "./child"
}

View File

@ -116,8 +116,10 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// in the graph. BUT if resource A is just pure destroy, then only
// destroy A is in the graph, and create A is not.
steps := []GraphTransformer{
&OutputTransformer{Module: t.Module},
&AttachResourceConfigTransformer{Module: t.Module},
&AttachStateTransformer{State: t.State},
&ReferenceTransformer{},
}
// Go through all the nodes being destroyed and create a graph.
@ -127,6 +129,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// A, B (with no edges)
//
var tempG Graph
var tempDestroyed []dag.Vertex
for d, _ := range destroyers {
// d is what is being destroyed. We parse the resource address
// which it came from it is a panic if this fails.
@ -140,6 +143,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// attach config and state transformers then ask for references.
node := &NodeAbstractResource{Addr: addr}
tempG.Add(node)
tempDestroyed = append(tempDestroyed, node)
}
// Run the graph transforms so we have the information we need to
@ -150,14 +154,22 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
}
}
// Create a reference map for easy lookup
refMap := NewReferenceMap(tempG.Vertices())
log.Printf("[TRACE] DestroyEdgeTransformer: reference graph: %s", tempG.String())
// Go through all the nodes in the graph and determine what they
// depend on.
for _, v := range tempG.Vertices() {
// Find all the references
refs, _ := refMap.References(v)
for _, v := range tempDestroyed {
// Find all descendents of this to determine the edges we'll depend on
vs, err := tempG.Ancestors(v)
if err != nil {
return err
}
refs := make([]dag.Vertex, 0, vs.Len())
for _, raw := range vs.List() {
refs = append(refs, raw.(dag.Vertex))
}
log.Printf(
"[TRACE] DestroyEdgeTransformer: creation node %q references %v",
dag.VertexName(v), refs)

View File

@ -78,6 +78,24 @@ func TestDestroyEdgeTransformer_selfRef(t *testing.T) {
}
}
func TestDestroyEdgeTransformer_module(t *testing.T) {
g := Graph{Path: RootModulePath}
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.aws_instance.b"})
g.Add(&graphNodeDestroyerTest{AddrString: "aws_instance.a"})
tf := &DestroyEdgeTransformer{
Module: testModule(t, "transform-destroy-edge-module"),
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDestroyEdgeModuleStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
type graphNodeCreatorTest struct {
AddrString string
}
@ -125,6 +143,7 @@ test.B (destroy)
const testTransformDestroyEdgeMultiStr = `
test.A (destroy)
test.B (destroy)
test.C (destroy)
test.B (destroy)
test.C (destroy)
test.C (destroy)
@ -133,3 +152,9 @@ test.C (destroy)
const testTransformDestroyEdgeSelfRefStr = `
test.A (destroy)
`
const testTransformDestroyEdgeModuleStr = `
aws_instance.a (destroy)
module.child.aws_instance.b (destroy)
aws_instance.a (destroy)
`