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

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