github.com/ricardclau/terraform@v0.6.17-0.20160519222547-283e3ae6b5a9/terraform/state_v0.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "encoding/gob" 6 "errors" 7 "fmt" 8 "io" 9 "sort" 10 "strings" 11 "sync" 12 13 "github.com/hashicorp/terraform/config" 14 "log" 15 ) 16 17 // The format byte is prefixed into the state file format so that we have 18 // the ability in the future to change the file format if we want for any 19 // reason. 20 const ( 21 stateFormatMagic = "tfstate" 22 stateFormatVersion byte = 1 23 ) 24 25 // StateV0 is used to represent the state of Terraform files before 26 // 0.3. It is automatically upgraded to a modern State representation 27 // on start. 28 type StateV0 struct { 29 Outputs map[string]string 30 Resources map[string]*ResourceStateV0 31 Tainted map[string]struct{} 32 33 once sync.Once 34 } 35 36 func (s *StateV0) init() { 37 s.once.Do(func() { 38 if s.Resources == nil { 39 s.Resources = make(map[string]*ResourceStateV0) 40 } 41 42 if s.Tainted == nil { 43 s.Tainted = make(map[string]struct{}) 44 } 45 }) 46 } 47 48 func (s *StateV0) deepcopy() *StateV0 { 49 result := new(StateV0) 50 result.init() 51 if s != nil { 52 for k, v := range s.Resources { 53 result.Resources[k] = v 54 } 55 for k, v := range s.Tainted { 56 result.Tainted[k] = v 57 } 58 } 59 60 return result 61 } 62 63 // prune is a helper that removes any empty IDs from the state 64 // and cleans it up in general. 65 func (s *StateV0) prune() { 66 for k, v := range s.Resources { 67 if v.ID == "" { 68 delete(s.Resources, k) 69 } 70 } 71 } 72 73 // Orphans returns a list of keys of resources that are in the State 74 // but aren't present in the configuration itself. Hence, these keys 75 // represent the state of resources that are orphans. 76 func (s *StateV0) Orphans(c *config.Config) []string { 77 keys := make(map[string]struct{}) 78 for k, _ := range s.Resources { 79 keys[k] = struct{}{} 80 } 81 82 for _, r := range c.Resources { 83 delete(keys, r.Id()) 84 85 for k, _ := range keys { 86 if strings.HasPrefix(k, r.Id()+".") { 87 delete(keys, k) 88 } 89 } 90 } 91 92 result := make([]string, 0, len(keys)) 93 for k, _ := range keys { 94 result = append(result, k) 95 } 96 97 return result 98 } 99 100 func (s *StateV0) String() string { 101 if len(s.Resources) == 0 { 102 return "<no state>" 103 } 104 105 var buf bytes.Buffer 106 107 names := make([]string, 0, len(s.Resources)) 108 for name, _ := range s.Resources { 109 names = append(names, name) 110 } 111 sort.Strings(names) 112 113 for _, k := range names { 114 rs := s.Resources[k] 115 id := rs.ID 116 if id == "" { 117 id = "<not created>" 118 } 119 120 taintStr := "" 121 if _, ok := s.Tainted[k]; ok { 122 taintStr = " (tainted)" 123 } 124 125 buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr)) 126 buf.WriteString(fmt.Sprintf(" ID = %s\n", id)) 127 128 attrKeys := make([]string, 0, len(rs.Attributes)) 129 for ak, _ := range rs.Attributes { 130 if ak == "id" { 131 continue 132 } 133 134 attrKeys = append(attrKeys, ak) 135 } 136 sort.Strings(attrKeys) 137 138 for _, ak := range attrKeys { 139 av := rs.Attributes[ak] 140 buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av)) 141 } 142 143 if len(rs.Dependencies) > 0 { 144 buf.WriteString(fmt.Sprintf("\n Dependencies:\n")) 145 for _, dep := range rs.Dependencies { 146 buf.WriteString(fmt.Sprintf(" %s\n", dep.ID)) 147 } 148 } 149 } 150 151 if len(s.Outputs) > 0 { 152 buf.WriteString("\nOutputs:\n\n") 153 154 ks := make([]string, 0, len(s.Outputs)) 155 for k, _ := range s.Outputs { 156 ks = append(ks, k) 157 } 158 sort.Strings(ks) 159 160 for _, k := range ks { 161 v := s.Outputs[k] 162 buf.WriteString(fmt.Sprintf("%s = %s\n", k, v)) 163 } 164 } 165 166 return buf.String() 167 } 168 169 /// ResourceState holds the state of a resource that is used so that 170 // a provider can find and manage an existing resource as well as for 171 // storing attributes that are uesd to populate variables of child 172 // resources. 173 // 174 // Attributes has attributes about the created resource that are 175 // queryable in interpolation: "${type.id.attr}" 176 // 177 // Extra is just extra data that a provider can return that we store 178 // for later, but is not exposed in any way to the user. 179 type ResourceStateV0 struct { 180 // This is filled in and managed by Terraform, and is the resource 181 // type itself such as "mycloud_instance". If a resource provider sets 182 // this value, it won't be persisted. 183 Type string 184 185 // The attributes below are all meant to be filled in by the 186 // resource providers themselves. Documentation for each are above 187 // each element. 188 189 // A unique ID for this resource. This is opaque to Terraform 190 // and is only meant as a lookup mechanism for the providers. 191 ID string 192 193 // Attributes are basic information about the resource. Any keys here 194 // are accessible in variable format within Terraform configurations: 195 // ${resourcetype.name.attribute}. 196 Attributes map[string]string 197 198 // ConnInfo is used for the providers to export information which is 199 // used to connect to the resource for provisioning. For example, 200 // this could contain SSH or WinRM credentials. 201 ConnInfo map[string]string 202 203 // Extra information that the provider can store about a resource. 204 // This data is opaque, never shown to the user, and is sent back to 205 // the provider as-is for whatever purpose appropriate. 206 Extra map[string]interface{} 207 208 // Dependencies are a list of things that this resource relies on 209 // existing to remain intact. For example: an AWS instance might 210 // depend on a subnet (which itself might depend on a VPC, and so 211 // on). 212 // 213 // Terraform uses this information to build valid destruction 214 // orders and to warn the user if they're destroying a resource that 215 // another resource depends on. 216 // 217 // Things can be put into this list that may not be managed by 218 // Terraform. If Terraform doesn't find a matching ID in the 219 // overall state, then it assumes it isn't managed and doesn't 220 // worry about it. 221 Dependencies []ResourceDependency 222 } 223 224 // MergeDiff takes a ResourceDiff and merges the attributes into 225 // this resource state in order to generate a new state. This new 226 // state can be used to provide updated attribute lookups for 227 // variable interpolation. 228 // 229 // If the diff attribute requires computing the value, and hence 230 // won't be available until apply, the value is replaced with the 231 // computeID. 232 func (s *ResourceStateV0) MergeDiff(d *InstanceDiff) *ResourceStateV0 { 233 var result ResourceStateV0 234 if s != nil { 235 result = *s 236 } 237 238 result.Attributes = make(map[string]string) 239 if s != nil { 240 for k, v := range s.Attributes { 241 result.Attributes[k] = v 242 } 243 } 244 if d != nil { 245 for k, diff := range d.Attributes { 246 if diff.NewRemoved { 247 delete(result.Attributes, k) 248 continue 249 } 250 if diff.NewComputed { 251 result.Attributes[k] = config.UnknownVariableValue 252 continue 253 } 254 255 result.Attributes[k] = diff.New 256 } 257 } 258 259 return &result 260 } 261 262 func (s *ResourceStateV0) GoString() string { 263 return fmt.Sprintf("*%#v", *s) 264 } 265 266 // ResourceDependency maps a resource to another resource that it 267 // depends on to remain intact and uncorrupted. 268 type ResourceDependency struct { 269 // ID of the resource that we depend on. This ID should map 270 // directly to another ResourceState's ID. 271 ID string 272 } 273 274 // ReadStateV0 reads a state structure out of a reader in the format that 275 // was written by WriteState. 276 func ReadStateV0(src io.Reader) (*StateV0, error) { 277 var result *StateV0 278 var err error 279 n := 0 280 281 // Verify the magic bytes 282 magic := make([]byte, len(stateFormatMagic)) 283 for n < len(magic) { 284 n, err = src.Read(magic[n:]) 285 if err != nil { 286 return nil, fmt.Errorf("error while reading magic bytes: %s", err) 287 } 288 } 289 if string(magic) != stateFormatMagic { 290 return nil, fmt.Errorf("not a valid state file") 291 } 292 293 // Verify the version is something we can read 294 var formatByte [1]byte 295 n, err = src.Read(formatByte[:]) 296 if err != nil { 297 return nil, err 298 } 299 if n != len(formatByte) { 300 return nil, errors.New("failed to read state version byte") 301 } 302 303 if formatByte[0] != stateFormatVersion { 304 return nil, fmt.Errorf("unknown state file version: %d", formatByte[0]) 305 } 306 307 // Decode 308 dec := gob.NewDecoder(src) 309 if err := dec.Decode(&result); err != nil { 310 return nil, err 311 } 312 313 return result, nil 314 } 315 316 // upgradeV0State is used to upgrade a V0 state representation 317 // into a State (current) representation. 318 func (old *StateV0) upgrade() (*State, error) { 319 s := &State{} 320 s.init() 321 322 // Old format had no modules, so we migrate everything 323 // directly into the root module. 324 root := s.RootModule() 325 326 // Copy the outputs, first converting them to map[string]interface{} 327 oldOutputs := make(map[string]*OutputState, len(old.Outputs)) 328 for key, value := range old.Outputs { 329 oldOutputs[key] = &OutputState{ 330 Type: "string", 331 Sensitive: false, 332 Value: value, 333 } 334 } 335 root.Outputs = oldOutputs 336 337 // Upgrade the resources 338 for id, rs := range old.Resources { 339 newRs := &ResourceState{ 340 Type: rs.Type, 341 } 342 root.Resources[id] = newRs 343 344 // Migrate to an instance state 345 instance := &InstanceState{ 346 ID: rs.ID, 347 Attributes: rs.Attributes, 348 } 349 350 // Check if this is the primary or tainted instance 351 if _, ok := old.Tainted[id]; ok { 352 newRs.Tainted = append(newRs.Tainted, instance) 353 } else { 354 newRs.Primary = instance 355 } 356 357 // Warn if the resource uses Extra, as there is 358 // no upgrade path for this! Now totally deprecated. 359 if len(rs.Extra) > 0 { 360 log.Printf( 361 "[WARN] Resource %s uses deprecated attribute "+ 362 "storage, state file upgrade may be incomplete.", 363 rs.ID, 364 ) 365 } 366 } 367 return s, nil 368 }