github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/states/sync.go (about)

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