5 // Add adds the item in the state at the given address.
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).
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.
17 // The full semantics of Add:
19 // ┌───────────────────┬───────────────────┬───────────────────┐
20 // │ Module Address │ Resource Address │ Instance Address │
21 // ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤
22 // │ ModuleState │ ✓ │ x │ x │
23 // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
24 // │ ResourceState │ ✓ │ ✓ │ maybe* │
25 // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
26 // │ Instance State │ ✓ │ ✓ │ ✓ │
27 // └─────────────────┴───────────────────┴───────────────────┴───────────────────┘
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"
33 func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error {
36 toAddr, err := ParseResourceAddress(toAddrRaw)
41 // Parse the from address
42 fromAddr, err := ParseResourceAddress(fromAddrRaw)
47 // Determine the types
48 from := detectValueAddLoc(raw)
49 to := detectAddrAddLoc(toAddr)
51 // Find the function to do this
52 fromMap, ok := stateAddFuncs[from]
54 return fmt.Errorf("invalid source to add to state: %T", raw)
58 return fmt.Errorf("invalid destination: %s (%d)", toAddr, to)
62 if err := f(s, fromAddr, toAddr, raw); err != nil {
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
76 var rest []*ModuleState
77 if list, ok := raw.([]*ModuleState); ok {
78 // We need at least one item
80 return fmt.Errorf("module move with no value to: %s", addr)
83 // The first item is always the root
90 // Get the actual module state
91 src := root.(*ModuleState).deepcopy()
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)
99 // Create it and copy our outputs and dependencies
100 mod := s.AddModule(path)
101 mod.Outputs = src.Outputs
102 mod.Dependencies = src.Dependencies
104 // Go through the resources perform an add for each of those
105 for k, v := range src.Resources {
106 resourceKey, err := ParseResourceStateKey(k)
111 // Update the resource address for this
113 addrCopy.Type = resourceKey.Type
114 addrCopy.Name = resourceKey.Name
115 addrCopy.Index = resourceKey.Index
116 addrCopy.Mode = resourceKey.Mode
119 if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil {
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) {
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...)
137 s.Add(fromAddr.String(), addrCopy.String(), item)
143 func stateAddFunc_Resource_Module(
144 s *State, from, to *ResourceAddress, raw interface{}) error {
145 // Build the more specific to addr
147 addr.Type = from.Type
148 addr.Name = from.Name
150 return s.Add(from.String(), addr.String(), raw)
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
157 if list, ok := raw.([]*ResourceState); ok {
158 // We need at least one item
160 return fmt.Errorf("resource move with no value to: %s", addr)
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 {
167 "multiple resources can't be moved to a single index: "+
168 "%s => %s", fromAddr, addr)
171 // Add each with a specific index
172 for i, rs := range list {
173 addrCopy := addr.Copy()
176 if err := s.Add(fromAddr.String(), addrCopy.String(), rs); err != nil {
184 src := raw.(*ResourceState).deepcopy()
186 // Initialize the resource
187 resourceRaw, exists := stateAddInitAddr(s, addr)
189 return fmt.Errorf("resource exists and not empty: %s", addr)
191 resource := resourceRaw.(*ResourceState)
192 resource.Type = src.Type
193 resource.Dependencies = src.Dependencies
194 resource.Provider = src.Provider
197 if src.Primary != nil {
199 addrCopy.InstanceType = TypePrimary
200 addrCopy.InstanceTypeSet = true
201 if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil {
207 if len(src.Deposed) > 0 {
208 resource.Deposed = src.Deposed
214 func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
215 src := raw.(*InstanceState).DeepCopy()
217 // Create the instance
218 instanceRaw, _ := stateAddInitAddr(s, addr)
219 instance := instanceRaw.(*InstanceState)
227 func stateAddFunc_Instance_Module(
228 s *State, from, to *ResourceAddress, raw interface{}) error {
230 addr.Type = from.Type
231 addr.Name = from.Name
233 return s.Add(from.String(), addr.String(), raw)
236 func stateAddFunc_Instance_Resource(
237 s *State, from, to *ResourceAddress, raw interface{}) error {
239 addr.InstanceType = TypePrimary
240 addr.InstanceTypeSet = true
242 return s.Add(from.String(), addr.String(), raw)
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
248 // stateAddFuncs has the full matrix mapping of the state adders.
249 var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc
252 stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{
254 stateAddModule: stateAddFunc_Module_Module,
257 stateAddModule: stateAddFunc_Resource_Module,
258 stateAddResource: stateAddFunc_Resource_Resource,
261 stateAddInstance: stateAddFunc_Instance_Instance,
262 stateAddModule: stateAddFunc_Instance_Module,
263 stateAddResource: stateAddFunc_Instance_Resource,
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
273 stateAddInvalid stateAddLoc = iota
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 {
284 return stateAddModule
287 if !addr.InstanceTypeSet {
288 return stateAddResource
291 return stateAddInstance
294 // detectValueAddLoc determines the stateAddLoc value from the raw value
295 // that is some State structure.
296 func detectValueAddLoc(raw interface{}) stateAddLoc {
299 return stateAddModule
301 return stateAddModule
303 return stateAddResource
304 case []*ResourceState:
305 return stateAddResource
307 return stateAddInstance
309 return stateAddInvalid
313 // stateAddInitAddr takes a ResourceAddress and creates the non-existing
314 // resources up to that point, returning the empty (or existing) interface
316 func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) {
317 addType := detectAddrAddLoc(addr)
320 path := append([]string{"root"}, addr.Path...)
322 mod := s.ModuleByPath(path)
324 mod = s.AddModule(path)
327 if addType == stateAddModule {
332 resourceKey := (&ResourceStateKey{
339 resource, ok := mod.Resources[resourceKey]
341 resource = &ResourceState{Type: addr.Type}
343 mod.Resources[resourceKey] = resource
346 if addType == stateAddResource {
347 return resource, exists
352 instance := &InstanceState{}
353 switch addr.InstanceType {
354 case TypePrimary, TypeTainted:
355 if v := resource.Primary; v != nil {
356 instance = resource.Primary
365 if len(resource.Deposed) > idx {
366 instance = resource.Deposed[idx]
368 resource.Deposed = append(resource.Deposed, instance)
373 return instance, exists