]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_resource_apply.go
1 package terraform
2
3 import (
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.
11 type NodeApplyableResource struct {
12 *NodeAbstractResource
13 }
14
15 // GraphNodeCreator
16 func (n *NodeApplyableResource) CreateAddr() *ResourceAddress {
17 return n.NodeAbstractResource.Addr
18 }
19
20 // GraphNodeReferencer, overriding NodeAbstractResource
21 func (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
51 func (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
90 func (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
127 // We need to re-interpolate the config here, rather than
128 // just using the diff's values directly, because we've
129 // potentially learned more variable values during the
130 // apply pass that weren't known when the diff was produced.
131 &EvalInterpolate{
132 Config: n.Config.RawConfig.Copy(),
133 Resource: resource,
134 Output: &config,
135 },
136
137 &EvalGetProvider{
138 Name: n.ProvidedBy()[0],
139 Output: &provider,
140 },
141
142 // Make a new diff with our newly-interpolated config.
143 &EvalReadDataDiff{
144 Info: info,
145 Config: &config,
146 Previous: &diff,
147 Provider: &provider,
148 Output: &diff,
149 },
150
151 &EvalReadDataApply{
152 Info: info,
153 Diff: &diff,
154 Provider: &provider,
155 Output: &state,
156 },
157
158 &EvalWriteState{
159 Name: stateId,
160 ResourceType: n.Config.Type,
161 Provider: n.Config.Provider,
162 Dependencies: stateDeps,
163 State: &state,
164 },
165
166 // Clear the diff now that we've applied it, so
167 // later nodes won't see a diff that's now a no-op.
168 &EvalWriteDiff{
169 Name: stateId,
170 Diff: nil,
171 },
172
173 &EvalUpdateStateHook{},
174 },
175 }
176 }
177
178 func (n *NodeApplyableResource) evalTreeManagedResource(
179 stateId string, info *InstanceInfo,
180 resource *Resource, stateDeps []string) EvalNode {
181 // Declare a bunch of variables that are used for state during
182 // evaluation. Most of this are written to by-address below.
183 var provider ResourceProvider
184 var diff, diffApply *InstanceDiff
185 var state *InstanceState
186 var resourceConfig *ResourceConfig
187 var err error
188 var createNew bool
189 var createBeforeDestroyEnabled bool
190
191 return &EvalSequence{
192 Nodes: []EvalNode{
193 // Build the instance info
194 &EvalInstanceInfo{
195 Info: info,
196 },
197
198 // Get the saved diff for apply
199 &EvalReadDiff{
200 Name: stateId,
201 Diff: &diffApply,
202 },
203
204 // We don't want to do any destroys
205 &EvalIf{
206 If: func(ctx EvalContext) (bool, error) {
207 if diffApply == nil {
208 return true, EvalEarlyExitError{}
209 }
210
211 if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 {
212 return true, EvalEarlyExitError{}
213 }
214
215 diffApply.SetDestroy(false)
216 return true, nil
217 },
218 Then: EvalNoop{},
219 },
220
221 &EvalIf{
222 If: func(ctx EvalContext) (bool, error) {
223 destroy := false
224 if diffApply != nil {
225 destroy = diffApply.GetDestroy() || diffApply.RequiresNew()
226 }
227
228 createBeforeDestroyEnabled =
229 n.Config.Lifecycle.CreateBeforeDestroy &&
230 destroy
231
232 return createBeforeDestroyEnabled, nil
233 },
234 Then: &EvalDeposeState{
235 Name: stateId,
236 },
237 },
238
239 &EvalInterpolate{
240 Config: n.Config.RawConfig.Copy(),
241 Resource: resource,
242 Output: &resourceConfig,
243 },
244 &EvalGetProvider{
245 Name: n.ProvidedBy()[0],
246 Output: &provider,
247 },
248 &EvalReadState{
249 Name: stateId,
250 Output: &state,
251 },
252 // Re-run validation to catch any errors we missed, e.g. type
253 // mismatches on computed values.
254 &EvalValidateResource{
255 Provider: &provider,
256 Config: &resourceConfig,
257 ResourceName: n.Config.Name,
258 ResourceType: n.Config.Type,
259 ResourceMode: n.Config.Mode,
260 IgnoreWarnings: true,
261 },
262 &EvalDiff{
263 Info: info,
264 Config: &resourceConfig,
265 Resource: n.Config,
266 Provider: &provider,
267 Diff: &diffApply,
268 State: &state,
269 OutputDiff: &diffApply,
270 },
271
272 // Get the saved diff
273 &EvalReadDiff{
274 Name: stateId,
275 Diff: &diff,
276 },
277
278 // Compare the diffs
279 &EvalCompareDiff{
280 Info: info,
281 One: &diff,
282 Two: &diffApply,
283 },
284
285 &EvalGetProvider{
286 Name: n.ProvidedBy()[0],
287 Output: &provider,
288 },
289 &EvalReadState{
290 Name: stateId,
291 Output: &state,
292 },
293 // Call pre-apply hook
294 &EvalApplyPre{
295 Info: info,
296 State: &state,
297 Diff: &diffApply,
298 },
299 &EvalApply{
300 Info: info,
301 State: &state,
302 Diff: &diffApply,
303 Provider: &provider,
304 Output: &state,
305 Error: &err,
306 CreateNew: &createNew,
307 },
308 &EvalWriteState{
309 Name: stateId,
310 ResourceType: n.Config.Type,
311 Provider: n.Config.Provider,
312 Dependencies: stateDeps,
313 State: &state,
314 },
315 &EvalApplyProvisioners{
316 Info: info,
317 State: &state,
318 Resource: n.Config,
319 InterpResource: resource,
320 CreateNew: &createNew,
321 Error: &err,
322 When: config.ProvisionerWhenCreate,
323 },
324 &EvalIf{
325 If: func(ctx EvalContext) (bool, error) {
326 return createBeforeDestroyEnabled && err != nil, nil
327 },
328 Then: &EvalUndeposeState{
329 Name: stateId,
330 State: &state,
331 },
332 Else: &EvalWriteState{
333 Name: stateId,
334 ResourceType: n.Config.Type,
335 Provider: n.Config.Provider,
336 Dependencies: stateDeps,
337 State: &state,
338 },
339 },
340
341 // We clear the diff out here so that future nodes
342 // don't see a diff that is already complete. There
343 // is no longer a diff!
344 &EvalWriteDiff{
345 Name: stateId,
346 Diff: nil,
347 },
348
349 &EvalApplyPost{
350 Info: info,
351 State: &state,
352 Error: &err,
353 },
354 &EvalUpdateStateHook{},
355 },
356 }
357 }