github.com/ljosa/terraform@v0.7.0-rc2.0.20160617205345-fe540b408f59/terraform/transform_destroy.go (about)

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