]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_destroy_edge.go
1 package terraform
2
3 import (
4 "log"
5
6 "github.com/hashicorp/terraform/addrs"
7 "github.com/hashicorp/terraform/states"
8
9 "github.com/hashicorp/terraform/configs"
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
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 nil
281 }