]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go
Initial transfer of provider code
[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/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 }