]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ||
107c1cdb ND |
6 | "github.com/hashicorp/terraform/plans" |
7 | "github.com/hashicorp/terraform/providers" | |
8 | "github.com/hashicorp/terraform/states" | |
9 | ||
10 | "github.com/hashicorp/terraform/addrs" | |
11 | "github.com/zclconf/go-cty/cty" | |
bae9f6d2 JC |
12 | ) |
13 | ||
14 | // NodePlannableResourceInstance represents a _single_ resource | |
15 | // instance that is plannable. This means this represents a single | |
16 | // count index, for example. | |
17 | type NodePlannableResourceInstance struct { | |
107c1cdb ND |
18 | *NodeAbstractResourceInstance |
19 | ForceCreateBeforeDestroy bool | |
bae9f6d2 JC |
20 | } |
21 | ||
107c1cdb ND |
22 | var ( |
23 | _ GraphNodeSubPath = (*NodePlannableResourceInstance)(nil) | |
24 | _ GraphNodeReferenceable = (*NodePlannableResourceInstance)(nil) | |
25 | _ GraphNodeReferencer = (*NodePlannableResourceInstance)(nil) | |
26 | _ GraphNodeResource = (*NodePlannableResourceInstance)(nil) | |
27 | _ GraphNodeResourceInstance = (*NodePlannableResourceInstance)(nil) | |
28 | _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstance)(nil) | |
29 | _ GraphNodeAttachResourceState = (*NodePlannableResourceInstance)(nil) | |
30 | _ GraphNodeEvalable = (*NodePlannableResourceInstance)(nil) | |
31 | ) | |
32 | ||
bae9f6d2 JC |
33 | // GraphNodeEvalable |
34 | func (n *NodePlannableResourceInstance) EvalTree() EvalNode { | |
107c1cdb | 35 | addr := n.ResourceInstanceAddr() |
bae9f6d2 | 36 | |
107c1cdb ND |
37 | // State still uses legacy-style internal ids, so we need to shim to get |
38 | // a suitable key to use. | |
39 | stateId := NewLegacyResourceInstanceAddress(addr).stateId() | |
bae9f6d2 JC |
40 | |
41 | // Determine the dependencies for the state. | |
42 | stateDeps := n.StateReferences() | |
43 | ||
44 | // Eval info is different depending on what kind of resource this is | |
107c1cdb ND |
45 | switch addr.Resource.Resource.Mode { |
46 | case addrs.ManagedResourceMode: | |
47 | return n.evalTreeManagedResource(addr, stateId, stateDeps) | |
48 | case addrs.DataResourceMode: | |
49 | return n.evalTreeDataResource(addr, stateId, stateDeps) | |
bae9f6d2 JC |
50 | default: |
51 | panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) | |
52 | } | |
53 | } | |
54 | ||
107c1cdb ND |
55 | func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance, stateId string, stateDeps []addrs.Referenceable) EvalNode { |
56 | config := n.Config | |
57 | var provider providers.Interface | |
58 | var providerSchema *ProviderSchema | |
59 | var change *plans.ResourceInstanceChange | |
60 | var state *states.ResourceInstanceObject | |
61 | var configVal cty.Value | |
bae9f6d2 JC |
62 | |
63 | return &EvalSequence{ | |
64 | Nodes: []EvalNode{ | |
107c1cdb ND |
65 | &EvalGetProvider{ |
66 | Addr: n.ResolvedProvider, | |
67 | Output: &provider, | |
68 | Schema: &providerSchema, | |
bae9f6d2 JC |
69 | }, |
70 | ||
107c1cdb ND |
71 | &EvalReadState{ |
72 | Addr: addr.Resource, | |
73 | Provider: &provider, | |
74 | ProviderSchema: &providerSchema, | |
75 | ||
76 | Output: &state, | |
bae9f6d2 JC |
77 | }, |
78 | ||
107c1cdb ND |
79 | // If we already have a non-planned state then we already dealt |
80 | // with this during the refresh walk and so we have nothing to do | |
81 | // here. | |
bae9f6d2 JC |
82 | &EvalIf{ |
83 | If: func(ctx EvalContext) (bool, error) { | |
107c1cdb ND |
84 | depChanges := false |
85 | ||
86 | // Check and see if any of our dependencies have changes. | |
87 | changes := ctx.Changes() | |
88 | for _, d := range n.StateReferences() { | |
89 | ri, ok := d.(addrs.ResourceInstance) | |
90 | if !ok { | |
91 | continue | |
92 | } | |
93 | change := changes.GetResourceInstanceChange(ri.Absolute(ctx.Path()), states.CurrentGen) | |
94 | if change != nil && change.Action != plans.NoOp { | |
95 | depChanges = true | |
96 | break | |
97 | } | |
bae9f6d2 JC |
98 | } |
99 | ||
107c1cdb ND |
100 | refreshed := state != nil && state.Status != states.ObjectPlanned |
101 | ||
102 | // If there are no dependency changes, and it's not a forced | |
103 | // read because we there was no Refresh, then we don't need | |
104 | // to re-read. If any dependencies have changes, it means | |
105 | // our config may also have changes and we need to Read the | |
106 | // data source again. | |
107 | if !depChanges && refreshed { | |
108 | return false, EvalEarlyExitError{} | |
109 | } | |
bae9f6d2 JC |
110 | return true, nil |
111 | }, | |
112 | Then: EvalNoop{}, | |
113 | }, | |
114 | ||
107c1cdb ND |
115 | &EvalValidateSelfRef{ |
116 | Addr: addr.Resource, | |
117 | Config: config.Config, | |
118 | ProviderSchema: &providerSchema, | |
bae9f6d2 JC |
119 | }, |
120 | ||
107c1cdb ND |
121 | &EvalReadData{ |
122 | Addr: addr.Resource, | |
123 | Config: n.Config, | |
124 | Dependencies: n.StateReferences(), | |
125 | Provider: &provider, | |
126 | ProviderAddr: n.ResolvedProvider, | |
127 | ProviderSchema: &providerSchema, | |
128 | ForcePlanRead: true, // _always_ produce a Read change, even if the config seems ready | |
129 | OutputChange: &change, | |
130 | OutputValue: &configVal, | |
131 | OutputState: &state, | |
bae9f6d2 JC |
132 | }, |
133 | ||
134 | &EvalWriteState{ | |
107c1cdb ND |
135 | Addr: addr.Resource, |
136 | ProviderAddr: n.ResolvedProvider, | |
137 | ProviderSchema: &providerSchema, | |
138 | State: &state, | |
bae9f6d2 JC |
139 | }, |
140 | ||
141 | &EvalWriteDiff{ | |
107c1cdb ND |
142 | Addr: addr.Resource, |
143 | ProviderSchema: &providerSchema, | |
144 | Change: &change, | |
bae9f6d2 JC |
145 | }, |
146 | }, | |
147 | } | |
148 | } | |
149 | ||
107c1cdb ND |
150 | func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance, stateId string, stateDeps []addrs.Referenceable) EvalNode { |
151 | config := n.Config | |
152 | var provider providers.Interface | |
153 | var providerSchema *ProviderSchema | |
154 | var change *plans.ResourceInstanceChange | |
155 | var state *states.ResourceInstanceObject | |
bae9f6d2 JC |
156 | |
157 | return &EvalSequence{ | |
158 | Nodes: []EvalNode{ | |
bae9f6d2 | 159 | &EvalGetProvider{ |
107c1cdb | 160 | Addr: n.ResolvedProvider, |
bae9f6d2 | 161 | Output: &provider, |
107c1cdb | 162 | Schema: &providerSchema, |
bae9f6d2 | 163 | }, |
107c1cdb | 164 | |
bae9f6d2 | 165 | &EvalReadState{ |
107c1cdb ND |
166 | Addr: addr.Resource, |
167 | Provider: &provider, | |
168 | ProviderSchema: &providerSchema, | |
169 | ||
bae9f6d2 JC |
170 | Output: &state, |
171 | }, | |
107c1cdb ND |
172 | |
173 | &EvalValidateSelfRef{ | |
174 | Addr: addr.Resource, | |
175 | Config: config.Config, | |
176 | ProviderSchema: &providerSchema, | |
177 | }, | |
178 | ||
bae9f6d2 | 179 | &EvalDiff{ |
107c1cdb ND |
180 | Addr: addr.Resource, |
181 | Config: n.Config, | |
182 | CreateBeforeDestroy: n.ForceCreateBeforeDestroy, | |
183 | Provider: &provider, | |
184 | ProviderAddr: n.ResolvedProvider, | |
185 | ProviderSchema: &providerSchema, | |
186 | State: &state, | |
187 | OutputChange: &change, | |
188 | OutputState: &state, | |
bae9f6d2 JC |
189 | }, |
190 | &EvalCheckPreventDestroy{ | |
107c1cdb ND |
191 | Addr: addr.Resource, |
192 | Config: n.Config, | |
193 | Change: &change, | |
bae9f6d2 JC |
194 | }, |
195 | &EvalWriteState{ | |
107c1cdb ND |
196 | Addr: addr.Resource, |
197 | ProviderAddr: n.ResolvedProvider, | |
198 | State: &state, | |
199 | ProviderSchema: &providerSchema, | |
bae9f6d2 JC |
200 | }, |
201 | &EvalWriteDiff{ | |
107c1cdb ND |
202 | Addr: addr.Resource, |
203 | ProviderSchema: &providerSchema, | |
204 | Change: &change, | |
bae9f6d2 JC |
205 | }, |
206 | }, | |
207 | } | |
208 | } |