github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/sync.go (about) 1 package states 2 3 import ( 4 "log" 5 "sync" 6 7 "github.com/hashicorp/terraform/addrs" 8 "github.com/zclconf/go-cty/cty" 9 ) 10 11 // SyncState is a wrapper around State that provides concurrency-safe access to 12 // various common operations that occur during a Terraform graph walk, or other 13 // similar concurrent contexts. 14 // 15 // When a SyncState wrapper is in use, no concurrent direct access to the 16 // underlying objects is permitted unless the caller first acquires an explicit 17 // lock, using the Lock and Unlock methods. Most callers should _not_ 18 // explicitly lock, and should instead use the other methods of this type that 19 // handle locking automatically. 20 // 21 // Since SyncState is able to safely consolidate multiple updates into a single 22 // atomic operation, many of its methods are at a higher level than those 23 // of the underlying types, and operate on the state as a whole rather than 24 // on individual sub-structures of the state. 25 // 26 // SyncState can only protect against races within its own methods. It cannot 27 // provide any guarantees about the order in which concurrent operations will 28 // be processed, so callers may still need to employ higher-level techniques 29 // for ensuring correct operation sequencing, such as building and walking 30 // a dependency graph. 31 type SyncState struct { 32 state *State 33 lock sync.RWMutex 34 } 35 36 // Module returns a snapshot of the state of the module instance with the given 37 // address, or nil if no such module is tracked. 38 // 39 // The return value is a pointer to a copy of the module state, which the 40 // caller may then freely access and mutate. However, since the module state 41 // tends to be a large data structure with many child objects, where possible 42 // callers should prefer to use a more granular accessor to access a child 43 // module directly, and thus reduce the amount of copying required. 44 func (s *SyncState) Module(addr addrs.ModuleInstance) *Module { 45 s.lock.RLock() 46 ret := s.state.Module(addr).DeepCopy() 47 s.lock.RUnlock() 48 return ret 49 } 50 51 // RemoveModule removes the entire state for the given module, taking with 52 // it any resources associated with the module. This should generally be 53 // called only for modules whose resources have all been destroyed, but 54 // that is not enforced by this method. 55 func (s *SyncState) RemoveModule(addr addrs.ModuleInstance) { 56 s.lock.Lock() 57 defer s.lock.Unlock() 58 59 s.state.RemoveModule(addr) 60 } 61 62 // OutputValue returns a snapshot of the state of the output value with the 63 // given address, or nil if no such output value is tracked. 64 // 65 // The return value is a pointer to a copy of the output value state, which the 66 // caller may then freely access and mutate. 67 func (s *SyncState) OutputValue(addr addrs.AbsOutputValue) *OutputValue { 68 s.lock.RLock() 69 ret := s.state.OutputValue(addr).DeepCopy() 70 s.lock.RUnlock() 71 return ret 72 } 73 74 // SetOutputValue writes a given output value into the state, overwriting 75 // any existing value of the same name. 76 // 77 // If the module containing the output is not yet tracked in state then it 78 // be added as a side-effect. 79 func (s *SyncState) SetOutputValue(addr addrs.AbsOutputValue, value cty.Value, sensitive bool) { 80 s.lock.Lock() 81 defer s.lock.Unlock() 82 83 ms := s.state.EnsureModule(addr.Module) 84 ms.SetOutputValue(addr.OutputValue.Name, value, sensitive) 85 } 86 87 // RemoveOutputValue removes the stored value for the output value with the 88 // given address. 89 // 90 // If this results in its containing module being empty, the module will be 91 // pruned from the state as a side-effect. 92 func (s *SyncState) RemoveOutputValue(addr addrs.AbsOutputValue) { 93 s.lock.Lock() 94 defer s.lock.Unlock() 95 96 ms := s.state.Module(addr.Module) 97 if ms == nil { 98 return 99 } 100 ms.RemoveOutputValue(addr.OutputValue.Name) 101 s.maybePruneModule(addr.Module) 102 } 103 104 // LocalValue returns the current value associated with the given local value 105 // address. 106 func (s *SyncState) LocalValue(addr addrs.AbsLocalValue) cty.Value { 107 s.lock.RLock() 108 // cty.Value is immutable, so we don't need any extra copying here. 109 ret := s.state.LocalValue(addr) 110 s.lock.RUnlock() 111 return ret 112 } 113 114 // SetLocalValue writes a given output value into the state, overwriting 115 // any existing value of the same name. 116 // 117 // If the module containing the local value is not yet tracked in state then it 118 // will be added as a side-effect. 119 func (s *SyncState) SetLocalValue(addr addrs.AbsLocalValue, value cty.Value) { 120 s.lock.Lock() 121 defer s.lock.Unlock() 122 123 ms := s.state.EnsureModule(addr.Module) 124 ms.SetLocalValue(addr.LocalValue.Name, value) 125 } 126 127 // RemoveLocalValue removes the stored value for the local value with the 128 // given address. 129 // 130 // If this results in its containing module being empty, the module will be 131 // pruned from the state as a side-effect. 132 func (s *SyncState) RemoveLocalValue(addr addrs.AbsLocalValue) { 133 s.lock.Lock() 134 defer s.lock.Unlock() 135 136 ms := s.state.Module(addr.Module) 137 if ms == nil { 138 return 139 } 140 ms.RemoveLocalValue(addr.LocalValue.Name) 141 s.maybePruneModule(addr.Module) 142 } 143 144 // Resource returns a snapshot of the state of the resource with the given 145 // address, or nil if no such resource is tracked. 146 // 147 // The return value is a pointer to a copy of the resource state, which the 148 // caller may then freely access and mutate. 149 func (s *SyncState) Resource(addr addrs.AbsResource) *Resource { 150 s.lock.RLock() 151 ret := s.state.Resource(addr).DeepCopy() 152 s.lock.RUnlock() 153 return ret 154 } 155 156 // ResourceInstance returns a snapshot of the state the resource instance with 157 // the given address, or nil if no such instance is tracked. 158 // 159 // The return value is a pointer to a copy of the instance state, which the 160 // caller may then freely access and mutate. 161 func (s *SyncState) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance { 162 s.lock.RLock() 163 ret := s.state.ResourceInstance(addr).DeepCopy() 164 s.lock.RUnlock() 165 return ret 166 } 167 168 // ResourceInstanceObject returns a snapshot of the current instance object 169 // of the given generation belonging to the instance with the given address, 170 // or nil if no such object is tracked.. 171 // 172 // The return value is a pointer to a copy of the object, which the caller may 173 // then freely access and mutate. 174 func (s *SyncState) ResourceInstanceObject(addr addrs.AbsResourceInstance, gen Generation) *ResourceInstanceObjectSrc { 175 s.lock.RLock() 176 defer s.lock.RUnlock() 177 178 inst := s.state.ResourceInstance(addr) 179 if inst == nil { 180 return nil 181 } 182 return inst.GetGeneration(gen).DeepCopy() 183 } 184 185 // SetResourceMeta updates the resource-level metadata for the resource at 186 // the given address, creating the containing module state and resource state 187 // as a side-effect if not already present. 188 func (s *SyncState) SetResourceMeta(addr addrs.AbsResource, eachMode EachMode, provider addrs.AbsProviderConfig) { 189 s.lock.Lock() 190 defer s.lock.Unlock() 191 192 ms := s.state.EnsureModule(addr.Module) 193 ms.SetResourceMeta(addr.Resource, eachMode, provider) 194 } 195 196 // RemoveResource removes the entire state for the given resource, taking with 197 // it any instances associated with the resource. This should generally be 198 // called only for resource objects whose instances have all been destroyed, 199 // but that is not enforced by this method. (Use RemoveResourceIfEmpty instead 200 // to safely check first.) 201 func (s *SyncState) RemoveResource(addr addrs.AbsResource) { 202 s.lock.Lock() 203 defer s.lock.Unlock() 204 205 ms := s.state.EnsureModule(addr.Module) 206 ms.RemoveResource(addr.Resource) 207 s.maybePruneModule(addr.Module) 208 } 209 210 // RemoveResourceIfEmpty is similar to RemoveResource but first checks to 211 // make sure there are no instances or objects left in the resource. 212 // 213 // Returns true if the resource was removed, or false if remaining child 214 // objects prevented its removal. Returns true also if the resource was 215 // already absent, and thus no action needed to be taken. 216 func (s *SyncState) RemoveResourceIfEmpty(addr addrs.AbsResource) bool { 217 s.lock.Lock() 218 defer s.lock.Unlock() 219 220 ms := s.state.Module(addr.Module) 221 if ms == nil { 222 return true // nothing to do 223 } 224 rs := ms.Resource(addr.Resource) 225 if rs == nil { 226 return true // nothing to do 227 } 228 if len(rs.Instances) != 0 { 229 // We don't check here for the possibility of instances that exist 230 // but don't have any objects because it's the responsibility of the 231 // instance-mutation methods to prune those away automatically. 232 return false 233 } 234 ms.RemoveResource(addr.Resource) 235 s.maybePruneModule(addr.Module) 236 return true 237 } 238 239 // MaybeFixUpResourceInstanceAddressForCount deals with the situation where a 240 // resource has changed from having "count" set to not set, or vice-versa, and 241 // so we need to rename the zeroth instance key to no key at all, or vice-versa. 242 // 243 // Set countEnabled to true if the resource has count set in its new 244 // configuration, or false if it does not. 245 // 246 // The state is modified in-place if necessary, moving a resource instance 247 // between the two addresses. The return value is true if a change was made, 248 // and false otherwise. 249 func (s *SyncState) MaybeFixUpResourceInstanceAddressForCount(addr addrs.AbsResource, countEnabled bool) bool { 250 s.lock.Lock() 251 defer s.lock.Unlock() 252 253 ms := s.state.Module(addr.Module) 254 if ms == nil { 255 return false 256 } 257 258 relAddr := addr.Resource 259 rs := ms.Resource(relAddr) 260 if rs == nil { 261 return false 262 } 263 huntKey := addrs.NoKey 264 replaceKey := addrs.InstanceKey(addrs.IntKey(0)) 265 if !countEnabled { 266 huntKey, replaceKey = replaceKey, huntKey 267 } 268 269 is, exists := rs.Instances[huntKey] 270 if !exists { 271 return false 272 } 273 274 if _, exists := rs.Instances[replaceKey]; exists { 275 // If the replacement key also exists then we'll do nothing and keep both. 276 return false 277 } 278 279 // If we get here then we need to "rename" from hunt to replace 280 rs.Instances[replaceKey] = is 281 delete(rs.Instances, huntKey) 282 return true 283 } 284 285 // SetResourceInstanceCurrent saves the given instance object as the current 286 // generation of the resource instance with the given address, simultaneously 287 // updating the recorded provider configuration address, dependencies, and 288 // resource EachMode. 289 // 290 // Any existing current instance object for the given resource is overwritten. 291 // Set obj to nil to remove the primary generation object altogether. If there 292 // are no deposed objects then the instance as a whole will be removed, which 293 // may in turn also remove the containing module if it becomes empty. 294 // 295 // The caller must ensure that the given ResourceInstanceObject is not 296 // concurrently mutated during this call, but may be freely used again once 297 // this function returns. 298 // 299 // The provider address and "each mode" are resource-wide settings and so they 300 // are updated for all other instances of the same resource as a side-effect of 301 // this call. 302 // 303 // If the containing module for this resource or the resource itself are not 304 // already tracked in state then they will be added as a side-effect. 305 func (s *SyncState) SetResourceInstanceCurrent(addr addrs.AbsResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { 306 s.lock.Lock() 307 defer s.lock.Unlock() 308 309 ms := s.state.EnsureModule(addr.Module) 310 ms.SetResourceInstanceCurrent(addr.Resource, obj.DeepCopy(), provider) 311 s.maybePruneModule(addr.Module) 312 } 313 314 // SetResourceInstanceDeposed saves the given instance object as a deposed 315 // generation of the resource instance with the given address and deposed key. 316 // 317 // Call this method only for pre-existing deposed objects that already have 318 // a known DeposedKey. For example, this method is useful if reloading objects 319 // that were persisted to a state file. To mark the current object as deposed, 320 // use DeposeResourceInstanceObject instead. 321 // 322 // The caller must ensure that the given ResourceInstanceObject is not 323 // concurrently mutated during this call, but may be freely used again once 324 // this function returns. 325 // 326 // The resource that contains the given instance must already exist in the 327 // state, or this method will panic. Use Resource to check first if its 328 // presence is not already guaranteed. 329 // 330 // Any existing current instance object for the given resource and deposed key 331 // is overwritten. Set obj to nil to remove the deposed object altogether. If 332 // the instance is left with no objects after this operation then it will 333 // be removed from its containing resource altogether. 334 // 335 // If the containing module for this resource or the resource itself are not 336 // already tracked in state then they will be added as a side-effect. 337 func (s *SyncState) SetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { 338 s.lock.Lock() 339 defer s.lock.Unlock() 340 341 ms := s.state.EnsureModule(addr.Module) 342 ms.SetResourceInstanceDeposed(addr.Resource, key, obj.DeepCopy(), provider) 343 s.maybePruneModule(addr.Module) 344 } 345 346 // DeposeResourceInstanceObject moves the current instance object for the 347 // given resource instance address into the deposed set, leaving the instance 348 // without a current object. 349 // 350 // The return value is the newly-allocated deposed key, or NotDeposed if the 351 // given instance is already lacking a current object. 352 // 353 // If the containing module for this resource or the resource itself are not 354 // already tracked in state then there cannot be a current object for the 355 // given instance, and so NotDeposed will be returned without modifying the 356 // state at all. 357 func (s *SyncState) DeposeResourceInstanceObject(addr addrs.AbsResourceInstance) DeposedKey { 358 s.lock.Lock() 359 defer s.lock.Unlock() 360 361 ms := s.state.Module(addr.Module) 362 if ms == nil { 363 return NotDeposed 364 } 365 366 return ms.deposeResourceInstanceObject(addr.Resource, NotDeposed) 367 } 368 369 // DeposeResourceInstanceObjectForceKey is like DeposeResourceInstanceObject 370 // but uses a pre-allocated key. It's the caller's responsibility to ensure 371 // that there aren't any races to use a particular key; this method will panic 372 // if the given key is already in use. 373 func (s *SyncState) DeposeResourceInstanceObjectForceKey(addr addrs.AbsResourceInstance, forcedKey DeposedKey) { 374 s.lock.Lock() 375 defer s.lock.Unlock() 376 377 if forcedKey == NotDeposed { 378 // Usage error: should use DeposeResourceInstanceObject in this case 379 panic("DeposeResourceInstanceObjectForceKey called without forced key") 380 } 381 382 ms := s.state.Module(addr.Module) 383 if ms == nil { 384 return // Nothing to do, since there can't be any current object either. 385 } 386 387 ms.deposeResourceInstanceObject(addr.Resource, forcedKey) 388 } 389 390 // ForgetResourceInstanceAll removes the record of all objects associated with 391 // the specified resource instance, if present. If not present, this is a no-op. 392 func (s *SyncState) ForgetResourceInstanceAll(addr addrs.AbsResourceInstance) { 393 s.lock.Lock() 394 defer s.lock.Unlock() 395 396 ms := s.state.Module(addr.Module) 397 if ms == nil { 398 return 399 } 400 ms.ForgetResourceInstanceAll(addr.Resource) 401 s.maybePruneModule(addr.Module) 402 } 403 404 // ForgetResourceInstanceDeposed removes the record of the deposed object with 405 // the given address and key, if present. If not present, this is a no-op. 406 func (s *SyncState) ForgetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) { 407 s.lock.Lock() 408 defer s.lock.Unlock() 409 410 ms := s.state.Module(addr.Module) 411 if ms == nil { 412 return 413 } 414 ms.ForgetResourceInstanceDeposed(addr.Resource, key) 415 s.maybePruneModule(addr.Module) 416 } 417 418 // MaybeRestoreResourceInstanceDeposed will restore the deposed object with the 419 // given key on the specified resource as the current object for that instance 420 // if and only if that would not cause us to forget an existing current 421 // object for that instance. 422 // 423 // Returns true if the object was restored to current, or false if no change 424 // was made at all. 425 func (s *SyncState) MaybeRestoreResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) bool { 426 s.lock.Lock() 427 defer s.lock.Unlock() 428 429 if key == NotDeposed { 430 panic("MaybeRestoreResourceInstanceDeposed called without DeposedKey") 431 } 432 433 ms := s.state.Module(addr.Module) 434 if ms == nil { 435 // Nothing to do, since the specified deposed object cannot exist. 436 return false 437 } 438 439 return ms.maybeRestoreResourceInstanceDeposed(addr.Resource, key) 440 } 441 442 // RemovePlannedResourceInstanceObjects removes from the state any resource 443 // instance objects that have the status ObjectPlanned, indiciating that they 444 // are just transient placeholders created during planning. 445 // 446 // Note that this does not restore any "ready" or "tainted" object that might 447 // have been present before the planned object was written. The only real use 448 // for this method is in preparing the state created during a refresh walk, 449 // where we run the planning step for certain instances just to create enough 450 // information to allow correct expression evaluation within provider and 451 // data resource blocks. Discarding planned instances in that case is okay 452 // because the refresh phase only creates planned objects to stand in for 453 // objects that don't exist yet, and thus the planned object must have been 454 // absent before by definition. 455 func (s *SyncState) RemovePlannedResourceInstanceObjects() { 456 // TODO: Merge together the refresh and plan phases into a single walk, 457 // so we can remove the need to create this "partial plan" during refresh 458 // that we then need to clean up before proceeding. 459 460 s.lock.Lock() 461 defer s.lock.Unlock() 462 463 for _, ms := range s.state.Modules { 464 moduleAddr := ms.Addr 465 466 for _, rs := range ms.Resources { 467 resAddr := rs.Addr 468 469 for ik, is := range rs.Instances { 470 instAddr := resAddr.Instance(ik) 471 472 if is.Current != nil && is.Current.Status == ObjectPlanned { 473 // Setting the current instance to nil removes it from the 474 // state altogether if there are not also deposed instances. 475 ms.SetResourceInstanceCurrent(instAddr, nil, rs.ProviderConfig) 476 } 477 478 for dk, obj := range is.Deposed { 479 // Deposed objects should never be "planned", but we'll 480 // do this anyway for the sake of completeness. 481 if obj.Status == ObjectPlanned { 482 ms.ForgetResourceInstanceDeposed(instAddr, dk) 483 } 484 } 485 } 486 } 487 488 // We may have deleted some objects, which means that we may have 489 // left a module empty, and so we must prune to preserve the invariant 490 // that only the root module is allowed to be empty. 491 s.maybePruneModule(moduleAddr) 492 } 493 } 494 495 // Lock acquires an explicit lock on the state, allowing direct read and write 496 // access to the returned state object. The caller must call Unlock once 497 // access is no longer needed, and then immediately discard the state pointer 498 // pointer. 499 // 500 // Most callers should not use this. Instead, use the concurrency-safe 501 // accessors and mutators provided directly on SyncState. 502 func (s *SyncState) Lock() *State { 503 s.lock.Lock() 504 return s.state 505 } 506 507 // Unlock releases a lock previously acquired by Lock, at which point the 508 // caller must cease all use of the state pointer that was returned. 509 // 510 // Do not call this method except to end an explicit lock acquired by 511 // Lock. If a caller calls Unlock without first holding the lock, behavior 512 // is undefined. 513 func (s *SyncState) Unlock() { 514 s.lock.Unlock() 515 } 516 517 // maybePruneModule will remove a module from the state altogether if it is 518 // empty, unless it's the root module which must always be present. 519 // 520 // This helper method is not concurrency-safe on its own, so must only be 521 // called while the caller is already holding the lock for writing. 522 func (s *SyncState) maybePruneModule(addr addrs.ModuleInstance) { 523 if addr.IsRoot() { 524 // We never prune the root. 525 return 526 } 527 528 ms := s.state.Module(addr) 529 if ms == nil { 530 return 531 } 532 533 if ms.empty() { 534 log.Printf("[TRACE] states.SyncState: pruning %s because it is empty", addr) 535 s.state.RemoveModule(addr) 536 } 537 }