github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/terraform/state.go (about) 1 package terraform 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 "log" 10 "reflect" 11 "sort" 12 "strings" 13 14 "github.com/hashicorp/terraform/config" 15 ) 16 17 const ( 18 // textStateVersion is the current version for our state file 19 textStateVersion = 1 20 ) 21 22 // rootModulePath is the path of the root module 23 var rootModulePath = []string{"root"} 24 25 // State keeps track of a snapshot state-of-the-world that Terraform 26 // can use to keep track of what real world resources it is actually 27 // managing. This is the latest format as of Terraform 0.3 28 type State struct { 29 // Version is the protocol version. Currently only "1". 30 Version int `json:"version"` 31 32 // Serial is incremented on any operation that modifies 33 // the State file. It is used to detect potentially conflicting 34 // updates. 35 Serial int64 `json:"serial"` 36 37 // Modules contains all the modules in a breadth-first order 38 Modules []*ModuleState `json:"modules"` 39 } 40 41 // Children returns the ModuleStates that are direct children of 42 // the given path. If the path is "root", for example, then children 43 // returned might be "root.child", but not "root.child.grandchild". 44 func (s *State) Children(path []string) []*ModuleState { 45 // TODO: test 46 47 result := make([]*ModuleState, 0) 48 for _, m := range s.Modules { 49 if len(m.Path) != len(path)+1 { 50 continue 51 } 52 if !reflect.DeepEqual(path, m.Path[:len(path)]) { 53 continue 54 } 55 56 result = append(result, m) 57 } 58 59 return result 60 } 61 62 // AddModule adds the module with the given path to the state. 63 // 64 // This should be the preferred method to add module states since it 65 // allows us to optimize lookups later as well as control sorting. 66 func (s *State) AddModule(path []string) *ModuleState { 67 m := &ModuleState{Path: path} 68 m.init() 69 s.Modules = append(s.Modules, m) 70 s.sort() 71 return m 72 } 73 74 // ModuleByPath is used to lookup the module state for the given path. 75 // This should be the prefered lookup mechanism as it allows for future 76 // lookup optimizations. 77 func (s *State) ModuleByPath(path []string) *ModuleState { 78 if s == nil { 79 return nil 80 } 81 for _, mod := range s.Modules { 82 if mod.Path == nil { 83 panic("missing module path") 84 } 85 if reflect.DeepEqual(mod.Path, path) { 86 return mod 87 } 88 } 89 return nil 90 } 91 92 // RootModule returns the ModuleState for the root module 93 func (s *State) RootModule() *ModuleState { 94 root := s.ModuleByPath(rootModulePath) 95 if root == nil { 96 panic("missing root module") 97 } 98 return root 99 } 100 101 func (s *State) init() { 102 if s.Version == 0 { 103 s.Version = textStateVersion 104 } 105 if len(s.Modules) == 0 { 106 root := &ModuleState{ 107 Path: rootModulePath, 108 } 109 root.init() 110 s.Modules = []*ModuleState{root} 111 } 112 } 113 114 func (s *State) deepcopy() *State { 115 if s == nil { 116 return nil 117 } 118 n := &State{ 119 Version: s.Version, 120 Serial: s.Serial, 121 Modules: make([]*ModuleState, 0, len(s.Modules)), 122 } 123 for _, mod := range s.Modules { 124 n.Modules = append(n.Modules, mod.deepcopy()) 125 } 126 return n 127 } 128 129 // prune is used to remove any resources that are no longer required 130 func (s *State) prune() { 131 if s == nil { 132 return 133 } 134 for _, mod := range s.Modules { 135 mod.prune() 136 } 137 } 138 139 // sort sorts the modules 140 func (s *State) sort() { 141 sort.Sort(moduleStateSort(s.Modules)) 142 } 143 144 func (s *State) GoString() string { 145 return fmt.Sprintf("*%#v", *s) 146 } 147 148 func (s *State) String() string { 149 var buf bytes.Buffer 150 for _, m := range s.Modules { 151 mStr := m.String() 152 153 // If we're the root module, we just write the output directly. 154 if reflect.DeepEqual(m.Path, rootModulePath) { 155 buf.WriteString(mStr + "\n") 156 continue 157 } 158 159 buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], "."))) 160 161 s := bufio.NewScanner(strings.NewReader(mStr)) 162 for s.Scan() { 163 buf.WriteString(fmt.Sprintf(" %s\n", s.Text())) 164 } 165 } 166 167 return strings.TrimSpace(buf.String()) 168 } 169 170 // ModuleState is used to track all the state relevant to a single 171 // module. Previous to Terraform 0.3, all state belonged to the "root" 172 // module. 173 type ModuleState struct { 174 // Path is the import path from the root module. Modules imports are 175 // always disjoint, so the path represents amodule tree 176 Path []string `json:"path"` 177 178 // Outputs declared by the module and maintained for each module 179 // even though only the root module technically needs to be kept. 180 // This allows operators to inspect values at the boundaries. 181 Outputs map[string]string `json:"outputs"` 182 183 // Resources is a mapping of the logically named resource to 184 // the state of the resource. Each resource may actually have 185 // N instances underneath, although a user only needs to think 186 // about the 1:1 case. 187 Resources map[string]*ResourceState `json:"resources"` 188 } 189 190 // IsRoot says whether or not this module diff is for the root module. 191 func (m *ModuleState) IsRoot() bool { 192 return reflect.DeepEqual(m.Path, rootModulePath) 193 } 194 195 // Orphans returns a list of keys of resources that are in the State 196 // but aren't present in the configuration itself. Hence, these keys 197 // represent the state of resources that are orphans. 198 func (m *ModuleState) Orphans(c *config.Config) []string { 199 keys := make(map[string]struct{}) 200 for k, _ := range m.Resources { 201 keys[k] = struct{}{} 202 } 203 204 for _, r := range c.Resources { 205 delete(keys, r.Id()) 206 207 for k, _ := range keys { 208 if strings.HasPrefix(k, r.Id()+".") { 209 delete(keys, k) 210 } 211 } 212 } 213 214 result := make([]string, 0, len(keys)) 215 for k, _ := range keys { 216 result = append(result, k) 217 } 218 219 return result 220 } 221 222 // View returns a view with the given resource prefix. 223 func (m *ModuleState) View(id string) *ModuleState { 224 if m == nil { 225 return m 226 } 227 228 r := m.deepcopy() 229 for k, _ := range r.Resources { 230 if id == k || strings.HasPrefix(k, id+".") { 231 continue 232 } 233 234 delete(r.Resources, k) 235 } 236 237 return r 238 } 239 240 func (m *ModuleState) init() { 241 if m.Outputs == nil { 242 m.Outputs = make(map[string]string) 243 } 244 if m.Resources == nil { 245 m.Resources = make(map[string]*ResourceState) 246 } 247 } 248 249 func (m *ModuleState) deepcopy() *ModuleState { 250 if m == nil { 251 return nil 252 } 253 n := &ModuleState{ 254 Path: make([]string, len(m.Path)), 255 Outputs: make(map[string]string, len(m.Outputs)), 256 Resources: make(map[string]*ResourceState, len(m.Resources)), 257 } 258 copy(n.Path, m.Path) 259 for k, v := range m.Outputs { 260 n.Outputs[k] = v 261 } 262 for k, v := range m.Resources { 263 n.Resources[k] = v.deepcopy() 264 } 265 return n 266 } 267 268 // prune is used to remove any resources that are no longer required 269 func (m *ModuleState) prune() { 270 for k, v := range m.Resources { 271 v.prune() 272 if (v.Primary == nil || v.Primary.ID == "") && len(v.Tainted) == 0 { 273 delete(m.Resources, k) 274 } 275 } 276 } 277 278 func (m *ModuleState) GoString() string { 279 return fmt.Sprintf("*%#v", *m) 280 } 281 282 func (m *ModuleState) String() string { 283 if len(m.Resources) == 0 { 284 return "<no state>" 285 } 286 287 var buf bytes.Buffer 288 289 names := make([]string, 0, len(m.Resources)) 290 for name, _ := range m.Resources { 291 names = append(names, name) 292 } 293 sort.Strings(names) 294 295 for _, k := range names { 296 rs := m.Resources[k] 297 var id string 298 if rs.Primary != nil { 299 id = rs.Primary.ID 300 } 301 if id == "" { 302 id = "<not created>" 303 } 304 305 taintStr := "" 306 if len(rs.Tainted) > 0 { 307 taintStr = fmt.Sprintf(" (%d tainted)", len(rs.Tainted)) 308 } 309 310 buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr)) 311 buf.WriteString(fmt.Sprintf(" ID = %s\n", id)) 312 313 var attributes map[string]string 314 if rs.Primary != nil { 315 attributes = rs.Primary.Attributes 316 } 317 attrKeys := make([]string, 0, len(attributes)) 318 for ak, _ := range attributes { 319 if ak == "id" { 320 continue 321 } 322 323 attrKeys = append(attrKeys, ak) 324 } 325 sort.Strings(attrKeys) 326 327 for _, ak := range attrKeys { 328 av := attributes[ak] 329 buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av)) 330 } 331 332 for idx, t := range rs.Tainted { 333 buf.WriteString(fmt.Sprintf(" Tainted ID %d = %s\n", idx+1, t.ID)) 334 } 335 336 if len(rs.Dependencies) > 0 { 337 buf.WriteString(fmt.Sprintf("\n Dependencies:\n")) 338 for _, dep := range rs.Dependencies { 339 buf.WriteString(fmt.Sprintf(" %s\n", dep)) 340 } 341 } 342 } 343 344 if len(m.Outputs) > 0 { 345 buf.WriteString("\nOutputs:\n\n") 346 347 ks := make([]string, 0, len(m.Outputs)) 348 for k, _ := range m.Outputs { 349 ks = append(ks, k) 350 } 351 sort.Strings(ks) 352 353 for _, k := range ks { 354 v := m.Outputs[k] 355 buf.WriteString(fmt.Sprintf("%s = %s\n", k, v)) 356 } 357 } 358 359 return buf.String() 360 } 361 362 // ResourceState holds the state of a resource that is used so that 363 // a provider can find and manage an existing resource as well as for 364 // storing attributes that are used to populate variables of child 365 // resources. 366 // 367 // Attributes has attributes about the created resource that are 368 // queryable in interpolation: "${type.id.attr}" 369 // 370 // Extra is just extra data that a provider can return that we store 371 // for later, but is not exposed in any way to the user. 372 // 373 type ResourceState struct { 374 // This is filled in and managed by Terraform, and is the resource 375 // type itself such as "mycloud_instance". If a resource provider sets 376 // this value, it won't be persisted. 377 Type string `json:"type"` 378 379 // Dependencies are a list of things that this resource relies on 380 // existing to remain intact. For example: an AWS instance might 381 // depend on a subnet (which itself might depend on a VPC, and so 382 // on). 383 // 384 // Terraform uses this information to build valid destruction 385 // orders and to warn the user if they're destroying a resource that 386 // another resource depends on. 387 // 388 // Things can be put into this list that may not be managed by 389 // Terraform. If Terraform doesn't find a matching ID in the 390 // overall state, then it assumes it isn't managed and doesn't 391 // worry about it. 392 Dependencies []string `json:"depends_on,omitempty"` 393 394 // Primary is the current active instance for this resource. 395 // It can be replaced but only after a successful creation. 396 // This is the instances on which providers will act. 397 Primary *InstanceState `json:"primary"` 398 399 // Tainted is used to track any underlying instances that 400 // have been created but are in a bad or unknown state and 401 // need to be cleaned up subsequently. In the 402 // standard case, there is only at most a single instance. 403 // However, in pathological cases, it is possible for the number 404 // of instances to accumulate. 405 Tainted []*InstanceState `json:"tainted,omitempty"` 406 } 407 408 func (r *ResourceState) init() { 409 if r.Primary == nil { 410 r.Primary = &InstanceState{} 411 } 412 r.Primary.init() 413 } 414 415 func (r *ResourceState) deepcopy() *ResourceState { 416 if r == nil { 417 return nil 418 } 419 n := &ResourceState{ 420 Type: r.Type, 421 Dependencies: make([]string, len(r.Dependencies)), 422 Primary: r.Primary.deepcopy(), 423 Tainted: make([]*InstanceState, 0, len(r.Tainted)), 424 } 425 copy(n.Dependencies, r.Dependencies) 426 for _, inst := range r.Tainted { 427 n.Tainted = append(n.Tainted, inst.deepcopy()) 428 } 429 return n 430 } 431 432 // prune is used to remove any instances that are no longer required 433 func (r *ResourceState) prune() { 434 n := len(r.Tainted) 435 for i := 0; i < n; i++ { 436 inst := r.Tainted[i] 437 if inst.ID == "" { 438 copy(r.Tainted[i:], r.Tainted[i+1:]) 439 r.Tainted[n-1] = nil 440 n-- 441 } 442 } 443 r.Tainted = r.Tainted[:n] 444 } 445 446 func (s *ResourceState) GoString() string { 447 return fmt.Sprintf("*%#v", *s) 448 } 449 450 func (s *ResourceState) String() string { 451 var buf bytes.Buffer 452 buf.WriteString(fmt.Sprintf("Type = %s", s.Type)) 453 return buf.String() 454 } 455 456 // InstanceState is used to track the unique state information belonging 457 // to a given instance. 458 type InstanceState struct { 459 // A unique ID for this resource. This is opaque to Terraform 460 // and is only meant as a lookup mechanism for the providers. 461 ID string `json:"id"` 462 463 // Attributes are basic information about the resource. Any keys here 464 // are accessible in variable format within Terraform configurations: 465 // ${resourcetype.name.attribute}. 466 Attributes map[string]string `json:"attributes,omitempty"` 467 468 // Ephemeral is used to store any state associated with this instance 469 // that is necessary for the Terraform run to complete, but is not 470 // persisted to a state file. 471 Ephemeral EphemeralState `json:"-"` 472 } 473 474 func (i *InstanceState) init() { 475 if i.Attributes == nil { 476 i.Attributes = make(map[string]string) 477 } 478 i.Ephemeral.init() 479 } 480 481 func (i *InstanceState) deepcopy() *InstanceState { 482 if i == nil { 483 return nil 484 } 485 n := &InstanceState{ 486 ID: i.ID, 487 Ephemeral: *i.Ephemeral.deepcopy(), 488 } 489 if i.Attributes != nil { 490 n.Attributes = make(map[string]string, len(i.Attributes)) 491 for k, v := range i.Attributes { 492 n.Attributes[k] = v 493 } 494 } 495 return n 496 } 497 498 // MergeDiff takes a ResourceDiff and merges the attributes into 499 // this resource state in order to generate a new state. This new 500 // state can be used to provide updated attribute lookups for 501 // variable interpolation. 502 // 503 // If the diff attribute requires computing the value, and hence 504 // won't be available until apply, the value is replaced with the 505 // computeID. 506 func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState { 507 result := s.deepcopy() 508 if result == nil { 509 result = new(InstanceState) 510 } 511 result.init() 512 513 if s != nil { 514 for k, v := range s.Attributes { 515 result.Attributes[k] = v 516 } 517 } 518 if d != nil { 519 for k, diff := range d.Attributes { 520 if diff.NewRemoved { 521 delete(result.Attributes, k) 522 continue 523 } 524 if diff.NewComputed { 525 result.Attributes[k] = config.UnknownVariableValue 526 continue 527 } 528 529 result.Attributes[k] = diff.New 530 } 531 } 532 533 return result 534 } 535 536 func (i *InstanceState) GoString() string { 537 return fmt.Sprintf("*%#v", *i) 538 } 539 540 func (i *InstanceState) String() string { 541 var buf bytes.Buffer 542 543 if i == nil || i.ID == "" { 544 return "<not created>" 545 } 546 547 buf.WriteString(fmt.Sprintf("ID = %s\n", i.ID)) 548 549 attributes := i.Attributes 550 attrKeys := make([]string, 0, len(attributes)) 551 for ak, _ := range attributes { 552 if ak == "id" { 553 continue 554 } 555 556 attrKeys = append(attrKeys, ak) 557 } 558 sort.Strings(attrKeys) 559 560 for _, ak := range attrKeys { 561 av := attributes[ak] 562 buf.WriteString(fmt.Sprintf("%s = %s\n", ak, av)) 563 } 564 565 return buf.String() 566 } 567 568 // EphemeralState is used for transient state that is only kept in-memory 569 type EphemeralState struct { 570 // ConnInfo is used for the providers to export information which is 571 // used to connect to the resource for provisioning. For example, 572 // this could contain SSH or WinRM credentials. 573 ConnInfo map[string]string `json:"-"` 574 } 575 576 func (e *EphemeralState) init() { 577 if e.ConnInfo == nil { 578 e.ConnInfo = make(map[string]string) 579 } 580 } 581 582 func (e *EphemeralState) deepcopy() *EphemeralState { 583 if e == nil { 584 return nil 585 } 586 n := &EphemeralState{} 587 if e.ConnInfo != nil { 588 n.ConnInfo = make(map[string]string, len(e.ConnInfo)) 589 for k, v := range e.ConnInfo { 590 n.ConnInfo[k] = v 591 } 592 } 593 return n 594 } 595 596 // ReadState reads a state structure out of a reader in the format that 597 // was written by WriteState. 598 func ReadState(src io.Reader) (*State, error) { 599 buf := bufio.NewReader(src) 600 601 // Check if this is a V1 format 602 start, err := buf.Peek(len(stateFormatMagic)) 603 if err != nil { 604 return nil, fmt.Errorf("Failed to check for magic bytes: %v", err) 605 } 606 if string(start) == stateFormatMagic { 607 // Read the old state 608 old, err := ReadStateV1(buf) 609 if err != nil { 610 return nil, err 611 } 612 return upgradeV1State(old) 613 } 614 615 // Otherwise, must be V2 616 dec := json.NewDecoder(buf) 617 state := &State{} 618 if err := dec.Decode(state); err != nil { 619 return nil, fmt.Errorf("Decoding state file failed: %v", err) 620 } 621 622 // Check the version, this to ensure we don't read a future 623 // version that we don't understand 624 if state.Version > textStateVersion { 625 return nil, fmt.Errorf("State version %d not supported, please update.", 626 state.Version) 627 } 628 629 // Sort it 630 state.sort() 631 632 return state, nil 633 } 634 635 // WriteState writes a state somewhere in a binary format. 636 func WriteState(d *State, dst io.Writer) error { 637 // Make sure it is sorted 638 d.sort() 639 640 // Ensure the version is set 641 d.Version = textStateVersion 642 643 // Always increment the serial number 644 d.Serial++ 645 646 // Encode the data in a human-friendly way 647 data, err := json.MarshalIndent(d, "", " ") 648 if err != nil { 649 return fmt.Errorf("Failed to encode state: %s", err) 650 } 651 652 // We append a newline to the data because MarshalIndent doesn't 653 data = append(data, '\n') 654 655 // Write the data out to the dst 656 if _, err := io.Copy(dst, bytes.NewReader(data)); err != nil { 657 return fmt.Errorf("Failed to write state: %v", err) 658 } 659 660 return nil 661 } 662 663 // upgradeV1State is used to upgrade a V1 state representation 664 // into a proper State representation. 665 func upgradeV1State(old *StateV1) (*State, error) { 666 s := &State{} 667 s.init() 668 669 // Old format had no modules, so we migrate everything 670 // directly into the root module. 671 root := s.RootModule() 672 673 // Copy the outputs 674 root.Outputs = old.Outputs 675 676 // Upgrade the resources 677 for id, rs := range old.Resources { 678 newRs := &ResourceState{ 679 Type: rs.Type, 680 } 681 root.Resources[id] = newRs 682 683 // Migrate to an instance state 684 instance := &InstanceState{ 685 ID: rs.ID, 686 Attributes: rs.Attributes, 687 } 688 689 // Check if this is the primary or tainted instance 690 if _, ok := old.Tainted[id]; ok { 691 newRs.Tainted = append(newRs.Tainted, instance) 692 } else { 693 newRs.Primary = instance 694 } 695 696 // Warn if the resource uses Extra, as there is 697 // no upgrade path for this! Now totally deprecated. 698 if len(rs.Extra) > 0 { 699 log.Printf( 700 "[WARN] Resource %s uses deprecated attribute "+ 701 "storage, state file upgrade may be incomplete.", 702 rs.ID, 703 ) 704 } 705 } 706 return s, nil 707 } 708 709 // moduleStateSort implements sort.Interface to sort module states 710 type moduleStateSort []*ModuleState 711 712 func (s moduleStateSort) Len() int { 713 return len(s) 714 } 715 716 func (s moduleStateSort) Less(i, j int) bool { 717 a := s[i] 718 b := s[j] 719 720 // If the lengths are different, then the shorter one always wins 721 if len(a.Path) != len(b.Path) { 722 return len(a.Path) < len(b.Path) 723 } 724 725 // Otherwise, compare by last path element 726 idx := len(a.Path) - 1 727 return a.Path[idx] < b.Path[idx] 728 } 729 730 func (s moduleStateSort) Swap(i, j int) { 731 s[i], s[j] = s[j], s[i] 732 }