github.com/ricardclau/terraform@v0.6.17-0.20160519222547-283e3ae6b5a9/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 full semantics of Add: 15 // 16 // ┌───────────────────────┬───────────────────────┬───────────────────────┐ 17 // │ Module Address │ Resource Address │ Instance Address │ 18 // ┌───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤ 19 // │ ModuleState │ ✓ │ x │ x │ 20 // ├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤ 21 // │ ResourceState │ ✓ │ ✓ │ maybe* │ 22 // ├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤ 23 // │ Instance State │ ✓ │ ✓ │ ✓ │ 24 // └───────────────────────┴───────────────────────┴───────────────────────┴───────────────────────┘ 25 // 26 // *maybe - Resources can be added at an instance address only if the resource 27 // represents a single instance (primary). Example: 28 // "aws_instance.foo" can be moved to "aws_instance.bar.tainted" 29 // 30 func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error { 31 // Parse the address 32 toAddr, err := ParseResourceAddress(toAddrRaw) 33 if err != nil { 34 return err 35 } 36 37 // Parse the from address 38 fromAddr, err := ParseResourceAddress(fromAddrRaw) 39 if err != nil { 40 return err 41 } 42 43 // Determine the types 44 from := detectValueAddLoc(raw) 45 to := detectAddrAddLoc(toAddr) 46 47 // Find the function to do this 48 fromMap, ok := stateAddFuncs[from] 49 if !ok { 50 return fmt.Errorf("invalid source to add to state: %T", raw) 51 } 52 f, ok := fromMap[to] 53 if !ok { 54 return fmt.Errorf("invalid destination: %s (%d)", toAddr, to) 55 } 56 57 // Call the migrator 58 if err := f(s, fromAddr, toAddr, raw); err != nil { 59 return err 60 } 61 62 // Prune the state 63 s.prune() 64 return nil 65 } 66 67 func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 68 src := raw.(*ModuleState).deepcopy() 69 70 // If the target module exists, it is an error 71 path := append([]string{"root"}, addr.Path...) 72 if s.ModuleByPath(path) != nil { 73 return fmt.Errorf("module target is not empty: %s", addr) 74 } 75 76 // Create it and copy our outputs and dependencies 77 mod := s.AddModule(path) 78 mod.Outputs = src.Outputs 79 mod.Dependencies = src.Dependencies 80 81 // Go through the resources perform an add for each of those 82 for k, v := range src.Resources { 83 resourceKey, err := ParseResourceStateKey(k) 84 if err != nil { 85 return err 86 } 87 88 // Update the resource address for this 89 addrCopy := *addr 90 addrCopy.Type = resourceKey.Type 91 addrCopy.Name = resourceKey.Name 92 addrCopy.Index = resourceKey.Index 93 94 // Perform an add 95 if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil { 96 return err 97 } 98 } 99 100 return nil 101 } 102 103 func stateAddFunc_Resource_Module( 104 s *State, from, to *ResourceAddress, raw interface{}) error { 105 // Build the more specific to addr 106 addr := *to 107 addr.Type = from.Type 108 addr.Name = from.Name 109 110 return s.Add(from.String(), addr.String(), raw) 111 } 112 113 func stateAddFunc_Resource_Resource(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 114 src := raw.(*ResourceState).deepcopy() 115 116 // Initialize the resource 117 resourceRaw, exists := stateAddInitAddr(s, addr) 118 if exists { 119 return fmt.Errorf("resource exists and not empty: %s", addr) 120 } 121 resource := resourceRaw.(*ResourceState) 122 resource.Type = src.Type 123 resource.Dependencies = src.Dependencies 124 resource.Provider = src.Provider 125 126 // Move the primary 127 if src.Primary != nil { 128 addrCopy := *addr 129 addrCopy.InstanceType = TypePrimary 130 addrCopy.InstanceTypeSet = true 131 if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil { 132 return err 133 } 134 } 135 136 // Move all tainted 137 if len(src.Tainted) > 0 { 138 resource.Tainted = src.Tainted 139 } 140 141 // Move all deposed 142 if len(src.Deposed) > 0 { 143 resource.Deposed = src.Deposed 144 } 145 146 return nil 147 } 148 149 func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 150 src := raw.(*InstanceState).DeepCopy() 151 152 // Create the instance 153 instanceRaw, _ := stateAddInitAddr(s, addr) 154 instance := instanceRaw.(*InstanceState) 155 156 // Set it 157 *instance = *src 158 159 return nil 160 } 161 162 func stateAddFunc_Instance_Module( 163 s *State, from, to *ResourceAddress, raw interface{}) error { 164 addr := *to 165 addr.Type = from.Type 166 addr.Name = from.Name 167 168 return s.Add(from.String(), addr.String(), raw) 169 } 170 171 func stateAddFunc_Instance_Resource( 172 s *State, from, to *ResourceAddress, raw interface{}) error { 173 addr := *to 174 addr.InstanceType = TypePrimary 175 addr.InstanceTypeSet = true 176 177 return s.Add(from.String(), addr.String(), raw) 178 } 179 180 // stateAddFunc is the type of function for adding an item to a state 181 type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error 182 183 // stateAddFuncs has the full matrix mapping of the state adders. 184 var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc 185 186 func init() { 187 stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{ 188 stateAddModule: { 189 stateAddModule: stateAddFunc_Module_Module, 190 }, 191 stateAddResource: { 192 stateAddModule: stateAddFunc_Resource_Module, 193 stateAddResource: stateAddFunc_Resource_Resource, 194 }, 195 stateAddInstance: { 196 stateAddInstance: stateAddFunc_Instance_Instance, 197 stateAddModule: stateAddFunc_Instance_Module, 198 stateAddResource: stateAddFunc_Instance_Resource, 199 }, 200 } 201 } 202 203 // stateAddLoc is an enum to represent the location where state is being 204 // moved from/to. We use this for quick lookups in a function map. 205 type stateAddLoc uint 206 207 const ( 208 stateAddInvalid stateAddLoc = iota 209 stateAddModule 210 stateAddResource 211 stateAddInstance 212 ) 213 214 // detectAddrAddLoc detects the state type for the given address. This 215 // function is specifically not unit tested since we consider the State.Add 216 // functionality to be comprehensive enough to cover this. 217 func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc { 218 if addr.Name == "" { 219 return stateAddModule 220 } 221 222 if !addr.InstanceTypeSet { 223 return stateAddResource 224 } 225 226 return stateAddInstance 227 } 228 229 // detectValueAddLoc determines the stateAddLoc value from the raw value 230 // that is some State structure. 231 func detectValueAddLoc(raw interface{}) stateAddLoc { 232 switch raw.(type) { 233 case *ModuleState: 234 return stateAddModule 235 case *ResourceState: 236 return stateAddResource 237 case *InstanceState: 238 return stateAddInstance 239 default: 240 return stateAddInvalid 241 } 242 } 243 244 // stateAddInitAddr takes a ResourceAddress and creates the non-existing 245 // resources up to that point, returning the empty (or existing) interface 246 // at that address. 247 func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) { 248 addType := detectAddrAddLoc(addr) 249 250 // Get the module 251 path := append([]string{"root"}, addr.Path...) 252 exists := true 253 mod := s.ModuleByPath(path) 254 if mod == nil { 255 mod = s.AddModule(path) 256 exists = false 257 } 258 if addType == stateAddModule { 259 return mod, exists 260 } 261 262 // Add the resource 263 resourceKey := (&ResourceStateKey{ 264 Name: addr.Name, 265 Type: addr.Type, 266 Index: addr.Index, 267 }).String() 268 exists = true 269 resource, ok := mod.Resources[resourceKey] 270 if !ok { 271 resource = &ResourceState{Type: addr.Type} 272 resource.init() 273 mod.Resources[resourceKey] = resource 274 exists = false 275 } 276 if addType == stateAddResource { 277 return resource, exists 278 } 279 280 // Get the instance 281 exists = true 282 instance := &InstanceState{} 283 switch addr.InstanceType { 284 case TypePrimary: 285 if v := resource.Primary; v != nil { 286 instance = resource.Primary 287 } else { 288 exists = false 289 } 290 case TypeTainted: 291 idx := addr.Index 292 if addr.Index < 0 { 293 idx = 0 294 } 295 if len(resource.Tainted) > idx { 296 instance = resource.Tainted[idx] 297 } else { 298 resource.Tainted = append(resource.Tainted, instance) 299 exists = false 300 } 301 case TypeDeposed: 302 idx := addr.Index 303 if addr.Index < 0 { 304 idx = 0 305 } 306 if len(resource.Deposed) > idx { 307 instance = resource.Deposed[idx] 308 } else { 309 resource.Deposed = append(resource.Deposed, instance) 310 exists = false 311 } 312 } 313 314 return instance, exists 315 }