package terraform import "fmt" // Add adds the item in the state at the given address. // // The item can be a ModuleState, ResourceState, or InstanceState. Depending // on the item type, the address may or may not be valid. For example, a // module cannot be moved to a resource address, however a resource can be // moved to a module address (it retains the same name, under that resource). // // The item can also be a []*ModuleState, which is the case for nested // modules. In this case, Add will expect the zero-index to be the top-most // module to add and will only nest children from there. For semantics, this // is equivalent to module => module. // // The full semantics of Add: // // ┌───────────────────┬───────────────────┬───────────────────┐ // │ Module Address │ Resource Address │ Instance Address │ // ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤ // │ ModuleState │ ✓ │ x │ x │ // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤ // │ ResourceState │ ✓ │ ✓ │ maybe* │ // ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤ // │ Instance State │ ✓ │ ✓ │ ✓ │ // └─────────────────┴───────────────────┴───────────────────┴───────────────────┘ // // *maybe - Resources can be added at an instance address only if the resource // represents a single instance (primary). Example: // "aws_instance.foo" can be moved to "aws_instance.bar.tainted" // func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error { // Parse the address toAddr, err := ParseResourceAddress(toAddrRaw) if err != nil { return err } // Parse the from address fromAddr, err := ParseResourceAddress(fromAddrRaw) if err != nil { return err } // Determine the types from := detectValueAddLoc(raw) to := detectAddrAddLoc(toAddr) // Find the function to do this fromMap, ok := stateAddFuncs[from] if !ok { return fmt.Errorf("invalid source to add to state: %T", raw) } f, ok := fromMap[to] if !ok { return fmt.Errorf("invalid destination: %s (%d)", toAddr, to) } // Call the migrator if err := f(s, fromAddr, toAddr, raw); err != nil { return err } // Prune the state s.prune() return nil } func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { // raw can be either *ModuleState or []*ModuleState. The former means // we're moving just one module. The latter means we're moving a module // and children. root := raw var rest []*ModuleState if list, ok := raw.([]*ModuleState); ok { // We need at least one item if len(list) == 0 { return fmt.Errorf("module move with no value to: %s", addr) } // The first item is always the root root = list[0] if len(list) > 1 { rest = list[1:] } } // Get the actual module state src := root.(*ModuleState).deepcopy() // If the target module exists, it is an error path := append([]string{"root"}, addr.Path...) if s.ModuleByPath(path) != nil { return fmt.Errorf("module target is not empty: %s", addr) } // Create it and copy our outputs and dependencies mod := s.AddModule(path) mod.Outputs = src.Outputs mod.Dependencies = src.Dependencies // Go through the resources perform an add for each of those for k, v := range src.Resources { resourceKey, err := ParseResourceStateKey(k) if err != nil { return err } // Update the resource address for this addrCopy := *addr addrCopy.Type = resourceKey.Type addrCopy.Name = resourceKey.Name addrCopy.Index = resourceKey.Index addrCopy.Mode = resourceKey.Mode // Perform an add if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil { return err } } // Add all the children if we have them for _, item := range rest { // If item isn't a descendent of our root, then ignore it if !src.IsDescendent(item) { continue } // It is! Strip the leading prefix and attach that to our address extra := item.Path[len(src.Path):] addrCopy := addr.Copy() addrCopy.Path = append(addrCopy.Path, extra...) // Add it s.Add(fromAddr.String(), addrCopy.String(), item) } return nil } func stateAddFunc_Resource_Module( s *State, from, to *ResourceAddress, raw interface{}) error { // Build the more specific to addr addr := *to addr.Type = from.Type addr.Name = from.Name return s.Add(from.String(), addr.String(), raw) } func stateAddFunc_Resource_Resource(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { // raw can be either *ResourceState or []*ResourceState. The former means // we're moving just one resource. The latter means we're moving a count // of resources. if list, ok := raw.([]*ResourceState); ok { // We need at least one item if len(list) == 0 { return fmt.Errorf("resource move with no value to: %s", addr) } // If there is an index, this is an error since we can't assign // a set of resources to a single index if addr.Index >= 0 && len(list) > 1 { return fmt.Errorf( "multiple resources can't be moved to a single index: "+ "%s => %s", fromAddr, addr) } // Add each with a specific index for i, rs := range list { addrCopy := addr.Copy() addrCopy.Index = i if err := s.Add(fromAddr.String(), addrCopy.String(), rs); err != nil { return err } } return nil } src := raw.(*ResourceState).deepcopy() // Initialize the resource resourceRaw, exists := stateAddInitAddr(s, addr) if exists { return fmt.Errorf("resource exists and not empty: %s", addr) } resource := resourceRaw.(*ResourceState) resource.Type = src.Type resource.Dependencies = src.Dependencies resource.Provider = src.Provider // Move the primary if src.Primary != nil { addrCopy := *addr addrCopy.InstanceType = TypePrimary addrCopy.InstanceTypeSet = true if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil { return err } } // Move all deposed if len(src.Deposed) > 0 { resource.Deposed = src.Deposed } return nil } func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error { src := raw.(*InstanceState).DeepCopy() // Create the instance instanceRaw, _ := stateAddInitAddr(s, addr) instance := instanceRaw.(*InstanceState) // Set it instance.Set(src) return nil } func stateAddFunc_Instance_Module( s *State, from, to *ResourceAddress, raw interface{}) error { addr := *to addr.Type = from.Type addr.Name = from.Name return s.Add(from.String(), addr.String(), raw) } func stateAddFunc_Instance_Resource( s *State, from, to *ResourceAddress, raw interface{}) error { addr := *to addr.InstanceType = TypePrimary addr.InstanceTypeSet = true return s.Add(from.String(), addr.String(), raw) } // stateAddFunc is the type of function for adding an item to a state type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error // stateAddFuncs has the full matrix mapping of the state adders. var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc func init() { stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{ stateAddModule: { stateAddModule: stateAddFunc_Module_Module, }, stateAddResource: { stateAddModule: stateAddFunc_Resource_Module, stateAddResource: stateAddFunc_Resource_Resource, }, stateAddInstance: { stateAddInstance: stateAddFunc_Instance_Instance, stateAddModule: stateAddFunc_Instance_Module, stateAddResource: stateAddFunc_Instance_Resource, }, } } // stateAddLoc is an enum to represent the location where state is being // moved from/to. We use this for quick lookups in a function map. type stateAddLoc uint const ( stateAddInvalid stateAddLoc = iota stateAddModule stateAddResource stateAddInstance ) // detectAddrAddLoc detects the state type for the given address. This // function is specifically not unit tested since we consider the State.Add // functionality to be comprehensive enough to cover this. func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc { if addr.Name == "" { return stateAddModule } if !addr.InstanceTypeSet { return stateAddResource } return stateAddInstance } // detectValueAddLoc determines the stateAddLoc value from the raw value // that is some State structure. func detectValueAddLoc(raw interface{}) stateAddLoc { switch raw.(type) { case *ModuleState: return stateAddModule case []*ModuleState: return stateAddModule case *ResourceState: return stateAddResource case []*ResourceState: return stateAddResource case *InstanceState: return stateAddInstance default: return stateAddInvalid } } // stateAddInitAddr takes a ResourceAddress and creates the non-existing // resources up to that point, returning the empty (or existing) interface // at that address. func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) { addType := detectAddrAddLoc(addr) // Get the module path := append([]string{"root"}, addr.Path...) exists := true mod := s.ModuleByPath(path) if mod == nil { mod = s.AddModule(path) exists = false } if addType == stateAddModule { return mod, exists } // Add the resource resourceKey := (&ResourceStateKey{ Name: addr.Name, Type: addr.Type, Index: addr.Index, Mode: addr.Mode, }).String() exists = true resource, ok := mod.Resources[resourceKey] if !ok { resource = &ResourceState{Type: addr.Type} resource.init() mod.Resources[resourceKey] = resource exists = false } if addType == stateAddResource { return resource, exists } // Get the instance exists = true instance := &InstanceState{} switch addr.InstanceType { case TypePrimary, TypeTainted: if v := resource.Primary; v != nil { instance = resource.Primary } else { exists = false } case TypeDeposed: idx := addr.Index if addr.Index < 0 { idx = 0 } if len(resource.Deposed) > idx { instance = resource.Deposed[idx] } else { resource.Deposed = append(resource.Deposed, instance) exists = false } } return instance, exists }