diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/states/sync.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/states/sync.go | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/states/sync.go b/vendor/github.com/hashicorp/terraform/states/sync.go new file mode 100644 index 0000000..a377446 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/states/sync.go | |||
@@ -0,0 +1,537 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
4 | "log" | ||
5 | "sync" | ||
6 | |||
7 | "github.com/hashicorp/terraform/addrs" | ||
8 | "github.com/zclconf/go-cty/cty" | ||
9 | ) | ||
10 | |||
11 | // SyncState is a wrapper around State that provides concurrency-safe access to | ||
12 | // various common operations that occur during a Terraform graph walk, or other | ||
13 | // similar concurrent contexts. | ||
14 | // | ||
15 | // When a SyncState wrapper is in use, no concurrent direct access to the | ||
16 | // underlying objects is permitted unless the caller first acquires an explicit | ||
17 | // lock, using the Lock and Unlock methods. Most callers should _not_ | ||
18 | // explicitly lock, and should instead use the other methods of this type that | ||
19 | // handle locking automatically. | ||
20 | // | ||
21 | // Since SyncState is able to safely consolidate multiple updates into a single | ||
22 | // atomic operation, many of its methods are at a higher level than those | ||
23 | // of the underlying types, and operate on the state as a whole rather than | ||
24 | // on individual sub-structures of the state. | ||
25 | // | ||
26 | // SyncState can only protect against races within its own methods. It cannot | ||
27 | // provide any guarantees about the order in which concurrent operations will | ||
28 | // be processed, so callers may still need to employ higher-level techniques | ||
29 | // for ensuring correct operation sequencing, such as building and walking | ||
30 | // a dependency graph. | ||
31 | type SyncState struct { | ||
32 | state *State | ||
33 | lock sync.RWMutex | ||
34 | } | ||
35 | |||
36 | // Module returns a snapshot of the state of the module instance with the given | ||
37 | // address, or nil if no such module is tracked. | ||
38 | // | ||
39 | // The return value is a pointer to a copy of the module state, which the | ||
40 | // caller may then freely access and mutate. However, since the module state | ||
41 | // tends to be a large data structure with many child objects, where possible | ||
42 | // callers should prefer to use a more granular accessor to access a child | ||
43 | // module directly, and thus reduce the amount of copying required. | ||
44 | func (s *SyncState) Module(addr addrs.ModuleInstance) *Module { | ||
45 | s.lock.RLock() | ||
46 | ret := s.state.Module(addr).DeepCopy() | ||
47 | s.lock.RUnlock() | ||
48 | return ret | ||
49 | } | ||
50 | |||
51 | // RemoveModule removes the entire state for the given module, taking with | ||
52 | // it any resources associated with the module. This should generally be | ||
53 | // called only for modules whose resources have all been destroyed, but | ||
54 | // that is not enforced by this method. | ||
55 | func (s *SyncState) RemoveModule(addr addrs.ModuleInstance) { | ||
56 | s.lock.Lock() | ||
57 | defer s.lock.Unlock() | ||
58 | |||
59 | s.state.RemoveModule(addr) | ||
60 | } | ||
61 | |||
62 | // OutputValue returns a snapshot of the state of the output value with the | ||
63 | // given address, or nil if no such output value is tracked. | ||
64 | // | ||
65 | // The return value is a pointer to a copy of the output value state, which the | ||
66 | // caller may then freely access and mutate. | ||
67 | func (s *SyncState) OutputValue(addr addrs.AbsOutputValue) *OutputValue { | ||
68 | s.lock.RLock() | ||
69 | ret := s.state.OutputValue(addr).DeepCopy() | ||
70 | s.lock.RUnlock() | ||
71 | return ret | ||
72 | } | ||
73 | |||
74 | // SetOutputValue writes a given output value into the state, overwriting | ||
75 | // any existing value of the same name. | ||
76 | // | ||
77 | // If the module containing the output is not yet tracked in state then it | ||
78 | // be added as a side-effect. | ||
79 | func (s *SyncState) SetOutputValue(addr addrs.AbsOutputValue, value cty.Value, sensitive bool) { | ||
80 | s.lock.Lock() | ||
81 | defer s.lock.Unlock() | ||
82 | |||
83 | ms := s.state.EnsureModule(addr.Module) | ||
84 | ms.SetOutputValue(addr.OutputValue.Name, value, sensitive) | ||
85 | } | ||
86 | |||
87 | // RemoveOutputValue removes the stored value for the output value with the | ||
88 | // given address. | ||
89 | // | ||
90 | // If this results in its containing module being empty, the module will be | ||
91 | // pruned from the state as a side-effect. | ||
92 | func (s *SyncState) RemoveOutputValue(addr addrs.AbsOutputValue) { | ||
93 | s.lock.Lock() | ||
94 | defer s.lock.Unlock() | ||
95 | |||
96 | ms := s.state.Module(addr.Module) | ||
97 | if ms == nil { | ||
98 | return | ||
99 | } | ||
100 | ms.RemoveOutputValue(addr.OutputValue.Name) | ||
101 | s.maybePruneModule(addr.Module) | ||
102 | } | ||
103 | |||
104 | // LocalValue returns the current value associated with the given local value | ||
105 | // address. | ||
106 | func (s *SyncState) LocalValue(addr addrs.AbsLocalValue) cty.Value { | ||
107 | s.lock.RLock() | ||
108 | // cty.Value is immutable, so we don't need any extra copying here. | ||
109 | ret := s.state.LocalValue(addr) | ||
110 | s.lock.RUnlock() | ||
111 | return ret | ||
112 | } | ||
113 | |||
114 | // SetLocalValue writes a given output value into the state, overwriting | ||
115 | // any existing value of the same name. | ||
116 | // | ||
117 | // If the module containing the local value is not yet tracked in state then it | ||
118 | // will be added as a side-effect. | ||
119 | func (s *SyncState) SetLocalValue(addr addrs.AbsLocalValue, value cty.Value) { | ||
120 | s.lock.Lock() | ||
121 | defer s.lock.Unlock() | ||
122 | |||
123 | ms := s.state.EnsureModule(addr.Module) | ||
124 | ms.SetLocalValue(addr.LocalValue.Name, value) | ||
125 | } | ||
126 | |||
127 | // RemoveLocalValue removes the stored value for the local value with the | ||
128 | // given address. | ||
129 | // | ||
130 | // If this results in its containing module being empty, the module will be | ||
131 | // pruned from the state as a side-effect. | ||
132 | func (s *SyncState) RemoveLocalValue(addr addrs.AbsLocalValue) { | ||
133 | s.lock.Lock() | ||
134 | defer s.lock.Unlock() | ||
135 | |||
136 | ms := s.state.Module(addr.Module) | ||
137 | if ms == nil { | ||
138 | return | ||
139 | } | ||
140 | ms.RemoveLocalValue(addr.LocalValue.Name) | ||
141 | s.maybePruneModule(addr.Module) | ||
142 | } | ||
143 | |||
144 | // Resource returns a snapshot of the state of the resource with the given | ||
145 | // address, or nil if no such resource is tracked. | ||
146 | // | ||
147 | // The return value is a pointer to a copy of the resource state, which the | ||
148 | // caller may then freely access and mutate. | ||
149 | func (s *SyncState) Resource(addr addrs.AbsResource) *Resource { | ||
150 | s.lock.RLock() | ||
151 | ret := s.state.Resource(addr).DeepCopy() | ||
152 | s.lock.RUnlock() | ||
153 | return ret | ||
154 | } | ||
155 | |||
156 | // ResourceInstance returns a snapshot of the state the resource instance with | ||
157 | // the given address, or nil if no such instance is tracked. | ||
158 | // | ||
159 | // The return value is a pointer to a copy of the instance state, which the | ||
160 | // caller may then freely access and mutate. | ||
161 | func (s *SyncState) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance { | ||
162 | s.lock.RLock() | ||
163 | ret := s.state.ResourceInstance(addr).DeepCopy() | ||
164 | s.lock.RUnlock() | ||
165 | return ret | ||
166 | } | ||
167 | |||
168 | // ResourceInstanceObject returns a snapshot of the current instance object | ||
169 | // of the given generation belonging to the instance with the given address, | ||
170 | // or nil if no such object is tracked.. | ||
171 | // | ||
172 | // The return value is a pointer to a copy of the object, which the caller may | ||
173 | // then freely access and mutate. | ||
174 | func (s *SyncState) ResourceInstanceObject(addr addrs.AbsResourceInstance, gen Generation) *ResourceInstanceObjectSrc { | ||
175 | s.lock.RLock() | ||
176 | defer s.lock.RUnlock() | ||
177 | |||
178 | inst := s.state.ResourceInstance(addr) | ||
179 | if inst == nil { | ||
180 | return nil | ||
181 | } | ||
182 | return inst.GetGeneration(gen).DeepCopy() | ||
183 | } | ||
184 | |||
185 | // SetResourceMeta updates the resource-level metadata for the resource at | ||
186 | // the given address, creating the containing module state and resource state | ||
187 | // as a side-effect if not already present. | ||
188 | func (s *SyncState) SetResourceMeta(addr addrs.AbsResource, eachMode EachMode, provider addrs.AbsProviderConfig) { | ||
189 | s.lock.Lock() | ||
190 | defer s.lock.Unlock() | ||
191 | |||
192 | ms := s.state.EnsureModule(addr.Module) | ||
193 | ms.SetResourceMeta(addr.Resource, eachMode, provider) | ||
194 | } | ||
195 | |||
196 | // RemoveResource removes the entire state for the given resource, taking with | ||
197 | // it any instances associated with the resource. This should generally be | ||
198 | // called only for resource objects whose instances have all been destroyed, | ||
199 | // but that is not enforced by this method. (Use RemoveResourceIfEmpty instead | ||
200 | // to safely check first.) | ||
201 | func (s *SyncState) RemoveResource(addr addrs.AbsResource) { | ||
202 | s.lock.Lock() | ||
203 | defer s.lock.Unlock() | ||
204 | |||
205 | ms := s.state.EnsureModule(addr.Module) | ||
206 | ms.RemoveResource(addr.Resource) | ||
207 | s.maybePruneModule(addr.Module) | ||
208 | } | ||
209 | |||
210 | // RemoveResourceIfEmpty is similar to RemoveResource but first checks to | ||
211 | // make sure there are no instances or objects left in the resource. | ||
212 | // | ||
213 | // Returns true if the resource was removed, or false if remaining child | ||
214 | // objects prevented its removal. Returns true also if the resource was | ||
215 | // already absent, and thus no action needed to be taken. | ||
216 | func (s *SyncState) RemoveResourceIfEmpty(addr addrs.AbsResource) bool { | ||
217 | s.lock.Lock() | ||
218 | defer s.lock.Unlock() | ||
219 | |||
220 | ms := s.state.Module(addr.Module) | ||
221 | if ms == nil { | ||
222 | return true // nothing to do | ||
223 | } | ||
224 | rs := ms.Resource(addr.Resource) | ||
225 | if rs == nil { | ||
226 | return true // nothing to do | ||
227 | } | ||
228 | if len(rs.Instances) != 0 { | ||
229 | // We don't check here for the possibility of instances that exist | ||
230 | // but don't have any objects because it's the responsibility of the | ||
231 | // instance-mutation methods to prune those away automatically. | ||
232 | return false | ||
233 | } | ||
234 | ms.RemoveResource(addr.Resource) | ||
235 | s.maybePruneModule(addr.Module) | ||
236 | return true | ||
237 | } | ||
238 | |||
239 | // MaybeFixUpResourceInstanceAddressForCount deals with the situation where a | ||
240 | // resource has changed from having "count" set to not set, or vice-versa, and | ||
241 | // so we need to rename the zeroth instance key to no key at all, or vice-versa. | ||
242 | // | ||
243 | // Set countEnabled to true if the resource has count set in its new | ||
244 | // configuration, or false if it does not. | ||
245 | // | ||
246 | // The state is modified in-place if necessary, moving a resource instance | ||
247 | // between the two addresses. The return value is true if a change was made, | ||
248 | // and false otherwise. | ||
249 | func (s *SyncState) MaybeFixUpResourceInstanceAddressForCount(addr addrs.AbsResource, countEnabled bool) bool { | ||
250 | s.lock.Lock() | ||
251 | defer s.lock.Unlock() | ||
252 | |||
253 | ms := s.state.Module(addr.Module) | ||
254 | if ms == nil { | ||
255 | return false | ||
256 | } | ||
257 | |||
258 | relAddr := addr.Resource | ||
259 | rs := ms.Resource(relAddr) | ||
260 | if rs == nil { | ||
261 | return false | ||
262 | } | ||
263 | huntKey := addrs.NoKey | ||
264 | replaceKey := addrs.InstanceKey(addrs.IntKey(0)) | ||
265 | if !countEnabled { | ||
266 | huntKey, replaceKey = replaceKey, huntKey | ||
267 | } | ||
268 | |||
269 | is, exists := rs.Instances[huntKey] | ||
270 | if !exists { | ||
271 | return false | ||
272 | } | ||
273 | |||
274 | if _, exists := rs.Instances[replaceKey]; exists { | ||
275 | // If the replacement key also exists then we'll do nothing and keep both. | ||
276 | return false | ||
277 | } | ||
278 | |||
279 | // If we get here then we need to "rename" from hunt to replace | ||
280 | rs.Instances[replaceKey] = is | ||
281 | delete(rs.Instances, huntKey) | ||
282 | return true | ||
283 | } | ||
284 | |||
285 | // SetResourceInstanceCurrent saves the given instance object as the current | ||
286 | // generation of the resource instance with the given address, simulataneously | ||
287 | // updating the recorded provider configuration address, dependencies, and | ||
288 | // resource EachMode. | ||
289 | // | ||
290 | // Any existing current instance object for the given resource is overwritten. | ||
291 | // Set obj to nil to remove the primary generation object altogether. If there | ||
292 | // are no deposed objects then the instance as a whole will be removed, which | ||
293 | // may in turn also remove the containing module if it becomes empty. | ||
294 | // | ||
295 | // The caller must ensure that the given ResourceInstanceObject is not | ||
296 | // concurrently mutated during this call, but may be freely used again once | ||
297 | // this function returns. | ||
298 | // | ||
299 | // The provider address and "each mode" are resource-wide settings and so they | ||
300 | // are updated for all other instances of the same resource as a side-effect of | ||
301 | // this call. | ||
302 | // | ||
303 | // If the containing module for this resource or the resource itself are not | ||
304 | // already tracked in state then they will be added as a side-effect. | ||
305 | func (s *SyncState) SetResourceInstanceCurrent(addr addrs.AbsResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { | ||
306 | s.lock.Lock() | ||
307 | defer s.lock.Unlock() | ||
308 | |||
309 | ms := s.state.EnsureModule(addr.Module) | ||
310 | ms.SetResourceInstanceCurrent(addr.Resource, obj.DeepCopy(), provider) | ||
311 | s.maybePruneModule(addr.Module) | ||
312 | } | ||
313 | |||
314 | // SetResourceInstanceDeposed saves the given instance object as a deposed | ||
315 | // generation of the resource instance with the given address and deposed key. | ||
316 | // | ||
317 | // Call this method only for pre-existing deposed objects that already have | ||
318 | // a known DeposedKey. For example, this method is useful if reloading objects | ||
319 | // that were persisted to a state file. To mark the current object as deposed, | ||
320 | // use DeposeResourceInstanceObject instead. | ||
321 | // | ||
322 | // The caller must ensure that the given ResourceInstanceObject is not | ||
323 | // concurrently mutated during this call, but may be freely used again once | ||
324 | // this function returns. | ||
325 | // | ||
326 | // The resource that contains the given instance must already exist in the | ||
327 | // state, or this method will panic. Use Resource to check first if its | ||
328 | // presence is not already guaranteed. | ||
329 | // | ||
330 | // Any existing current instance object for the given resource and deposed key | ||
331 | // is overwritten. Set obj to nil to remove the deposed object altogether. If | ||
332 | // the instance is left with no objects after this operation then it will | ||
333 | // be removed from its containing resource altogether. | ||
334 | // | ||
335 | // If the containing module for this resource or the resource itself are not | ||
336 | // already tracked in state then they will be added as a side-effect. | ||
337 | func (s *SyncState) SetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { | ||
338 | s.lock.Lock() | ||
339 | defer s.lock.Unlock() | ||
340 | |||
341 | ms := s.state.EnsureModule(addr.Module) | ||
342 | ms.SetResourceInstanceDeposed(addr.Resource, key, obj.DeepCopy(), provider) | ||
343 | s.maybePruneModule(addr.Module) | ||
344 | } | ||
345 | |||
346 | // DeposeResourceInstanceObject moves the current instance object for the | ||
347 | // given resource instance address into the deposed set, leaving the instance | ||
348 | // without a current object. | ||
349 | // | ||
350 | // The return value is the newly-allocated deposed key, or NotDeposed if the | ||
351 | // given instance is already lacking a current object. | ||
352 | // | ||
353 | // If the containing module for this resource or the resource itself are not | ||
354 | // already tracked in state then there cannot be a current object for the | ||
355 | // given instance, and so NotDeposed will be returned without modifying the | ||
356 | // state at all. | ||
357 | func (s *SyncState) DeposeResourceInstanceObject(addr addrs.AbsResourceInstance) DeposedKey { | ||
358 | s.lock.Lock() | ||
359 | defer s.lock.Unlock() | ||
360 | |||
361 | ms := s.state.Module(addr.Module) | ||
362 | if ms == nil { | ||
363 | return NotDeposed | ||
364 | } | ||
365 | |||
366 | return ms.deposeResourceInstanceObject(addr.Resource, NotDeposed) | ||
367 | } | ||
368 | |||
369 | // DeposeResourceInstanceObjectForceKey is like DeposeResourceInstanceObject | ||
370 | // but uses a pre-allocated key. It's the caller's responsibility to ensure | ||
371 | // that there aren't any races to use a particular key; this method will panic | ||
372 | // if the given key is already in use. | ||
373 | func (s *SyncState) DeposeResourceInstanceObjectForceKey(addr addrs.AbsResourceInstance, forcedKey DeposedKey) { | ||
374 | s.lock.Lock() | ||
375 | defer s.lock.Unlock() | ||
376 | |||
377 | if forcedKey == NotDeposed { | ||
378 | // Usage error: should use DeposeResourceInstanceObject in this case | ||
379 | panic("DeposeResourceInstanceObjectForceKey called without forced key") | ||
380 | } | ||
381 | |||
382 | ms := s.state.Module(addr.Module) | ||
383 | if ms == nil { | ||
384 | return // Nothing to do, since there can't be any current object either. | ||
385 | } | ||
386 | |||
387 | ms.deposeResourceInstanceObject(addr.Resource, forcedKey) | ||
388 | } | ||
389 | |||
390 | // ForgetResourceInstanceAll removes the record of all objects associated with | ||
391 | // the specified resource instance, if present. If not present, this is a no-op. | ||
392 | func (s *SyncState) ForgetResourceInstanceAll(addr addrs.AbsResourceInstance) { | ||
393 | s.lock.Lock() | ||
394 | defer s.lock.Unlock() | ||
395 | |||
396 | ms := s.state.Module(addr.Module) | ||
397 | if ms == nil { | ||
398 | return | ||
399 | } | ||
400 | ms.ForgetResourceInstanceAll(addr.Resource) | ||
401 | s.maybePruneModule(addr.Module) | ||
402 | } | ||
403 | |||
404 | // ForgetResourceInstanceDeposed removes the record of the deposed object with | ||
405 | // the given address and key, if present. If not present, this is a no-op. | ||
406 | func (s *SyncState) ForgetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) { | ||
407 | s.lock.Lock() | ||
408 | defer s.lock.Unlock() | ||
409 | |||
410 | ms := s.state.Module(addr.Module) | ||
411 | if ms == nil { | ||
412 | return | ||
413 | } | ||
414 | ms.ForgetResourceInstanceDeposed(addr.Resource, key) | ||
415 | s.maybePruneModule(addr.Module) | ||
416 | } | ||
417 | |||
418 | // MaybeRestoreResourceInstanceDeposed will restore the deposed object with the | ||
419 | // given key on the specified resource as the current object for that instance | ||
420 | // if and only if that would not cause us to forget an existing current | ||
421 | // object for that instance. | ||
422 | // | ||
423 | // Returns true if the object was restored to current, or false if no change | ||
424 | // was made at all. | ||
425 | func (s *SyncState) MaybeRestoreResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) bool { | ||
426 | s.lock.Lock() | ||
427 | defer s.lock.Unlock() | ||
428 | |||
429 | if key == NotDeposed { | ||
430 | panic("MaybeRestoreResourceInstanceDeposed called without DeposedKey") | ||
431 | } | ||
432 | |||
433 | ms := s.state.Module(addr.Module) | ||
434 | if ms == nil { | ||
435 | // Nothing to do, since the specified deposed object cannot exist. | ||
436 | return false | ||
437 | } | ||
438 | |||
439 | return ms.maybeRestoreResourceInstanceDeposed(addr.Resource, key) | ||
440 | } | ||
441 | |||
442 | // RemovePlannedResourceInstanceObjects removes from the state any resource | ||
443 | // instance objects that have the status ObjectPlanned, indiciating that they | ||
444 | // are just transient placeholders created during planning. | ||
445 | // | ||
446 | // Note that this does not restore any "ready" or "tainted" object that might | ||
447 | // have been present before the planned object was written. The only real use | ||
448 | // for this method is in preparing the state created during a refresh walk, | ||
449 | // where we run the planning step for certain instances just to create enough | ||
450 | // information to allow correct expression evaluation within provider and | ||
451 | // data resource blocks. Discarding planned instances in that case is okay | ||
452 | // because the refresh phase only creates planned objects to stand in for | ||
453 | // objects that don't exist yet, and thus the planned object must have been | ||
454 | // absent before by definition. | ||
455 | func (s *SyncState) RemovePlannedResourceInstanceObjects() { | ||
456 | // TODO: Merge together the refresh and plan phases into a single walk, | ||
457 | // so we can remove the need to create this "partial plan" during refresh | ||
458 | // that we then need to clean up before proceeding. | ||
459 | |||
460 | s.lock.Lock() | ||
461 | defer s.lock.Unlock() | ||
462 | |||
463 | for _, ms := range s.state.Modules { | ||
464 | moduleAddr := ms.Addr | ||
465 | |||
466 | for _, rs := range ms.Resources { | ||
467 | resAddr := rs.Addr | ||
468 | |||
469 | for ik, is := range rs.Instances { | ||
470 | instAddr := resAddr.Instance(ik) | ||
471 | |||
472 | if is.Current != nil && is.Current.Status == ObjectPlanned { | ||
473 | // Setting the current instance to nil removes it from the | ||
474 | // state altogether if there are not also deposed instances. | ||
475 | ms.SetResourceInstanceCurrent(instAddr, nil, rs.ProviderConfig) | ||
476 | } | ||
477 | |||
478 | for dk, obj := range is.Deposed { | ||
479 | // Deposed objects should never be "planned", but we'll | ||
480 | // do this anyway for the sake of completeness. | ||
481 | if obj.Status == ObjectPlanned { | ||
482 | ms.ForgetResourceInstanceDeposed(instAddr, dk) | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | |||
488 | // We may have deleted some objects, which means that we may have | ||
489 | // left a module empty, and so we must prune to preserve the invariant | ||
490 | // that only the root module is allowed to be empty. | ||
491 | s.maybePruneModule(moduleAddr) | ||
492 | } | ||
493 | } | ||
494 | |||
495 | // Lock acquires an explicit lock on the state, allowing direct read and write | ||
496 | // access to the returned state object. The caller must call Unlock once | ||
497 | // access is no longer needed, and then immediately discard the state pointer | ||
498 | // pointer. | ||
499 | // | ||
500 | // Most callers should not use this. Instead, use the concurrency-safe | ||
501 | // accessors and mutators provided directly on SyncState. | ||
502 | func (s *SyncState) Lock() *State { | ||
503 | s.lock.Lock() | ||
504 | return s.state | ||
505 | } | ||
506 | |||
507 | // Unlock releases a lock previously acquired by Lock, at which point the | ||
508 | // caller must cease all use of the state pointer that was returned. | ||
509 | // | ||
510 | // Do not call this method except to end an explicit lock acquired by | ||
511 | // Lock. If a caller calls Unlock without first holding the lock, behavior | ||
512 | // is undefined. | ||
513 | func (s *SyncState) Unlock() { | ||
514 | s.lock.Unlock() | ||
515 | } | ||
516 | |||
517 | // maybePruneModule will remove a module from the state altogether if it is | ||
518 | // empty, unless it's the root module which must always be present. | ||
519 | // | ||
520 | // This helper method is not concurrency-safe on its own, so must only be | ||
521 | // called while the caller is already holding the lock for writing. | ||
522 | func (s *SyncState) maybePruneModule(addr addrs.ModuleInstance) { | ||
523 | if addr.IsRoot() { | ||
524 | // We never prune the root. | ||
525 | return | ||
526 | } | ||
527 | |||
528 | ms := s.state.Module(addr) | ||
529 | if ms == nil { | ||
530 | return | ||
531 | } | ||
532 | |||
533 | if ms.empty() { | ||
534 | log.Printf("[TRACE] states.SyncState: pruning %s because it is empty", addr) | ||
535 | s.state.RemoveModule(addr) | ||
536 | } | ||
537 | } | ||