github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/terraform/transform_destroy.go (about)

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