github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/transform_destroy_edge.go (about)

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