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

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