terraform: test the destroy edge transform
This commit is contained in:
parent
bd5d97f9f5
commit
7b2bd93094
|
@ -0,0 +1,2 @@
|
|||
resource "test" "A" {}
|
||||
resource "test" "B" { value = "${test.A.value}" }
|
|
@ -29,6 +29,8 @@ type AttachResourceConfigTransformer struct {
|
|||
}
|
||||
|
||||
func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {
|
||||
log.Printf("[TRACE] AttachResourceConfigTransformer: Beginning...")
|
||||
|
||||
// Go through and find GraphNodeAttachResource
|
||||
for _, v := range g.Vertices() {
|
||||
// Only care about GraphNodeAttachResource implementations
|
||||
|
@ -39,7 +41,7 @@ func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {
|
|||
|
||||
// Determine what we're looking for
|
||||
addr := arn.ResourceAddr()
|
||||
log.Printf("[TRACE] Attach resource request: %s", addr)
|
||||
log.Printf("[TRACE] AttachResourceConfigTransformer: Attach resource request: %s", addr)
|
||||
|
||||
// Get the configuration.
|
||||
path := normalizeModulePath(addr.Path)
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
)
|
||||
|
||||
// GraphNodeDestroyer must be implemented by nodes that destroy resources.
|
||||
type GraphNodeDestroyer interface {
|
||||
dag.Vertex
|
||||
|
||||
// ResourceAddr is the address of the resource that is being
|
||||
// destroyed by this node. If this returns nil, then this node
|
||||
// is not destroying anything.
|
||||
|
@ -22,9 +31,16 @@ type GraphNodeDestroyer interface {
|
|||
// dependent resources will block parent resources from deleting. Concrete
|
||||
// example: VPC with subnets, the VPC can't be deleted while there are
|
||||
// still subnets.
|
||||
type DestroyEdgeTransformer struct{}
|
||||
type DestroyEdgeTransformer struct {
|
||||
// Module and State are only needed to look up dependencies in
|
||||
// any way possible. Either can be nil if not availabile.
|
||||
Module *module.Tree
|
||||
State *State
|
||||
}
|
||||
|
||||
func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
|
||||
log.Printf("[TRACE] DestroyEdgeTransformer: Beginning destroy edge transformation...")
|
||||
|
||||
// Build a map of what is being destroyed (by address string) to
|
||||
// the list of destroyers. In general there will only be one destroyer
|
||||
// but to make it more robust we support multiple.
|
||||
|
@ -41,6 +57,9 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
|
|||
}
|
||||
|
||||
key := addr.String()
|
||||
log.Printf(
|
||||
"[TRACE] DestroyEdgeTransformer: %s destroying %q",
|
||||
dag.VertexName(dn), key)
|
||||
destroyers[key] = append(destroyers[key], dn)
|
||||
}
|
||||
|
||||
|
@ -50,13 +69,83 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// This is strange but is the easiest way to get the dependencies
|
||||
// of a node that is being destroyed. We use another graph to make sure
|
||||
// the resource is in the graph and ask for references. We have to do this
|
||||
// because the node that is being destroyed may NOT be in the graph.
|
||||
//
|
||||
// Example: resource A is force new, then destroy A AND create A are
|
||||
// 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{
|
||||
&AttachResourceConfigTransformer{Module: t.Module},
|
||||
&AttachStateTransformer{State: t.State},
|
||||
}
|
||||
|
||||
// Go through the all destroyers and find what they're destroying.
|
||||
// Use this to find the dependencies, look up if any of them are being
|
||||
// destroyed, and to make the proper edge.
|
||||
for _, ds := range destroyers {
|
||||
for d, dns := range destroyers {
|
||||
// d is what is being destroyed. We parse the resource address
|
||||
// which it came from it is a panic if this fails.
|
||||
addr, err := ParseResourceAddress(d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// This part is a little bit weird but is the best way to
|
||||
// find the dependencies we need to: build a graph and use the
|
||||
// attach config and state transformers then ask for references.
|
||||
node := &NodeApplyableResource{Addr: addr}
|
||||
{
|
||||
var g Graph
|
||||
g.Add(node)
|
||||
for _, s := range steps {
|
||||
if err := s.Transform(&g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the references of the creation node. If it has none,
|
||||
// then there are no edges to make here.
|
||||
prefix := modulePrefixStr(normalizeModulePath(addr.Path))
|
||||
deps := modulePrefixList(node.References(), prefix)
|
||||
log.Printf(
|
||||
"[TRACE] DestroyEdgeTransformer: creation of %q depends on %#v",
|
||||
d, deps)
|
||||
if len(deps) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have dependencies, check if any are being destroyed
|
||||
// to build the list of things that we must depend on!
|
||||
//
|
||||
// In the example of the struct, if we have:
|
||||
//
|
||||
// B_d => A_d => A => B
|
||||
//
|
||||
// Then at this point in the algorithm we started with A_d,
|
||||
// we built A (to get dependencies), and we found B. We're now looking
|
||||
// to see if B_d exists.
|
||||
var depDestroyers []dag.Vertex
|
||||
for _, d := range deps {
|
||||
if ds, ok := destroyers[d]; ok {
|
||||
for _, d := range ds {
|
||||
// TODO
|
||||
println(d)
|
||||
depDestroyers = append(depDestroyers, d.(dag.Vertex))
|
||||
log.Printf(
|
||||
"[TRACE] DestroyEdgeTransformer: destruction of %q depends on %s",
|
||||
addr.String(), dag.VertexName(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through and make the connections. Use the variable
|
||||
// names "a_d" and "b_d" to reference our example.
|
||||
for _, a_d := range dns {
|
||||
for _, b_d := range depDestroyers {
|
||||
g.Connect(dag.BasicEdge(b_d, a_d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDestroyEdgeTransformer(t *testing.T) {
|
||||
g := Graph{Path: RootModulePath}
|
||||
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"})
|
||||
g.Add(&graphNodeDestroyerTest{AddrString: "test.B"})
|
||||
tf := &DestroyEdgeTransformer{
|
||||
Module: testModule(t, "transform-destroy-edge-basic"),
|
||||
}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.String())
|
||||
expected := strings.TrimSpace(testTransformDestroyEdgeBasicStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
type graphNodeDestroyerTest struct {
|
||||
AddrString string
|
||||
}
|
||||
|
||||
func (n *graphNodeDestroyerTest) Name() string { return n.DestroyAddr().String() + " (destroy)" }
|
||||
func (n *graphNodeDestroyerTest) DestroyAddr() *ResourceAddress {
|
||||
addr, err := ParseResourceAddress(n.AddrString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
const testTransformDestroyEdgeBasicStr = `
|
||||
test.A (destroy)
|
||||
test.B (destroy)
|
||||
test.B (destroy)
|
||||
`
|
Loading…
Reference in New Issue