github.com/mehmetalisavas/terraform@v0.7.10/terraform/transform_destroy.go (about)

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