]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import "fmt" | |
4 | ||
5 | // EvalReadState is an EvalNode implementation that reads the | |
6 | // primary InstanceState for a specific resource out of the state. | |
7 | type EvalReadState struct { | |
8 | Name string | |
9 | Output **InstanceState | |
10 | } | |
11 | ||
12 | func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) { | |
13 | return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { | |
14 | return rs.Primary, nil | |
15 | }) | |
16 | } | |
17 | ||
18 | // EvalReadStateDeposed is an EvalNode implementation that reads the | |
19 | // deposed InstanceState for a specific resource out of the state | |
20 | type EvalReadStateDeposed struct { | |
21 | Name string | |
22 | Output **InstanceState | |
23 | // Index indicates which instance in the Deposed list to target, or -1 for | |
24 | // the last item. | |
25 | Index int | |
26 | } | |
27 | ||
28 | func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) { | |
29 | return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { | |
30 | // Get the index. If it is negative, then we get the last one | |
31 | idx := n.Index | |
32 | if idx < 0 { | |
33 | idx = len(rs.Deposed) - 1 | |
34 | } | |
35 | if idx >= 0 && idx < len(rs.Deposed) { | |
36 | return rs.Deposed[idx], nil | |
37 | } else { | |
38 | return nil, fmt.Errorf("bad deposed index: %d, for resource: %#v", idx, rs) | |
39 | } | |
40 | }) | |
41 | } | |
42 | ||
43 | // Does the bulk of the work for the various flavors of ReadState eval nodes. | |
44 | // Each node just provides a reader function to get from the ResourceState to the | |
45 | // InstanceState, and this takes care of all the plumbing. | |
46 | func readInstanceFromState( | |
47 | ctx EvalContext, | |
48 | resourceName string, | |
49 | output **InstanceState, | |
50 | readerFn func(*ResourceState) (*InstanceState, error), | |
51 | ) (*InstanceState, error) { | |
52 | state, lock := ctx.State() | |
53 | ||
54 | // Get a read lock so we can access this instance | |
55 | lock.RLock() | |
56 | defer lock.RUnlock() | |
57 | ||
58 | // Look for the module state. If we don't have one, then it doesn't matter. | |
59 | mod := state.ModuleByPath(ctx.Path()) | |
60 | if mod == nil { | |
61 | return nil, nil | |
62 | } | |
63 | ||
64 | // Look for the resource state. If we don't have one, then it is okay. | |
65 | rs := mod.Resources[resourceName] | |
66 | if rs == nil { | |
67 | return nil, nil | |
68 | } | |
69 | ||
70 | // Use the delegate function to get the instance state from the resource state | |
71 | is, err := readerFn(rs) | |
72 | if err != nil { | |
73 | return nil, err | |
74 | } | |
75 | ||
76 | // Write the result to the output pointer | |
77 | if output != nil { | |
78 | *output = is | |
79 | } | |
80 | ||
81 | return is, nil | |
82 | } | |
83 | ||
84 | // EvalRequireState is an EvalNode implementation that early exits | |
85 | // if the state doesn't have an ID. | |
86 | type EvalRequireState struct { | |
87 | State **InstanceState | |
88 | } | |
89 | ||
90 | func (n *EvalRequireState) Eval(ctx EvalContext) (interface{}, error) { | |
91 | if n.State == nil { | |
92 | return nil, EvalEarlyExitError{} | |
93 | } | |
94 | ||
95 | state := *n.State | |
96 | if state == nil || state.ID == "" { | |
97 | return nil, EvalEarlyExitError{} | |
98 | } | |
99 | ||
100 | return nil, nil | |
101 | } | |
102 | ||
103 | // EvalUpdateStateHook is an EvalNode implementation that calls the | |
104 | // PostStateUpdate hook with the current state. | |
105 | type EvalUpdateStateHook struct{} | |
106 | ||
107 | func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) { | |
108 | state, lock := ctx.State() | |
109 | ||
110 | // Get a full lock. Even calling something like WriteState can modify | |
111 | // (prune) the state, so we need the full lock. | |
112 | lock.Lock() | |
113 | defer lock.Unlock() | |
114 | ||
115 | // Call the hook | |
116 | err := ctx.Hook(func(h Hook) (HookAction, error) { | |
117 | return h.PostStateUpdate(state) | |
118 | }) | |
119 | if err != nil { | |
120 | return nil, err | |
121 | } | |
122 | ||
123 | return nil, nil | |
124 | } | |
125 | ||
126 | // EvalWriteState is an EvalNode implementation that writes the | |
127 | // primary InstanceState for a specific resource into the state. | |
128 | type EvalWriteState struct { | |
129 | Name string | |
130 | ResourceType string | |
131 | Provider string | |
132 | Dependencies []string | |
133 | State **InstanceState | |
134 | } | |
135 | ||
136 | func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { | |
137 | return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, | |
138 | func(rs *ResourceState) error { | |
139 | rs.Primary = *n.State | |
140 | return nil | |
141 | }, | |
142 | ) | |
143 | } | |
144 | ||
145 | // EvalWriteStateDeposed is an EvalNode implementation that writes | |
146 | // an InstanceState out to the Deposed list of a resource in the state. | |
147 | type EvalWriteStateDeposed struct { | |
148 | Name string | |
149 | ResourceType string | |
150 | Provider string | |
151 | Dependencies []string | |
152 | State **InstanceState | |
153 | // Index indicates which instance in the Deposed list to target, or -1 to append. | |
154 | Index int | |
155 | } | |
156 | ||
157 | func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { | |
158 | return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, | |
159 | func(rs *ResourceState) error { | |
160 | if n.Index == -1 { | |
161 | rs.Deposed = append(rs.Deposed, *n.State) | |
162 | } else { | |
163 | rs.Deposed[n.Index] = *n.State | |
164 | } | |
165 | return nil | |
166 | }, | |
167 | ) | |
168 | } | |
169 | ||
170 | // Pulls together the common tasks of the EvalWriteState nodes. All the args | |
171 | // are passed directly down from the EvalNode along with a `writer` function | |
172 | // which is yielded the *ResourceState and is responsible for writing an | |
173 | // InstanceState to the proper field in the ResourceState. | |
174 | func writeInstanceToState( | |
175 | ctx EvalContext, | |
176 | resourceName string, | |
177 | resourceType string, | |
178 | provider string, | |
179 | dependencies []string, | |
180 | writerFn func(*ResourceState) error, | |
181 | ) (*InstanceState, error) { | |
182 | state, lock := ctx.State() | |
183 | if state == nil { | |
184 | return nil, fmt.Errorf("cannot write state to nil state") | |
185 | } | |
186 | ||
187 | // Get a write lock so we can access this instance | |
188 | lock.Lock() | |
189 | defer lock.Unlock() | |
190 | ||
191 | // Look for the module state. If we don't have one, create it. | |
192 | mod := state.ModuleByPath(ctx.Path()) | |
193 | if mod == nil { | |
194 | mod = state.AddModule(ctx.Path()) | |
195 | } | |
196 | ||
197 | // Look for the resource state. | |
198 | rs := mod.Resources[resourceName] | |
199 | if rs == nil { | |
200 | rs = &ResourceState{} | |
201 | rs.init() | |
202 | mod.Resources[resourceName] = rs | |
203 | } | |
204 | rs.Type = resourceType | |
205 | rs.Dependencies = dependencies | |
206 | rs.Provider = provider | |
207 | ||
208 | if err := writerFn(rs); err != nil { | |
209 | return nil, err | |
210 | } | |
211 | ||
212 | return nil, nil | |
213 | } | |
214 | ||
215 | // EvalClearPrimaryState is an EvalNode implementation that clears the primary | |
216 | // instance from a resource state. | |
217 | type EvalClearPrimaryState struct { | |
218 | Name string | |
219 | } | |
220 | ||
221 | func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) { | |
222 | state, lock := ctx.State() | |
223 | ||
224 | // Get a read lock so we can access this instance | |
225 | lock.RLock() | |
226 | defer lock.RUnlock() | |
227 | ||
228 | // Look for the module state. If we don't have one, then it doesn't matter. | |
229 | mod := state.ModuleByPath(ctx.Path()) | |
230 | if mod == nil { | |
231 | return nil, nil | |
232 | } | |
233 | ||
234 | // Look for the resource state. If we don't have one, then it is okay. | |
235 | rs := mod.Resources[n.Name] | |
236 | if rs == nil { | |
237 | return nil, nil | |
238 | } | |
239 | ||
240 | // Clear primary from the resource state | |
241 | rs.Primary = nil | |
242 | ||
243 | return nil, nil | |
244 | } | |
245 | ||
246 | // EvalDeposeState is an EvalNode implementation that takes the primary | |
247 | // out of a state and makes it Deposed. This is done at the beginning of | |
248 | // create-before-destroy calls so that the create can create while preserving | |
249 | // the old state of the to-be-destroyed resource. | |
250 | type EvalDeposeState struct { | |
251 | Name string | |
252 | } | |
253 | ||
254 | // TODO: test | |
255 | func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { | |
256 | state, lock := ctx.State() | |
257 | ||
258 | // Get a read lock so we can access this instance | |
259 | lock.RLock() | |
260 | defer lock.RUnlock() | |
261 | ||
262 | // Look for the module state. If we don't have one, then it doesn't matter. | |
263 | mod := state.ModuleByPath(ctx.Path()) | |
264 | if mod == nil { | |
265 | return nil, nil | |
266 | } | |
267 | ||
268 | // Look for the resource state. If we don't have one, then it is okay. | |
269 | rs := mod.Resources[n.Name] | |
270 | if rs == nil { | |
271 | return nil, nil | |
272 | } | |
273 | ||
274 | // If we don't have a primary, we have nothing to depose | |
275 | if rs.Primary == nil { | |
276 | return nil, nil | |
277 | } | |
278 | ||
279 | // Depose | |
280 | rs.Deposed = append(rs.Deposed, rs.Primary) | |
281 | rs.Primary = nil | |
282 | ||
283 | return nil, nil | |
284 | } | |
285 | ||
286 | // EvalUndeposeState is an EvalNode implementation that reads the | |
287 | // InstanceState for a specific resource out of the state. | |
288 | type EvalUndeposeState struct { | |
289 | Name string | |
290 | State **InstanceState | |
291 | } | |
292 | ||
293 | // TODO: test | |
294 | func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) { | |
295 | state, lock := ctx.State() | |
296 | ||
297 | // Get a read lock so we can access this instance | |
298 | lock.RLock() | |
299 | defer lock.RUnlock() | |
300 | ||
301 | // Look for the module state. If we don't have one, then it doesn't matter. | |
302 | mod := state.ModuleByPath(ctx.Path()) | |
303 | if mod == nil { | |
304 | return nil, nil | |
305 | } | |
306 | ||
307 | // Look for the resource state. If we don't have one, then it is okay. | |
308 | rs := mod.Resources[n.Name] | |
309 | if rs == nil { | |
310 | return nil, nil | |
311 | } | |
312 | ||
313 | // If we don't have any desposed resource, then we don't have anything to do | |
314 | if len(rs.Deposed) == 0 { | |
315 | return nil, nil | |
316 | } | |
317 | ||
318 | // Undepose | |
319 | idx := len(rs.Deposed) - 1 | |
320 | rs.Primary = rs.Deposed[idx] | |
321 | rs.Deposed[idx] = *n.State | |
322 | ||
323 | return nil, nil | |
324 | } |