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() {}