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 }