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
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)
19 "failed to parse resource address '%s': %s",
23 nodes = append(nodes, &graphNodeImportState{
26 Provider: target.Provider,
30 // Build the graph vertices
31 for _, n := range nodes {
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
43 states []*InstanceState
46 func (n *graphNodeImportState) Name() string {
47 return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID)
50 func (n *graphNodeImportState) ProvidedBy() []string {
51 return []string{resourceProvider(n.Addr.Type, n.Provider)}
55 func (n *graphNodeImportState) Path() []string {
56 return normalizeModulePath(n.Addr.Path)
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),
71 // Return our sequence
75 Name: n.ProvidedBy()[0],
88 // GraphNodeDynamicExpandable impl.
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()}
97 // nameCounter is used to de-dup names in the state.
98 nameCounter := make(map[string]int)
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 {
106 if t := state.Ephemeral.Type; t != "" {
110 // Determine if we need to suffix the name to de-dup
112 count, ok := nameCounter[key]
115 addr.Name += fmt.Sprintf("-%d", count)
117 nameCounter[key] = count
119 // Add it to our list
123 // Verify that all the addresses are clear
124 state, lock := ctx.State()
127 filter := &StateFilter{State: state}
128 for _, addr := range addrs {
129 result, err := filter.Filter(addr.String())
131 return nil, fmt.Errorf("Error verifying address %s: %s", addr, err)
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.",
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
150 for i, state := range n.states {
151 g.Add(&graphNodeImportStateSub{
155 Provider: n.Provider,
159 // Root transform for a single root
160 t := &RootTransformer{}
161 if err := t.Transform(g); err != nil {
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
179 func (n *graphNodeImportStateSub) Name() string {
180 return fmt.Sprintf("import %s result: %s", n.Target, n.State.ID)
183 func (n *graphNodeImportStateSub) Path() []string {
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 == "" {
192 "import of %s didn't set type for %s",
193 n.Target.String(), n.State.ID)
194 return &EvalReturnError{Error: &err}
197 // DeepCopy so we're only modifying our local copy
198 state := n.State.DeepCopy()
200 // Build the resource info
201 info := &InstanceInfo{
202 Id: fmt.Sprintf("%s.%s", n.Target.Type, n.Target.Name),
204 Type: n.State.Ephemeral.Type,
207 // Key is the resource key
208 key := &ResourceStateKey{
211 Index: n.Target.Index,
215 var provider ResourceProvider
216 return &EvalSequence{
219 Name: resourceProvider(info.Type, n.Provider),
228 &EvalImportStateVerify{
235 ResourceType: info.Type,
236 Provider: resourceProvider(info.Type, n.Provider),