github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/terraform/state_add.go (about)

     1  package terraform
     2  
     3  import "fmt"
     4  
     5  // Add adds the item in the state at the given address.
     6  //
     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).
    11  //
    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.
    16  //
    17  // The full semantics of Add:
    18  //
    19  //                      ┌───────────────────┬───────────────────┬───────────────────┐
    20  //                      │  Module Address   │ Resource Address  │ Instance Address  │
    21  //    ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤
    22  //    │   ModuleState   │         ✓         │         x         │         x         │
    23  //    ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
    24  //    │  ResourceState  │         ✓         │         ✓         │      maybe*       │
    25  //    ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
    26  //    │ Instance State  │         ✓         │         ✓         │         ✓         │
    27  //    └─────────────────┴───────────────────┴───────────────────┴───────────────────┘
    28  //
    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"
    32  //
    33  func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error {
    34  	// Parse the address
    35  
    36  	toAddr, err := ParseResourceAddress(toAddrRaw)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	// Parse the from address
    42  	fromAddr, err := ParseResourceAddress(fromAddrRaw)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	// Determine the types
    48  	from := detectValueAddLoc(raw)
    49  	to := detectAddrAddLoc(toAddr)
    50  
    51  	// Find the function to do this
    52  	fromMap, ok := stateAddFuncs[from]
    53  	if !ok {
    54  		return fmt.Errorf("invalid source to add to state: %T", raw)
    55  	}
    56  	f, ok := fromMap[to]
    57  	if !ok {
    58  		return fmt.Errorf("invalid destination: %s (%d)", toAddr, to)
    59  	}
    60  
    61  	// Call the migrator
    62  	if err := f(s, fromAddr, toAddr, raw); err != nil {
    63  		return err
    64  	}
    65  
    66  	// Prune the state
    67  	s.prune()
    68  	return nil
    69  }
    70  
    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
    74  	// and children.
    75  	root := raw
    76  	var rest []*ModuleState
    77  	if list, ok := raw.([]*ModuleState); ok {
    78  		// We need at least one item
    79  		if len(list) == 0 {
    80  			return fmt.Errorf("module move with no value to: %s", addr)
    81  		}
    82  
    83  		// The first item is always the root
    84  		root = list[0]
    85  		if len(list) > 1 {
    86  			rest = list[1:]
    87  		}
    88  	}
    89  
    90  	// Get the actual module state
    91  	src := root.(*ModuleState).deepcopy()
    92  
    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)
    97  	}
    98  
    99  	// Create it and copy our outputs and dependencies
   100  	mod := s.AddModule(path)
   101  	mod.Outputs = src.Outputs
   102  	mod.Dependencies = src.Dependencies
   103  
   104  	// Go through the resources perform an add for each of those
   105  	for k, v := range src.Resources {
   106  		resourceKey, err := ParseResourceStateKey(k)
   107  		if err != nil {
   108  			return err
   109  		}
   110  
   111  		// Update the resource address for this
   112  		addrCopy := *addr
   113  		addrCopy.Type = resourceKey.Type
   114  		addrCopy.Name = resourceKey.Name
   115  		addrCopy.Index = resourceKey.Index
   116  		addrCopy.Mode = resourceKey.Mode
   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  		Mode:  addr.Mode,
   337  	}).String()
   338  	exists = true
   339  	resource, ok := mod.Resources[resourceKey]
   340  	if !ok {
   341  		resource = &ResourceState{Type: addr.Type}
   342  		resource.init()
   343  		mod.Resources[resourceKey] = resource
   344  		exists = false
   345  	}
   346  	if addType == stateAddResource {
   347  		return resource, exists
   348  	}
   349  
   350  	// Get the instance
   351  	exists = true
   352  	instance := &InstanceState{}
   353  	switch addr.InstanceType {
   354  	case TypePrimary, TypeTainted:
   355  		if v := resource.Primary; v != nil {
   356  			instance = resource.Primary
   357  		} else {
   358  			exists = false
   359  		}
   360  	case TypeDeposed:
   361  		idx := addr.Index
   362  		if addr.Index < 0 {
   363  			idx = 0
   364  		}
   365  		if len(resource.Deposed) > idx {
   366  			instance = resource.Deposed[idx]
   367  		} else {
   368  			resource.Deposed = append(resource.Deposed, instance)
   369  			exists = false
   370  		}
   371  	}
   372  
   373  	return instance, exists
   374  }