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