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

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