github.com/aspring/terraform@v0.8.2-0.20161216122603-6a8619a5db2e/terraform/transform_destroy.go (about)

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