]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "log" | |
5 | ||
107c1cdb ND |
6 | "github.com/hashicorp/terraform/addrs" |
7 | "github.com/hashicorp/terraform/states" | |
8 | ||
9 | "github.com/hashicorp/terraform/configs" | |
bae9f6d2 JC |
10 | "github.com/hashicorp/terraform/dag" |
11 | ) | |
12 | ||
13 | // GraphNodeDestroyer must be implemented by nodes that destroy resources. | |
14 | type GraphNodeDestroyer interface { | |
15 | dag.Vertex | |
16 | ||
107c1cdb | 17 | // DestroyAddr is the address of the resource that is being |
bae9f6d2 JC |
18 | // destroyed by this node. If this returns nil, then this node |
19 | // is not destroying anything. | |
107c1cdb | 20 | DestroyAddr() *addrs.AbsResourceInstance |
bae9f6d2 JC |
21 | } |
22 | ||
23 | // GraphNodeCreator must be implemented by nodes that create OR update resources. | |
24 | type GraphNodeCreator interface { | |
107c1cdb ND |
25 | // CreateAddr is the address of the resource being created or updated |
26 | CreateAddr() *addrs.AbsResourceInstance | |
bae9f6d2 JC |
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. | |
107c1cdb ND |
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 | |
bae9f6d2 JC |
53 | } |
54 | ||
55 | func (t *DestroyEdgeTransformer) Transform(g *Graph) error { | |
bae9f6d2 | 56 | // Build a map of what is being destroyed (by address string) to |
107c1cdb ND |
57 | // the list of destroyers. Usually there will be at most one destroyer |
58 | // per node, but we allow multiple if present for completeness. | |
bae9f6d2 | 59 | destroyers := make(map[string][]GraphNodeDestroyer) |
107c1cdb | 60 | destroyerAddrs := make(map[string]addrs.AbsResourceInstance) |
bae9f6d2 JC |
61 | for _, v := range g.Vertices() { |
62 | dn, ok := v.(GraphNodeDestroyer) | |
63 | if !ok { | |
64 | continue | |
65 | } | |
66 | ||
107c1cdb ND |
67 | addrP := dn.DestroyAddr() |
68 | if addrP == nil { | |
bae9f6d2 JC |
69 | continue |
70 | } | |
107c1cdb | 71 | addr := *addrP |
bae9f6d2 JC |
72 | |
73 | key := addr.String() | |
107c1cdb | 74 | log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(dn), v, key) |
bae9f6d2 | 75 | destroyers[key] = append(destroyers[key], dn) |
107c1cdb | 76 | destroyerAddrs[key] = addr |
bae9f6d2 JC |
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( | |
107c1cdb | 110 | "[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q", |
bae9f6d2 JC |
111 | dag.VertexName(a), dag.VertexName(a_d)) |
112 | ||
113 | g.Connect(&DestroyEdge{S: a, T: a_d}) | |
107c1cdb ND |
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 | } | |
bae9f6d2 JC |
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{ | |
15c0b25d | 139 | // Add the local values |
107c1cdb | 140 | &LocalTransformer{Config: t.Config}, |
15c0b25d | 141 | |
bae9f6d2 | 142 | // Add outputs and metadata |
107c1cdb ND |
143 | &OutputTransformer{Config: t.Config}, |
144 | &AttachResourceConfigTransformer{Config: t.Config}, | |
bae9f6d2 JC |
145 | &AttachStateTransformer{State: t.State}, |
146 | ||
bae9f6d2 JC |
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. | |
107c1cdb ND |
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}, | |
bae9f6d2 JC |
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 | |
107c1cdb ND |
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] | |
bae9f6d2 JC |
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. | |
107c1cdb | 178 | abstract := NewNodeAbstractResourceInstance(addr) |
bae9f6d2 JC |
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). | |
107c1cdb | 184 | destroy := &NodeDestroyResourceInstance{NodeAbstractResourceInstance: abstract} |
bae9f6d2 JC |
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. | |
107c1cdb | 191 | log.Printf("[TRACE] DestroyEdgeTransformer: constructing temporary graph for analysis of references, starting from:\n%s", tempG.StringWithNodeTypes()) |
bae9f6d2 | 192 | for _, s := range steps { |
107c1cdb | 193 | log.Printf("[TRACE] DestroyEdgeTransformer: running %T on temporary graph", s) |
bae9f6d2 | 194 | if err := s.Transform(&tempG); err != nil { |
107c1cdb | 195 | log.Printf("[TRACE] DestroyEdgeTransformer: %T failed: %s", s, err) |
bae9f6d2 JC |
196 | return err |
197 | } | |
198 | } | |
107c1cdb | 199 | log.Printf("[TRACE] DestroyEdgeTransformer: temporary reference graph:\n%s", tempG.String()) |
bae9f6d2 JC |
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. | |
107c1cdb | 230 | rn, ok := v.(GraphNodeResourceInstance) |
bae9f6d2 | 231 | if !ok { |
107c1cdb | 232 | log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s, since it's not a resource", dag.VertexName(v)) |
bae9f6d2 JC |
233 | continue |
234 | } | |
235 | ||
107c1cdb | 236 | addr := rn.ResourceInstanceAddr() |
bae9f6d2 JC |
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 { | |
107c1cdb | 251 | rn, ok := v.(GraphNodeResourceInstance) |
bae9f6d2 JC |
252 | if !ok { |
253 | continue | |
254 | } | |
255 | ||
107c1cdb | 256 | addr := rn.ResourceInstanceAddr() |
bae9f6d2 JC |
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 { | |
107c1cdb | 273 | log.Printf("[TRACE] DestroyEdgeTransformer: %q depends on %q", dag.VertexName(b_d), dag.VertexName(a_d)) |
bae9f6d2 JC |
274 | g.Connect(dag.BasicEdge(b_d, a_d)) |
275 | } | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | return nil | |
281 | } |