github.com/articulate/terraform@v0.6.13-0.20160303003731-8d31c93862de/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  // GraphNodeEdgeInclude can be implemented to not include something
    49  // as an edge within the destroy graph. This is usually done because it
    50  // might cause unnecessary cycles.
    51  type GraphNodeDestroyEdgeInclude interface {
    52  	DestroyEdgeInclude(dag.Vertex) bool
    53  }
    54  
    55  // DestroyTransformer is a GraphTransformer that creates the destruction
    56  // nodes for things that _might_ be destroyed.
    57  type DestroyTransformer struct {
    58  	FullDestroy bool
    59  }
    60  
    61  func (t *DestroyTransformer) Transform(g *Graph) error {
    62  	var connect, remove []dag.Edge
    63  
    64  	modes := []GraphNodeDestroyMode{DestroyPrimary, DestroyTainted}
    65  	for _, m := range modes {
    66  		connectMode, removeMode, err := t.transform(g, m)
    67  		if err != nil {
    68  			return err
    69  		}
    70  
    71  		connect = append(connect, connectMode...)
    72  		remove = append(remove, removeMode...)
    73  	}
    74  
    75  	// Atomatically add/remove the edges
    76  	for _, e := range connect {
    77  		g.Connect(e)
    78  	}
    79  	for _, e := range remove {
    80  		g.RemoveEdge(e)
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func (t *DestroyTransformer) transform(
    87  	g *Graph, mode GraphNodeDestroyMode) ([]dag.Edge, []dag.Edge, error) {
    88  	var connect, remove []dag.Edge
    89  	nodeToCn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices()))
    90  	nodeToDn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices()))
    91  	for _, v := range g.Vertices() {
    92  		// If it is not a destroyable, we don't care
    93  		cn, ok := v.(GraphNodeDestroyable)
    94  		if !ok {
    95  			continue
    96  		}
    97  
    98  		// Grab the destroy side of the node and connect it through
    99  		n := cn.DestroyNode(mode)
   100  		if n == nil {
   101  			continue
   102  		}
   103  
   104  		// Store it
   105  		nodeToCn[n] = cn
   106  		nodeToDn[cn] = n
   107  
   108  		// If the creation node is equal to the destroy node, then
   109  		// don't do any of the edge jump rope below.
   110  		if n.(interface{}) == cn.(interface{}) {
   111  			continue
   112  		}
   113  
   114  		// Add it to the graph
   115  		g.Add(n)
   116  
   117  		// Inherit all the edges from the old node
   118  		downEdges := g.DownEdges(v).List()
   119  		for _, edgeRaw := range downEdges {
   120  			// If this thing specifically requests to not be depended on
   121  			// by destroy nodes, then don't.
   122  			if i, ok := edgeRaw.(GraphNodeDestroyEdgeInclude); ok &&
   123  				!i.DestroyEdgeInclude(v) {
   124  				continue
   125  			}
   126  
   127  			g.Connect(dag.BasicEdge(n, edgeRaw.(dag.Vertex)))
   128  		}
   129  
   130  		// Add a new edge to connect the node to be created to
   131  		// the destroy node.
   132  		connect = append(connect, dag.BasicEdge(v, n))
   133  	}
   134  
   135  	// Go through the nodes we added and determine if they depend
   136  	// on any nodes with a destroy node. If so, depend on that instead.
   137  	for n, _ := range nodeToCn {
   138  		for _, downRaw := range g.DownEdges(n).List() {
   139  			target := downRaw.(dag.Vertex)
   140  			cn2, ok := target.(GraphNodeDestroyable)
   141  			if !ok {
   142  				continue
   143  			}
   144  
   145  			newTarget := nodeToDn[cn2]
   146  			if newTarget == nil {
   147  				continue
   148  			}
   149  
   150  			// Make the new edge and transpose
   151  			connect = append(connect, dag.BasicEdge(newTarget, n))
   152  
   153  			// Remove the old edge
   154  			remove = append(remove, dag.BasicEdge(n, target))
   155  		}
   156  	}
   157  
   158  	return connect, remove, nil
   159  }
   160  
   161  // CreateBeforeDestroyTransformer is a GraphTransformer that modifies
   162  // the destroys of some nodes so that the creation happens before the
   163  // destroy.
   164  type CreateBeforeDestroyTransformer struct{}
   165  
   166  func (t *CreateBeforeDestroyTransformer) Transform(g *Graph) error {
   167  	// We "stage" the edge connections/destroys in these slices so that
   168  	// while we're doing the edge transformations (transpositions) in
   169  	// the graph, we're not affecting future edge transpositions. These
   170  	// slices let us stage ALL the changes that WILL happen so that all
   171  	// of the transformations happen atomically.
   172  	var connect, destroy []dag.Edge
   173  
   174  	for _, v := range g.Vertices() {
   175  		// We only care to use the destroy nodes
   176  		dn, ok := v.(GraphNodeDestroy)
   177  		if !ok {
   178  			continue
   179  		}
   180  
   181  		// If the node doesn't need to create before destroy, then continue
   182  		if !dn.CreateBeforeDestroy() {
   183  			continue
   184  		}
   185  
   186  		// Get the creation side of this node
   187  		cn := dn.CreateNode()
   188  
   189  		// Take all the things which depend on the creation node and
   190  		// make them dependencies on the destruction. Clarifying this
   191  		// with an example: if you have a web server and a load balancer
   192  		// and the load balancer depends on the web server, then when we
   193  		// do a create before destroy, we want to make sure the steps are:
   194  		//
   195  		// 1.) Create new web server
   196  		// 2.) Update load balancer
   197  		// 3.) Delete old web server
   198  		//
   199  		// This ensures that.
   200  		for _, sourceRaw := range g.UpEdges(cn).List() {
   201  			source := sourceRaw.(dag.Vertex)
   202  
   203  			// If the graph has a "root" node (one added by a RootTransformer and not
   204  			// just a resource that happens to have no ancestors), we don't want to
   205  			// add any edges to it, because then it ceases to be a root.
   206  			if _, ok := source.(graphNodeRoot); ok {
   207  				continue
   208  			}
   209  
   210  			connect = append(connect, dag.BasicEdge(dn, source))
   211  		}
   212  
   213  		// Swap the edge so that the destroy depends on the creation
   214  		// happening...
   215  		connect = append(connect, dag.BasicEdge(dn, cn))
   216  		destroy = append(destroy, dag.BasicEdge(cn, dn))
   217  	}
   218  
   219  	for _, edge := range connect {
   220  		g.Connect(edge)
   221  	}
   222  	for _, edge := range destroy {
   223  		g.RemoveEdge(edge)
   224  	}
   225  
   226  	return nil
   227  }
   228  
   229  // PruneDestroyTransformer is a GraphTransformer that removes the destroy
   230  // nodes that aren't in the diff.
   231  type PruneDestroyTransformer struct {
   232  	Diff  *Diff
   233  	State *State
   234  }
   235  
   236  func (t *PruneDestroyTransformer) Transform(g *Graph) error {
   237  	for _, v := range g.Vertices() {
   238  		// If it is not a destroyer, we don't care
   239  		dn, ok := v.(GraphNodeDestroyPrunable)
   240  		if !ok {
   241  			continue
   242  		}
   243  
   244  		path := g.Path
   245  		if pn, ok := v.(GraphNodeSubPath); ok {
   246  			path = pn.Path()
   247  		}
   248  
   249  		var modDiff *ModuleDiff
   250  		var modState *ModuleState
   251  		if t.Diff != nil {
   252  			modDiff = t.Diff.ModuleByPath(path)
   253  		}
   254  		if t.State != nil {
   255  			modState = t.State.ModuleByPath(path)
   256  		}
   257  
   258  		// Remove it if we should
   259  		if !dn.DestroyInclude(modDiff, modState) {
   260  			g.Remove(v)
   261  		}
   262  	}
   263  
   264  	return nil
   265  }