]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | } |