github.com/ricardclau/terraform@v0.6.17-0.20160519222547-283e3ae6b5a9/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 tainted
   137  	if len(src.Tainted) > 0 {
   138  		resource.Tainted = src.Tainted
   139  	}
   140  
   141  	// Move all deposed
   142  	if len(src.Deposed) > 0 {
   143  		resource.Deposed = src.Deposed
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
   150  	src := raw.(*InstanceState).DeepCopy()
   151  
   152  	// Create the instance
   153  	instanceRaw, _ := stateAddInitAddr(s, addr)
   154  	instance := instanceRaw.(*InstanceState)
   155  
   156  	// Set it
   157  	*instance = *src
   158  
   159  	return nil
   160  }
   161  
   162  func stateAddFunc_Instance_Module(
   163  	s *State, from, to *ResourceAddress, raw interface{}) error {
   164  	addr := *to
   165  	addr.Type = from.Type
   166  	addr.Name = from.Name
   167  
   168  	return s.Add(from.String(), addr.String(), raw)
   169  }
   170  
   171  func stateAddFunc_Instance_Resource(
   172  	s *State, from, to *ResourceAddress, raw interface{}) error {
   173  	addr := *to
   174  	addr.InstanceType = TypePrimary
   175  	addr.InstanceTypeSet = true
   176  
   177  	return s.Add(from.String(), addr.String(), raw)
   178  }
   179  
   180  // stateAddFunc is the type of function for adding an item to a state
   181  type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error
   182  
   183  // stateAddFuncs has the full matrix mapping of the state adders.
   184  var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc
   185  
   186  func init() {
   187  	stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{
   188  		stateAddModule: {
   189  			stateAddModule: stateAddFunc_Module_Module,
   190  		},
   191  		stateAddResource: {
   192  			stateAddModule:   stateAddFunc_Resource_Module,
   193  			stateAddResource: stateAddFunc_Resource_Resource,
   194  		},
   195  		stateAddInstance: {
   196  			stateAddInstance: stateAddFunc_Instance_Instance,
   197  			stateAddModule:   stateAddFunc_Instance_Module,
   198  			stateAddResource: stateAddFunc_Instance_Resource,
   199  		},
   200  	}
   201  }
   202  
   203  // stateAddLoc is an enum to represent the location where state is being
   204  // moved from/to. We use this for quick lookups in a function map.
   205  type stateAddLoc uint
   206  
   207  const (
   208  	stateAddInvalid stateAddLoc = iota
   209  	stateAddModule
   210  	stateAddResource
   211  	stateAddInstance
   212  )
   213  
   214  // detectAddrAddLoc detects the state type for the given address. This
   215  // function is specifically not unit tested since we consider the State.Add
   216  // functionality to be comprehensive enough to cover this.
   217  func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc {
   218  	if addr.Name == "" {
   219  		return stateAddModule
   220  	}
   221  
   222  	if !addr.InstanceTypeSet {
   223  		return stateAddResource
   224  	}
   225  
   226  	return stateAddInstance
   227  }
   228  
   229  // detectValueAddLoc determines the stateAddLoc value from the raw value
   230  // that is some State structure.
   231  func detectValueAddLoc(raw interface{}) stateAddLoc {
   232  	switch raw.(type) {
   233  	case *ModuleState:
   234  		return stateAddModule
   235  	case *ResourceState:
   236  		return stateAddResource
   237  	case *InstanceState:
   238  		return stateAddInstance
   239  	default:
   240  		return stateAddInvalid
   241  	}
   242  }
   243  
   244  // stateAddInitAddr takes a ResourceAddress and creates the non-existing
   245  // resources up to that point, returning the empty (or existing) interface
   246  // at that address.
   247  func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) {
   248  	addType := detectAddrAddLoc(addr)
   249  
   250  	// Get the module
   251  	path := append([]string{"root"}, addr.Path...)
   252  	exists := true
   253  	mod := s.ModuleByPath(path)
   254  	if mod == nil {
   255  		mod = s.AddModule(path)
   256  		exists = false
   257  	}
   258  	if addType == stateAddModule {
   259  		return mod, exists
   260  	}
   261  
   262  	// Add the resource
   263  	resourceKey := (&ResourceStateKey{
   264  		Name:  addr.Name,
   265  		Type:  addr.Type,
   266  		Index: addr.Index,
   267  	}).String()
   268  	exists = true
   269  	resource, ok := mod.Resources[resourceKey]
   270  	if !ok {
   271  		resource = &ResourceState{Type: addr.Type}
   272  		resource.init()
   273  		mod.Resources[resourceKey] = resource
   274  		exists = false
   275  	}
   276  	if addType == stateAddResource {
   277  		return resource, exists
   278  	}
   279  
   280  	// Get the instance
   281  	exists = true
   282  	instance := &InstanceState{}
   283  	switch addr.InstanceType {
   284  	case TypePrimary:
   285  		if v := resource.Primary; v != nil {
   286  			instance = resource.Primary
   287  		} else {
   288  			exists = false
   289  		}
   290  	case TypeTainted:
   291  		idx := addr.Index
   292  		if addr.Index < 0 {
   293  			idx = 0
   294  		}
   295  		if len(resource.Tainted) > idx {
   296  			instance = resource.Tainted[idx]
   297  		} else {
   298  			resource.Tainted = append(resource.Tainted, instance)
   299  			exists = false
   300  		}
   301  	case TypeDeposed:
   302  		idx := addr.Index
   303  		if addr.Index < 0 {
   304  			idx = 0
   305  		}
   306  		if len(resource.Deposed) > idx {
   307  			instance = resource.Deposed[idx]
   308  		} else {
   309  			resource.Deposed = append(resource.Deposed, instance)
   310  			exists = false
   311  		}
   312  	}
   313  
   314  	return instance, exists
   315  }