6 "github.com/hashicorp/terraform/config"
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 {
16 func (n *NodeApplyableResource) CreateAddr() *ResourceAddress {
17 return n.NodeAbstractResource.Addr
20 // GraphNodeReferencer, overriding NodeAbstractResource
21 func (n *NodeApplyableResource) References() []string {
22 result := n.NodeAbstractResource.References()
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.
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.
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.
37 // This behavior is tested in graph_build_apply_test.go to test ordering.
38 cbd := n.Config != nil && n.Config.Lifecycle.CreateBeforeDestroy
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")
51 func (n *NodeApplyableResource) EvalTree() EvalNode {
52 addr := n.NodeAbstractResource.Addr
54 // stateId is the ID to put into the state
55 stateId := addr.stateId()
57 // Build the instance info. More of this will be populated during eval
58 info := &InstanceInfo{
63 // Build the resource for eval
64 resource := &Resource{
67 CountIndex: addr.Index,
69 if resource.CountIndex < 0 {
70 resource.CountIndex = 0
73 // Determine the dependencies for the state.
74 stateDeps := n.StateReferences()
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,
82 case config.DataResourceMode:
83 return n.evalTreeDataResource(
84 stateId, info, resource, stateDeps)
86 panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
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
100 // Build the instance info
105 // Get the saved diff for apply
111 // Stop here if we don't actually have a diff
113 If: func(ctx EvalContext) (bool, error) {
115 return true, EvalEarlyExitError{}
118 if diff.GetAttributesLen() == 0 {
119 return true, EvalEarlyExitError{}
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.
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.
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.
140 // Here we are just populating the interpolated value in-place
141 // inside this RawConfig object, like we would in
142 // NodeAbstractCountResource.
144 Config: n.Config.RawCount,
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.
153 Config: n.Config.RawConfig.Copy(),
159 Name: n.ResolvedProvider,
163 // Make a new diff with our newly-interpolated config.
181 ResourceType: n.Config.Type,
182 Provider: n.ResolvedProvider,
183 Dependencies: stateDeps,
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.
194 &EvalUpdateStateHook{},
199 func (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
210 var createBeforeDestroyEnabled bool
212 return &EvalSequence{
214 // Build the instance info
219 // Get the saved diff for apply
225 // We don't want to do any destroys
227 If: func(ctx EvalContext) (bool, error) {
228 if diffApply == nil {
229 return true, EvalEarlyExitError{}
232 if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 {
233 return true, EvalEarlyExitError{}
236 diffApply.SetDestroy(false)
243 If: func(ctx EvalContext) (bool, error) {
245 if diffApply != nil {
246 destroy = diffApply.GetDestroy() || diffApply.RequiresNew()
249 createBeforeDestroyEnabled =
250 n.Config.Lifecycle.CreateBeforeDestroy &&
253 return createBeforeDestroyEnabled, nil
255 Then: &EvalDeposeState{
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.
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.
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.
274 // Here we are just populating the interpolated value in-place
275 // inside this RawConfig object, like we would in
276 // NodeAbstractCountResource.
278 Config: n.Config.RawCount,
283 Config: n.Config.RawConfig.Copy(),
285 Output: &resourceConfig,
288 Name: n.ResolvedProvider,
295 // Re-run validation to catch any errors we missed, e.g. type
296 // mismatches on computed values.
297 &EvalValidateResource{
299 Config: &resourceConfig,
300 ResourceName: n.Config.Name,
301 ResourceType: n.Config.Type,
302 ResourceMode: n.Config.Mode,
303 IgnoreWarnings: true,
307 Config: &resourceConfig,
312 OutputDiff: &diffApply,
315 // Get the saved diff
329 Name: n.ResolvedProvider,
336 // Call pre-apply hook
349 CreateNew: &createNew,
353 ResourceType: n.Config.Type,
354 Provider: n.ResolvedProvider,
355 Dependencies: stateDeps,
358 &EvalApplyProvisioners{
362 InterpResource: resource,
363 CreateNew: &createNew,
365 When: config.ProvisionerWhenCreate,
368 If: func(ctx EvalContext) (bool, error) {
369 return createBeforeDestroyEnabled && err != nil, nil
371 Then: &EvalUndeposeState{
375 Else: &EvalWriteState{
377 ResourceType: n.Config.Type,
378 Provider: n.ResolvedProvider,
379 Dependencies: stateDeps,
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!
397 &EvalUpdateStateHook{},