]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
107c1cdb | 5 | "log" |
bae9f6d2 | 6 | |
107c1cdb ND |
7 | "github.com/hashicorp/terraform/plans" |
8 | "github.com/hashicorp/terraform/providers" | |
9 | ||
10 | "github.com/hashicorp/terraform/addrs" | |
11 | "github.com/hashicorp/terraform/configs" | |
12 | "github.com/hashicorp/terraform/states" | |
bae9f6d2 JC |
13 | ) |
14 | ||
107c1cdb ND |
15 | // NodeDestroyResourceInstance represents a resource instance that is to be |
16 | // destroyed. | |
17 | type NodeDestroyResourceInstance struct { | |
18 | *NodeAbstractResourceInstance | |
19 | ||
20 | // If DeposedKey is set to anything other than states.NotDeposed then | |
21 | // this node destroys a deposed object of the associated instance | |
22 | // rather than its current object. | |
23 | DeposedKey states.DeposedKey | |
24 | ||
25 | CreateBeforeDestroyOverride *bool | |
bae9f6d2 JC |
26 | } |
27 | ||
107c1cdb ND |
28 | var ( |
29 | _ GraphNodeResource = (*NodeDestroyResourceInstance)(nil) | |
30 | _ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil) | |
31 | _ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil) | |
32 | _ GraphNodeDestroyerCBD = (*NodeDestroyResourceInstance)(nil) | |
33 | _ GraphNodeReferenceable = (*NodeDestroyResourceInstance)(nil) | |
34 | _ GraphNodeReferencer = (*NodeDestroyResourceInstance)(nil) | |
35 | _ GraphNodeEvalable = (*NodeDestroyResourceInstance)(nil) | |
36 | _ GraphNodeProviderConsumer = (*NodeDestroyResourceInstance)(nil) | |
37 | _ GraphNodeProvisionerConsumer = (*NodeDestroyResourceInstance)(nil) | |
38 | ) | |
39 | ||
40 | func (n *NodeDestroyResourceInstance) Name() string { | |
41 | if n.DeposedKey != states.NotDeposed { | |
42 | return fmt.Sprintf("%s (destroy deposed %s)", n.ResourceInstanceAddr(), n.DeposedKey) | |
43 | } | |
44 | return n.ResourceInstanceAddr().String() + " (destroy)" | |
bae9f6d2 JC |
45 | } |
46 | ||
47 | // GraphNodeDestroyer | |
107c1cdb ND |
48 | func (n *NodeDestroyResourceInstance) DestroyAddr() *addrs.AbsResourceInstance { |
49 | addr := n.ResourceInstanceAddr() | |
50 | return &addr | |
bae9f6d2 JC |
51 | } |
52 | ||
53 | // GraphNodeDestroyerCBD | |
107c1cdb ND |
54 | func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool { |
55 | if n.CreateBeforeDestroyOverride != nil { | |
56 | return *n.CreateBeforeDestroyOverride | |
57 | } | |
58 | ||
bae9f6d2 | 59 | // If we have no config, we just assume no |
107c1cdb | 60 | if n.Config == nil || n.Config.Managed == nil { |
bae9f6d2 JC |
61 | return false |
62 | } | |
63 | ||
107c1cdb | 64 | return n.Config.Managed.CreateBeforeDestroy |
bae9f6d2 JC |
65 | } |
66 | ||
67 | // GraphNodeDestroyerCBD | |
107c1cdb ND |
68 | func (n *NodeDestroyResourceInstance) ModifyCreateBeforeDestroy(v bool) error { |
69 | n.CreateBeforeDestroyOverride = &v | |
bae9f6d2 JC |
70 | return nil |
71 | } | |
72 | ||
73 | // GraphNodeReferenceable, overriding NodeAbstractResource | |
107c1cdb ND |
74 | func (n *NodeDestroyResourceInstance) ReferenceableAddrs() []addrs.Referenceable { |
75 | normalAddrs := n.NodeAbstractResourceInstance.ReferenceableAddrs() | |
76 | destroyAddrs := make([]addrs.Referenceable, len(normalAddrs)) | |
77 | ||
78 | phaseType := addrs.ResourceInstancePhaseDestroy | |
bae9f6d2 | 79 | if n.CreateBeforeDestroy() { |
107c1cdb | 80 | phaseType = addrs.ResourceInstancePhaseDestroyCBD |
bae9f6d2 JC |
81 | } |
82 | ||
107c1cdb ND |
83 | for i, normalAddr := range normalAddrs { |
84 | switch ta := normalAddr.(type) { | |
85 | case addrs.Resource: | |
86 | destroyAddrs[i] = ta.Phase(phaseType) | |
87 | case addrs.ResourceInstance: | |
88 | destroyAddrs[i] = ta.Phase(phaseType) | |
89 | default: | |
90 | destroyAddrs[i] = normalAddr | |
91 | } | |
bae9f6d2 JC |
92 | } |
93 | ||
107c1cdb | 94 | return destroyAddrs |
bae9f6d2 JC |
95 | } |
96 | ||
97 | // GraphNodeReferencer, overriding NodeAbstractResource | |
107c1cdb | 98 | func (n *NodeDestroyResourceInstance) References() []*addrs.Reference { |
bae9f6d2 | 99 | // If we have a config, then we need to include destroy-time dependencies |
107c1cdb ND |
100 | if c := n.Config; c != nil && c.Managed != nil { |
101 | var result []*addrs.Reference | |
102 | ||
103 | // We include conn info and config for destroy time provisioners | |
104 | // as dependencies that we have. | |
105 | for _, p := range c.Managed.Provisioners { | |
106 | schema := n.ProvisionerSchemas[p.Type] | |
107 | ||
108 | if p.When == configs.ProvisionerWhenDestroy { | |
109 | if p.Connection != nil { | |
110 | result = append(result, ReferencesFromConfig(p.Connection.Config, connectionBlockSupersetSchema)...) | |
111 | } | |
112 | result = append(result, ReferencesFromConfig(p.Config, schema)...) | |
bae9f6d2 JC |
113 | } |
114 | } | |
115 | ||
116 | return result | |
117 | } | |
118 | ||
119 | return nil | |
120 | } | |
121 | ||
bae9f6d2 | 122 | // GraphNodeEvalable |
107c1cdb ND |
123 | func (n *NodeDestroyResourceInstance) EvalTree() EvalNode { |
124 | addr := n.ResourceInstanceAddr() | |
bae9f6d2 JC |
125 | |
126 | // Get our state | |
127 | rs := n.ResourceState | |
107c1cdb ND |
128 | var is *states.ResourceInstance |
129 | if rs != nil { | |
130 | is = rs.Instance(n.InstanceKey) | |
131 | } | |
132 | if is == nil { | |
133 | log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr) | |
bae9f6d2 JC |
134 | } |
135 | ||
107c1cdb ND |
136 | var changeApply *plans.ResourceInstanceChange |
137 | var provider providers.Interface | |
138 | var providerSchema *ProviderSchema | |
139 | var state *states.ResourceInstanceObject | |
bae9f6d2 JC |
140 | var err error |
141 | return &EvalOpFilter{ | |
142 | Ops: []walkOperation{walkApply, walkDestroy}, | |
143 | Node: &EvalSequence{ | |
144 | Nodes: []EvalNode{ | |
107c1cdb ND |
145 | &EvalGetProvider{ |
146 | Addr: n.ResolvedProvider, | |
147 | Output: &provider, | |
148 | Schema: &providerSchema, | |
149 | }, | |
150 | ||
bae9f6d2 JC |
151 | // Get the saved diff for apply |
152 | &EvalReadDiff{ | |
107c1cdb ND |
153 | Addr: addr.Resource, |
154 | ProviderSchema: &providerSchema, | |
155 | Change: &changeApply, | |
bae9f6d2 JC |
156 | }, |
157 | ||
107c1cdb ND |
158 | &EvalReduceDiff{ |
159 | Addr: addr.Resource, | |
160 | InChange: &changeApply, | |
161 | Destroy: true, | |
162 | OutChange: &changeApply, | |
bae9f6d2 JC |
163 | }, |
164 | ||
107c1cdb ND |
165 | // EvalReduceDiff may have simplified our planned change |
166 | // into a NoOp if it does not require destroying. | |
bae9f6d2 JC |
167 | &EvalIf{ |
168 | If: func(ctx EvalContext) (bool, error) { | |
107c1cdb ND |
169 | if changeApply == nil || changeApply.Action == plans.NoOp { |
170 | return true, EvalEarlyExitError{} | |
bae9f6d2 | 171 | } |
107c1cdb | 172 | return true, nil |
bae9f6d2 JC |
173 | }, |
174 | Then: EvalNoop{}, | |
175 | }, | |
176 | ||
bae9f6d2 | 177 | &EvalReadState{ |
107c1cdb ND |
178 | Addr: addr.Resource, |
179 | Output: &state, | |
180 | Provider: &provider, | |
181 | ProviderSchema: &providerSchema, | |
bae9f6d2 JC |
182 | }, |
183 | &EvalRequireState{ | |
184 | State: &state, | |
185 | }, | |
186 | ||
187 | // Call pre-apply hook | |
188 | &EvalApplyPre{ | |
107c1cdb ND |
189 | Addr: addr.Resource, |
190 | State: &state, | |
191 | Change: &changeApply, | |
bae9f6d2 JC |
192 | }, |
193 | ||
194 | // Run destroy provisioners if not tainted | |
195 | &EvalIf{ | |
196 | If: func(ctx EvalContext) (bool, error) { | |
107c1cdb | 197 | if state != nil && state.Status == states.ObjectTainted { |
bae9f6d2 JC |
198 | return false, nil |
199 | } | |
200 | ||
201 | return true, nil | |
202 | }, | |
203 | ||
204 | Then: &EvalApplyProvisioners{ | |
107c1cdb | 205 | Addr: addr.Resource, |
bae9f6d2 | 206 | State: &state, |
107c1cdb | 207 | ResourceConfig: n.Config, |
bae9f6d2 | 208 | Error: &err, |
107c1cdb | 209 | When: configs.ProvisionerWhenDestroy, |
bae9f6d2 JC |
210 | }, |
211 | }, | |
212 | ||
213 | // If we have a provisioning error, then we just call | |
214 | // the post-apply hook now. | |
215 | &EvalIf{ | |
216 | If: func(ctx EvalContext) (bool, error) { | |
217 | return err != nil, nil | |
218 | }, | |
219 | ||
220 | Then: &EvalApplyPost{ | |
107c1cdb | 221 | Addr: addr.Resource, |
bae9f6d2 JC |
222 | State: &state, |
223 | Error: &err, | |
224 | }, | |
225 | }, | |
226 | ||
227 | // Make sure we handle data sources properly. | |
228 | &EvalIf{ | |
229 | If: func(ctx EvalContext) (bool, error) { | |
107c1cdb | 230 | return addr.Resource.Resource.Mode == addrs.DataResourceMode, nil |
bae9f6d2 JC |
231 | }, |
232 | ||
233 | Then: &EvalReadDataApply{ | |
107c1cdb ND |
234 | Addr: addr.Resource, |
235 | Config: n.Config, | |
236 | Change: &changeApply, | |
237 | Provider: &provider, | |
238 | ProviderAddr: n.ResolvedProvider, | |
239 | ProviderSchema: &providerSchema, | |
240 | Output: &state, | |
bae9f6d2 JC |
241 | }, |
242 | Else: &EvalApply{ | |
107c1cdb ND |
243 | Addr: addr.Resource, |
244 | Config: nil, // No configuration because we are destroying | |
245 | State: &state, | |
246 | Change: &changeApply, | |
247 | Provider: &provider, | |
248 | ProviderAddr: n.ResolvedProvider, | |
249 | ProviderSchema: &providerSchema, | |
250 | Output: &state, | |
251 | Error: &err, | |
bae9f6d2 JC |
252 | }, |
253 | }, | |
254 | &EvalWriteState{ | |
107c1cdb ND |
255 | Addr: addr.Resource, |
256 | ProviderAddr: n.ResolvedProvider, | |
257 | ProviderSchema: &providerSchema, | |
258 | State: &state, | |
bae9f6d2 JC |
259 | }, |
260 | &EvalApplyPost{ | |
107c1cdb | 261 | Addr: addr.Resource, |
bae9f6d2 JC |
262 | State: &state, |
263 | Error: &err, | |
264 | }, | |
265 | &EvalUpdateStateHook{}, | |
266 | }, | |
267 | }, | |
268 | } | |
269 | } | |
107c1cdb ND |
270 | |
271 | // NodeDestroyResourceInstance represents a resource that is to be destroyed. | |
272 | // | |
273 | // Destroying a resource is a state-only operation: it is the individual | |
274 | // instances being destroyed that affects remote objects. During graph | |
275 | // construction, NodeDestroyResource should always depend on any other node | |
276 | // related to the given resource, since it's just a final cleanup to avoid | |
277 | // leaving skeleton resource objects in state after their instances have | |
278 | // all been destroyed. | |
279 | type NodeDestroyResource struct { | |
280 | *NodeAbstractResource | |
281 | } | |
282 | ||
283 | var ( | |
284 | _ GraphNodeResource = (*NodeDestroyResource)(nil) | |
285 | _ GraphNodeReferenceable = (*NodeDestroyResource)(nil) | |
286 | _ GraphNodeReferencer = (*NodeDestroyResource)(nil) | |
287 | _ GraphNodeEvalable = (*NodeDestroyResource)(nil) | |
288 | ) | |
289 | ||
290 | func (n *NodeDestroyResource) Name() string { | |
291 | return n.ResourceAddr().String() + " (clean up state)" | |
292 | } | |
293 | ||
294 | // GraphNodeReferenceable, overriding NodeAbstractResource | |
295 | func (n *NodeDestroyResource) ReferenceableAddrs() []addrs.Referenceable { | |
296 | // NodeDestroyResource doesn't participate in references: the graph | |
297 | // builder that created it should ensure directly that it already depends | |
298 | // on every other node related to its resource, without relying on | |
299 | // references. | |
300 | return nil | |
301 | } | |
302 | ||
303 | // GraphNodeReferencer, overriding NodeAbstractResource | |
304 | func (n *NodeDestroyResource) References() []*addrs.Reference { | |
305 | // NodeDestroyResource doesn't participate in references: the graph | |
306 | // builder that created it should ensure directly that it already depends | |
307 | // on every other node related to its resource, without relying on | |
308 | // references. | |
309 | return nil | |
310 | } | |
311 | ||
312 | // GraphNodeEvalable | |
313 | func (n *NodeDestroyResource) EvalTree() EvalNode { | |
314 | // This EvalNode will produce an error if the resource isn't already | |
315 | // empty by the time it is called, since it should just be pruning the | |
316 | // leftover husk of a resource in state after all of the child instances | |
317 | // and their objects were destroyed. | |
318 | return &EvalForgetResourceState{ | |
319 | Addr: n.ResourceAddr().Resource, | |
320 | } | |
321 | } |