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