]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go
Merge pull request #27 from terraform-providers/go-modules-2019-02-22
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_resource_apply.go
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/config"
7)
8
9// NodeApplyableResource represents a resource that is "applyable":
10// it is ready to be applied and is represented by a diff.
11type NodeApplyableResource struct {
12 *NodeAbstractResource
13}
14
15// GraphNodeCreator
16func (n *NodeApplyableResource) CreateAddr() *ResourceAddress {
17 return n.NodeAbstractResource.Addr
18}
19
20// GraphNodeReferencer, overriding NodeAbstractResource
21func (n *NodeApplyableResource) References() []string {
22 result := n.NodeAbstractResource.References()
23
24 // The "apply" side of a resource generally also depends on the
25 // destruction of its dependencies as well. For example, if a LB
26 // references a set of VMs with ${vm.foo.*.id}, then we must wait for
27 // the destruction so we get the newly updated list of VMs.
28 //
29 // The exception here is CBD. When CBD is set, we don't do this since
30 // it would create a cycle. By not creating a cycle, we require two
31 // applies since the first apply the creation step will use the OLD
32 // values (pre-destroy) and the second step will update.
33 //
34 // This is how Terraform behaved with "legacy" graphs (TF <= 0.7.x).
35 // We mimic that behavior here now and can improve upon it in the future.
36 //
37 // This behavior is tested in graph_build_apply_test.go to test ordering.
38 cbd := n.Config != nil && n.Config.Lifecycle.CreateBeforeDestroy
39 if !cbd {
40 // The "apply" side of a resource always depends on the destruction
41 // of all its dependencies in addition to the creation.
42 for _, v := range result {
43 result = append(result, v+".destroy")
44 }
45 }
46
47 return result
48}
49
50// GraphNodeEvalable
51func (n *NodeApplyableResource) EvalTree() EvalNode {
52 addr := n.NodeAbstractResource.Addr
53
54 // stateId is the ID to put into the state
55 stateId := addr.stateId()
56
57 // Build the instance info. More of this will be populated during eval
58 info := &InstanceInfo{
59 Id: stateId,
60 Type: addr.Type,
61 }
62
63 // Build the resource for eval
64 resource := &Resource{
65 Name: addr.Name,
66 Type: addr.Type,
67 CountIndex: addr.Index,
68 }
69 if resource.CountIndex < 0 {
70 resource.CountIndex = 0
71 }
72
73 // Determine the dependencies for the state.
74 stateDeps := n.StateReferences()
75
76 // Eval info is different depending on what kind of resource this is
77 switch n.Config.Mode {
78 case config.ManagedResourceMode:
79 return n.evalTreeManagedResource(
80 stateId, info, resource, stateDeps,
81 )
82 case config.DataResourceMode:
83 return n.evalTreeDataResource(
84 stateId, info, resource, stateDeps)
85 default:
86 panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
87 }
88}
89
90func (n *NodeApplyableResource) evalTreeDataResource(
91 stateId string, info *InstanceInfo,
92 resource *Resource, stateDeps []string) EvalNode {
93 var provider ResourceProvider
94 var config *ResourceConfig
95 var diff *InstanceDiff
96 var state *InstanceState
97
98 return &EvalSequence{
99 Nodes: []EvalNode{
100 // Build the instance info
101 &EvalInstanceInfo{
102 Info: info,
103 },
104
105 // Get the saved diff for apply
106 &EvalReadDiff{
107 Name: stateId,
108 Diff: &diff,
109 },
110
111 // Stop here if we don't actually have a diff
112 &EvalIf{
113 If: func(ctx EvalContext) (bool, error) {
114 if diff == nil {
115 return true, EvalEarlyExitError{}
116 }
117
118 if diff.GetAttributesLen() == 0 {
119 return true, EvalEarlyExitError{}
120 }
121
122 return true, nil
123 },
124 Then: EvalNoop{},
125 },
126
15c0b25d
AP
127 // Normally we interpolate count as a preparation step before
128 // a DynamicExpand, but an apply graph has pre-expanded nodes
129 // and so the count would otherwise never be interpolated.
130 //
131 // This is redundant when there are multiple instances created
132 // from the same config (count > 1) but harmless since the
133 // underlying structures have mutexes to make this concurrency-safe.
134 //
135 // In most cases this isn't actually needed because we dealt with
136 // all of the counts during the plan walk, but we do it here
137 // for completeness because other code assumes that the
138 // final count is always available during interpolation.
139 //
140 // Here we are just populating the interpolated value in-place
141 // inside this RawConfig object, like we would in
142 // NodeAbstractCountResource.
143 &EvalInterpolate{
144 Config: n.Config.RawCount,
145 ContinueOnErr: true,
146 },
147
bae9f6d2
JC
148 // We need to re-interpolate the config here, rather than
149 // just using the diff's values directly, because we've
150 // potentially learned more variable values during the
151 // apply pass that weren't known when the diff was produced.
152 &EvalInterpolate{
153 Config: n.Config.RawConfig.Copy(),
154 Resource: resource,
155 Output: &config,
156 },
157
158 &EvalGetProvider{
15c0b25d 159 Name: n.ResolvedProvider,
bae9f6d2
JC
160 Output: &provider,
161 },
162
163 // Make a new diff with our newly-interpolated config.
164 &EvalReadDataDiff{
165 Info: info,
166 Config: &config,
167 Previous: &diff,
168 Provider: &provider,
169 Output: &diff,
170 },
171
172 &EvalReadDataApply{
173 Info: info,
174 Diff: &diff,
175 Provider: &provider,
176 Output: &state,
177 },
178
179 &EvalWriteState{
180 Name: stateId,
181 ResourceType: n.Config.Type,
15c0b25d 182 Provider: n.ResolvedProvider,
bae9f6d2
JC
183 Dependencies: stateDeps,
184 State: &state,
185 },
186
187 // Clear the diff now that we've applied it, so
188 // later nodes won't see a diff that's now a no-op.
189 &EvalWriteDiff{
190 Name: stateId,
191 Diff: nil,
192 },
193
194 &EvalUpdateStateHook{},
195 },
196 }
197}
198
199func (n *NodeApplyableResource) evalTreeManagedResource(
200 stateId string, info *InstanceInfo,
201 resource *Resource, stateDeps []string) EvalNode {
202 // Declare a bunch of variables that are used for state during
203 // evaluation. Most of this are written to by-address below.
204 var provider ResourceProvider
205 var diff, diffApply *InstanceDiff
206 var state *InstanceState
207 var resourceConfig *ResourceConfig
208 var err error
209 var createNew bool
210 var createBeforeDestroyEnabled bool
211
212 return &EvalSequence{
213 Nodes: []EvalNode{
214 // Build the instance info
215 &EvalInstanceInfo{
216 Info: info,
217 },
218
219 // Get the saved diff for apply
220 &EvalReadDiff{
221 Name: stateId,
222 Diff: &diffApply,
223 },
224
225 // We don't want to do any destroys
226 &EvalIf{
227 If: func(ctx EvalContext) (bool, error) {
228 if diffApply == nil {
229 return true, EvalEarlyExitError{}
230 }
231
232 if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 {
233 return true, EvalEarlyExitError{}
234 }
235
236 diffApply.SetDestroy(false)
237 return true, nil
238 },
239 Then: EvalNoop{},
240 },
241
242 &EvalIf{
243 If: func(ctx EvalContext) (bool, error) {
244 destroy := false
245 if diffApply != nil {
246 destroy = diffApply.GetDestroy() || diffApply.RequiresNew()
247 }
248
249 createBeforeDestroyEnabled =
250 n.Config.Lifecycle.CreateBeforeDestroy &&
251 destroy
252
253 return createBeforeDestroyEnabled, nil
254 },
255 Then: &EvalDeposeState{
256 Name: stateId,
257 },
258 },
259
15c0b25d
AP
260 // Normally we interpolate count as a preparation step before
261 // a DynamicExpand, but an apply graph has pre-expanded nodes
262 // and so the count would otherwise never be interpolated.
263 //
264 // This is redundant when there are multiple instances created
265 // from the same config (count > 1) but harmless since the
266 // underlying structures have mutexes to make this concurrency-safe.
267 //
268 // In most cases this isn't actually needed because we dealt with
269 // all of the counts during the plan walk, but we need to do this
270 // in order to support interpolation of resource counts from
271 // apply-time-interpolated expressions, such as those in
272 // "provisioner" blocks.
273 //
274 // Here we are just populating the interpolated value in-place
275 // inside this RawConfig object, like we would in
276 // NodeAbstractCountResource.
277 &EvalInterpolate{
278 Config: n.Config.RawCount,
279 ContinueOnErr: true,
280 },
281
bae9f6d2
JC
282 &EvalInterpolate{
283 Config: n.Config.RawConfig.Copy(),
284 Resource: resource,
285 Output: &resourceConfig,
286 },
287 &EvalGetProvider{
15c0b25d 288 Name: n.ResolvedProvider,
bae9f6d2
JC
289 Output: &provider,
290 },
291 &EvalReadState{
292 Name: stateId,
293 Output: &state,
294 },
295 // Re-run validation to catch any errors we missed, e.g. type
296 // mismatches on computed values.
297 &EvalValidateResource{
298 Provider: &provider,
299 Config: &resourceConfig,
300 ResourceName: n.Config.Name,
301 ResourceType: n.Config.Type,
302 ResourceMode: n.Config.Mode,
303 IgnoreWarnings: true,
304 },
305 &EvalDiff{
306 Info: info,
307 Config: &resourceConfig,
308 Resource: n.Config,
309 Provider: &provider,
310 Diff: &diffApply,
311 State: &state,
312 OutputDiff: &diffApply,
313 },
314
315 // Get the saved diff
316 &EvalReadDiff{
317 Name: stateId,
318 Diff: &diff,
319 },
320
321 // Compare the diffs
322 &EvalCompareDiff{
323 Info: info,
324 One: &diff,
325 Two: &diffApply,
326 },
327
328 &EvalGetProvider{
15c0b25d 329 Name: n.ResolvedProvider,
bae9f6d2
JC
330 Output: &provider,
331 },
332 &EvalReadState{
333 Name: stateId,
334 Output: &state,
335 },
336 // Call pre-apply hook
337 &EvalApplyPre{
338 Info: info,
339 State: &state,
340 Diff: &diffApply,
341 },
342 &EvalApply{
343 Info: info,
344 State: &state,
345 Diff: &diffApply,
346 Provider: &provider,
347 Output: &state,
348 Error: &err,
349 CreateNew: &createNew,
350 },
351 &EvalWriteState{
352 Name: stateId,
353 ResourceType: n.Config.Type,
15c0b25d 354 Provider: n.ResolvedProvider,
bae9f6d2
JC
355 Dependencies: stateDeps,
356 State: &state,
357 },
358 &EvalApplyProvisioners{
359 Info: info,
360 State: &state,
361 Resource: n.Config,
362 InterpResource: resource,
363 CreateNew: &createNew,
364 Error: &err,
365 When: config.ProvisionerWhenCreate,
366 },
367 &EvalIf{
368 If: func(ctx EvalContext) (bool, error) {
369 return createBeforeDestroyEnabled && err != nil, nil
370 },
371 Then: &EvalUndeposeState{
372 Name: stateId,
373 State: &state,
374 },
375 Else: &EvalWriteState{
376 Name: stateId,
377 ResourceType: n.Config.Type,
15c0b25d 378 Provider: n.ResolvedProvider,
bae9f6d2
JC
379 Dependencies: stateDeps,
380 State: &state,
381 },
382 },
383
384 // We clear the diff out here so that future nodes
385 // don't see a diff that is already complete. There
386 // is no longer a diff!
387 &EvalWriteDiff{
388 Name: stateId,
389 Diff: nil,
390 },
391
392 &EvalApplyPost{
393 Info: info,
394 State: &state,
395 Error: &err,
396 },
397 &EvalUpdateStateHook{},
398 },
399 }
400}