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