github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/terraform/state_add.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 ) 6 7 // Add adds the item in the state at the given address. 8 // 9 // The item can be a ModuleState, ResourceState, or InstanceState. Depending 10 // on the item type, the address may or may not be valid. For example, a 11 // module cannot be moved to a resource address, however a resource can be 12 // moved to a module address (it retains the same name, under that resource). 13 // 14 // The item can also be a []*ModuleState, which is the case for nested 15 // modules. In this case, Add will expect the zero-index to be the top-most 16 // module to add and will only nest children from there. For semantics, this 17 // is equivalent to module => module. 18 // 19 // The full semantics of Add: 20 // 21 // ┌───────────────────┬───────────────────┬───────────────────┐ 22 // │ Module Address │ Resource Address │ Instance Address │ 23 // ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤ 24 // │ ModuleState │ ✓ │ x │ x │ 25 // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤ 26 // │ ResourceState │ ✓ │ ✓ │ maybe* │ 27 // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤ 28 // │ Instance State │ ✓ │ ✓ │ ✓ │ 29 // └─────────────────┴───────────────────┴───────────────────┴───────────────────┘ 30 // 31 // *maybe - Resources can be added at an instance address only if the resource 32 // represents a single instance (primary). Example: 33 // "aws_instance.foo" can be moved to "aws_instance.bar.tainted" 34 // 35 func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error { 36 // Parse the address 37 toAddr, err := ParseResourceAddress(toAddrRaw) 38 if err != nil { 39 return err 40 } 41 42 // Parse the from address 43 fromAddr, err := ParseResourceAddress(fromAddrRaw) 44 if err != nil { 45 return err 46 } 47 48 // Determine the types 49 from := detectValueAddLoc(raw) 50 to := detectAddrAddLoc(toAddr) 51 52 // Find the function to do this 53 fromMap, ok := stateAddFuncs[from] 54 if !ok { 55 return fmt.Errorf("invalid source to add to state: %T", raw) 56 } 57 f, ok := fromMap[to] 58 if !ok { 59 return fmt.Errorf("invalid destination: %s (%d)", toAddr, to) 60 } 61 62 // Call the migrator 63 if err := f(s, fromAddr, toAddr, raw); err != nil { 64 return err 65 } 66 67 // Prune the state 68 s.prune() 69 return nil 70 } 71 72 func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 73 // raw can be either *ModuleState or []*ModuleState. The former means 74 // we're moving just one module. The latter means we're moving a module 75 // and children. 76 root := raw 77 var rest []*ModuleState 78 if list, ok := raw.([]*ModuleState); ok { 79 // We need at least one item 80 if len(list) == 0 { 81 return fmt.Errorf("module move with no value to: %s", addr) 82 } 83 84 // The first item is always the root 85 root = list[0] 86 if len(list) > 1 { 87 rest = list[1:] 88 } 89 } 90 91 // Get the actual module state 92 src := root.(*ModuleState).deepcopy() 93 94 // If the target module exists, it is an error 95 path := append([]string{"root"}, addr.Path...) 96 if s.ModuleByPath(path) != nil { 97 return fmt.Errorf("module target is not empty: %s", addr) 98 } 99 100 // Create it and copy our outputs and dependencies 101 mod := s.AddModule(path) 102 mod.Outputs = src.Outputs 103 mod.Dependencies = src.Dependencies 104 105 // Go through the resources perform an add for each of those 106 for k, v := range src.Resources { 107 resourceKey, err := ParseResourceStateKey(k) 108 if err != nil { 109 return err 110 } 111 112 // Update the resource address for this 113 addrCopy := *addr 114 addrCopy.Type = resourceKey.Type 115 addrCopy.Name = resourceKey.Name 116 addrCopy.Index = resourceKey.Index 117 118 // Perform an add 119 if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil { 120 return err 121 } 122 } 123 124 // Add all the children if we have them 125 for _, item := range rest { 126 // If item isn't a descendent of our root, then ignore it 127 if !src.IsDescendent(item) { 128 continue 129 } 130 131 // It is! Strip the leading prefix and attach that to our address 132 extra := item.Path[len(src.Path):] 133 addrCopy := addr.Copy() 134 addrCopy.Path = append(addrCopy.Path, extra...) 135 136 // Add it 137 s.Add(fromAddr.String(), addrCopy.String(), item) 138 } 139 140 return nil 141 } 142 143 func stateAddFunc_Resource_Module( 144 s *State, from, to *ResourceAddress, raw interface{}) error { 145 // Build the more specific to addr 146 addr := *to 147 addr.Type = from.Type 148 addr.Name = from.Name 149 150 return s.Add(from.String(), addr.String(), raw) 151 } 152 153 func stateAddFunc_Resource_Resource(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 154 // raw can be either *ResourceState or []*ResourceState. The former means 155 // we're moving just one resource. The latter means we're moving a count 156 // of resources. 157 if list, ok := raw.([]*ResourceState); ok { 158 // We need at least one item 159 if len(list) == 0 { 160 return fmt.Errorf("resource move with no value to: %s", addr) 161 } 162 163 // If there is an index, this is an error since we can't assign 164 // a set of resources to a single index 165 if addr.Index >= 0 && len(list) > 1 { 166 return fmt.Errorf( 167 "multiple resources can't be moved to a single index: "+ 168 "%s => %s", fromAddr, addr) 169 } 170 171 // Add each with a specific index 172 for i, rs := range list { 173 addrCopy := addr.Copy() 174 addrCopy.Index = i 175 176 if err := s.Add(fromAddr.String(), addrCopy.String(), rs); err != nil { 177 return err 178 } 179 } 180 181 return nil 182 } 183 184 src := raw.(*ResourceState).deepcopy() 185 186 // Initialize the resource 187 resourceRaw, exists := stateAddInitAddr(s, addr) 188 if exists { 189 return fmt.Errorf("resource exists and not empty: %s", addr) 190 } 191 resource := resourceRaw.(*ResourceState) 192 resource.Type = src.Type 193 resource.Dependencies = src.Dependencies 194 resource.Provider = src.Provider 195 196 // Move the primary 197 if src.Primary != nil { 198 addrCopy := *addr 199 addrCopy.InstanceType = TypePrimary 200 addrCopy.InstanceTypeSet = true 201 if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil { 202 return err 203 } 204 } 205 206 // Move all deposed 207 if len(src.Deposed) > 0 { 208 resource.Deposed = src.Deposed 209 } 210 211 return nil 212 } 213 214 func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 215 src := raw.(*InstanceState).DeepCopy() 216 217 // Create the instance 218 instanceRaw, _ := stateAddInitAddr(s, addr) 219 instance := instanceRaw.(*InstanceState) 220 221 // Set it 222 instance.Set(src) 223 224 return nil 225 } 226 227 func stateAddFunc_Instance_Module( 228 s *State, from, to *ResourceAddress, raw interface{}) error { 229 addr := *to 230 addr.Type = from.Type 231 addr.Name = from.Name 232 233 return s.Add(from.String(), addr.String(), raw) 234 } 235 236 func stateAddFunc_Instance_Resource( 237 s *State, from, to *ResourceAddress, raw interface{}) error { 238 addr := *to 239 addr.InstanceType = TypePrimary 240 addr.InstanceTypeSet = true 241 242 return s.Add(from.String(), addr.String(), raw) 243 } 244 245 // stateAddFunc is the type of function for adding an item to a state 246 type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error 247 248 // stateAddFuncs has the full matrix mapping of the state adders. 249 var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc 250 251 func init() { 252 stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{ 253 stateAddModule: { 254 stateAddModule: stateAddFunc_Module_Module, 255 }, 256 stateAddResource: { 257 stateAddModule: stateAddFunc_Resource_Module, 258 stateAddResource: stateAddFunc_Resource_Resource, 259 }, 260 stateAddInstance: { 261 stateAddInstance: stateAddFunc_Instance_Instance, 262 stateAddModule: stateAddFunc_Instance_Module, 263 stateAddResource: stateAddFunc_Instance_Resource, 264 }, 265 } 266 } 267 268 // stateAddLoc is an enum to represent the location where state is being 269 // moved from/to. We use this for quick lookups in a function map. 270 type stateAddLoc uint 271 272 const ( 273 stateAddInvalid stateAddLoc = iota 274 stateAddModule 275 stateAddResource 276 stateAddInstance 277 ) 278 279 // detectAddrAddLoc detects the state type for the given address. This 280 // function is specifically not unit tested since we consider the State.Add 281 // functionality to be comprehensive enough to cover this. 282 func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc { 283 if addr.Name == "" { 284 return stateAddModule 285 } 286 287 if !addr.InstanceTypeSet { 288 return stateAddResource 289 } 290 291 return stateAddInstance 292 } 293 294 // detectValueAddLoc determines the stateAddLoc value from the raw value 295 // that is some State structure. 296 func detectValueAddLoc(raw interface{}) stateAddLoc { 297 switch raw.(type) { 298 case *ModuleState: 299 return stateAddModule 300 case []*ModuleState: 301 return stateAddModule 302 case *ResourceState: 303 return stateAddResource 304 case []*ResourceState: 305 return stateAddResource 306 case *InstanceState: 307 return stateAddInstance 308 default: 309 return stateAddInvalid 310 } 311 } 312 313 // stateAddInitAddr takes a ResourceAddress and creates the non-existing 314 // resources up to that point, returning the empty (or existing) interface 315 // at that address. 316 func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) { 317 addType := detectAddrAddLoc(addr) 318 319 // Get the module 320 path := append([]string{"root"}, addr.Path...) 321 exists := true 322 mod := s.ModuleByPath(path) 323 if mod == nil { 324 mod = s.AddModule(path) 325 exists = false 326 } 327 if addType == stateAddModule { 328 return mod, exists 329 } 330 331 // Add the resource 332 resourceKey := (&ResourceStateKey{ 333 Name: addr.Name, 334 Type: addr.Type, 335 Index: addr.Index, 336 }).String() 337 exists = true 338 resource, ok := mod.Resources[resourceKey] 339 if !ok { 340 resource = &ResourceState{Type: addr.Type} 341 resource.init() 342 mod.Resources[resourceKey] = resource 343 exists = false 344 } 345 if addType == stateAddResource { 346 return resource, exists 347 } 348 349 // Get the instance 350 exists = true 351 instance := &InstanceState{} 352 switch addr.InstanceType { 353 case TypePrimary, TypeTainted: 354 if v := resource.Primary; v != nil { 355 instance = resource.Primary 356 } else { 357 exists = false 358 } 359 case TypeDeposed: 360 idx := addr.Index 361 if addr.Index < 0 { 362 idx = 0 363 } 364 if len(resource.Deposed) > idx { 365 instance = resource.Deposed[idx] 366 } else { 367 resource.Deposed = append(resource.Deposed, instance) 368 exists = false 369 } 370 } 371 372 return instance, exists 373 }