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 }