github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/transform_destroy_edge.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 8 9 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 10 "github.com/hashicorp/terraform-plugin-sdk/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 // 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 55 func (t *DestroyEdgeTransformer) Transform(g *Graph) error { 56 // Build a map of what is being destroyed (by address string) to 57 // the list of destroyers. Usually there will be at most one destroyer 58 // per node, but we allow multiple if present for completeness. 59 destroyers := make(map[string][]GraphNodeDestroyer) 60 destroyerAddrs := make(map[string]addrs.AbsResourceInstance) 61 for _, v := range g.Vertices() { 62 dn, ok := v.(GraphNodeDestroyer) 63 if !ok { 64 continue 65 } 66 67 addrP := dn.DestroyAddr() 68 if addrP == nil { 69 continue 70 } 71 addr := *addrP 72 73 key := addr.String() 74 log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(dn), v, key) 75 destroyers[key] = append(destroyers[key], dn) 76 destroyerAddrs[key] = addr 77 } 78 79 // If we aren't destroying anything, there will be no edges to make 80 // so just exit early and avoid future work. 81 if len(destroyers) == 0 { 82 return nil 83 } 84 85 // Go through and connect creators to destroyers. Going along with 86 // our example, this makes: A_d => A 87 for _, v := range g.Vertices() { 88 cn, ok := v.(GraphNodeCreator) 89 if !ok { 90 continue 91 } 92 93 addr := cn.CreateAddr() 94 if addr == nil { 95 continue 96 } 97 98 key := addr.String() 99 ds := destroyers[key] 100 if len(ds) == 0 { 101 continue 102 } 103 104 for _, d := range ds { 105 // For illustrating our example 106 a_d := d.(dag.Vertex) 107 a := v 108 109 log.Printf( 110 "[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q", 111 dag.VertexName(a), dag.VertexName(a_d)) 112 113 g.Connect(&DestroyEdge{S: a, T: a_d}) 114 115 // Attach the destroy node to the creator 116 // There really shouldn't be more than one destroyer, but even if 117 // there are, any of them will represent the correct 118 // CreateBeforeDestroy status. 119 if n, ok := cn.(GraphNodeAttachDestroyer); ok { 120 if d, ok := d.(GraphNodeDestroyerCBD); ok { 121 n.AttachDestroyNode(d) 122 } 123 } 124 } 125 } 126 127 // This is strange but is the easiest way to get the dependencies 128 // of a node that is being destroyed. We use another graph to make sure 129 // the resource is in the graph and ask for references. We have to do this 130 // because the node that is being destroyed may NOT be in the graph. 131 // 132 // Example: resource A is force new, then destroy A AND create A are 133 // in the graph. BUT if resource A is just pure destroy, then only 134 // destroy A is in the graph, and create A is not. 135 providerFn := func(a *NodeAbstractProvider) dag.Vertex { 136 return &NodeApplyableProvider{NodeAbstractProvider: a} 137 } 138 steps := []GraphTransformer{ 139 // Add the local values 140 &LocalTransformer{Config: t.Config}, 141 142 // Add outputs and metadata 143 &OutputTransformer{Config: t.Config}, 144 &AttachResourceConfigTransformer{Config: t.Config}, 145 &AttachStateTransformer{State: t.State}, 146 147 // Add all the variables. We can depend on resources through 148 // variables due to module parameters, and we need to properly 149 // determine that. 150 &RootVariableTransformer{Config: t.Config}, 151 &ModuleVariableTransformer{Config: t.Config}, 152 153 TransformProviders(nil, providerFn, t.Config), 154 155 // Must attach schemas before ReferenceTransformer so that we can 156 // analyze the configuration to find references. 157 &AttachSchemaTransformer{Schemas: t.Schemas}, 158 159 &ReferenceTransformer{}, 160 } 161 162 // Go through all the nodes being destroyed and create a graph. 163 // The resulting graph is only of things being CREATED. For example, 164 // following our example, the resulting graph would be: 165 // 166 // A, B (with no edges) 167 // 168 var tempG Graph 169 var tempDestroyed []dag.Vertex 170 for d := range destroyers { 171 // d is the string key for the resource being destroyed. We actually 172 // want the address value, which we stashed earlier. 173 addr := destroyerAddrs[d] 174 175 // This part is a little bit weird but is the best way to 176 // find the dependencies we need to: build a graph and use the 177 // attach config and state transformers then ask for references. 178 abstract := NewNodeAbstractResourceInstance(addr) 179 tempG.Add(abstract) 180 tempDestroyed = append(tempDestroyed, abstract) 181 182 // We also add the destroy version here since the destroy can 183 // depend on things that the creation doesn't (destroy provisioners). 184 destroy := &NodeDestroyResourceInstance{NodeAbstractResourceInstance: abstract} 185 tempG.Add(destroy) 186 tempDestroyed = append(tempDestroyed, destroy) 187 } 188 189 // Run the graph transforms so we have the information we need to 190 // build references. 191 log.Printf("[TRACE] DestroyEdgeTransformer: constructing temporary graph for analysis of references, starting from:\n%s", tempG.StringWithNodeTypes()) 192 for _, s := range steps { 193 log.Printf("[TRACE] DestroyEdgeTransformer: running %T on temporary graph", s) 194 if err := s.Transform(&tempG); err != nil { 195 log.Printf("[TRACE] DestroyEdgeTransformer: %T failed: %s", s, err) 196 return err 197 } 198 } 199 log.Printf("[TRACE] DestroyEdgeTransformer: temporary reference graph:\n%s", tempG.String()) 200 201 // Go through all the nodes in the graph and determine what they 202 // depend on. 203 for _, v := range tempDestroyed { 204 // Find all ancestors of this to determine the edges we'll depend on 205 vs, err := tempG.Ancestors(v) 206 if err != nil { 207 return err 208 } 209 210 refs := make([]dag.Vertex, 0, vs.Len()) 211 for _, raw := range vs.List() { 212 refs = append(refs, raw.(dag.Vertex)) 213 } 214 215 refNames := make([]string, len(refs)) 216 for i, ref := range refs { 217 refNames[i] = dag.VertexName(ref) 218 } 219 log.Printf( 220 "[TRACE] DestroyEdgeTransformer: creation node %q references %s", 221 dag.VertexName(v), refNames) 222 223 // If we have no references, then we won't need to do anything 224 if len(refs) == 0 { 225 continue 226 } 227 228 // Get the destroy node for this. In the example of our struct, 229 // we are currently at B and we're looking for B_d. 230 rn, ok := v.(GraphNodeResourceInstance) 231 if !ok { 232 log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s, since it's not a resource", dag.VertexName(v)) 233 continue 234 } 235 236 addr := rn.ResourceInstanceAddr() 237 dns := destroyers[addr.String()] 238 239 // We have dependencies, check if any are being destroyed 240 // to build the list of things that we must depend on! 241 // 242 // In the example of the struct, if we have: 243 // 244 // B_d => A_d => A => B 245 // 246 // Then at this point in the algorithm we started with B_d, 247 // we built B (to get dependencies), and we found A. We're now looking 248 // to see if A_d exists. 249 var depDestroyers []dag.Vertex 250 for _, v := range refs { 251 rn, ok := v.(GraphNodeResourceInstance) 252 if !ok { 253 continue 254 } 255 256 addr := rn.ResourceInstanceAddr() 257 key := addr.String() 258 if ds, ok := destroyers[key]; ok { 259 for _, d := range ds { 260 depDestroyers = append(depDestroyers, d.(dag.Vertex)) 261 log.Printf( 262 "[TRACE] DestroyEdgeTransformer: destruction of %q depends on %s", 263 key, dag.VertexName(d)) 264 } 265 } 266 } 267 268 // Go through and make the connections. Use the variable 269 // names "a_d" and "b_d" to reference our example. 270 for _, a_d := range dns { 271 for _, b_d := range depDestroyers { 272 if b_d != a_d { 273 log.Printf("[TRACE] DestroyEdgeTransformer: %q depends on %q", dag.VertexName(b_d), dag.VertexName(a_d)) 274 g.Connect(dag.BasicEdge(b_d, a_d)) 275 } 276 } 277 } 278 } 279 280 return t.pruneResources(g) 281 } 282 283 // If there are only destroy instances for a particular resource, there's no 284 // reason for the resource node to prepare the state. Remove Resource nodes so 285 // that they don't fail by trying to evaluate a resource that is only being 286 // destroyed along with its dependencies. 287 func (t *DestroyEdgeTransformer) pruneResources(g *Graph) error { 288 for _, v := range g.Vertices() { 289 n, ok := v.(*NodeApplyableResource) 290 if !ok { 291 continue 292 } 293 294 // if there are only destroy dependencies, we don't need this node 295 des, err := g.Descendents(n) 296 if err != nil { 297 return err 298 } 299 300 descendents := des.List() 301 nonDestroyInstanceFound := false 302 for _, v := range descendents { 303 if _, ok := v.(*NodeApplyableResourceInstance); ok { 304 nonDestroyInstanceFound = true 305 break 306 } 307 } 308 309 if nonDestroyInstanceFound { 310 continue 311 } 312 313 // connect all the through-edges, then delete the node 314 for _, d := range g.DownEdges(n).List() { 315 for _, u := range g.UpEdges(n).List() { 316 g.Connect(dag.BasicEdge(u, d)) 317 } 318 } 319 log.Printf("DestroyEdgeTransformer: pruning unused resource node %s", dag.VertexName(n)) 320 g.Remove(n) 321 } 322 return nil 323 }