github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/transform_destroy_edge.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/muratcelep/terraform/not-internal/addrs"
     7  	"github.com/muratcelep/terraform/not-internal/states"
     8  
     9  	"github.com/muratcelep/terraform/not-internal/configs"
    10  	"github.com/muratcelep/terraform/not-internal/dag"
    11  )
    12  
    13  // GraphNodeDestroyer must be implemented by nodes that destroy resources.
    14  type GraphNodeDestroyer interface {
    15  	dag.Vertex
    16  
    17  	// DestroyAddr is the address of the resource that is being
    18  	// destroyed by this node. If this returns nil, then this node
    19  	// is not destroying anything.
    20  	DestroyAddr() *addrs.AbsResourceInstance
    21  }
    22  
    23  // GraphNodeCreator must be implemented by nodes that create OR update resources.
    24  type GraphNodeCreator interface {
    25  	// CreateAddr is the address of the resource being created or updated
    26  	CreateAddr() *addrs.AbsResourceInstance
    27  }
    28  
    29  // DestroyEdgeTransformer is a GraphTransformer that creates the proper
    30  // references for destroy resources. Destroy resources are more complex
    31  // in that they must be depend on the destruction of resources that
    32  // in turn depend on the CREATION of the node being destroy.
    33  //
    34  // That is complicated. Visually:
    35  //
    36  //   B_d -> A_d -> A -> B
    37  //
    38  // Notice that A destroy depends on B destroy, while B create depends on
    39  // A create. They're inverted. This must be done for example because often
    40  // dependent resources will block parent resources from deleting. Concrete
    41  // example: VPC with subnets, the VPC can't be deleted while there are
    42  // still subnets.
    43  type DestroyEdgeTransformer struct {
    44  	// These are needed to properly build the graph of dependencies
    45  	// to determine what a destroy node depends on. Any of these can be nil.
    46  	Config *configs.Config
    47  	State  *states.State
    48  }
    49  
    50  func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
    51  	// Build a map of what is being destroyed (by address string) to
    52  	// the list of destroyers.
    53  	destroyers := make(map[string][]GraphNodeDestroyer)
    54  
    55  	// Record the creators, which will need to depend on the destroyers if they
    56  	// are only being updated.
    57  	creators := make(map[string][]GraphNodeCreator)
    58  
    59  	// destroyersByResource records each destroyer by the ConfigResource
    60  	// address.  We use this because dependencies are only referenced as
    61  	// resources and have no index or module instance information, but we will
    62  	// want to connect all the individual instances for correct ordering.
    63  	destroyersByResource := make(map[string][]GraphNodeDestroyer)
    64  	for _, v := range g.Vertices() {
    65  		switch n := v.(type) {
    66  		case GraphNodeDestroyer:
    67  			addrP := n.DestroyAddr()
    68  			if addrP == nil {
    69  				log.Printf("[WARN] DestroyEdgeTransformer: %q (%T) has no destroy address", dag.VertexName(n), v)
    70  				continue
    71  			}
    72  			addr := *addrP
    73  
    74  			key := addr.String()
    75  			log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(n), v, key)
    76  			destroyers[key] = append(destroyers[key], n)
    77  
    78  			resAddr := addr.ContainingResource().Config().String()
    79  			destroyersByResource[resAddr] = append(destroyersByResource[resAddr], n)
    80  		case GraphNodeCreator:
    81  			addr := n.CreateAddr().ContainingResource().Config().String()
    82  			creators[addr] = append(creators[addr], n)
    83  		}
    84  	}
    85  
    86  	// If we aren't destroying anything, there will be no edges to make
    87  	// so just exit early and avoid future work.
    88  	if len(destroyers) == 0 {
    89  		return nil
    90  	}
    91  
    92  	// Connect destroy despendencies as stored in the state
    93  	for _, ds := range destroyers {
    94  		for _, des := range ds {
    95  			ri, ok := des.(GraphNodeResourceInstance)
    96  			if !ok {
    97  				continue
    98  			}
    99  
   100  			for _, resAddr := range ri.StateDependencies() {
   101  				for _, desDep := range destroyersByResource[resAddr.String()] {
   102  					if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(desDep, des) {
   103  						log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(desDep), dag.VertexName(des))
   104  						g.Connect(dag.BasicEdge(desDep, des))
   105  					} else {
   106  						log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(desDep), dag.VertexName(des))
   107  					}
   108  				}
   109  
   110  				// We can have some create or update nodes which were
   111  				// dependents of the destroy node. If they have no destroyer
   112  				// themselves, make the connection directly from the creator.
   113  				for _, createDep := range creators[resAddr.String()] {
   114  					if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(createDep, des) {
   115  						log.Printf("[DEBUG] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(createDep), dag.VertexName(des))
   116  						g.Connect(dag.BasicEdge(createDep, des))
   117  					} else {
   118  						log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(createDep), dag.VertexName(des))
   119  					}
   120  				}
   121  			}
   122  		}
   123  	}
   124  
   125  	// connect creators to any destroyers on which they may depend
   126  	for _, cs := range creators {
   127  		for _, c := range cs {
   128  			ri, ok := c.(GraphNodeResourceInstance)
   129  			if !ok {
   130  				continue
   131  			}
   132  
   133  			for _, resAddr := range ri.StateDependencies() {
   134  				for _, desDep := range destroyersByResource[resAddr.String()] {
   135  					if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(c, desDep) {
   136  						log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(c), dag.VertexName(desDep))
   137  						g.Connect(dag.BasicEdge(c, desDep))
   138  					} else {
   139  						log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(c), dag.VertexName(desDep))
   140  					}
   141  				}
   142  			}
   143  		}
   144  	}
   145  
   146  	// Go through and connect creators to destroyers. Going along with
   147  	// our example, this makes: A_d => A
   148  	for _, v := range g.Vertices() {
   149  		cn, ok := v.(GraphNodeCreator)
   150  		if !ok {
   151  			continue
   152  		}
   153  
   154  		addr := cn.CreateAddr()
   155  		if addr == nil {
   156  			continue
   157  		}
   158  
   159  		for _, d := range destroyers[addr.String()] {
   160  			// For illustrating our example
   161  			a_d := d.(dag.Vertex)
   162  			a := v
   163  
   164  			log.Printf(
   165  				"[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q",
   166  				dag.VertexName(a), dag.VertexName(a_d))
   167  
   168  			g.Connect(dag.BasicEdge(a, a_d))
   169  		}
   170  	}
   171  
   172  	return nil
   173  }
   174  
   175  // Remove any nodes that aren't needed when destroying modules.
   176  // Variables, outputs, locals, and expanders may not be able to evaluate
   177  // correctly, so we can remove these if nothing depends on them. The module
   178  // closers also need to disable their use of expansion if the module itself is
   179  // no longer present.
   180  type pruneUnusedNodesTransformer struct {
   181  }
   182  
   183  func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error {
   184  	// We need a reverse depth first walk of modules, processing them in order
   185  	// from the leaf modules to the root. This allows us to remove unneeded
   186  	// dependencies from child modules, freeing up nodes in the parent module
   187  	// to also be removed.
   188  
   189  	nodes := g.Vertices()
   190  
   191  	for removed := true; removed; {
   192  		removed = false
   193  
   194  		for i := 0; i < len(nodes); i++ {
   195  			// run this in a closure, so we can return early rather than
   196  			// dealing with complex looping and labels
   197  			func() {
   198  				n := nodes[i]
   199  				switch n := n.(type) {
   200  				case graphNodeTemporaryValue:
   201  					// root module outputs indicate they are not temporary by
   202  					// returning false here.
   203  					if !n.temporaryValue() {
   204  						return
   205  					}
   206  
   207  					// temporary values, which consist of variables, locals,
   208  					// and outputs, must be kept if anything refers to them.
   209  					for _, v := range g.UpEdges(n) {
   210  						// keep any value which is connected through a
   211  						// reference
   212  						if _, ok := v.(GraphNodeReferencer); ok {
   213  							return
   214  						}
   215  					}
   216  
   217  				case graphNodeExpandsInstances:
   218  					// Any nodes that expand instances are kept when their
   219  					// instances may need to be evaluated.
   220  					for _, v := range g.UpEdges(n) {
   221  						switch v.(type) {
   222  						case graphNodeExpandsInstances:
   223  							// expanders can always depend on module expansion
   224  							// themselves
   225  							return
   226  						case GraphNodeResourceInstance:
   227  							// resource instances always depend on their
   228  							// resource node, which is an expander
   229  							return
   230  						}
   231  					}
   232  
   233  				case GraphNodeProvider:
   234  					// Providers that may have been required by expansion nodes
   235  					// that we no longer need can also be removed.
   236  					if g.UpEdges(n).Len() > 0 {
   237  						return
   238  					}
   239  
   240  				default:
   241  					return
   242  				}
   243  
   244  				log.Printf("[DEBUG] pruneUnusedNodes: %s is no longer needed, removing", dag.VertexName(n))
   245  				g.Remove(n)
   246  				removed = true
   247  
   248  				// remove the node from our iteration as well
   249  				last := len(nodes) - 1
   250  				nodes[i], nodes[last] = nodes[last], nodes[i]
   251  				nodes = nodes[:last]
   252  			}()
   253  		}
   254  	}
   255  
   256  	return nil
   257  }