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

     1  package states
     2  
     3  import (
     4  	"github.com/zclconf/go-cty/cty"
     5  
     6  	"github.com/hashicorp/terraform/addrs"
     7  )
     8  
     9  // Module is a container for the states of objects within a particular module.
    10  type Module struct {
    11  	Addr addrs.ModuleInstance
    12  
    13  	// Resources contains the state for each resource. The keys in this map are
    14  	// an implementation detail and must not be used by outside callers.
    15  	Resources map[string]*Resource
    16  
    17  	// OutputValues contains the state for each output value. The keys in this
    18  	// map are output value names.
    19  	OutputValues map[string]*OutputValue
    20  
    21  	// LocalValues contains the value for each named output value. The keys
    22  	// in this map are local value names.
    23  	LocalValues map[string]cty.Value
    24  }
    25  
    26  // NewModule constructs an empty module state for the given module address.
    27  func NewModule(addr addrs.ModuleInstance) *Module {
    28  	return &Module{
    29  		Addr:         addr,
    30  		Resources:    map[string]*Resource{},
    31  		OutputValues: map[string]*OutputValue{},
    32  		LocalValues:  map[string]cty.Value{},
    33  	}
    34  }
    35  
    36  // Resource returns the state for the resource with the given address within
    37  // the receiving module state, or nil if the requested resource is not tracked
    38  // in the state.
    39  func (ms *Module) Resource(addr addrs.Resource) *Resource {
    40  	return ms.Resources[addr.String()]
    41  }
    42  
    43  // ResourceInstance returns the state for the resource instance with the given
    44  // address within the receiving module state, or nil if the requested instance
    45  // is not tracked in the state.
    46  func (ms *Module) ResourceInstance(addr addrs.ResourceInstance) *ResourceInstance {
    47  	rs := ms.Resource(addr.Resource)
    48  	if rs == nil {
    49  		return nil
    50  	}
    51  	return rs.Instance(addr.Key)
    52  }
    53  
    54  // SetResourceMeta updates the resource-level metadata for the resource
    55  // with the given address, creating the resource state for it if it doesn't
    56  // already exist.
    57  func (ms *Module) SetResourceMeta(addr addrs.Resource, eachMode EachMode, provider addrs.AbsProviderConfig) {
    58  	rs := ms.Resource(addr)
    59  	if rs == nil {
    60  		rs = &Resource{
    61  			Addr:      addr,
    62  			Instances: map[addrs.InstanceKey]*ResourceInstance{},
    63  		}
    64  		ms.Resources[addr.String()] = rs
    65  	}
    66  
    67  	rs.EachMode = eachMode
    68  	rs.ProviderConfig = provider
    69  }
    70  
    71  // RemoveResource removes the entire state for the given resource, taking with
    72  // it any instances associated with the resource. This should generally be
    73  // called only for resource objects whose instances have all been destroyed.
    74  func (ms *Module) RemoveResource(addr addrs.Resource) {
    75  	delete(ms.Resources, addr.String())
    76  }
    77  
    78  // SetResourceInstanceCurrent saves the given instance object as the current
    79  // generation of the resource instance with the given address, simultaneously
    80  // updating the recorded provider configuration address, dependencies, and
    81  // resource EachMode.
    82  //
    83  // Any existing current instance object for the given resource is overwritten.
    84  // Set obj to nil to remove the primary generation object altogether. If there
    85  // are no deposed objects then the instance will be removed altogether.
    86  //
    87  // The provider address and "each mode" are resource-wide settings and so they
    88  // are updated for all other instances of the same resource as a side-effect of
    89  // this call.
    90  func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
    91  	rs := ms.Resource(addr.Resource)
    92  	// if the resource is nil and the object is nil, don't do anything!
    93  	// you'll probably just cause issues
    94  	if obj == nil && rs == nil {
    95  		return
    96  	}
    97  	if obj == nil && rs != nil {
    98  		// does the resource have any other objects?
    99  		// if not then delete the whole resource
   100  		// When deleting the resource, ensure that its EachMode is NoEach,
   101  		// as a resource with EachList or EachMap can have 0 instances and be valid
   102  		if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   103  			delete(ms.Resources, addr.Resource.String())
   104  			return
   105  		}
   106  		// check for an existing resource, now that we've ensured that rs.Instances is more than 0/not nil
   107  		is := rs.Instance(addr.Key)
   108  		if is == nil {
   109  			// if there is no instance on the resource with this address and obj is nil, return and change nothing
   110  			return
   111  		}
   112  		// if we have an instance, update the current
   113  		is.Current = obj
   114  		if !is.HasObjects() {
   115  			// If we have no objects at all then we'll clean up.
   116  			delete(rs.Instances, addr.Key)
   117  			// Delete the resource if it has no instances, but only if NoEach
   118  			if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   119  				delete(ms.Resources, addr.Resource.String())
   120  				return
   121  			}
   122  		}
   123  		// Nothing more to do here, so return!
   124  		return
   125  	}
   126  	if rs == nil && obj != nil {
   127  		// We don't have have a resource so make one, which is a side effect of setResourceMeta
   128  		ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
   129  		// now we have a resource! so update the rs value to point to it
   130  		rs = ms.Resource(addr.Resource)
   131  	}
   132  	// Get our instance from the resource; it could be there or not at this point
   133  	is := rs.Instance(addr.Key)
   134  	if is == nil {
   135  		// if we don't have a resource, create one and add to the instances
   136  		is = rs.CreateInstance(addr.Key)
   137  		// update the resource meta because we have a new instance, so EachMode may have changed
   138  		ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
   139  	}
   140  	// Update the resource's ProviderConfig, in case the provider has updated
   141  	rs.ProviderConfig = provider
   142  	is.Current = obj
   143  }
   144  
   145  // SetResourceInstanceDeposed saves the given instance object as a deposed
   146  // generation of the resource instance with the given address and deposed key.
   147  //
   148  // Call this method only for pre-existing deposed objects that already have
   149  // a known DeposedKey. For example, this method is useful if reloading objects
   150  // that were persisted to a state file. To mark the current object as deposed,
   151  // use DeposeResourceInstanceObject instead.
   152  //
   153  // The resource that contains the given instance must already exist in the
   154  // state, or this method will panic. Use Resource to check first if its
   155  // presence is not already guaranteed.
   156  //
   157  // Any existing current instance object for the given resource and deposed key
   158  // is overwritten. Set obj to nil to remove the deposed object altogether. If
   159  // the instance is left with no objects after this operation then it will
   160  // be removed from its containing resource altogether.
   161  func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
   162  	ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
   163  
   164  	rs := ms.Resource(addr.Resource)
   165  	is := rs.EnsureInstance(addr.Key)
   166  	if obj != nil {
   167  		is.Deposed[key] = obj
   168  	} else {
   169  		delete(is.Deposed, key)
   170  	}
   171  
   172  	if !is.HasObjects() {
   173  		// If we have no objects at all then we'll clean up.
   174  		delete(rs.Instances, addr.Key)
   175  	}
   176  	if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   177  		// Also clean up if we only expect to have one instance anyway
   178  		// and there are none. We leave the resource behind if an each mode
   179  		// is active because an empty list or map of instances is a valid state.
   180  		delete(ms.Resources, addr.Resource.String())
   181  	}
   182  }
   183  
   184  // ForgetResourceInstanceAll removes the record of all objects associated with
   185  // the specified resource instance, if present. If not present, this is a no-op.
   186  func (ms *Module) ForgetResourceInstanceAll(addr addrs.ResourceInstance) {
   187  	rs := ms.Resource(addr.Resource)
   188  	if rs == nil {
   189  		return
   190  	}
   191  	delete(rs.Instances, addr.Key)
   192  
   193  	if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   194  		// Also clean up if we only expect to have one instance anyway
   195  		// and there are none. We leave the resource behind if an each mode
   196  		// is active because an empty list or map of instances is a valid state.
   197  		delete(ms.Resources, addr.Resource.String())
   198  	}
   199  }
   200  
   201  // ForgetResourceInstanceDeposed removes the record of the deposed object with
   202  // the given address and key, if present. If not present, this is a no-op.
   203  func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) {
   204  	rs := ms.Resource(addr.Resource)
   205  	if rs == nil {
   206  		return
   207  	}
   208  	is := rs.Instance(addr.Key)
   209  	if is == nil {
   210  		return
   211  	}
   212  	delete(is.Deposed, key)
   213  
   214  	if !is.HasObjects() {
   215  		// If we have no objects at all then we'll clean up.
   216  		delete(rs.Instances, addr.Key)
   217  	}
   218  	if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   219  		// Also clean up if we only expect to have one instance anyway
   220  		// and there are none. We leave the resource behind if an each mode
   221  		// is active because an empty list or map of instances is a valid state.
   222  		delete(ms.Resources, addr.Resource.String())
   223  	}
   224  }
   225  
   226  // deposeResourceInstanceObject is the real implementation of
   227  // SyncState.DeposeResourceInstanceObject.
   228  func (ms *Module) deposeResourceInstanceObject(addr addrs.ResourceInstance, forceKey DeposedKey) DeposedKey {
   229  	is := ms.ResourceInstance(addr)
   230  	if is == nil {
   231  		return NotDeposed
   232  	}
   233  	return is.deposeCurrentObject(forceKey)
   234  }
   235  
   236  // maybeRestoreResourceInstanceDeposed is the real implementation of
   237  // SyncState.MaybeRestoreResourceInstanceDeposed.
   238  func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) bool {
   239  	rs := ms.Resource(addr.Resource)
   240  	if rs == nil {
   241  		return false
   242  	}
   243  	is := rs.Instance(addr.Key)
   244  	if is == nil {
   245  		return false
   246  	}
   247  	if is.Current != nil {
   248  		return false
   249  	}
   250  	if len(is.Deposed) == 0 {
   251  		return false
   252  	}
   253  	is.Current = is.Deposed[key]
   254  	delete(is.Deposed, key)
   255  	return true
   256  }
   257  
   258  // SetOutputValue writes an output value into the state, overwriting any
   259  // existing value of the same name.
   260  func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue {
   261  	os := &OutputValue{
   262  		Value:     value,
   263  		Sensitive: sensitive,
   264  	}
   265  	ms.OutputValues[name] = os
   266  	return os
   267  }
   268  
   269  // RemoveOutputValue removes the output value of the given name from the state,
   270  // if it exists. This method is a no-op if there is no value of the given
   271  // name.
   272  func (ms *Module) RemoveOutputValue(name string) {
   273  	delete(ms.OutputValues, name)
   274  }
   275  
   276  // SetLocalValue writes a local value into the state, overwriting any
   277  // existing value of the same name.
   278  func (ms *Module) SetLocalValue(name string, value cty.Value) {
   279  	ms.LocalValues[name] = value
   280  }
   281  
   282  // RemoveLocalValue removes the local value of the given name from the state,
   283  // if it exists. This method is a no-op if there is no value of the given
   284  // name.
   285  func (ms *Module) RemoveLocalValue(name string) {
   286  	delete(ms.LocalValues, name)
   287  }
   288  
   289  // PruneResourceHusks is a specialized method that will remove any Resource
   290  // objects that do not contain any instances, even if they have an EachMode.
   291  //
   292  // You probably shouldn't call this! See the method of the same name on
   293  // type State for more information on what this is for and the rare situations
   294  // where it is safe to use.
   295  func (ms *Module) PruneResourceHusks() {
   296  	for _, rs := range ms.Resources {
   297  		if len(rs.Instances) == 0 {
   298  			ms.RemoveResource(rs.Addr)
   299  		}
   300  	}
   301  }
   302  
   303  // empty returns true if the receving module state is contributing nothing
   304  // to the state. In other words, it returns true if the module could be
   305  // removed from the state altogether without changing the meaning of the state.
   306  //
   307  // In practice a module containing no objects is the same as a non-existent
   308  // module, and so we can opportunistically clean up once a module becomes
   309  // empty on the assumption that it will be re-added if needed later.
   310  func (ms *Module) empty() bool {
   311  	if ms == nil {
   312  		return true
   313  	}
   314  
   315  	// This must be updated to cover any new collections added to Module
   316  	// in future.
   317  	return (len(ms.Resources) == 0 &&
   318  		len(ms.OutputValues) == 0 &&
   319  		len(ms.LocalValues) == 0)
   320  }