github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/states/sync.go (about)

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