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