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