]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | } |