github.com/sarguru/terraform@v0.6.17-0.20160525232901-8fcdfd7e3dc9/terraform/state_v1.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/mitchellh/copystructure" 7 ) 8 9 // stateV1 keeps track of a snapshot state-of-the-world that Terraform 10 // can use to keep track of what real world resources it is actually 11 // managing. 12 // 13 // stateV1 is _only used for the purposes of backwards compatibility 14 // and is no longer used in Terraform. 15 type stateV1 struct { 16 // Version is the protocol version. "1" for a StateV1. 17 Version int `json:"version"` 18 19 // Serial is incremented on any operation that modifies 20 // the State file. It is used to detect potentially conflicting 21 // updates. 22 Serial int64 `json:"serial"` 23 24 // Remote is used to track the metadata required to 25 // pull and push state files from a remote storage endpoint. 26 Remote *remoteStateV1 `json:"remote,omitempty"` 27 28 // Modules contains all the modules in a breadth-first order 29 Modules []*moduleStateV1 `json:"modules"` 30 } 31 32 // upgrade is used to upgrade a V1 state representation 33 // into a State (current) representation. 34 func (old *stateV1) upgrade() (*State, error) { 35 if old == nil { 36 return nil, nil 37 } 38 39 remote, err := old.Remote.upgrade() 40 if err != nil { 41 return nil, fmt.Errorf("Error upgrading State V1: %v", err) 42 } 43 44 modules := make([]*ModuleState, len(old.Modules)) 45 for i, module := range old.Modules { 46 upgraded, err := module.upgrade() 47 if err != nil { 48 return nil, fmt.Errorf("Error upgrading State V1: %v", err) 49 } 50 modules[i] = upgraded 51 } 52 if len(modules) == 0 { 53 modules = nil 54 } 55 56 newState := &State{ 57 Version: old.Version, 58 Serial: old.Serial, 59 Remote: remote, 60 Modules: modules, 61 } 62 63 newState.sort() 64 65 return newState, nil 66 } 67 68 type remoteStateV1 struct { 69 // Type controls the client we use for the remote state 70 Type string `json:"type"` 71 72 // Config is used to store arbitrary configuration that 73 // is type specific 74 Config map[string]string `json:"config"` 75 } 76 77 func (old *remoteStateV1) upgrade() (*RemoteState, error) { 78 if old == nil { 79 return nil, nil 80 } 81 82 config, err := copystructure.Copy(old.Config) 83 if err != nil { 84 return nil, fmt.Errorf("Error upgrading RemoteState V1: %v", err) 85 } 86 87 return &RemoteState{ 88 Type: old.Type, 89 Config: config.(map[string]string), 90 }, nil 91 } 92 93 type moduleStateV1 struct { 94 // Path is the import path from the root module. Modules imports are 95 // always disjoint, so the path represents amodule tree 96 Path []string `json:"path"` 97 98 // Outputs declared by the module and maintained for each module 99 // even though only the root module technically needs to be kept. 100 // This allows operators to inspect values at the boundaries. 101 Outputs map[string]string `json:"outputs"` 102 103 // Resources is a mapping of the logically named resource to 104 // the state of the resource. Each resource may actually have 105 // N instances underneath, although a user only needs to think 106 // about the 1:1 case. 107 Resources map[string]*resourceStateV1 `json:"resources"` 108 109 // Dependencies are a list of things that this module relies on 110 // existing to remain intact. For example: an module may depend 111 // on a VPC ID given by an aws_vpc resource. 112 // 113 // Terraform uses this information to build valid destruction 114 // orders and to warn the user if they're destroying a module that 115 // another resource depends on. 116 // 117 // Things can be put into this list that may not be managed by 118 // Terraform. If Terraform doesn't find a matching ID in the 119 // overall state, then it assumes it isn't managed and doesn't 120 // worry about it. 121 Dependencies []string `json:"depends_on,omitempty"` 122 } 123 124 func (old *moduleStateV1) upgrade() (*ModuleState, error) { 125 if old == nil { 126 return nil, nil 127 } 128 129 path, err := copystructure.Copy(old.Path) 130 if err != nil { 131 return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) 132 } 133 134 // Outputs needs upgrading to use the new structure 135 outputs := make(map[string]*OutputState) 136 for key, output := range old.Outputs { 137 outputs[key] = &OutputState{ 138 Type: "string", 139 Value: output, 140 Sensitive: false, 141 } 142 } 143 if len(outputs) == 0 { 144 outputs = nil 145 } 146 147 resources := make(map[string]*ResourceState) 148 for key, oldResource := range old.Resources { 149 upgraded, err := oldResource.upgrade() 150 if err != nil { 151 return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) 152 } 153 resources[key] = upgraded 154 } 155 if len(resources) == 0 { 156 resources = nil 157 } 158 159 dependencies, err := copystructure.Copy(old.Dependencies) 160 if err != nil { 161 return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) 162 } 163 164 return &ModuleState{ 165 Path: path.([]string), 166 Outputs: outputs, 167 Resources: resources, 168 Dependencies: dependencies.([]string), 169 }, nil 170 } 171 172 type resourceStateV1 struct { 173 // This is filled in and managed by Terraform, and is the resource 174 // type itself such as "mycloud_instance". If a resource provider sets 175 // this value, it won't be persisted. 176 Type string `json:"type"` 177 178 // Dependencies are a list of things that this resource relies on 179 // existing to remain intact. For example: an AWS instance might 180 // depend on a subnet (which itself might depend on a VPC, and so 181 // on). 182 // 183 // Terraform uses this information to build valid destruction 184 // orders and to warn the user if they're destroying a resource that 185 // another resource depends on. 186 // 187 // Things can be put into this list that may not be managed by 188 // Terraform. If Terraform doesn't find a matching ID in the 189 // overall state, then it assumes it isn't managed and doesn't 190 // worry about it. 191 Dependencies []string `json:"depends_on,omitempty"` 192 193 // Primary is the current active instance for this resource. 194 // It can be replaced but only after a successful creation. 195 // This is the instances on which providers will act. 196 Primary *instanceStateV1 `json:"primary"` 197 198 // Tainted is used to track any underlying instances that 199 // have been created but are in a bad or unknown state and 200 // need to be cleaned up subsequently. In the 201 // standard case, there is only at most a single instance. 202 // However, in pathological cases, it is possible for the number 203 // of instances to accumulate. 204 Tainted []*instanceStateV1 `json:"tainted,omitempty"` 205 206 // Deposed is used in the mechanics of CreateBeforeDestroy: the existing 207 // Primary is Deposed to get it out of the way for the replacement Primary to 208 // be created by Apply. If the replacement Primary creates successfully, the 209 // Deposed instance is cleaned up. If there were problems creating the 210 // replacement, the instance remains in the Deposed list so it can be 211 // destroyed in a future run. Functionally, Deposed instances are very 212 // similar to Tainted instances in that Terraform is only tracking them in 213 // order to remember to destroy them. 214 Deposed []*instanceStateV1 `json:"deposed,omitempty"` 215 216 // Provider is used when a resource is connected to a provider with an alias. 217 // If this string is empty, the resource is connected to the default provider, 218 // e.g. "aws_instance" goes with the "aws" provider. 219 // If the resource block contained a "provider" key, that value will be set here. 220 Provider string `json:"provider,omitempty"` 221 } 222 223 func (old *resourceStateV1) upgrade() (*ResourceState, error) { 224 if old == nil { 225 return nil, nil 226 } 227 228 dependencies, err := copystructure.Copy(old.Dependencies) 229 if err != nil { 230 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) 231 } 232 233 primary, err := old.Primary.upgrade() 234 if err != nil { 235 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) 236 } 237 238 tainted := make([]*InstanceState, len(old.Tainted)) 239 for i, v := range old.Tainted { 240 upgraded, err := v.upgrade() 241 if err != nil { 242 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) 243 } 244 tainted[i] = upgraded 245 } 246 if len(tainted) == 0 { 247 tainted = nil 248 } 249 250 deposed := make([]*InstanceState, len(old.Deposed)) 251 for i, v := range old.Deposed { 252 upgraded, err := v.upgrade() 253 if err != nil { 254 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) 255 } 256 deposed[i] = upgraded 257 } 258 if len(deposed) == 0 { 259 deposed = nil 260 } 261 262 return &ResourceState{ 263 Type: old.Type, 264 Dependencies: dependencies.([]string), 265 Primary: primary, 266 Tainted: tainted, 267 Deposed: deposed, 268 Provider: old.Provider, 269 }, nil 270 } 271 272 type instanceStateV1 struct { 273 // A unique ID for this resource. This is opaque to Terraform 274 // and is only meant as a lookup mechanism for the providers. 275 ID string `json:"id"` 276 277 // Attributes are basic information about the resource. Any keys here 278 // are accessible in variable format within Terraform configurations: 279 // ${resourcetype.name.attribute}. 280 Attributes map[string]string `json:"attributes,omitempty"` 281 282 // Ephemeral is used to store any state associated with this instance 283 // that is necessary for the Terraform run to complete, but is not 284 // persisted to a state file. 285 Ephemeral ephemeralStateV1 `json:"-"` 286 287 // Meta is a simple K/V map that is persisted to the State but otherwise 288 // ignored by Terraform core. It's meant to be used for accounting by 289 // external client code. 290 Meta map[string]string `json:"meta,omitempty"` 291 } 292 293 func (old *instanceStateV1) upgrade() (*InstanceState, error) { 294 if old == nil { 295 return nil, nil 296 } 297 298 attributes, err := copystructure.Copy(old.Attributes) 299 if err != nil { 300 return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) 301 } 302 ephemeral, err := old.Ephemeral.upgrade() 303 if err != nil { 304 return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) 305 } 306 meta, err := copystructure.Copy(old.Meta) 307 if err != nil { 308 return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) 309 } 310 311 return &InstanceState{ 312 ID: old.ID, 313 Attributes: attributes.(map[string]string), 314 Ephemeral: *ephemeral, 315 Meta: meta.(map[string]string), 316 }, nil 317 } 318 319 type ephemeralStateV1 struct { 320 // ConnInfo is used for the providers to export information which is 321 // used to connect to the resource for provisioning. For example, 322 // this could contain SSH or WinRM credentials. 323 ConnInfo map[string]string `json:"-"` 324 } 325 326 func (old *ephemeralStateV1) upgrade() (*EphemeralState, error) { 327 connInfo, err := copystructure.Copy(old.ConnInfo) 328 if err != nil { 329 return nil, fmt.Errorf("Error upgrading EphemeralState V1: %v", err) 330 } 331 return &EphemeralState{ 332 ConnInfo: connInfo.(map[string]string), 333 }, nil 334 }