github.com/serbaut/terraform@v0.6.12-0.20160607213102-ac2d195cc560/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 full semantics of Add:
    15  //
    16  //                         ┌───────────────────────┬───────────────────────┬───────────────────────┐
    17  //                         │    Module Address     │   Resource Address    │   Instance Address    │
    18  // ┌───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤
    19  // │      ModuleState      │           ✓           │           x           │           x           │
    20  // ├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤
    21  // │     ResourceState     │           ✓           │           ✓           │        maybe*         │
    22  // ├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤
    23  // │    Instance State     │           ✓           │           ✓           │           ✓           │
    24  // └───────────────────────┴───────────────────────┴───────────────────────┴───────────────────────┘
    25  //
    26  // *maybe - Resources can be added at an instance address only if the resource
    27  //          represents a single instance (primary). Example:
    28  //          "aws_instance.foo" can be moved to "aws_instance.bar.tainted"
    29  //
    30  func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error {
    31  	// Parse the address
    32  	toAddr, err := ParseResourceAddress(toAddrRaw)
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	// Parse the from address
    38  	fromAddr, err := ParseResourceAddress(fromAddrRaw)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	// Determine the types
    44  	from := detectValueAddLoc(raw)
    45  	to := detectAddrAddLoc(toAddr)
    46  
    47  	// Find the function to do this
    48  	fromMap, ok := stateAddFuncs[from]
    49  	if !ok {
    50  		return fmt.Errorf("invalid source to add to state: %T", raw)
    51  	}
    52  	f, ok := fromMap[to]
    53  	if !ok {
    54  		return fmt.Errorf("invalid destination: %s (%d)", toAddr, to)
    55  	}
    56  
    57  	// Call the migrator
    58  	if err := f(s, fromAddr, toAddr, raw); err != nil {
    59  		return err
    60  	}
    61  
    62  	// Prune the state
    63  	s.prune()
    64  	return nil
    65  }
    66  
    67  func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
    68  	src := raw.(*ModuleState).deepcopy()
    69  
    70  	// If the target module exists, it is an error
    71  	path := append([]string{"root"}, addr.Path...)
    72  	if s.ModuleByPath(path) != nil {
    73  		return fmt.Errorf("module target is not empty: %s", addr)
    74  	}
    75  
    76  	// Create it and copy our outputs and dependencies
    77  	mod := s.AddModule(path)
    78  	mod.Outputs = src.Outputs
    79  	mod.Dependencies = src.Dependencies
    80  
    81  	// Go through the resources perform an add for each of those
    82  	for k, v := range src.Resources {
    83  		resourceKey, err := ParseResourceStateKey(k)
    84  		if err != nil {
    85  			return err
    86  		}
    87  
    88  		// Update the resource address for this
    89  		addrCopy := *addr
    90  		addrCopy.Type = resourceKey.Type
    91  		addrCopy.Name = resourceKey.Name
    92  		addrCopy.Index = resourceKey.Index
    93  
    94  		// Perform an add
    95  		if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func stateAddFunc_Resource_Module(
   104  	s *State, from, to *ResourceAddress, raw interface{}) error {
   105  	// Build the more specific to addr
   106  	addr := *to
   107  	addr.Type = from.Type
   108  	addr.Name = from.Name
   109  
   110  	return s.Add(from.String(), addr.String(), raw)
   111  }
   112  
   113  func stateAddFunc_Resource_Resource(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
   114  	src := raw.(*ResourceState).deepcopy()
   115  
   116  	// Initialize the resource
   117  	resourceRaw, exists := stateAddInitAddr(s, addr)
   118  	if exists {
   119  		return fmt.Errorf("resource exists and not empty: %s", addr)
   120  	}
   121  	resource := resourceRaw.(*ResourceState)
   122  	resource.Type = src.Type
   123  	resource.Dependencies = src.Dependencies
   124  	resource.Provider = src.Provider
   125  
   126  	// Move the primary
   127  	if src.Primary != nil {
   128  		addrCopy := *addr
   129  		addrCopy.InstanceType = TypePrimary
   130  		addrCopy.InstanceTypeSet = true
   131  		if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil {
   132  			return err
   133  		}
   134  	}
   135  
   136  	// Move all deposed
   137  	if len(src.Deposed) > 0 {
   138  		resource.Deposed = src.Deposed
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
   145  	src := raw.(*InstanceState).DeepCopy()
   146  
   147  	// Create the instance
   148  	instanceRaw, _ := stateAddInitAddr(s, addr)
   149  	instance := instanceRaw.(*InstanceState)
   150  
   151  	// Set it
   152  	*instance = *src
   153  
   154  	return nil
   155  }
   156  
   157  func stateAddFunc_Instance_Module(
   158  	s *State, from, to *ResourceAddress, raw interface{}) error {
   159  	addr := *to
   160  	addr.Type = from.Type
   161  	addr.Name = from.Name
   162  
   163  	return s.Add(from.String(), addr.String(), raw)
   164  }
   165  
   166  func stateAddFunc_Instance_Resource(
   167  	s *State, from, to *ResourceAddress, raw interface{}) error {
   168  	addr := *to
   169  	addr.InstanceType = TypePrimary
   170  	addr.InstanceTypeSet = true
   171  
   172  	return s.Add(from.String(), addr.String(), raw)
   173  }
   174  
   175  // stateAddFunc is the type of function for adding an item to a state
   176  type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error
   177  
   178  // stateAddFuncs has the full matrix mapping of the state adders.
   179  var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc
   180  
   181  func init() {
   182  	stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{
   183  		stateAddModule: {
   184  			stateAddModule: stateAddFunc_Module_Module,
   185  		},
   186  		stateAddResource: {
   187  			stateAddModule:   stateAddFunc_Resource_Module,
   188  			stateAddResource: stateAddFunc_Resource_Resource,
   189  		},
   190  		stateAddInstance: {
   191  			stateAddInstance: stateAddFunc_Instance_Instance,
   192  			stateAddModule:   stateAddFunc_Instance_Module,
   193  			stateAddResource: stateAddFunc_Instance_Resource,
   194  		},
   195  	}
   196  }
   197  
   198  // stateAddLoc is an enum to represent the location where state is being
   199  // moved from/to. We use this for quick lookups in a function map.
   200  type stateAddLoc uint
   201  
   202  const (
   203  	stateAddInvalid stateAddLoc = iota
   204  	stateAddModule
   205  	stateAddResource
   206  	stateAddInstance
   207  )
   208  
   209  // detectAddrAddLoc detects the state type for the given address. This
   210  // function is specifically not unit tested since we consider the State.Add
   211  // functionality to be comprehensive enough to cover this.
   212  func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc {
   213  	if addr.Name == "" {
   214  		return stateAddModule
   215  	}
   216  
   217  	if !addr.InstanceTypeSet {
   218  		return stateAddResource
   219  	}
   220  
   221  	return stateAddInstance
   222  }
   223  
   224  // detectValueAddLoc determines the stateAddLoc value from the raw value
   225  // that is some State structure.
   226  func detectValueAddLoc(raw interface{}) stateAddLoc {
   227  	switch raw.(type) {
   228  	case *ModuleState:
   229  		return stateAddModule
   230  	case *ResourceState:
   231  		return stateAddResource
   232  	case *InstanceState:
   233  		return stateAddInstance
   234  	default:
   235  		return stateAddInvalid
   236  	}
   237  }
   238  
   239  // stateAddInitAddr takes a ResourceAddress and creates the non-existing
   240  // resources up to that point, returning the empty (or existing) interface
   241  // at that address.
   242  func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) {
   243  	addType := detectAddrAddLoc(addr)
   244  
   245  	// Get the module
   246  	path := append([]string{"root"}, addr.Path...)
   247  	exists := true
   248  	mod := s.ModuleByPath(path)
   249  	if mod == nil {
   250  		mod = s.AddModule(path)
   251  		exists = false
   252  	}
   253  	if addType == stateAddModule {
   254  		return mod, exists
   255  	}
   256  
   257  	// Add the resource
   258  	resourceKey := (&ResourceStateKey{
   259  		Name:  addr.Name,
   260  		Type:  addr.Type,
   261  		Index: addr.Index,
   262  	}).String()
   263  	exists = true
   264  	resource, ok := mod.Resources[resourceKey]
   265  	if !ok {
   266  		resource = &ResourceState{Type: addr.Type}
   267  		resource.init()
   268  		mod.Resources[resourceKey] = resource
   269  		exists = false
   270  	}
   271  	if addType == stateAddResource {
   272  		return resource, exists
   273  	}
   274  
   275  	// Get the instance
   276  	exists = true
   277  	instance := &InstanceState{}
   278  	switch addr.InstanceType {
   279  	case TypePrimary, TypeTainted:
   280  		if v := resource.Primary; v != nil {
   281  			instance = resource.Primary
   282  		} else {
   283  			exists = false
   284  		}
   285  	case TypeDeposed:
   286  		idx := addr.Index
   287  		if addr.Index < 0 {
   288  			idx = 0
   289  		}
   290  		if len(resource.Deposed) > idx {
   291  			instance = resource.Deposed[idx]
   292  		} else {
   293  			resource.Deposed = append(resource.Deposed, instance)
   294  			exists = false
   295  		}
   296  	}
   297  
   298  	return instance, exists
   299  }