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

     1  package states
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"time"
     7  
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     9  )
    10  
    11  // Resource represents the state of a resource.
    12  type Resource struct {
    13  	// Addr is the module-relative address for the resource this state object
    14  	// belongs to.
    15  	Addr addrs.Resource
    16  
    17  	// EachMode is the multi-instance mode currently in use for this resource,
    18  	// or NoEach if this is a single-instance resource. This dictates what
    19  	// type of value is returned when accessing this resource via expressions
    20  	// in the Terraform language.
    21  	EachMode EachMode
    22  
    23  	// Instances contains the potentially-multiple instances associated with
    24  	// this resource. This map can contain a mixture of different key types,
    25  	// but only the ones of InstanceKeyType are considered current.
    26  	Instances map[addrs.InstanceKey]*ResourceInstance
    27  
    28  	// ProviderConfig is the absolute address for the provider configuration that
    29  	// most recently managed this resource. This is used to connect a resource
    30  	// with a provider configuration when the resource configuration block is
    31  	// not available, such as if it has been removed from configuration
    32  	// altogether.
    33  	ProviderConfig addrs.AbsProviderConfig
    34  }
    35  
    36  // Instance returns the state for the instance with the given key, or nil
    37  // if no such instance is tracked within the state.
    38  func (rs *Resource) Instance(key addrs.InstanceKey) *ResourceInstance {
    39  	return rs.Instances[key]
    40  }
    41  
    42  // EnsureInstance returns the state for the instance with the given key,
    43  // creating a new empty state for it if one doesn't already exist.
    44  //
    45  // Because this may create and save a new state, it is considered to be
    46  // a write operation.
    47  func (rs *Resource) EnsureInstance(key addrs.InstanceKey) *ResourceInstance {
    48  	ret := rs.Instance(key)
    49  	if ret == nil {
    50  		ret = NewResourceInstance()
    51  		rs.Instances[key] = ret
    52  	}
    53  	return ret
    54  }
    55  
    56  // ResourceInstance represents the state of a particular instance of a resource.
    57  type ResourceInstance struct {
    58  	// Current, if non-nil, is the remote object that is currently represented
    59  	// by the corresponding resource instance.
    60  	Current *ResourceInstanceObjectSrc
    61  
    62  	// Deposed, if len > 0, contains any remote objects that were previously
    63  	// represented by the corresponding resource instance but have been
    64  	// replaced and are pending destruction due to the create_before_destroy
    65  	// lifecycle mode.
    66  	Deposed map[DeposedKey]*ResourceInstanceObjectSrc
    67  }
    68  
    69  // NewResourceInstance constructs and returns a new ResourceInstance, ready to
    70  // use.
    71  func NewResourceInstance() *ResourceInstance {
    72  	return &ResourceInstance{
    73  		Deposed: map[DeposedKey]*ResourceInstanceObjectSrc{},
    74  	}
    75  }
    76  
    77  // HasCurrent returns true if this resource instance has a "current"-generation
    78  // object. Most instances do, but this can briefly be false during a
    79  // create-before-destroy replace operation when the current has been deposed
    80  // but its replacement has not yet been created.
    81  func (i *ResourceInstance) HasCurrent() bool {
    82  	return i != nil && i.Current != nil
    83  }
    84  
    85  // HasDeposed returns true if this resource instance has a deposed object
    86  // with the given key.
    87  func (i *ResourceInstance) HasDeposed(key DeposedKey) bool {
    88  	return i != nil && i.Deposed[key] != nil
    89  }
    90  
    91  // HasObjects returns true if this resource has any objects at all, whether
    92  // current or deposed.
    93  func (i *ResourceInstance) HasObjects() bool {
    94  	return i.Current != nil || len(i.Deposed) != 0
    95  }
    96  
    97  // deposeCurrentObject is part of the real implementation of
    98  // SyncState.DeposeResourceInstanceObject. The exported method uses a lock
    99  // to ensure that we can safely allocate an unused deposed key without
   100  // collision.
   101  func (i *ResourceInstance) deposeCurrentObject(forceKey DeposedKey) DeposedKey {
   102  	if !i.HasCurrent() {
   103  		return NotDeposed
   104  	}
   105  
   106  	key := forceKey
   107  	if key == NotDeposed {
   108  		key = i.findUnusedDeposedKey()
   109  	} else {
   110  		if _, exists := i.Deposed[key]; exists {
   111  			panic(fmt.Sprintf("forced key %s is already in use", forceKey))
   112  		}
   113  	}
   114  	i.Deposed[key] = i.Current
   115  	i.Current = nil
   116  	return key
   117  }
   118  
   119  // GetGeneration retrieves the object of the given generation from the
   120  // ResourceInstance, or returns nil if there is no such object.
   121  //
   122  // If the given generation is nil or invalid, this method will panic.
   123  func (i *ResourceInstance) GetGeneration(gen Generation) *ResourceInstanceObjectSrc {
   124  	if gen == CurrentGen {
   125  		return i.Current
   126  	}
   127  	if dk, ok := gen.(DeposedKey); ok {
   128  		return i.Deposed[dk]
   129  	}
   130  	if gen == nil {
   131  		panic(fmt.Sprintf("get with nil Generation"))
   132  	}
   133  	// Should never fall out here, since the above covers all possible
   134  	// Generation values.
   135  	panic(fmt.Sprintf("get invalid Generation %#v", gen))
   136  }
   137  
   138  // FindUnusedDeposedKey generates a unique DeposedKey that is guaranteed not to
   139  // already be in use for this instance at the time of the call.
   140  //
   141  // Note that the validity of this result may change if new deposed keys are
   142  // allocated before it is used. To avoid this risk, instead use the
   143  // DeposeResourceInstanceObject method on the SyncState wrapper type, which
   144  // allocates a key and uses it atomically.
   145  func (i *ResourceInstance) FindUnusedDeposedKey() DeposedKey {
   146  	return i.findUnusedDeposedKey()
   147  }
   148  
   149  // findUnusedDeposedKey generates a unique DeposedKey that is guaranteed not to
   150  // already be in use for this instance.
   151  func (i *ResourceInstance) findUnusedDeposedKey() DeposedKey {
   152  	for {
   153  		key := NewDeposedKey()
   154  		if _, exists := i.Deposed[key]; !exists {
   155  			return key
   156  		}
   157  		// Spin until we find a unique one. This shouldn't take long, because
   158  		// we have a 32-bit keyspace and there's rarely more than one deposed
   159  		// instance.
   160  	}
   161  }
   162  
   163  // EachMode specifies the multi-instance mode for a resource.
   164  type EachMode rune
   165  
   166  const (
   167  	NoEach   EachMode = 0
   168  	EachList EachMode = 'L'
   169  	EachMap  EachMode = 'M'
   170  )
   171  
   172  //go:generate go run golang.org/x/tools/cmd/stringer -type EachMode
   173  
   174  func eachModeForInstanceKey(key addrs.InstanceKey) EachMode {
   175  	switch key.(type) {
   176  	case addrs.IntKey:
   177  		return EachList
   178  	case addrs.StringKey:
   179  		return EachMap
   180  	default:
   181  		if key == addrs.NoKey {
   182  			return NoEach
   183  		}
   184  		panic(fmt.Sprintf("don't know an each mode for instance key %#v", key))
   185  	}
   186  }
   187  
   188  // DeposedKey is a 8-character hex string used to uniquely identify deposed
   189  // instance objects in the state.
   190  type DeposedKey string
   191  
   192  // NotDeposed is a special invalid value of DeposedKey that is used to represent
   193  // the absense of a deposed key. It must not be used as an actual deposed key.
   194  const NotDeposed = DeposedKey("")
   195  
   196  var deposedKeyRand = rand.New(rand.NewSource(time.Now().UnixNano()))
   197  
   198  // NewDeposedKey generates a pseudo-random deposed key. Because of the short
   199  // length of these keys, uniqueness is not a natural consequence and so the
   200  // caller should test to see if the generated key is already in use and generate
   201  // another if so, until a unique key is found.
   202  func NewDeposedKey() DeposedKey {
   203  	v := deposedKeyRand.Uint32()
   204  	return DeposedKey(fmt.Sprintf("%08x", v))
   205  }
   206  
   207  func (k DeposedKey) String() string {
   208  	return string(k)
   209  }
   210  
   211  func (k DeposedKey) GoString() string {
   212  	ks := string(k)
   213  	switch {
   214  	case ks == "":
   215  		return "states.NotDeposed"
   216  	default:
   217  		return fmt.Sprintf("states.DeposedKey(%s)", ks)
   218  	}
   219  }
   220  
   221  // Generation is a helper method to convert a DeposedKey into a Generation.
   222  // If the reciever is anything other than NotDeposed then the result is
   223  // just the same value as a Generation. If the receiver is NotDeposed then
   224  // the result is CurrentGen.
   225  func (k DeposedKey) Generation() Generation {
   226  	if k == NotDeposed {
   227  		return CurrentGen
   228  	}
   229  	return k
   230  }
   231  
   232  // generation is an implementation of Generation.
   233  func (k DeposedKey) generation() {}