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