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