github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/terraform/state_add.go (about) 1 package terraform 2 3 import "fmt" 4 5 // Add adds the item in the state at the given address. 6 // 7 // The item can be a ModuleState, ResourceState, or InstanceState. Depending 8 // on the item type, the address may or may not be valid. For example, a 9 // module cannot be moved to a resource address, however a resource can be 10 // moved to a module address (it retains the same name, under that resource). 11 // 12 // The item can also be a []*ModuleState, which is the case for nested 13 // modules. In this case, Add will expect the zero-index to be the top-most 14 // module to add and will only nest children from there. For semantics, this 15 // is equivalent to module => module. 16 // 17 // The full semantics of Add: 18 // 19 // ┌───────────────────┬───────────────────┬───────────────────┐ 20 // │ Module Address │ Resource Address │ Instance Address │ 21 // ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤ 22 // │ ModuleState │ ✓ │ x │ x │ 23 // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤ 24 // │ ResourceState │ ✓ │ ✓ │ maybe* │ 25 // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤ 26 // │ Instance State │ ✓ │ ✓ │ ✓ │ 27 // └─────────────────┴───────────────────┴───────────────────┴───────────────────┘ 28 // 29 // *maybe - Resources can be added at an instance address only if the resource 30 // represents a single instance (primary). Example: 31 // "aws_instance.foo" can be moved to "aws_instance.bar.tainted" 32 // 33 func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error { 34 // Parse the address 35 36 toAddr, err := ParseResourceAddress(toAddrRaw) 37 if err != nil { 38 return err 39 } 40 41 // Parse the from address 42 fromAddr, err := ParseResourceAddress(fromAddrRaw) 43 if err != nil { 44 return err 45 } 46 47 // Determine the types 48 from := detectValueAddLoc(raw) 49 to := detectAddrAddLoc(toAddr) 50 51 // Find the function to do this 52 fromMap, ok := stateAddFuncs[from] 53 if !ok { 54 return fmt.Errorf("invalid source to add to state: %T", raw) 55 } 56 f, ok := fromMap[to] 57 if !ok { 58 return fmt.Errorf("invalid destination: %s (%d)", toAddr, to) 59 } 60 61 // Call the migrator 62 if err := f(s, fromAddr, toAddr, raw); err != nil { 63 return err 64 } 65 66 // Prune the state 67 s.prune() 68 return nil 69 } 70 71 func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 72 // raw can be either *ModuleState or []*ModuleState. The former means 73 // we're moving just one module. The latter means we're moving a module 74 // and children. 75 root := raw 76 var rest []*ModuleState 77 if list, ok := raw.([]*ModuleState); ok { 78 // We need at least one item 79 if len(list) == 0 { 80 return fmt.Errorf("module move with no value to: %s", addr) 81 } 82 83 // The first item is always the root 84 root = list[0] 85 if len(list) > 1 { 86 rest = list[1:] 87 } 88 } 89 90 // Get the actual module state 91 src := root.(*ModuleState).deepcopy() 92 93 // If the target module exists, it is an error 94 path := append([]string{"root"}, addr.Path...) 95 if s.ModuleByPath(path) != nil { 96 return fmt.Errorf("module target is not empty: %s", addr) 97 } 98 99 // Create it and copy our outputs and dependencies 100 mod := s.AddModule(path) 101 mod.Outputs = src.Outputs 102 mod.Dependencies = src.Dependencies 103 104 // Go through the resources perform an add for each of those 105 for k, v := range src.Resources { 106 resourceKey, err := ParseResourceStateKey(k) 107 if err != nil { 108 return err 109 } 110 111 // Update the resource address for this 112 addrCopy := *addr 113 addrCopy.Type = resourceKey.Type 114 addrCopy.Name = resourceKey.Name 115 addrCopy.Index = resourceKey.Index 116 addrCopy.Mode = resourceKey.Mode 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 Mode: addr.Mode, 337 }).String() 338 exists = true 339 resource, ok := mod.Resources[resourceKey] 340 if !ok { 341 resource = &ResourceState{Type: addr.Type} 342 resource.init() 343 mod.Resources[resourceKey] = resource 344 exists = false 345 } 346 if addType == stateAddResource { 347 return resource, exists 348 } 349 350 // Get the instance 351 exists = true 352 instance := &InstanceState{} 353 switch addr.InstanceType { 354 case TypePrimary, TypeTainted: 355 if v := resource.Primary; v != nil { 356 instance = resource.Primary 357 } else { 358 exists = false 359 } 360 case TypeDeposed: 361 idx := addr.Index 362 if addr.Index < 0 { 363 idx = 0 364 } 365 if len(resource.Deposed) > idx { 366 instance = resource.Deposed[idx] 367 } else { 368 resource.Deposed = append(resource.Deposed, instance) 369 exists = false 370 } 371 } 372 373 return instance, exists 374 }