summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/terraform/state_add.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/state_add.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/state_add.go374
1 files changed, 374 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/state_add.go b/vendor/github.com/hashicorp/terraform/terraform/state_add.go
new file mode 100644
index 00000000..11637303
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/state_add.go
@@ -0,0 +1,374 @@
+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
+}