github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/terraform/transform_destroy_edge.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform/config/module" 7 "github.com/hashicorp/terraform/dag" 8 ) 9 10 // GraphNodeDestroyer must be implemented by nodes that destroy resources. 11 type GraphNodeDestroyer interface { 12 dag.Vertex 13 14 // ResourceAddr 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() *ResourceAddress 18 } 19 20 // GraphNodeCreator must be implemented by nodes that create OR update resources. 21 type GraphNodeCreator interface { 22 // ResourceAddr is the address of the resource being created or updated 23 CreateAddr() *ResourceAddress 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 // These are needed to properly build the graph of dependencies 42 // to determine what a destroy node depends on. Any of these can be nil. 43 Module *module.Tree 44 State *State 45 } 46 47 func (t *DestroyEdgeTransformer) Transform(g *Graph) error { 48 log.Printf("[TRACE] DestroyEdgeTransformer: Beginning destroy edge transformation...") 49 50 // Build a map of what is being destroyed (by address string) to 51 // the list of destroyers. In general there will only be one destroyer 52 // but to make it more robust we support multiple. 53 destroyers := make(map[string][]GraphNodeDestroyer) 54 for _, v := range g.Vertices() { 55 dn, ok := v.(GraphNodeDestroyer) 56 if !ok { 57 continue 58 } 59 60 addr := dn.DestroyAddr() 61 if addr == nil { 62 continue 63 } 64 65 key := addr.String() 66 log.Printf( 67 "[TRACE] DestroyEdgeTransformer: %s destroying %q", 68 dag.VertexName(dn), key) 69 destroyers[key] = append(destroyers[key], dn) 70 } 71 72 // If we aren't destroying anything, there will be no edges to make 73 // so just exit early and avoid future work. 74 if len(destroyers) == 0 { 75 return nil 76 } 77 78 // Go through and connect creators to destroyers. Going along with 79 // our example, this makes: A_d => A 80 for _, v := range g.Vertices() { 81 cn, ok := v.(GraphNodeCreator) 82 if !ok { 83 continue 84 } 85 86 addr := cn.CreateAddr() 87 if addr == nil { 88 continue 89 } 90 91 key := addr.String() 92 ds := destroyers[key] 93 if len(ds) == 0 { 94 continue 95 } 96 97 for _, d := range ds { 98 // For illustrating our example 99 a_d := d.(dag.Vertex) 100 a := v 101 102 log.Printf( 103 "[TRACE] DestroyEdgeTransformer: connecting creator/destroyer: %s, %s", 104 dag.VertexName(a), dag.VertexName(a_d)) 105 106 g.Connect(&DestroyEdge{S: a, T: a_d}) 107 } 108 } 109 110 // This is strange but is the easiest way to get the dependencies 111 // of a node that is being destroyed. We use another graph to make sure 112 // the resource is in the graph and ask for references. We have to do this 113 // because the node that is being destroyed may NOT be in the graph. 114 // 115 // Example: resource A is force new, then destroy A AND create A are 116 // in the graph. BUT if resource A is just pure destroy, then only 117 // destroy A is in the graph, and create A is not. 118 providerFn := func(a *NodeAbstractProvider) dag.Vertex { 119 return &NodeApplyableProvider{NodeAbstractProvider: a} 120 } 121 steps := []GraphTransformer{ 122 // Add outputs and metadata 123 &OutputTransformer{Module: t.Module}, 124 &AttachResourceConfigTransformer{Module: t.Module}, 125 &AttachStateTransformer{State: t.State}, 126 127 // Add providers since they can affect destroy order as well 128 &MissingProviderTransformer{AllowAny: true, Concrete: providerFn}, 129 &ProviderTransformer{}, 130 &DisableProviderTransformer{}, 131 &ParentProviderTransformer{}, 132 &AttachProviderConfigTransformer{Module: t.Module}, 133 134 // Add all the variables. We can depend on resources through 135 // variables due to module parameters, and we need to properly 136 // determine that. 137 &RootVariableTransformer{Module: t.Module}, 138 &ModuleVariableTransformer{Module: t.Module}, 139 140 &ReferenceTransformer{}, 141 } 142 143 // Go through all the nodes being destroyed and create a graph. 144 // The resulting graph is only of things being CREATED. For example, 145 // following our example, the resulting graph would be: 146 // 147 // A, B (with no edges) 148 // 149 var tempG Graph 150 var tempDestroyed []dag.Vertex 151 for d, _ := range destroyers { 152 // d is what is being destroyed. We parse the resource address 153 // which it came from it is a panic if this fails. 154 addr, err := ParseResourceAddress(d) 155 if err != nil { 156 panic(err) 157 } 158 159 // This part is a little bit weird but is the best way to 160 // find the dependencies we need to: build a graph and use the 161 // attach config and state transformers then ask for references. 162 abstract := &NodeAbstractResource{Addr: addr} 163 tempG.Add(abstract) 164 tempDestroyed = append(tempDestroyed, abstract) 165 166 // We also add the destroy version here since the destroy can 167 // depend on things that the creation doesn't (destroy provisioners). 168 destroy := &NodeDestroyResource{NodeAbstractResource: abstract} 169 tempG.Add(destroy) 170 tempDestroyed = append(tempDestroyed, destroy) 171 } 172 173 // Run the graph transforms so we have the information we need to 174 // build references. 175 for _, s := range steps { 176 if err := s.Transform(&tempG); err != nil { 177 return err 178 } 179 } 180 181 log.Printf("[TRACE] DestroyEdgeTransformer: reference graph: %s", tempG.String()) 182 183 // Go through all the nodes in the graph and determine what they 184 // depend on. 185 for _, v := range tempDestroyed { 186 // Find all ancestors of this to determine the edges we'll depend on 187 vs, err := tempG.Ancestors(v) 188 if err != nil { 189 return err 190 } 191 192 refs := make([]dag.Vertex, 0, vs.Len()) 193 for _, raw := range vs.List() { 194 refs = append(refs, raw.(dag.Vertex)) 195 } 196 197 refNames := make([]string, len(refs)) 198 for i, ref := range refs { 199 refNames[i] = dag.VertexName(ref) 200 } 201 log.Printf( 202 "[TRACE] DestroyEdgeTransformer: creation node %q references %s", 203 dag.VertexName(v), refNames) 204 205 // If we have no references, then we won't need to do anything 206 if len(refs) == 0 { 207 continue 208 } 209 210 // Get the destroy node for this. In the example of our struct, 211 // we are currently at B and we're looking for B_d. 212 rn, ok := v.(GraphNodeResource) 213 if !ok { 214 continue 215 } 216 217 addr := rn.ResourceAddr() 218 if addr == nil { 219 continue 220 } 221 222 dns := destroyers[addr.String()] 223 224 // We have dependencies, check if any are being destroyed 225 // to build the list of things that we must depend on! 226 // 227 // In the example of the struct, if we have: 228 // 229 // B_d => A_d => A => B 230 // 231 // Then at this point in the algorithm we started with B_d, 232 // we built B (to get dependencies), and we found A. We're now looking 233 // to see if A_d exists. 234 var depDestroyers []dag.Vertex 235 for _, v := range refs { 236 rn, ok := v.(GraphNodeResource) 237 if !ok { 238 continue 239 } 240 241 addr := rn.ResourceAddr() 242 if addr == nil { 243 continue 244 } 245 246 key := addr.String() 247 if ds, ok := destroyers[key]; ok { 248 for _, d := range ds { 249 depDestroyers = append(depDestroyers, d.(dag.Vertex)) 250 log.Printf( 251 "[TRACE] DestroyEdgeTransformer: destruction of %q depends on %s", 252 key, dag.VertexName(d)) 253 } 254 } 255 } 256 257 // Go through and make the connections. Use the variable 258 // names "a_d" and "b_d" to reference our example. 259 for _, a_d := range dns { 260 for _, b_d := range depDestroyers { 261 if b_d != a_d { 262 g.Connect(dag.BasicEdge(b_d, a_d)) 263 } 264 } 265 } 266 } 267 268 return nil 269 }