]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[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 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 }