github.com/serbaut/terraform@v0.6.12-0.20160607213102-ac2d195cc560/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 deposed 137 if len(src.Deposed) > 0 { 138 resource.Deposed = src.Deposed 139 } 140 141 return nil 142 } 143 144 func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { 145 src := raw.(*InstanceState).DeepCopy() 146 147 // Create the instance 148 instanceRaw, _ := stateAddInitAddr(s, addr) 149 instance := instanceRaw.(*InstanceState) 150 151 // Set it 152 *instance = *src 153 154 return nil 155 } 156 157 func stateAddFunc_Instance_Module( 158 s *State, from, to *ResourceAddress, raw interface{}) error { 159 addr := *to 160 addr.Type = from.Type 161 addr.Name = from.Name 162 163 return s.Add(from.String(), addr.String(), raw) 164 } 165 166 func stateAddFunc_Instance_Resource( 167 s *State, from, to *ResourceAddress, raw interface{}) error { 168 addr := *to 169 addr.InstanceType = TypePrimary 170 addr.InstanceTypeSet = true 171 172 return s.Add(from.String(), addr.String(), raw) 173 } 174 175 // stateAddFunc is the type of function for adding an item to a state 176 type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error 177 178 // stateAddFuncs has the full matrix mapping of the state adders. 179 var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc 180 181 func init() { 182 stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{ 183 stateAddModule: { 184 stateAddModule: stateAddFunc_Module_Module, 185 }, 186 stateAddResource: { 187 stateAddModule: stateAddFunc_Resource_Module, 188 stateAddResource: stateAddFunc_Resource_Resource, 189 }, 190 stateAddInstance: { 191 stateAddInstance: stateAddFunc_Instance_Instance, 192 stateAddModule: stateAddFunc_Instance_Module, 193 stateAddResource: stateAddFunc_Instance_Resource, 194 }, 195 } 196 } 197 198 // stateAddLoc is an enum to represent the location where state is being 199 // moved from/to. We use this for quick lookups in a function map. 200 type stateAddLoc uint 201 202 const ( 203 stateAddInvalid stateAddLoc = iota 204 stateAddModule 205 stateAddResource 206 stateAddInstance 207 ) 208 209 // detectAddrAddLoc detects the state type for the given address. This 210 // function is specifically not unit tested since we consider the State.Add 211 // functionality to be comprehensive enough to cover this. 212 func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc { 213 if addr.Name == "" { 214 return stateAddModule 215 } 216 217 if !addr.InstanceTypeSet { 218 return stateAddResource 219 } 220 221 return stateAddInstance 222 } 223 224 // detectValueAddLoc determines the stateAddLoc value from the raw value 225 // that is some State structure. 226 func detectValueAddLoc(raw interface{}) stateAddLoc { 227 switch raw.(type) { 228 case *ModuleState: 229 return stateAddModule 230 case *ResourceState: 231 return stateAddResource 232 case *InstanceState: 233 return stateAddInstance 234 default: 235 return stateAddInvalid 236 } 237 } 238 239 // stateAddInitAddr takes a ResourceAddress and creates the non-existing 240 // resources up to that point, returning the empty (or existing) interface 241 // at that address. 242 func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) { 243 addType := detectAddrAddLoc(addr) 244 245 // Get the module 246 path := append([]string{"root"}, addr.Path...) 247 exists := true 248 mod := s.ModuleByPath(path) 249 if mod == nil { 250 mod = s.AddModule(path) 251 exists = false 252 } 253 if addType == stateAddModule { 254 return mod, exists 255 } 256 257 // Add the resource 258 resourceKey := (&ResourceStateKey{ 259 Name: addr.Name, 260 Type: addr.Type, 261 Index: addr.Index, 262 }).String() 263 exists = true 264 resource, ok := mod.Resources[resourceKey] 265 if !ok { 266 resource = &ResourceState{Type: addr.Type} 267 resource.init() 268 mod.Resources[resourceKey] = resource 269 exists = false 270 } 271 if addType == stateAddResource { 272 return resource, exists 273 } 274 275 // Get the instance 276 exists = true 277 instance := &InstanceState{} 278 switch addr.InstanceType { 279 case TypePrimary, TypeTainted: 280 if v := resource.Primary; v != nil { 281 instance = resource.Primary 282 } else { 283 exists = false 284 } 285 case TypeDeposed: 286 idx := addr.Index 287 if addr.Index < 0 { 288 idx = 0 289 } 290 if len(resource.Deposed) > idx { 291 instance = resource.Deposed[idx] 292 } else { 293 resource.Deposed = append(resource.Deposed, instance) 294 exists = false 295 } 296 } 297 298 return instance, exists 299 }