github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/states/state.go (about) 1 package states 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/zclconf/go-cty/cty" 8 9 "github.com/hashicorp/terraform/internal/addrs" 10 "github.com/hashicorp/terraform/internal/getproviders" 11 ) 12 13 // State is the top-level type of a Terraform state. 14 // 15 // A state should be mutated only via its accessor methods, to ensure that 16 // invariants are preserved. 17 // 18 // Access to State and the nested values within it is not concurrency-safe, 19 // so when accessing a State object concurrently it is the caller's 20 // responsibility to ensure that only one write is in progress at a time 21 // and that reads only occur when no write is in progress. The most common 22 // way to achieve this is to wrap the State in a SyncState and use the 23 // higher-level atomic operations supported by that type. 24 type State struct { 25 // Modules contains the state for each module. The keys in this map are 26 // an implementation detail and must not be used by outside callers. 27 Modules map[string]*Module 28 29 // CheckResults contains a snapshot of the statuses of checks at the 30 // end of the most recent update to the state. Callers might compare 31 // checks between runs to see if e.g. a previously-failing check has 32 // been fixed since the last run, or similar. 33 // 34 // CheckResults can be nil to indicate that there are no check results 35 // from the previous run at all, which is subtly different than the 36 // previous run having affirmatively recorded that there are no checks 37 // to run. For example, if this object was created from a state snapshot 38 // created by a version of Terraform that didn't yet support checks 39 // then this field will be nil. 40 CheckResults *CheckResults 41 } 42 43 // NewState constructs a minimal empty state, containing an empty root module. 44 func NewState() *State { 45 modules := map[string]*Module{} 46 modules[addrs.RootModuleInstance.String()] = NewModule(addrs.RootModuleInstance) 47 return &State{ 48 Modules: modules, 49 } 50 } 51 52 // BuildState is a helper -- primarily intended for tests -- to build a state 53 // using imperative code against the StateSync type while still acting as 54 // an expression of type *State to assign into a containing struct. 55 func BuildState(cb func(*SyncState)) *State { 56 s := NewState() 57 cb(s.SyncWrapper()) 58 return s 59 } 60 61 // Empty returns true if there are no resources or populated output values 62 // in the receiver. In other words, if this state could be safely replaced 63 // with the return value of NewState and be functionally equivalent. 64 func (s *State) Empty() bool { 65 if s == nil { 66 return true 67 } 68 for _, ms := range s.Modules { 69 if len(ms.Resources) != 0 { 70 return false 71 } 72 if len(ms.OutputValues) != 0 { 73 return false 74 } 75 } 76 return true 77 } 78 79 // Module returns the state for the module with the given address, or nil if 80 // the requested module is not tracked in the state. 81 func (s *State) Module(addr addrs.ModuleInstance) *Module { 82 if s == nil { 83 panic("State.Module on nil *State") 84 } 85 return s.Modules[addr.String()] 86 } 87 88 // ModuleInstances returns the set of Module states that matches the given path. 89 func (s *State) ModuleInstances(addr addrs.Module) []*Module { 90 var ms []*Module 91 for _, m := range s.Modules { 92 if m.Addr.Module().Equal(addr) { 93 ms = append(ms, m) 94 } 95 } 96 return ms 97 } 98 99 // ModuleOutputs returns all outputs for the given module call under the 100 // parentAddr instance. 101 func (s *State) ModuleOutputs(parentAddr addrs.ModuleInstance, module addrs.ModuleCall) []*OutputValue { 102 var os []*OutputValue 103 for _, m := range s.Modules { 104 // can't get outputs from the root module 105 if m.Addr.IsRoot() { 106 continue 107 } 108 109 parent, call := m.Addr.Call() 110 // make sure this is a descendent in the correct path 111 if !parentAddr.Equal(parent) { 112 continue 113 } 114 115 // and check if this is the correct child 116 if call.Name != module.Name { 117 continue 118 } 119 120 for _, o := range m.OutputValues { 121 os = append(os, o) 122 } 123 } 124 125 return os 126 } 127 128 // RemoveModule removes the module with the given address from the state, 129 // unless it is the root module. The root module cannot be deleted, and so 130 // this method will panic if that is attempted. 131 // 132 // Removing a module implicitly discards all of the resources, outputs and 133 // local values within it, and so this should usually be done only for empty 134 // modules. For callers accessing the state through a SyncState wrapper, modules 135 // are automatically pruned if they are empty after one of their contained 136 // elements is removed. 137 func (s *State) RemoveModule(addr addrs.ModuleInstance) { 138 if addr.IsRoot() { 139 panic("attempted to remove root module") 140 } 141 142 delete(s.Modules, addr.String()) 143 } 144 145 // RootModule is a convenient alias for Module(addrs.RootModuleInstance). 146 func (s *State) RootModule() *Module { 147 if s == nil { 148 panic("RootModule called on nil State") 149 } 150 return s.Modules[addrs.RootModuleInstance.String()] 151 } 152 153 // EnsureModule returns the state for the module with the given address, 154 // creating and adding a new one if necessary. 155 // 156 // Since this might modify the state to add a new instance, it is considered 157 // to be a write operation. 158 func (s *State) EnsureModule(addr addrs.ModuleInstance) *Module { 159 ms := s.Module(addr) 160 if ms == nil { 161 ms = NewModule(addr) 162 s.Modules[addr.String()] = ms 163 } 164 return ms 165 } 166 167 // HasManagedResourceInstanceObjects returns true if there is at least one 168 // resource instance object (current or deposed) associated with a managed 169 // resource in the receiving state. 170 // 171 // A true result would suggest that just discarding this state without first 172 // destroying these objects could leave "dangling" objects in remote systems, 173 // no longer tracked by any Terraform state. 174 func (s *State) HasManagedResourceInstanceObjects() bool { 175 if s == nil { 176 return false 177 } 178 for _, ms := range s.Modules { 179 for _, rs := range ms.Resources { 180 if rs.Addr.Resource.Mode != addrs.ManagedResourceMode { 181 continue 182 } 183 for _, is := range rs.Instances { 184 if is.Current != nil || len(is.Deposed) != 0 { 185 return true 186 } 187 } 188 } 189 } 190 return false 191 } 192 193 // Resource returns the state for the resource with the given address, or nil 194 // if no such resource is tracked in the state. 195 func (s *State) Resource(addr addrs.AbsResource) *Resource { 196 ms := s.Module(addr.Module) 197 if ms == nil { 198 return nil 199 } 200 return ms.Resource(addr.Resource) 201 } 202 203 // Resources returns the set of resources that match the given configuration path. 204 func (s *State) Resources(addr addrs.ConfigResource) []*Resource { 205 var ret []*Resource 206 for _, m := range s.ModuleInstances(addr.Module) { 207 r := m.Resource(addr.Resource) 208 if r != nil { 209 ret = append(ret, r) 210 } 211 } 212 return ret 213 } 214 215 // AllManagedResourceInstanceObjectAddrs returns a set of addresses for all of 216 // the leaf resource instance objects associated with managed resources that 217 // are tracked in this state. 218 // 219 // This result is the set of objects that would be effectively "forgotten" 220 // (like "terraform state rm") if this state were totally discarded, such as 221 // by deleting a workspace. This function is intended only for reporting 222 // context in error messages, such as when we reject deleting a "non-empty" 223 // workspace as detected by s.HasManagedResourceInstanceObjects. 224 // 225 // The ordering of the result is meaningless but consistent. DeposedKey will 226 // be NotDeposed (the zero value of DeposedKey) for any "current" objects. 227 // This method is guaranteed to return at least one item if 228 // s.HasManagedResourceInstanceObjects returns true for the same state, and 229 // to return a zero-length slice if it returns false. 230 func (s *State) AllResourceInstanceObjectAddrs() []struct { 231 Instance addrs.AbsResourceInstance 232 DeposedKey DeposedKey 233 } { 234 if s == nil { 235 return nil 236 } 237 238 // We use an unnamed return type here just because we currently have no 239 // general need to return pairs of instance address and deposed key aside 240 // from this method, and this method itself is only of marginal value 241 // when producing some error messages. 242 // 243 // If that need ends up arising more in future then it might make sense to 244 // name this as addrs.AbsResourceInstanceObject, although that would require 245 // moving DeposedKey into the addrs package too. 246 type ResourceInstanceObject = struct { 247 Instance addrs.AbsResourceInstance 248 DeposedKey DeposedKey 249 } 250 var ret []ResourceInstanceObject 251 252 for _, ms := range s.Modules { 253 for _, rs := range ms.Resources { 254 if rs.Addr.Resource.Mode != addrs.ManagedResourceMode { 255 continue 256 } 257 258 for instKey, is := range rs.Instances { 259 instAddr := rs.Addr.Instance(instKey) 260 if is.Current != nil { 261 ret = append(ret, ResourceInstanceObject{instAddr, NotDeposed}) 262 } 263 for deposedKey := range is.Deposed { 264 ret = append(ret, ResourceInstanceObject{instAddr, deposedKey}) 265 } 266 } 267 } 268 } 269 270 sort.SliceStable(ret, func(i, j int) bool { 271 objI, objJ := ret[i], ret[j] 272 switch { 273 case !objI.Instance.Equal(objJ.Instance): 274 return objI.Instance.Less(objJ.Instance) 275 default: 276 return objI.DeposedKey < objJ.DeposedKey 277 } 278 }) 279 280 return ret 281 } 282 283 // ResourceInstance returns the state for the resource instance with the given 284 // address, or nil if no such resource is tracked in the state. 285 func (s *State) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance { 286 if s == nil { 287 panic("State.ResourceInstance on nil *State") 288 } 289 ms := s.Module(addr.Module) 290 if ms == nil { 291 return nil 292 } 293 return ms.ResourceInstance(addr.Resource) 294 } 295 296 // OutputValue returns the state for the output value with the given address, 297 // or nil if no such output value is tracked in the state. 298 func (s *State) OutputValue(addr addrs.AbsOutputValue) *OutputValue { 299 ms := s.Module(addr.Module) 300 if ms == nil { 301 return nil 302 } 303 return ms.OutputValues[addr.OutputValue.Name] 304 } 305 306 // LocalValue returns the value of the named local value with the given address, 307 // or cty.NilVal if no such value is tracked in the state. 308 func (s *State) LocalValue(addr addrs.AbsLocalValue) cty.Value { 309 ms := s.Module(addr.Module) 310 if ms == nil { 311 return cty.NilVal 312 } 313 return ms.LocalValues[addr.LocalValue.Name] 314 } 315 316 // ProviderAddrs returns a list of all of the provider configuration addresses 317 // referenced throughout the receiving state. 318 // 319 // The result is de-duplicated so that each distinct address appears only once. 320 func (s *State) ProviderAddrs() []addrs.AbsProviderConfig { 321 if s == nil { 322 return nil 323 } 324 325 m := map[string]addrs.AbsProviderConfig{} 326 for _, ms := range s.Modules { 327 for _, rc := range ms.Resources { 328 m[rc.ProviderConfig.String()] = rc.ProviderConfig 329 } 330 } 331 if len(m) == 0 { 332 return nil 333 } 334 335 // This is mainly just so we'll get stable results for testing purposes. 336 keys := make([]string, 0, len(m)) 337 for k := range m { 338 keys = append(keys, k) 339 } 340 sort.Strings(keys) 341 342 ret := make([]addrs.AbsProviderConfig, len(keys)) 343 for i, key := range keys { 344 ret[i] = m[key] 345 } 346 347 return ret 348 } 349 350 // ProviderRequirements returns a description of all of the providers that 351 // are required to work with the receiving state. 352 // 353 // Because the state does not track specific version information for providers, 354 // the requirements returned by this method will always be unconstrained. 355 // The result should usually be merged with a Requirements derived from the 356 // current configuration in order to apply some constraints. 357 func (s *State) ProviderRequirements() getproviders.Requirements { 358 configAddrs := s.ProviderAddrs() 359 ret := make(getproviders.Requirements, len(configAddrs)) 360 for _, configAddr := range configAddrs { 361 ret[configAddr.Provider] = nil // unconstrained dependency 362 } 363 return ret 364 } 365 366 // PruneResourceHusks is a specialized method that will remove any Resource 367 // objects that do not contain any instances, even if they have an EachMode. 368 // 369 // This should generally be used only after a "terraform destroy" operation, 370 // to finalize the cleanup of the state. It is not correct to use this after 371 // other operations because if a resource has "count = 0" or "for_each" over 372 // an empty collection then we want to retain it in the state so that references 373 // to it, particularly in "strange" contexts like "terraform console", can be 374 // properly resolved. 375 // 376 // This method MUST NOT be called concurrently with other readers and writers 377 // of the receiving state. 378 func (s *State) PruneResourceHusks() { 379 for _, m := range s.Modules { 380 m.PruneResourceHusks() 381 if len(m.Resources) == 0 && !m.Addr.IsRoot() { 382 s.RemoveModule(m.Addr) 383 } 384 } 385 } 386 387 // SyncWrapper returns a SyncState object wrapping the receiver. 388 func (s *State) SyncWrapper() *SyncState { 389 return &SyncState{ 390 state: s, 391 } 392 } 393 394 // MoveAbsResource moves the given src AbsResource's current state to the new 395 // dst address. This will panic if the src AbsResource does not exist in state, 396 // or if there is already a resource at the dst address. It is the caller's 397 // responsibility to verify the validity of the move (for example, that the src 398 // and dst are compatible types). 399 func (s *State) MoveAbsResource(src, dst addrs.AbsResource) { 400 // verify that the src address exists and the dst address does not 401 rs := s.Resource(src) 402 if rs == nil { 403 panic(fmt.Sprintf("no state for src address %s", src.String())) 404 } 405 406 ds := s.Resource(dst) 407 if ds != nil { 408 panic(fmt.Sprintf("dst resource %s already exists", dst.String())) 409 } 410 411 ms := s.Module(src.Module) 412 ms.RemoveResource(src.Resource) 413 414 // Remove the module if it is empty (and not root) after removing the 415 // resource. 416 if !ms.Addr.IsRoot() && ms.empty() { 417 s.RemoveModule(src.Module) 418 } 419 420 // Update the address before adding it to the state 421 rs.Addr = dst 422 s.EnsureModule(dst.Module).Resources[dst.Resource.String()] = rs 423 } 424 425 // MaybeMoveAbsResource moves the given src AbsResource's current state to the 426 // new dst address. This function will succeed if both the src address does not 427 // exist in state and the dst address does; the return value indicates whether 428 // or not the move occurred. This function will panic if either the src does not 429 // exist or the dst does exist (but not both). 430 func (s *State) MaybeMoveAbsResource(src, dst addrs.AbsResource) bool { 431 // Get the source and destinatation addresses from state. 432 rs := s.Resource(src) 433 ds := s.Resource(dst) 434 435 // Normal case: the src exists in state, dst does not 436 if rs != nil && ds == nil { 437 s.MoveAbsResource(src, dst) 438 return true 439 } 440 441 if rs == nil && ds != nil { 442 // The source is not in state, the destination is. This is not 443 // guaranteed to be idempotent since we aren't tracking exact moves, but 444 // it's useful information for the caller. 445 return false 446 } else { 447 panic("invalid move") 448 } 449 } 450 451 // MoveAbsResourceInstance moves the given src AbsResourceInstance's current state to 452 // the new dst address. This will panic if the src AbsResourceInstance does not 453 // exist in state, or if there is already a resource at the dst address. It is 454 // the caller's responsibility to verify the validity of the move (for example, 455 // that the src and dst are compatible types). 456 func (s *State) MoveAbsResourceInstance(src, dst addrs.AbsResourceInstance) { 457 srcInstanceState := s.ResourceInstance(src) 458 if srcInstanceState == nil { 459 panic(fmt.Sprintf("no state for src address %s", src.String())) 460 } 461 462 dstInstanceState := s.ResourceInstance(dst) 463 if dstInstanceState != nil { 464 panic(fmt.Sprintf("dst resource %s already exists", dst.String())) 465 } 466 467 srcResourceState := s.Resource(src.ContainingResource()) 468 srcProviderAddr := srcResourceState.ProviderConfig 469 dstResourceAddr := dst.ContainingResource() 470 471 // Remove the source resource instance from the module's state, and then the 472 // module if empty. 473 ms := s.Module(src.Module) 474 ms.ForgetResourceInstanceAll(src.Resource) 475 if !ms.Addr.IsRoot() && ms.empty() { 476 s.RemoveModule(src.Module) 477 } 478 479 dstModule := s.EnsureModule(dst.Module) 480 481 // See if there is already a resource we can add this instance to. 482 dstResourceState := s.Resource(dstResourceAddr) 483 if dstResourceState == nil { 484 // If we're moving to an address without an index then that 485 // suggests the user's intent is to establish both the 486 // resource and the instance at the same time (since the 487 // address covers both). If there's an index in the 488 // target then allow creating the new instance here. 489 dstModule.SetResourceProvider( 490 dstResourceAddr.Resource, 491 srcProviderAddr, // in this case, we bring the provider along as if we were moving the whole resource 492 ) 493 dstResourceState = dstModule.Resource(dstResourceAddr.Resource) 494 } 495 496 dstResourceState.Instances[dst.Resource.Key] = srcInstanceState 497 } 498 499 // MaybeMoveAbsResourceInstance moves the given src AbsResourceInstance's 500 // current state to the new dst address. This function will succeed if both the 501 // src address does not exist in state and the dst address does; the return 502 // value indicates whether or not the move occured. This function will panic if 503 // either the src does not exist or the dst does exist (but not both). 504 func (s *State) MaybeMoveAbsResourceInstance(src, dst addrs.AbsResourceInstance) bool { 505 // get the src and dst resource instances from state 506 rs := s.ResourceInstance(src) 507 ds := s.ResourceInstance(dst) 508 509 // Normal case: the src exists in state, dst does not 510 if rs != nil && ds == nil { 511 s.MoveAbsResourceInstance(src, dst) 512 return true 513 } 514 515 if rs == nil && ds != nil { 516 // The source is not in state, the destination is. This is not 517 // guaranteed to be idempotent since we aren't tracking exact moves, but 518 // it's useful information. 519 return false 520 } else { 521 panic("invalid move") 522 } 523 } 524 525 // MoveModuleInstance moves the given src ModuleInstance's current state to the 526 // new dst address. This will panic if the src ModuleInstance does not 527 // exist in state, or if there is already a resource at the dst address. It is 528 // the caller's responsibility to verify the validity of the move. 529 func (s *State) MoveModuleInstance(src, dst addrs.ModuleInstance) { 530 if src.IsRoot() || dst.IsRoot() { 531 panic("cannot move to or from root module") 532 } 533 534 srcMod := s.Module(src) 535 if srcMod == nil { 536 panic(fmt.Sprintf("no state for src module %s", src.String())) 537 } 538 539 dstMod := s.Module(dst) 540 if dstMod != nil { 541 panic(fmt.Sprintf("dst module %s already exists in state", dst.String())) 542 } 543 544 s.RemoveModule(src) 545 546 srcMod.Addr = dst 547 s.EnsureModule(dst) 548 s.Modules[dst.String()] = srcMod 549 550 // Update any Resource's addresses. 551 if srcMod.Resources != nil { 552 for _, r := range srcMod.Resources { 553 r.Addr.Module = dst 554 } 555 } 556 557 // Update any OutputValues's addresses. 558 if srcMod.OutputValues != nil { 559 for _, ov := range srcMod.OutputValues { 560 ov.Addr.Module = dst 561 } 562 } 563 } 564 565 // MaybeMoveModuleInstance moves the given src ModuleInstance's current state to 566 // the new dst address. This function will succeed if both the src address does 567 // not exist in state and the dst address does; the return value indicates 568 // whether or not the move occured. This function will panic if either the src 569 // does not exist or the dst does exist (but not both). 570 func (s *State) MaybeMoveModuleInstance(src, dst addrs.ModuleInstance) bool { 571 if src.IsRoot() || dst.IsRoot() { 572 panic("cannot move to or from root module") 573 } 574 575 srcMod := s.Module(src) 576 dstMod := s.Module(dst) 577 578 // Normal case: the src exists in state, dst does not 579 if srcMod != nil && dstMod == nil { 580 s.MoveModuleInstance(src, dst) 581 return true 582 } 583 584 if srcMod == nil || src.IsRoot() && dstMod != nil { 585 // The source is not in state, the destination is. This is not 586 // guaranteed to be idempotent since we aren't tracking exact moves, but 587 // it's useful information. 588 return false 589 } else { 590 panic("invalid move") 591 } 592 } 593 594 // MoveModule takes a source and destination addrs.Module address, and moves all 595 // state Modules which are contained by the src address to the new address. 596 func (s *State) MoveModule(src, dst addrs.AbsModuleCall) { 597 if src.Module.IsRoot() || dst.Module.IsRoot() { 598 panic("cannot move to or from root module") 599 } 600 601 // Modules only exist as ModuleInstances in state, so we need to check each 602 // state Module and see if it is contained by the src address to get a full 603 // list of modules to move. 604 var srcMIs []*Module 605 for _, module := range s.Modules { 606 if !module.Addr.IsRoot() { 607 if src.Module.TargetContains(module.Addr) { 608 srcMIs = append(srcMIs, module) 609 } 610 } 611 } 612 613 if len(srcMIs) == 0 { 614 panic(fmt.Sprintf("no matching module instances found for src module %s", src.String())) 615 } 616 617 for _, ms := range srcMIs { 618 newInst := make(addrs.ModuleInstance, len(ms.Addr)) 619 copy(newInst, ms.Addr) 620 if ms.Addr.IsDeclaredByCall(src) { 621 // Easy case: we just need to update the last step with the new name 622 newInst[len(newInst)-1].Name = dst.Call.Name 623 } else { 624 // Trickier: this Module is a submodule. we need to find and update 625 // only that appropriate step 626 for s := range newInst { 627 if newInst[s].Name == src.Call.Name { 628 newInst[s].Name = dst.Call.Name 629 } 630 } 631 } 632 s.MoveModuleInstance(ms.Addr, newInst) 633 } 634 }