2016-04-11 19:20:37 -05:00
|
|
|
package terraform
|
|
|
|
|
2016-11-21 17:13:15 -06:00
|
|
|
import "fmt"
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
// 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).
|
|
|
|
//
|
2016-08-18 14:05:42 -05:00
|
|
|
// 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.
|
|
|
|
//
|
2016-04-11 19:20:37 -05:00
|
|
|
// The full semantics of Add:
|
|
|
|
//
|
2016-08-24 14:37:34 -05:00
|
|
|
// ┌───────────────────┬───────────────────┬───────────────────┐
|
|
|
|
// │ Module Address │ Resource Address │ Instance Address │
|
|
|
|
// ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤
|
|
|
|
// │ ModuleState │ ✓ │ x │ x │
|
|
|
|
// ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
|
|
|
|
// │ ResourceState │ ✓ │ ✓ │ maybe* │
|
|
|
|
// ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
|
|
|
|
// │ Instance State │ ✓ │ ✓ │ ✓ │
|
|
|
|
// └─────────────────┴───────────────────┴───────────────────┴───────────────────┘
|
2016-04-11 19:20:37 -05:00
|
|
|
//
|
|
|
|
// *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"
|
|
|
|
//
|
2016-04-12 11:46:16 -05:00
|
|
|
func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error {
|
2016-04-11 19:20:37 -05:00
|
|
|
// Parse the address
|
2016-11-21 17:13:15 -06:00
|
|
|
|
2016-04-12 11:46:16 -05:00
|
|
|
toAddr, err := ParseResourceAddress(toAddrRaw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the from address
|
|
|
|
fromAddr, err := ParseResourceAddress(fromAddrRaw)
|
2016-04-11 19:20:37 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the types
|
|
|
|
from := detectValueAddLoc(raw)
|
2016-04-12 11:46:16 -05:00
|
|
|
to := detectAddrAddLoc(toAddr)
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
// 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 {
|
2016-04-12 11:46:16 -05:00
|
|
|
return fmt.Errorf("invalid destination: %s (%d)", toAddr, to)
|
2016-04-11 19:20:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call the migrator
|
2016-04-12 11:46:16 -05:00
|
|
|
if err := f(s, fromAddr, toAddr, raw); err != nil {
|
2016-04-11 19:20:37 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prune the state
|
|
|
|
s.prune()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:46:16 -05:00
|
|
|
func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
2016-08-18 14:05:42 -05:00
|
|
|
// 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()
|
2016-04-11 19:20:37 -05:00
|
|
|
|
2016-04-11 19:40:23 -05:00
|
|
|
// If the target module exists, it is an error
|
terraform: ugly huge change to weave in new HCL2-oriented types
Due to how deeply the configuration types go into Terraform Core, there
isn't a great way to switch out to HCL2 gradually. As a consequence, this
huge commit gets us from the old state to a _compilable_ new state, but
does not yet attempt to fix any tests and has a number of known missing
parts and bugs. We will continue to iterate on this in forthcoming
commits, heading back towards passing tests and making Terraform
fully-functional again.
The three main goals here are:
- Use the configuration models from the "configs" package instead of the
older models in the "config" package, which is now deprecated and
preserved only to help us write our migration tool.
- Do expression inspection and evaluation using the functionality of the
new "lang" package, instead of the Interpolator type and related
functionality in the main "terraform" package.
- Represent addresses of various objects using types in the addrs package,
rather than hand-constructed strings. This is not critical to support
the above, but was a big help during the implementation of these other
points since it made it much more explicit what kind of address is
expected in each context.
Since our new packages are built to accommodate some future planned
features that are not yet implemented (e.g. the "for_each" argument on
resources, "count"/"for_each" on modules), and since there's still a fair
amount of functionality still using old-style APIs, there is a moderate
amount of shimming here to connect new assumptions with old, hopefully in
a way that makes it easier to find and eliminate these shims later.
I apologize in advance to the person who inevitably just found this huge
commit while spelunking through the commit history.
2018-04-30 12:33:53 -05:00
|
|
|
path := normalizeModulePath(addr.Path)
|
2016-04-11 19:40:23 -05:00
|
|
|
if s.ModuleByPath(path) != nil {
|
|
|
|
return fmt.Errorf("module target is not empty: %s", addr)
|
|
|
|
}
|
|
|
|
|
2016-04-11 20:05:55 -05:00
|
|
|
// Create it and copy our outputs and dependencies
|
|
|
|
mod := s.AddModule(path)
|
|
|
|
mod.Outputs = src.Outputs
|
|
|
|
mod.Dependencies = src.Dependencies
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
// 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
|
2016-11-21 17:26:29 -06:00
|
|
|
addrCopy.Mode = resourceKey.Mode
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
// Perform an add
|
2016-04-12 11:46:16 -05:00
|
|
|
if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil {
|
2016-04-11 19:20:37 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-18 14:05:42 -05:00
|
|
|
// 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
|
2016-08-18 16:39:07 -05:00
|
|
|
extra := item.Path[len(src.Path):]
|
2016-08-18 14:05:42 -05:00
|
|
|
addrCopy := addr.Copy()
|
2016-08-18 16:13:53 -05:00
|
|
|
addrCopy.Path = append(addrCopy.Path, extra...)
|
2016-08-18 14:05:42 -05:00
|
|
|
|
|
|
|
// Add it
|
|
|
|
s.Add(fromAddr.String(), addrCopy.String(), item)
|
|
|
|
}
|
|
|
|
|
2016-04-11 19:20:37 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:46:16 -05:00
|
|
|
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 {
|
2016-08-19 10:46:52 -05:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2016-08-19 10:51:31 -05:00
|
|
|
// If there is an index, this is an error since we can't assign
|
|
|
|
// a set of resources to a single index
|
2016-08-19 10:54:53 -05:00
|
|
|
if addr.Index >= 0 && len(list) > 1 {
|
2016-08-19 10:51:31 -05:00
|
|
|
return fmt.Errorf(
|
|
|
|
"multiple resources can't be moved to a single index: "+
|
|
|
|
"%s => %s", fromAddr, addr)
|
|
|
|
}
|
|
|
|
|
2016-08-19 10:46:52 -05:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-04-11 20:29:51 -05:00
|
|
|
src := raw.(*ResourceState).deepcopy()
|
2016-04-11 19:20:37 -05:00
|
|
|
|
2016-04-11 20:22:11 -05:00
|
|
|
// Initialize the resource
|
2016-04-11 20:26:15 -05:00
|
|
|
resourceRaw, exists := stateAddInitAddr(s, addr)
|
|
|
|
if exists {
|
|
|
|
return fmt.Errorf("resource exists and not empty: %s", addr)
|
|
|
|
}
|
|
|
|
resource := resourceRaw.(*ResourceState)
|
2016-04-11 20:22:11 -05:00
|
|
|
resource.Type = src.Type
|
2016-04-11 20:29:51 -05:00
|
|
|
resource.Dependencies = src.Dependencies
|
|
|
|
resource.Provider = src.Provider
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
// Move the primary
|
|
|
|
if src.Primary != nil {
|
|
|
|
addrCopy := *addr
|
|
|
|
addrCopy.InstanceType = TypePrimary
|
|
|
|
addrCopy.InstanceTypeSet = true
|
2016-04-12 11:46:16 -05:00
|
|
|
if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil {
|
2016-04-11 19:20:37 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-11 20:41:48 -05:00
|
|
|
// Move all deposed
|
|
|
|
if len(src.Deposed) > 0 {
|
|
|
|
resource.Deposed = src.Deposed
|
|
|
|
}
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:46:16 -05:00
|
|
|
func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
2016-05-11 15:07:33 -05:00
|
|
|
src := raw.(*InstanceState).DeepCopy()
|
2016-04-11 19:20:37 -05:00
|
|
|
|
2016-04-11 20:28:36 -05:00
|
|
|
// Create the instance
|
|
|
|
instanceRaw, _ := stateAddInitAddr(s, addr)
|
|
|
|
instance := instanceRaw.(*InstanceState)
|
2016-04-11 19:20:37 -05:00
|
|
|
|
2016-04-11 20:41:48 -05:00
|
|
|
// Set it
|
2016-08-24 14:48:47 -05:00
|
|
|
instance.Set(src)
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:55:17 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:51:43 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2016-04-11 19:20:37 -05:00
|
|
|
// stateAddFunc is the type of function for adding an item to a state
|
2016-04-12 11:46:16 -05:00
|
|
|
type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error
|
2016-04-11 19:20:37 -05:00
|
|
|
|
|
|
|
// 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: {
|
2016-04-12 11:46:16 -05:00
|
|
|
stateAddModule: stateAddFunc_Resource_Module,
|
2016-04-11 19:20:37 -05:00
|
|
|
stateAddResource: stateAddFunc_Resource_Resource,
|
|
|
|
},
|
|
|
|
stateAddInstance: {
|
|
|
|
stateAddInstance: stateAddFunc_Instance_Instance,
|
2016-04-12 11:55:17 -05:00
|
|
|
stateAddModule: stateAddFunc_Instance_Module,
|
2016-04-12 11:51:43 -05:00
|
|
|
stateAddResource: stateAddFunc_Instance_Resource,
|
2016-04-11 19:20:37 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2016-08-18 14:05:42 -05:00
|
|
|
case []*ModuleState:
|
|
|
|
return stateAddModule
|
2016-04-11 19:20:37 -05:00
|
|
|
case *ResourceState:
|
|
|
|
return stateAddResource
|
2016-08-19 10:46:52 -05:00
|
|
|
case []*ResourceState:
|
|
|
|
return stateAddResource
|
2016-04-11 19:20:37 -05:00
|
|
|
case *InstanceState:
|
|
|
|
return stateAddInstance
|
|
|
|
default:
|
|
|
|
return stateAddInvalid
|
|
|
|
}
|
|
|
|
}
|
2016-04-11 20:22:11 -05:00
|
|
|
|
|
|
|
// stateAddInitAddr takes a ResourceAddress and creates the non-existing
|
|
|
|
// resources up to that point, returning the empty (or existing) interface
|
|
|
|
// at that address.
|
2016-04-11 20:26:15 -05:00
|
|
|
func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) {
|
2016-04-11 20:22:11 -05:00
|
|
|
addType := detectAddrAddLoc(addr)
|
|
|
|
|
|
|
|
// Get the module
|
terraform: ugly huge change to weave in new HCL2-oriented types
Due to how deeply the configuration types go into Terraform Core, there
isn't a great way to switch out to HCL2 gradually. As a consequence, this
huge commit gets us from the old state to a _compilable_ new state, but
does not yet attempt to fix any tests and has a number of known missing
parts and bugs. We will continue to iterate on this in forthcoming
commits, heading back towards passing tests and making Terraform
fully-functional again.
The three main goals here are:
- Use the configuration models from the "configs" package instead of the
older models in the "config" package, which is now deprecated and
preserved only to help us write our migration tool.
- Do expression inspection and evaluation using the functionality of the
new "lang" package, instead of the Interpolator type and related
functionality in the main "terraform" package.
- Represent addresses of various objects using types in the addrs package,
rather than hand-constructed strings. This is not critical to support
the above, but was a big help during the implementation of these other
points since it made it much more explicit what kind of address is
expected in each context.
Since our new packages are built to accommodate some future planned
features that are not yet implemented (e.g. the "for_each" argument on
resources, "count"/"for_each" on modules), and since there's still a fair
amount of functionality still using old-style APIs, there is a moderate
amount of shimming here to connect new assumptions with old, hopefully in
a way that makes it easier to find and eliminate these shims later.
I apologize in advance to the person who inevitably just found this huge
commit while spelunking through the commit history.
2018-04-30 12:33:53 -05:00
|
|
|
path := normalizeModulePath(addr.Path)
|
2016-04-11 20:26:15 -05:00
|
|
|
exists := true
|
2016-04-11 20:22:11 -05:00
|
|
|
mod := s.ModuleByPath(path)
|
|
|
|
if mod == nil {
|
|
|
|
mod = s.AddModule(path)
|
2016-04-11 20:26:15 -05:00
|
|
|
exists = false
|
2016-04-11 20:22:11 -05:00
|
|
|
}
|
|
|
|
if addType == stateAddModule {
|
2016-04-11 20:26:15 -05:00
|
|
|
return mod, exists
|
2016-04-11 20:22:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add the resource
|
|
|
|
resourceKey := (&ResourceStateKey{
|
|
|
|
Name: addr.Name,
|
|
|
|
Type: addr.Type,
|
|
|
|
Index: addr.Index,
|
2016-11-21 17:26:29 -06:00
|
|
|
Mode: addr.Mode,
|
2016-04-11 20:22:11 -05:00
|
|
|
}).String()
|
2016-04-11 20:26:15 -05:00
|
|
|
exists = true
|
2016-04-11 20:22:11 -05:00
|
|
|
resource, ok := mod.Resources[resourceKey]
|
|
|
|
if !ok {
|
|
|
|
resource = &ResourceState{Type: addr.Type}
|
|
|
|
resource.init()
|
|
|
|
mod.Resources[resourceKey] = resource
|
2016-04-11 20:26:15 -05:00
|
|
|
exists = false
|
2016-04-11 20:22:11 -05:00
|
|
|
}
|
|
|
|
if addType == stateAddResource {
|
2016-04-11 20:26:15 -05:00
|
|
|
return resource, exists
|
2016-04-11 20:22:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the instance
|
2016-04-11 20:26:15 -05:00
|
|
|
exists = true
|
2016-04-11 20:41:48 -05:00
|
|
|
instance := &InstanceState{}
|
2016-04-11 20:22:11 -05:00
|
|
|
switch addr.InstanceType {
|
2016-05-23 05:23:48 -05:00
|
|
|
case TypePrimary, TypeTainted:
|
2016-04-11 20:41:48 -05:00
|
|
|
if v := resource.Primary; v != nil {
|
|
|
|
instance = resource.Primary
|
|
|
|
} else {
|
|
|
|
exists = false
|
|
|
|
}
|
2016-04-11 20:22:11 -05:00
|
|
|
case TypeDeposed:
|
|
|
|
idx := addr.Index
|
|
|
|
if addr.Index < 0 {
|
|
|
|
idx = 0
|
|
|
|
}
|
|
|
|
if len(resource.Deposed) > idx {
|
|
|
|
instance = resource.Deposed[idx]
|
2016-04-11 20:41:48 -05:00
|
|
|
} else {
|
|
|
|
resource.Deposed = append(resource.Deposed, instance)
|
|
|
|
exists = false
|
2016-04-11 20:22:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-11 20:26:15 -05:00
|
|
|
return instance, exists
|
2016-04-11 20:22:11 -05:00
|
|
|
}
|