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