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 ProviderName: 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 ProviderName string // Provider string
42 ResolvedProvider string // provider node address
44 states []*InstanceState
47 func (n *graphNodeImportState) Name() string {
48 return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID)
51 func (n *graphNodeImportState) ProvidedBy() string {
52 return resourceProvider(n.Addr.Type, n.ProviderName)
55 func (n *graphNodeImportState) SetProvider(p string) {
56 n.ResolvedProvider = p
60 func (n *graphNodeImportState) Path() []string {
61 return normalizeModulePath(n.Addr.Path)
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),
76 // Return our sequence
80 Name: n.ResolvedProvider,
93 // GraphNodeDynamicExpandable impl.
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()}
102 // nameCounter is used to de-dup names in the state.
103 nameCounter := make(map[string]int)
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 {
111 if t := state.Ephemeral.Type; t != "" {
115 // Determine if we need to suffix the name to de-dup
117 count, ok := nameCounter[key]
120 addr.Name += fmt.Sprintf("-%d", count)
122 nameCounter[key] = count
124 // Add it to our list
128 // Verify that all the addresses are clear
129 state, lock := ctx.State()
132 filter := &StateFilter{State: state}
133 for _, addr := range addrs {
134 result, err := filter.Filter(addr.String())
136 return nil, fmt.Errorf("Error verifying address %s: %s", addr, err)
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.",
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
155 for i, state := range n.states {
156 g.Add(&graphNodeImportStateSub{
160 ProviderName: n.ProviderName,
161 ResolvedProvider: n.ResolvedProvider,
165 // Root transform for a single root
166 t := &RootTransformer{}
167 if err := t.Transform(g); err != nil {
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
183 ResolvedProvider string
186 func (n *graphNodeImportStateSub) Name() string {
187 return fmt.Sprintf("import %s result: %s", n.Target, n.State.ID)
190 func (n *graphNodeImportStateSub) Path() []string {
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 == "" {
199 "import of %s didn't set type for %s",
200 n.Target.String(), n.State.ID)
201 return &EvalReturnError{Error: &err}
204 // DeepCopy so we're only modifying our local copy
205 state := n.State.DeepCopy()
207 // Build the resource info
208 info := &InstanceInfo{
209 Id: fmt.Sprintf("%s.%s", n.Target.Type, n.Target.Name),
211 Type: n.State.Ephemeral.Type,
214 // Key is the resource key
215 key := &ResourceStateKey{
218 Index: n.Target.Index,
222 var provider ResourceProvider
223 return &EvalSequence{
226 Name: n.ResolvedProvider,
235 &EvalImportStateVerify{
242 ResourceType: info.Type,
243 Provider: n.ResolvedProvider,