1package states
2
3import (
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.
31type 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.
44func (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.
55func (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.
67func (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.
79func (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.
92func (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.
106func (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.
119func (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.
132func (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.
149func (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.
161func (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.
174func (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.
188func (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.)
201func (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.
216func (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.
249func (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.
305func (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.
337func (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.
357func (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.
373func (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.
392func (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.
406func (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.
425func (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.
455func (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.
502func (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.
513func (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.
522func (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}
538