diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go new file mode 100644 index 0000000..22be1ab --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go | |||
@@ -0,0 +1,269 @@ | |||
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 | } | ||