terraform: starting CBD, destroy edge for the destroy relationship
This commit is contained in:
parent
4988378ccb
commit
7baf64f806
|
@ -0,0 +1,17 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
)
|
||||
|
||||
// DestroyEdge is an edge that represents a standard "destroy" relationship:
|
||||
// Target depends on Source because Source is destroying.
|
||||
type DestroyEdge struct {
|
||||
S, T dag.Vertex
|
||||
}
|
||||
|
||||
func (e *DestroyEdge) Hashcode() interface{} { return fmt.Sprintf("%p-%p", e.S, e.T) }
|
||||
func (e *DestroyEdge) Source() dag.Vertex { return e.S }
|
||||
func (e *DestroyEdge) Target() dag.Vertex { return e.T }
|
|
@ -90,6 +90,11 @@ func (n *NodeApplyableResource) ProvisionedBy() []string {
|
|||
return result
|
||||
}
|
||||
|
||||
// GraphNodeCreator
|
||||
func (n *NodeApplyableResource) CreateAddr() *ResourceAddress {
|
||||
return n.Addr
|
||||
}
|
||||
|
||||
// GraphNodeAttachResourceState
|
||||
func (n *NodeApplyableResource) ResourceAddr() *ResourceAddress {
|
||||
return n.Addr
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
)
|
||||
|
||||
// GraphNodeDestroyerCBD must be implemented by nodes that might be
|
||||
// create-before-destroy destroyers.
|
||||
type GraphNodeDestroyerCBD interface {
|
||||
GraphNodeDestroyer
|
||||
|
||||
// CreateBeforeDestroy returns true if this node represents a node
|
||||
// that is doing a CBD.
|
||||
CreateBeforeDestroy() bool
|
||||
}
|
||||
|
||||
// CBDEdgeTransformer modifies the edges of CBD nodes that went through
|
||||
// the DestroyEdgeTransformer to have the right dependencies. There are
|
||||
// two real tasks here:
|
||||
//
|
||||
// 1. With CBD, the destroy edge is inverted: the destroy depends on
|
||||
// the creation.
|
||||
//
|
||||
// 2. A_d must depend on resources that depend on A. This is to enable
|
||||
// the destroy to only happen once nodes that depend on A successfully
|
||||
// update to A. Example: adding a web server updates the load balancer
|
||||
// before deleting the old web server.
|
||||
//
|
||||
type CBDEdgeTransformer 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 *CBDEdgeTransformer) Transform(g *Graph) error {
|
||||
log.Printf("[TRACE] CBDEdgeTransformer: Beginning CBD transformation...")
|
||||
|
||||
// Go through and reverse any destroy edges
|
||||
for _, v := range g.Vertices() {
|
||||
dn, ok := v.(GraphNodeDestroyerCBD)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if !dn.CreateBeforeDestroy() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the destroy edge. There should only be one.
|
||||
for _, e := range g.DownEdges(v).List() {
|
||||
// Not a destroy edge, ignore it
|
||||
de, ok := e.(*DestroyEdge)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Found it! Invert.
|
||||
g.RemoveEdge(de)
|
||||
g.Connect(&DestroyEdge{S: de.Target(), T: de.Source()})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCBDEdgeTransformer(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)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
tf := &CBDEdgeTransformer{}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.String())
|
||||
expected := strings.TrimSpace(testTransformCBDEdgeBasicStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
const testTransformCBDEdgeBasicStr = `
|
||||
test.A (destroy)
|
||||
test.B (destroy)
|
||||
test.B (destroy)
|
||||
`
|
|
@ -17,6 +17,12 @@ type GraphNodeDestroyer interface {
|
|||
DestroyAddr() *ResourceAddress
|
||||
}
|
||||
|
||||
// GraphNodeCreator must be implemented by nodes that create OR update resources.
|
||||
type GraphNodeCreator interface {
|
||||
// ResourceAddr is the address of the resource being created or updated
|
||||
CreateAddr() *ResourceAddress
|
||||
}
|
||||
|
||||
// DestroyEdgeTransformer is a GraphTransformer that creates the proper
|
||||
// references for destroy resources. Destroy resources are more complex
|
||||
// in that they must be depend on the destruction of resources that
|
||||
|
@ -69,6 +75,34 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Go through and connect creators to destroyers. Going along with
|
||||
// our example, this makes: A_d => A
|
||||
for _, v := range g.Vertices() {
|
||||
cn, ok := v.(GraphNodeCreator)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
addr := cn.CreateAddr()
|
||||
if addr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key := addr.String()
|
||||
ds := destroyers[key]
|
||||
if len(ds) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, d := range ds {
|
||||
// For illustrating our example
|
||||
a_d := d.(dag.Vertex)
|
||||
a := v
|
||||
|
||||
g.Connect(&DestroyEdge{S: a, T: a_d})
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -23,6 +23,25 @@ func TestDestroyEdgeTransformer(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDestroyEdgeTransformer_create(t *testing.T) {
|
||||
g := Graph{Path: RootModulePath}
|
||||
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"})
|
||||
g.Add(&graphNodeDestroyerTest{AddrString: "test.B"})
|
||||
g.Add(&graphNodeCreatorTest{AddrString: "test.A"})
|
||||
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(testTransformDestroyEdgeCreatorStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDestroyEdgeTransformer_multi(t *testing.T) {
|
||||
g := Graph{Path: RootModulePath}
|
||||
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"})
|
||||
|
@ -42,11 +61,27 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type graphNodeDestroyerTest struct {
|
||||
type graphNodeCreatorTest struct {
|
||||
AddrString string
|
||||
}
|
||||
|
||||
func (n *graphNodeDestroyerTest) Name() string { return n.DestroyAddr().String() + " (destroy)" }
|
||||
func (n *graphNodeCreatorTest) Name() string { return n.CreateAddr().String() }
|
||||
func (n *graphNodeCreatorTest) CreateAddr() *ResourceAddress {
|
||||
addr, err := ParseResourceAddress(n.AddrString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
type graphNodeDestroyerTest struct {
|
||||
AddrString string
|
||||
CBD bool
|
||||
}
|
||||
|
||||
func (n *graphNodeDestroyerTest) Name() string { return n.DestroyAddr().String() + " (destroy)" }
|
||||
func (n *graphNodeDestroyerTest) CreateBeforeDestroy() bool { return n.CBD }
|
||||
func (n *graphNodeDestroyerTest) DestroyAddr() *ResourceAddress {
|
||||
addr, err := ParseResourceAddress(n.AddrString)
|
||||
if err != nil {
|
||||
|
@ -62,6 +97,14 @@ test.A (destroy)
|
|||
test.B (destroy)
|
||||
`
|
||||
|
||||
const testTransformDestroyEdgeCreatorStr = `
|
||||
test.A
|
||||
test.A (destroy)
|
||||
test.A (destroy)
|
||||
test.B (destroy)
|
||||
test.B (destroy)
|
||||
`
|
||||
|
||||
const testTransformDestroyEdgeMultiStr = `
|
||||
test.A (destroy)
|
||||
test.B (destroy)
|
||||
|
|
Loading…
Reference in New Issue