github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/terraform/transform_destroy_cbd.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform/config/module"
     7  	"github.com/hashicorp/terraform/dag"
     8  )
     9  
    10  // GraphNodeDestroyerCBD must be implemented by nodes that might be
    11  // create-before-destroy destroyers.
    12  type GraphNodeDestroyerCBD interface {
    13  	GraphNodeDestroyer
    14  
    15  	// CreateBeforeDestroy returns true if this node represents a node
    16  	// that is doing a CBD.
    17  	CreateBeforeDestroy() bool
    18  }
    19  
    20  // CBDEdgeTransformer modifies the edges of CBD nodes that went through
    21  // the DestroyEdgeTransformer to have the right dependencies. There are
    22  // two real tasks here:
    23  //
    24  //   1. With CBD, the destroy edge is inverted: the destroy depends on
    25  //      the creation.
    26  //
    27  //   2. A_d must depend on resources that depend on A. This is to enable
    28  //      the destroy to only happen once nodes that depend on A successfully
    29  //      update to A. Example: adding a web server updates the load balancer
    30  //      before deleting the old web server.
    31  //
    32  type CBDEdgeTransformer struct {
    33  	// Module and State are only needed to look up dependencies in
    34  	// any way possible. Either can be nil if not availabile.
    35  	Module *module.Tree
    36  	State  *State
    37  }
    38  
    39  func (t *CBDEdgeTransformer) Transform(g *Graph) error {
    40  	log.Printf("[TRACE] CBDEdgeTransformer: Beginning CBD transformation...")
    41  
    42  	// Go through and reverse any destroy edges
    43  	destroyMap := make(map[string][]dag.Vertex)
    44  	for _, v := range g.Vertices() {
    45  		dn, ok := v.(GraphNodeDestroyerCBD)
    46  		if !ok {
    47  			continue
    48  		}
    49  
    50  		if !dn.CreateBeforeDestroy() {
    51  			continue
    52  		}
    53  
    54  		// Find the destroy edge. There should only be one.
    55  		for _, e := range g.EdgesTo(v) {
    56  			// Not a destroy edge, ignore it
    57  			de, ok := e.(*DestroyEdge)
    58  			if !ok {
    59  				continue
    60  			}
    61  
    62  			log.Printf("[TRACE] CBDEdgeTransformer: inverting edge: %s => %s",
    63  				dag.VertexName(de.Source()), dag.VertexName(de.Target()))
    64  
    65  			// Found it! Invert.
    66  			g.RemoveEdge(de)
    67  			g.Connect(&DestroyEdge{S: de.Target(), T: de.Source()})
    68  		}
    69  
    70  		// Add this to the list of nodes that we need to fix up
    71  		// the edges for (step 2 above in the docs).
    72  		key := dn.DestroyAddr().String()
    73  		destroyMap[key] = append(destroyMap[key], v)
    74  	}
    75  
    76  	// If we have no CBD nodes, then our work here is done
    77  	if len(destroyMap) == 0 {
    78  		return nil
    79  	}
    80  
    81  	// We have CBD nodes. We now have to move on to the much more difficult
    82  	// task of connecting dependencies of the creation side of the destroy
    83  	// to the destruction node. The easiest way to explain this is an example:
    84  	//
    85  	// Given a pre-destroy dependence of: A => B
    86  	//   And A has CBD set.
    87  	//
    88  	// The resulting graph should be: A => B => A_d
    89  	//
    90  	// They key here is that B happens before A is destroyed. This is to
    91  	// facilitate the primary purpose for CBD: making sure that downstreams
    92  	// are properly updated to avoid downtime before the resource is destroyed.
    93  	//
    94  	// We can't trust that the resource being destroyed or anything that
    95  	// depends on it is actually in our current graph so we make a new
    96  	// graph in order to determine those dependencies and add them in.
    97  	log.Printf("[TRACE] CBDEdgeTransformer: building graph to find dependencies...")
    98  	depMap, err := t.depMap(destroyMap)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	// We now have the mapping of resource addresses to the destroy
   104  	// nodes they need to depend on. We now go through our own vertices to
   105  	// find any matching these addresses and make the connection.
   106  	for _, v := range g.Vertices() {
   107  		// We're looking for creators
   108  		rn, ok := v.(GraphNodeCreator)
   109  		if !ok {
   110  			continue
   111  		}
   112  
   113  		// Get the address
   114  		addr := rn.CreateAddr()
   115  		key := addr.String()
   116  
   117  		// If there is nothing this resource should depend on, ignore it
   118  		dns, ok := depMap[key]
   119  		if !ok {
   120  			continue
   121  		}
   122  
   123  		// We have nodes! Make the connection
   124  		for _, dn := range dns {
   125  			log.Printf("[TRACE] CBDEdgeTransformer: destroy depends on dependence: %s => %s",
   126  				dag.VertexName(dn), dag.VertexName(v))
   127  			g.Connect(dag.BasicEdge(dn, v))
   128  		}
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  func (t *CBDEdgeTransformer) depMap(
   135  	destroyMap map[string][]dag.Vertex) (map[string][]dag.Vertex, error) {
   136  	// Build the graph of our config, this ensures that all resources
   137  	// are present in the graph.
   138  	g, err := (&BasicGraphBuilder{
   139  		Steps: []GraphTransformer{
   140  			&FlatConfigTransformer{Module: t.Module},
   141  			&AttachResourceConfigTransformer{Module: t.Module},
   142  			&AttachStateTransformer{State: t.State},
   143  			&ReferenceTransformer{},
   144  		},
   145  	}).Build(nil)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	// Using this graph, build the list of destroy nodes that each resource
   151  	// address should depend on. For example, when we find B, we map the
   152  	// address of B to A_d in the "depMap" variable below.
   153  	depMap := make(map[string][]dag.Vertex)
   154  	for _, v := range g.Vertices() {
   155  		// We're looking for resources.
   156  		rn, ok := v.(GraphNodeResource)
   157  		if !ok {
   158  			continue
   159  		}
   160  
   161  		// Get the address
   162  		addr := rn.ResourceAddr()
   163  		key := addr.String()
   164  
   165  		// Get the destroy nodes that are destroying this resource.
   166  		// If there aren't any, then we don't need to worry about
   167  		// any connections.
   168  		dns, ok := destroyMap[key]
   169  		if !ok {
   170  			continue
   171  		}
   172  
   173  		// Get the nodes that depend on this on. In the example above:
   174  		// finding B in A => B.
   175  		for _, v := range g.UpEdges(v).List() {
   176  			// We're looking for resources.
   177  			rn, ok := v.(GraphNodeResource)
   178  			if !ok {
   179  				continue
   180  			}
   181  
   182  			// Keep track of the destroy nodes that this address
   183  			// needs to depend on.
   184  			key := rn.ResourceAddr().String()
   185  			depMap[key] = append(depMap[key], dns...)
   186  		}
   187  	}
   188  
   189  	return depMap, nil
   190  }