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

     1  package states
     2  
     3  import (
     4  	"github.com/zclconf/go-cty/cty"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/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, simulataneously
    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  	ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
    92  
    93  	rs := ms.Resource(addr.Resource)
    94  	is := rs.EnsureInstance(addr.Key)
    95  
    96  	is.Current = obj
    97  
    98  	if !is.HasObjects() {
    99  		// If we have no objects at all then we'll clean up.
   100  		delete(rs.Instances, addr.Key)
   101  	}
   102  	if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   103  		// Also clean up if we only expect to have one instance anyway
   104  		// and there are none. We leave the resource behind if an each mode
   105  		// is active because an empty list or map of instances is a valid state.
   106  		delete(ms.Resources, addr.Resource.String())
   107  	}
   108  }
   109  
   110  // SetResourceInstanceDeposed saves the given instance object as a deposed
   111  // generation of the resource instance with the given address and deposed key.
   112  //
   113  // Call this method only for pre-existing deposed objects that already have
   114  // a known DeposedKey. For example, this method is useful if reloading objects
   115  // that were persisted to a state file. To mark the current object as deposed,
   116  // use DeposeResourceInstanceObject instead.
   117  //
   118  // The resource that contains the given instance must already exist in the
   119  // state, or this method will panic. Use Resource to check first if its
   120  // presence is not already guaranteed.
   121  //
   122  // Any existing current instance object for the given resource and deposed key
   123  // is overwritten. Set obj to nil to remove the deposed object altogether. If
   124  // the instance is left with no objects after this operation then it will
   125  // be removed from its containing resource altogether.
   126  func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
   127  	ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
   128  
   129  	rs := ms.Resource(addr.Resource)
   130  	is := rs.EnsureInstance(addr.Key)
   131  	if obj != nil {
   132  		is.Deposed[key] = obj
   133  	} else {
   134  		delete(is.Deposed, key)
   135  	}
   136  
   137  	if !is.HasObjects() {
   138  		// If we have no objects at all then we'll clean up.
   139  		delete(rs.Instances, addr.Key)
   140  	}
   141  	if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   142  		// Also clean up if we only expect to have one instance anyway
   143  		// and there are none. We leave the resource behind if an each mode
   144  		// is active because an empty list or map of instances is a valid state.
   145  		delete(ms.Resources, addr.Resource.String())
   146  	}
   147  }
   148  
   149  // ForgetResourceInstanceDeposed removes the record of the deposed object with
   150  // the given address and key, if present. If not present, this is a no-op.
   151  func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) {
   152  	rs := ms.Resource(addr.Resource)
   153  	if rs == nil {
   154  		return
   155  	}
   156  	is := rs.Instance(addr.Key)
   157  	if is == nil {
   158  		return
   159  	}
   160  	delete(is.Deposed, key)
   161  
   162  	if !is.HasObjects() {
   163  		// If we have no objects at all then we'll clean up.
   164  		delete(rs.Instances, addr.Key)
   165  	}
   166  	if rs.EachMode == NoEach && len(rs.Instances) == 0 {
   167  		// Also clean up if we only expect to have one instance anyway
   168  		// and there are none. We leave the resource behind if an each mode
   169  		// is active because an empty list or map of instances is a valid state.
   170  		delete(ms.Resources, addr.Resource.String())
   171  	}
   172  }
   173  
   174  // deposeResourceInstanceObject is the real implementation of
   175  // SyncState.DeposeResourceInstanceObject.
   176  func (ms *Module) deposeResourceInstanceObject(addr addrs.ResourceInstance, forceKey DeposedKey) DeposedKey {
   177  	is := ms.ResourceInstance(addr)
   178  	if is == nil {
   179  		return NotDeposed
   180  	}
   181  	return is.deposeCurrentObject(forceKey)
   182  }
   183  
   184  // maybeRestoreResourceInstanceDeposed is the real implementation of
   185  // SyncState.MaybeRestoreResourceInstanceDeposed.
   186  func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) bool {
   187  	rs := ms.Resource(addr.Resource)
   188  	if rs == nil {
   189  		return false
   190  	}
   191  	is := rs.Instance(addr.Key)
   192  	if is == nil {
   193  		return false
   194  	}
   195  	if is.Current != nil {
   196  		return false
   197  	}
   198  	if len(is.Deposed) == 0 {
   199  		return false
   200  	}
   201  	is.Current = is.Deposed[key]
   202  	delete(is.Deposed, key)
   203  	return true
   204  }
   205  
   206  // SetOutputValue writes an output value into the state, overwriting any
   207  // existing value of the same name.
   208  func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue {
   209  	os := &OutputValue{
   210  		Value:     value,
   211  		Sensitive: sensitive,
   212  	}
   213  	ms.OutputValues[name] = os
   214  	return os
   215  }
   216  
   217  // RemoveOutputValue removes the output value of the given name from the state,
   218  // if it exists. This method is a no-op if there is no value of the given
   219  // name.
   220  func (ms *Module) RemoveOutputValue(name string) {
   221  	delete(ms.OutputValues, name)
   222  }
   223  
   224  // SetLocalValue writes a local value into the state, overwriting any
   225  // existing value of the same name.
   226  func (ms *Module) SetLocalValue(name string, value cty.Value) {
   227  	ms.LocalValues[name] = value
   228  }
   229  
   230  // RemoveLocalValue removes the local value of the given name from the state,
   231  // if it exists. This method is a no-op if there is no value of the given
   232  // name.
   233  func (ms *Module) RemoveLocalValue(name string) {
   234  	delete(ms.LocalValues, name)
   235  }
   236  
   237  // PruneResourceHusks is a specialized method that will remove any Resource
   238  // objects that do not contain any instances, even if they have an EachMode.
   239  //
   240  // You probably shouldn't call this! See the method of the same name on
   241  // type State for more information on what this is for and the rare situations
   242  // where it is safe to use.
   243  func (ms *Module) PruneResourceHusks() {
   244  	for _, rs := range ms.Resources {
   245  		if len(rs.Instances) == 0 {
   246  			ms.RemoveResource(rs.Addr)
   247  		}
   248  	}
   249  }
   250  
   251  // empty returns true if the receving module state is contributing nothing
   252  // to the state. In other words, it returns true if the module could be
   253  // removed from the state altogether without changing the meaning of the state.
   254  //
   255  // In practice a module containing no objects is the same as a non-existent
   256  // module, and so we can opportunistically clean up once a module becomes
   257  // empty on the assumption that it will be re-added if needed later.
   258  func (ms *Module) empty() bool {
   259  	if ms == nil {
   260  		return true
   261  	}
   262  
   263  	// This must be updated to cover any new collections added to Module
   264  	// in future.
   265  	return (len(ms.Resources) == 0 &&
   266  		len(ms.OutputValues) == 0 &&
   267  		len(ms.LocalValues) == 0)
   268  }