github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/terraform/transform_destroy.go (about)

     1  package terraform
     2  
     3  import (
     4  	"github.com/hashicorp/terraform/dag"
     5  )
     6  
     7  type GraphNodeDestroyMode byte
     8  
     9  const (
    10  	DestroyNone    GraphNodeDestroyMode = 0
    11  	DestroyPrimary GraphNodeDestroyMode = 1 << iota
    12  	DestroyTainted
    13  )
    14  
    15  // GraphNodeDestroyable is the interface that nodes that can be destroyed
    16  // must implement. This is used to automatically handle the creation of
    17  // destroy nodes in the graph and the dependency ordering of those destroys.
    18  type GraphNodeDestroyable interface {
    19  	// DestroyNode returns the node used for the destroy with the given
    20  	// mode. If this returns nil, then a destroy node for that mode
    21  	// will not be added.
    22  	DestroyNode(GraphNodeDestroyMode) GraphNodeDestroy
    23  }
    24  
    25  // GraphNodeDestroy is the interface that must implemented by
    26  // nodes that destroy.
    27  type GraphNodeDestroy interface {
    28  	dag.Vertex
    29  
    30  	// CreateBeforeDestroy is called to check whether this node
    31  	// should be created before it is destroyed. The CreateBeforeDestroy
    32  	// transformer uses this information to setup the graph.
    33  	CreateBeforeDestroy() bool
    34  
    35  	// CreateNode returns the node used for the create side of this
    36  	// destroy. This must already exist within the graph.
    37  	CreateNode() dag.Vertex
    38  }
    39  
    40  // GraphNodeDestroyPrunable is the interface that can be implemented to
    41  // signal that this node can be pruned depending on state.
    42  type GraphNodeDestroyPrunable interface {
    43  	// DestroyInclude is called to check if this node should be included
    44  	// with the given state. The state and diff must NOT be modified.
    45  	DestroyInclude(*ModuleDiff, *ModuleState) bool
    46  }
    47  
    48  // DestroyTransformer is a GraphTransformer that creates the destruction
    49  // nodes for things that _might_ be destroyed.
    50  type DestroyTransformer struct{}
    51  
    52  func (t *DestroyTransformer) Transform(g *Graph) error {
    53  	var connect, remove []dag.Edge
    54  
    55  	modes := []GraphNodeDestroyMode{DestroyPrimary, DestroyTainted}
    56  	for _, m := range modes {
    57  		connectMode, removeMode, err := t.transform(g, m)
    58  		if err != nil {
    59  			return err
    60  		}
    61  
    62  		connect = append(connect, connectMode...)
    63  		remove = append(remove, removeMode...)
    64  	}
    65  
    66  	// Atomatically add/remove the edges
    67  	for _, e := range connect {
    68  		g.Connect(e)
    69  	}
    70  	for _, e := range remove {
    71  		g.RemoveEdge(e)
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func (t *DestroyTransformer) transform(
    78  	g *Graph, mode GraphNodeDestroyMode) ([]dag.Edge, []dag.Edge, error) {
    79  	var connect, remove []dag.Edge
    80  	nodeToCn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices()))
    81  	nodeToDn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices()))
    82  	for _, v := range g.Vertices() {
    83  		// If it is not a destroyable, we don't care
    84  		cn, ok := v.(GraphNodeDestroyable)
    85  		if !ok {
    86  			continue
    87  		}
    88  
    89  		// Grab the destroy side of the node and connect it through
    90  		n := cn.DestroyNode(mode)
    91  		if n == nil {
    92  			continue
    93  		}
    94  
    95  		// Store it
    96  		nodeToCn[n] = cn
    97  		nodeToDn[cn] = n
    98  
    99  		// Add it to the graph
   100  		g.Add(n)
   101  
   102  		// Inherit all the edges from the old node
   103  		downEdges := g.DownEdges(v).List()
   104  		for _, edgeRaw := range downEdges {
   105  			g.Connect(dag.BasicEdge(n, edgeRaw.(dag.Vertex)))
   106  		}
   107  
   108  		// Add a new edge to connect the node to be created to
   109  		// the destroy node.
   110  		connect = append(connect, dag.BasicEdge(v, n))
   111  	}
   112  
   113  	// Go through the nodes we added and determine if they depend
   114  	// on any nodes with a destroy node. If so, depend on that instead.
   115  	for n, _ := range nodeToCn {
   116  		for _, downRaw := range g.DownEdges(n).List() {
   117  			target := downRaw.(dag.Vertex)
   118  			cn2, ok := target.(GraphNodeDestroyable)
   119  			if !ok {
   120  				continue
   121  			}
   122  
   123  			newTarget := nodeToDn[cn2]
   124  			if newTarget == nil {
   125  				continue
   126  			}
   127  
   128  			// Make the new edge and transpose
   129  			connect = append(connect, dag.BasicEdge(newTarget, n))
   130  
   131  			// Remove the old edge
   132  			remove = append(remove, dag.BasicEdge(n, target))
   133  		}
   134  	}
   135  
   136  	return connect, remove, nil
   137  }
   138  
   139  // CreateBeforeDestroyTransformer is a GraphTransformer that modifies
   140  // the destroys of some nodes so that the creation happens before the
   141  // destroy.
   142  type CreateBeforeDestroyTransformer struct{}
   143  
   144  func (t *CreateBeforeDestroyTransformer) Transform(g *Graph) error {
   145  	// We "stage" the edge connections/destroys in these slices so that
   146  	// while we're doing the edge transformations (transpositions) in
   147  	// the graph, we're not affecting future edge transpositions. These
   148  	// slices let us stage ALL the changes that WILL happen so that all
   149  	// of the transformations happen atomically.
   150  	var connect, destroy []dag.Edge
   151  
   152  	for _, v := range g.Vertices() {
   153  		// We only care to use the destroy nodes
   154  		dn, ok := v.(GraphNodeDestroy)
   155  		if !ok {
   156  			continue
   157  		}
   158  
   159  		// If the node doesn't need to create before destroy, then continue
   160  		if !dn.CreateBeforeDestroy() {
   161  			continue
   162  		}
   163  
   164  		// Get the creation side of this node
   165  		cn := dn.CreateNode()
   166  
   167  		// Take all the things which depend on the creation node and
   168  		// make them dependencies on the destruction. Clarifying this
   169  		// with an example: if you have a web server and a load balancer
   170  		// and the load balancer depends on the web server, then when we
   171  		// do a create before destroy, we want to make sure the steps are:
   172  		//
   173  		// 1.) Create new web server
   174  		// 2.) Update load balancer
   175  		// 3.) Delete old web server
   176  		//
   177  		// This ensures that.
   178  		for _, sourceRaw := range g.UpEdges(cn).List() {
   179  			source := sourceRaw.(dag.Vertex)
   180  			connect = append(connect, dag.BasicEdge(dn, source))
   181  		}
   182  
   183  		// Swap the edge so that the destroy depends on the creation
   184  		// happening...
   185  		connect = append(connect, dag.BasicEdge(dn, cn))
   186  		destroy = append(destroy, dag.BasicEdge(cn, dn))
   187  	}
   188  
   189  	for _, edge := range connect {
   190  		g.Connect(edge)
   191  	}
   192  	for _, edge := range destroy {
   193  		g.RemoveEdge(edge)
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  // PruneDestroyTransformer is a GraphTransformer that removes the destroy
   200  // nodes that aren't in the diff.
   201  type PruneDestroyTransformer struct {
   202  	Diff  *Diff
   203  	State *State
   204  }
   205  
   206  func (t *PruneDestroyTransformer) Transform(g *Graph) error {
   207  	var modDiff *ModuleDiff
   208  	var modState *ModuleState
   209  	if t.Diff != nil {
   210  		modDiff = t.Diff.ModuleByPath(g.Path)
   211  	}
   212  	if t.State != nil {
   213  		modState = t.State.ModuleByPath(g.Path)
   214  	}
   215  
   216  	for _, v := range g.Vertices() {
   217  		// If it is not a destroyer, we don't care
   218  		dn, ok := v.(GraphNodeDestroyPrunable)
   219  		if !ok {
   220  			continue
   221  		}
   222  
   223  		// Remove it if we should
   224  		if !dn.DestroyInclude(modDiff, modState) {
   225  			g.Remove(v)
   226  		}
   227  	}
   228  
   229  	return nil
   230  }