github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/terraform/state_add.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  // Add adds the item in the state at the given address.
     8  //
     9  // The item can be a ModuleState, ResourceState, or InstanceState. Depending
    10  // on the item type, the address may or may not be valid. For example, a
    11  // module cannot be moved to a resource address, however a resource can be
    12  // moved to a module address (it retains the same name, under that resource).
    13  //
    14  // The item can also be a []*ModuleState, which is the case for nested
    15  // modules. In this case, Add will expect the zero-index to be the top-most
    16  // module to add and will only nest children from there. For semantics, this
    17  // is equivalent to module => module.
    18  //
    19  // The full semantics of Add:
    20  //
    21  //                      ┌───────────────────┬───────────────────┬───────────────────┐
    22  //                      │  Module Address   │ Resource Address  │ Instance Address  │
    23  //    ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤
    24  //    │   ModuleState   │         ✓         │         x         │         x         │
    25  //    ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
    26  //    │  ResourceState  │         ✓         │         ✓         │      maybe*       │
    27  //    ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
    28  //    │ Instance State  │         ✓         │         ✓         │         ✓         │
    29  //    └─────────────────┴───────────────────┴───────────────────┴───────────────────┘
    30  //
    31  // *maybe - Resources can be added at an instance address only if the resource
    32  //          represents a single instance (primary). Example:
    33  //          "aws_instance.foo" can be moved to "aws_instance.bar.tainted"
    34  //
    35  func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error {
    36  	// Parse the address
    37  	toAddr, err := ParseResourceAddress(toAddrRaw)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	// Parse the from address
    43  	fromAddr, err := ParseResourceAddress(fromAddrRaw)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	// Determine the types
    49  	from := detectValueAddLoc(raw)
    50  	to := detectAddrAddLoc(toAddr)
    51  
    52  	// Find the function to do this
    53  	fromMap, ok := stateAddFuncs[from]
    54  	if !ok {
    55  		return fmt.Errorf("invalid source to add to state: %T", raw)
    56  	}
    57  	f, ok := fromMap[to]
    58  	if !ok {
    59  		return fmt.Errorf("invalid destination: %s (%d)", toAddr, to)
    60  	}
    61  
    62  	// Call the migrator
    63  	if err := f(s, fromAddr, toAddr, raw); err != nil {
    64  		return err
    65  	}
    66  
    67  	// Prune the state
    68  	s.prune()
    69  	return nil
    70  }
    71  
    72  func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
    73  	// raw can be either *ModuleState or []*ModuleState. The former means
    74  	// we're moving just one module. The latter means we're moving a module
    75  	// and children.
    76  	root := raw
    77  	var rest []*ModuleState
    78  	if list, ok := raw.([]*ModuleState); ok {
    79  		// We need at least one item
    80  		if len(list) == 0 {
    81  			return fmt.Errorf("module move with no value to: %s", addr)
    82  		}
    83  
    84  		// The first item is always the root
    85  		root = list[0]
    86  		if len(list) > 1 {
    87  			rest = list[1:]
    88  		}
    89  	}
    90  
    91  	// Get the actual module state
    92  	src := root.(*ModuleState).deepcopy()
    93  
    94  	// If the target module exists, it is an error
    95  	path := append([]string{"root"}, addr.Path...)
    96  	if s.ModuleByPath(path) != nil {
    97  		return fmt.Errorf("module target is not empty: %s", addr)
    98  	}
    99  
   100  	// Create it and copy our outputs and dependencies
   101  	mod := s.AddModule(path)
   102  	mod.Outputs = src.Outputs
   103  	mod.Dependencies = src.Dependencies
   104  
   105  	// Go through the resources perform an add for each of those
   106  	for k, v := range src.Resources {
   107  		resourceKey, err := ParseResourceStateKey(k)
   108  		if err != nil {
   109  			return err
   110  		}
   111  
   112  		// Update the resource address for this
   113  		addrCopy := *addr
   114  		addrCopy.Type = resourceKey.Type
   115  		addrCopy.Name = resourceKey.Name
   116  		addrCopy.Index = resourceKey.Index
   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  	}).String()
   337  	exists = true
   338  	resource, ok := mod.Resources[resourceKey]
   339  	if !ok {
   340  		resource = &ResourceState{Type: addr.Type}
   341  		resource.init()
   342  		mod.Resources[resourceKey] = resource
   343  		exists = false
   344  	}
   345  	if addType == stateAddResource {
   346  		return resource, exists
   347  	}
   348  
   349  	// Get the instance
   350  	exists = true
   351  	instance := &InstanceState{}
   352  	switch addr.InstanceType {
   353  	case TypePrimary, TypeTainted:
   354  		if v := resource.Primary; v != nil {
   355  			instance = resource.Primary
   356  		} else {
   357  			exists = false
   358  		}
   359  	case TypeDeposed:
   360  		idx := addr.Index
   361  		if addr.Index < 0 {
   362  			idx = 0
   363  		}
   364  		if len(resource.Deposed) > idx {
   365  			instance = resource.Deposed[idx]
   366  		} else {
   367  			resource.Deposed = append(resource.Deposed, instance)
   368  			exists = false
   369  		}
   370  	}
   371  
   372  	return instance, exists
   373  }