]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go
fcbff653f5edaa9f720509843538fd620c005fe4
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_import_state.go
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 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: n.ResolvedProvider,
244 State: &state,
245 },
246 },
247 }
248 }