github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/terraform/transform_import_state.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  // ImportStateTransformer is a GraphTransformer that adds nodes to the
     8  // graph to represent the imports we want to do for resources.
     9  type ImportStateTransformer struct {
    10  	Targets []*ImportTarget
    11  }
    12  
    13  func (t *ImportStateTransformer) Transform(g *Graph) error {
    14  	nodes := make([]*graphNodeImportState, 0, len(t.Targets))
    15  	for _, target := range t.Targets {
    16  		addr, err := ParseResourceAddress(target.Addr)
    17  		if err != nil {
    18  			return fmt.Errorf(
    19  				"failed to parse resource address '%s': %s",
    20  				target.Addr, err)
    21  		}
    22  
    23  		nodes = append(nodes, &graphNodeImportState{
    24  			Addr: addr,
    25  			ID:   target.ID,
    26  		})
    27  	}
    28  
    29  	// Build the graph vertices
    30  	for _, n := range nodes {
    31  		g.Add(n)
    32  	}
    33  
    34  	return nil
    35  }
    36  
    37  type graphNodeImportState struct {
    38  	Addr *ResourceAddress // Addr is the resource address to import to
    39  	ID   string           // ID is the ID to import as
    40  
    41  	states []*InstanceState
    42  }
    43  
    44  func (n *graphNodeImportState) Name() string {
    45  	return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID)
    46  }
    47  
    48  func (n *graphNodeImportState) ProvidedBy() []string {
    49  	return []string{resourceProvider(n.Addr.Type, "")}
    50  }
    51  
    52  // GraphNodeSubPath
    53  func (n *graphNodeImportState) Path() []string {
    54  	return normalizeModulePath(n.Addr.Path)
    55  }
    56  
    57  // GraphNodeEvalable impl.
    58  func (n *graphNodeImportState) EvalTree() EvalNode {
    59  	var provider ResourceProvider
    60  	info := &InstanceInfo{
    61  		Id:         fmt.Sprintf("%s.%s", n.Addr.Type, n.Addr.Name),
    62  		ModulePath: n.Path(),
    63  		Type:       n.Addr.Type,
    64  	}
    65  
    66  	// Reset our states
    67  	n.states = nil
    68  
    69  	// Return our sequence
    70  	return &EvalSequence{
    71  		Nodes: []EvalNode{
    72  			&EvalGetProvider{
    73  				Name:   n.ProvidedBy()[0],
    74  				Output: &provider,
    75  			},
    76  			&EvalImportState{
    77  				Provider: &provider,
    78  				Info:     info,
    79  				Id:       n.ID,
    80  				Output:   &n.states,
    81  			},
    82  		},
    83  	}
    84  }
    85  
    86  // GraphNodeDynamicExpandable impl.
    87  //
    88  // We use DynamicExpand as a way to generate the subgraph of refreshes
    89  // and state inserts we need to do for our import state. Since they're new
    90  // resources they don't depend on anything else and refreshes are isolated
    91  // so this is nearly a perfect use case for dynamic expand.
    92  func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
    93  	g := &Graph{Path: ctx.Path()}
    94  
    95  	// nameCounter is used to de-dup names in the state.
    96  	nameCounter := make(map[string]int)
    97  
    98  	// Compile the list of addresses that we'll be inserting into the state.
    99  	// We do this ahead of time so we can verify that we aren't importing
   100  	// something that already exists.
   101  	addrs := make([]*ResourceAddress, len(n.states))
   102  	for i, state := range n.states {
   103  		addr := *n.Addr
   104  		if t := state.Ephemeral.Type; t != "" {
   105  			addr.Type = t
   106  		}
   107  
   108  		// Determine if we need to suffix the name to de-dup
   109  		key := addr.String()
   110  		count, ok := nameCounter[key]
   111  		if ok {
   112  			count++
   113  			addr.Name += fmt.Sprintf("-%d", count)
   114  		}
   115  		nameCounter[key] = count
   116  
   117  		// Add it to our list
   118  		addrs[i] = &addr
   119  	}
   120  
   121  	// Verify that all the addresses are clear
   122  	state, lock := ctx.State()
   123  	lock.RLock()
   124  	defer lock.RUnlock()
   125  	filter := &StateFilter{State: state}
   126  	for _, addr := range addrs {
   127  		result, err := filter.Filter(addr.String())
   128  		if err != nil {
   129  			return nil, fmt.Errorf("Error verifying address %s: %s", addr, err)
   130  		}
   131  
   132  		// Go through the filter results and it is an error if we find
   133  		// a matching InstanceState, meaning that we would have a collision.
   134  		for _, r := range result {
   135  			if _, ok := r.Value.(*InstanceState); ok {
   136  				return nil, fmt.Errorf(
   137  					"Can't import %s, would collide with an existing resource.\n\n"+
   138  						"Please remove or rename this resource before continuing.",
   139  					addr)
   140  			}
   141  		}
   142  	}
   143  
   144  	// For each of the states, we add a node to handle the refresh/add to state.
   145  	// "n.states" is populated by our own EvalTree with the result of
   146  	// ImportState. Since DynamicExpand is always called after EvalTree, this
   147  	// is safe.
   148  	for i, state := range n.states {
   149  		g.Add(&graphNodeImportStateSub{
   150  			Target: addrs[i],
   151  			Path_:  n.Path(),
   152  			State:  state,
   153  		})
   154  	}
   155  
   156  	// Root transform for a single root
   157  	t := &RootTransformer{}
   158  	if err := t.Transform(g); err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	// Done!
   163  	return g, nil
   164  }
   165  
   166  // graphNodeImportStateSub is the sub-node of graphNodeImportState
   167  // and is part of the subgraph. This node is responsible for refreshing
   168  // and adding a resource to the state once it is imported.
   169  type graphNodeImportStateSub struct {
   170  	Target *ResourceAddress
   171  	State  *InstanceState
   172  	Path_  []string
   173  }
   174  
   175  func (n *graphNodeImportStateSub) Name() string {
   176  	return fmt.Sprintf("import %s result: %s", n.Target, n.State.ID)
   177  }
   178  
   179  func (n *graphNodeImportStateSub) Path() []string {
   180  	return n.Path_
   181  }
   182  
   183  // GraphNodeEvalable impl.
   184  func (n *graphNodeImportStateSub) EvalTree() EvalNode {
   185  	// If the Ephemeral type isn't set, then it is an error
   186  	if n.State.Ephemeral.Type == "" {
   187  		err := fmt.Errorf(
   188  			"import of %s didn't set type for %s",
   189  			n.Target.String(), n.State.ID)
   190  		return &EvalReturnError{Error: &err}
   191  	}
   192  
   193  	// DeepCopy so we're only modifying our local copy
   194  	state := n.State.DeepCopy()
   195  
   196  	// Build the resource info
   197  	info := &InstanceInfo{
   198  		Id:         fmt.Sprintf("%s.%s", n.Target.Type, n.Target.Name),
   199  		ModulePath: n.Path_,
   200  		Type:       n.State.Ephemeral.Type,
   201  	}
   202  
   203  	// Key is the resource key
   204  	key := &ResourceStateKey{
   205  		Name:  n.Target.Name,
   206  		Type:  info.Type,
   207  		Index: n.Target.Index,
   208  	}
   209  
   210  	// The eval sequence
   211  	var provider ResourceProvider
   212  	return &EvalSequence{
   213  		Nodes: []EvalNode{
   214  			&EvalGetProvider{
   215  				Name:   resourceProvider(info.Type, ""),
   216  				Output: &provider,
   217  			},
   218  			&EvalRefresh{
   219  				Provider: &provider,
   220  				State:    &state,
   221  				Info:     info,
   222  				Output:   &state,
   223  			},
   224  			&EvalImportStateVerify{
   225  				Info:  info,
   226  				Id:    n.State.ID,
   227  				State: &state,
   228  			},
   229  			&EvalWriteState{
   230  				Name:         key.String(),
   231  				ResourceType: info.Type,
   232  				Provider:     resourceProvider(info.Type, ""),
   233  				State:        &state,
   234  			},
   235  		},
   236  	}
   237  }