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