github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/sync.go (about)

     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, simultaneously
   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  }