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