]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "log" | |
6 | "strings" | |
7 | ||
8 | "github.com/hashicorp/go-multierror" | |
9 | "github.com/hashicorp/terraform/dag" | |
10 | ) | |
11 | ||
12 | // GraphNodeProvider is an interface that nodes that can be a provider | |
13 | // must implement. The ProviderName returned is the name of the provider | |
14 | // they satisfy. | |
15 | type GraphNodeProvider interface { | |
16 | ProviderName() string | |
17 | } | |
18 | ||
19 | // GraphNodeCloseProvider is an interface that nodes that can be a close | |
20 | // provider must implement. The CloseProviderName returned is the name of | |
21 | // the provider they satisfy. | |
22 | type GraphNodeCloseProvider interface { | |
23 | CloseProviderName() string | |
24 | } | |
25 | ||
26 | // GraphNodeProviderConsumer is an interface that nodes that require | |
27 | // a provider must implement. ProvidedBy must return the name of the provider | |
28 | // to use. | |
29 | type GraphNodeProviderConsumer interface { | |
30 | ProvidedBy() []string | |
31 | } | |
32 | ||
33 | // ProviderTransformer is a GraphTransformer that maps resources to | |
34 | // providers within the graph. This will error if there are any resources | |
35 | // that don't map to proper resources. | |
36 | type ProviderTransformer struct{} | |
37 | ||
38 | func (t *ProviderTransformer) Transform(g *Graph) error { | |
39 | // Go through the other nodes and match them to providers they need | |
40 | var err error | |
41 | m := providerVertexMap(g) | |
42 | for _, v := range g.Vertices() { | |
43 | if pv, ok := v.(GraphNodeProviderConsumer); ok { | |
44 | for _, p := range pv.ProvidedBy() { | |
45 | target := m[providerMapKey(p, pv)] | |
46 | if target == nil { | |
47 | println(fmt.Sprintf("%#v\n\n%#v", m, providerMapKey(p, pv))) | |
48 | err = multierror.Append(err, fmt.Errorf( | |
49 | "%s: provider %s couldn't be found", | |
50 | dag.VertexName(v), p)) | |
51 | continue | |
52 | } | |
53 | ||
54 | g.Connect(dag.BasicEdge(v, target)) | |
55 | } | |
56 | } | |
57 | } | |
58 | ||
59 | return err | |
60 | } | |
61 | ||
62 | // CloseProviderTransformer is a GraphTransformer that adds nodes to the | |
63 | // graph that will close open provider connections that aren't needed anymore. | |
64 | // A provider connection is not needed anymore once all depended resources | |
65 | // in the graph are evaluated. | |
66 | type CloseProviderTransformer struct{} | |
67 | ||
68 | func (t *CloseProviderTransformer) Transform(g *Graph) error { | |
69 | pm := providerVertexMap(g) | |
70 | cpm := closeProviderVertexMap(g) | |
71 | var err error | |
72 | for _, v := range g.Vertices() { | |
73 | if pv, ok := v.(GraphNodeProviderConsumer); ok { | |
74 | for _, p := range pv.ProvidedBy() { | |
75 | key := p | |
76 | source := cpm[key] | |
77 | ||
78 | if source == nil { | |
79 | // Create a new graphNodeCloseProvider and add it to the graph | |
80 | source = &graphNodeCloseProvider{ProviderNameValue: p} | |
81 | g.Add(source) | |
82 | ||
83 | // Close node needs to depend on provider | |
84 | provider, ok := pm[key] | |
85 | if !ok { | |
86 | err = multierror.Append(err, fmt.Errorf( | |
87 | "%s: provider %s couldn't be found for closing", | |
88 | dag.VertexName(v), p)) | |
89 | continue | |
90 | } | |
91 | g.Connect(dag.BasicEdge(source, provider)) | |
92 | ||
93 | // Make sure we also add the new graphNodeCloseProvider to the map | |
94 | // so we don't create and add any duplicate graphNodeCloseProviders. | |
95 | cpm[key] = source | |
96 | } | |
97 | ||
98 | // Close node depends on all nodes provided by the provider | |
99 | g.Connect(dag.BasicEdge(source, v)) | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | return err | |
105 | } | |
106 | ||
107 | // MissingProviderTransformer is a GraphTransformer that adds nodes | |
108 | // for missing providers into the graph. Specifically, it creates provider | |
109 | // configuration nodes for all the providers that we support. These are | |
110 | // pruned later during an optimization pass. | |
111 | type MissingProviderTransformer struct { | |
112 | // Providers is the list of providers we support. | |
113 | Providers []string | |
114 | ||
115 | // AllowAny will not check that a provider is supported before adding | |
116 | // it to the graph. | |
117 | AllowAny bool | |
118 | ||
119 | // Concrete, if set, overrides how the providers are made. | |
120 | Concrete ConcreteProviderNodeFunc | |
121 | } | |
122 | ||
123 | func (t *MissingProviderTransformer) Transform(g *Graph) error { | |
124 | // Initialize factory | |
125 | if t.Concrete == nil { | |
126 | t.Concrete = func(a *NodeAbstractProvider) dag.Vertex { | |
127 | return a | |
128 | } | |
129 | } | |
130 | ||
131 | // Create a set of our supported providers | |
132 | supported := make(map[string]struct{}, len(t.Providers)) | |
133 | for _, v := range t.Providers { | |
134 | supported[v] = struct{}{} | |
135 | } | |
136 | ||
137 | // Get the map of providers we already have in our graph | |
138 | m := providerVertexMap(g) | |
139 | ||
140 | // Go through all the provider consumers and make sure we add | |
141 | // that provider if it is missing. We use a for loop here instead | |
142 | // of "range" since we'll modify check as we go to add more to check. | |
143 | check := g.Vertices() | |
144 | for i := 0; i < len(check); i++ { | |
145 | v := check[i] | |
146 | ||
147 | pv, ok := v.(GraphNodeProviderConsumer) | |
148 | if !ok { | |
149 | continue | |
150 | } | |
151 | ||
152 | // If this node has a subpath, then we use that as a prefix | |
153 | // into our map to check for an existing provider. | |
154 | var path []string | |
155 | if sp, ok := pv.(GraphNodeSubPath); ok { | |
156 | raw := normalizeModulePath(sp.Path()) | |
157 | if len(raw) > len(rootModulePath) { | |
158 | path = raw | |
159 | } | |
160 | } | |
161 | ||
162 | for _, p := range pv.ProvidedBy() { | |
163 | key := providerMapKey(p, pv) | |
164 | if _, ok := m[key]; ok { | |
165 | // This provider already exists as a configure node | |
166 | continue | |
167 | } | |
168 | ||
169 | // If the provider has an alias in it, we just want the type | |
170 | ptype := p | |
171 | if idx := strings.IndexRune(p, '.'); idx != -1 { | |
172 | ptype = p[:idx] | |
173 | } | |
174 | ||
175 | if !t.AllowAny { | |
176 | if _, ok := supported[ptype]; !ok { | |
177 | // If we don't support the provider type, skip it. | |
178 | // Validation later will catch this as an error. | |
179 | continue | |
180 | } | |
181 | } | |
182 | ||
183 | // Add the missing provider node to the graph | |
184 | v := t.Concrete(&NodeAbstractProvider{ | |
185 | NameValue: p, | |
186 | PathValue: path, | |
187 | }).(dag.Vertex) | |
188 | if len(path) > 0 { | |
189 | // We'll need the parent provider as well, so let's | |
190 | // add a dummy node to check to make sure that we add | |
191 | // that parent provider. | |
192 | check = append(check, &graphNodeProviderConsumerDummy{ | |
193 | ProviderValue: p, | |
194 | PathValue: path[:len(path)-1], | |
195 | }) | |
196 | } | |
197 | ||
198 | m[key] = g.Add(v) | |
199 | } | |
200 | } | |
201 | ||
202 | return nil | |
203 | } | |
204 | ||
205 | // ParentProviderTransformer connects provider nodes to their parents. | |
206 | // | |
207 | // This works by finding nodes that are both GraphNodeProviders and | |
208 | // GraphNodeSubPath. It then connects the providers to their parent | |
209 | // path. | |
210 | type ParentProviderTransformer struct{} | |
211 | ||
212 | func (t *ParentProviderTransformer) Transform(g *Graph) error { | |
213 | // Make a mapping of path to dag.Vertex, where path is: "path.name" | |
214 | m := make(map[string]dag.Vertex) | |
215 | ||
216 | // Also create a map that maps a provider to its parent | |
217 | parentMap := make(map[dag.Vertex]string) | |
218 | for _, raw := range g.Vertices() { | |
219 | // If it is the flat version, then make it the non-flat version. | |
220 | // We eventually want to get rid of the flat version entirely so | |
221 | // this is a stop-gap while it still exists. | |
222 | var v dag.Vertex = raw | |
223 | ||
224 | // Only care about providers | |
225 | pn, ok := v.(GraphNodeProvider) | |
226 | if !ok || pn.ProviderName() == "" { | |
227 | continue | |
228 | } | |
229 | ||
230 | // Also require a subpath, if there is no subpath then we | |
231 | // just totally ignore it. The expectation of this transform is | |
232 | // that it is used with a graph builder that is already flattened. | |
233 | var path []string | |
234 | if pn, ok := raw.(GraphNodeSubPath); ok { | |
235 | path = pn.Path() | |
236 | } | |
237 | path = normalizeModulePath(path) | |
238 | ||
239 | // Build the key with path.name i.e. "child.subchild.aws" | |
240 | key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName()) | |
241 | m[key] = raw | |
242 | ||
243 | // Determine the parent if we're non-root. This is length 1 since | |
244 | // the 0 index should be "root" since we normalize above. | |
245 | if len(path) > 1 { | |
246 | path = path[:len(path)-1] | |
247 | key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName()) | |
248 | parentMap[raw] = key | |
249 | } | |
250 | } | |
251 | ||
252 | // Connect! | |
253 | for v, key := range parentMap { | |
254 | if parent, ok := m[key]; ok { | |
255 | g.Connect(dag.BasicEdge(v, parent)) | |
256 | } | |
257 | } | |
258 | ||
259 | return nil | |
260 | } | |
261 | ||
262 | // PruneProviderTransformer is a GraphTransformer that prunes all the | |
263 | // providers that aren't needed from the graph. A provider is unneeded if | |
264 | // no resource or module is using that provider. | |
265 | type PruneProviderTransformer struct{} | |
266 | ||
267 | func (t *PruneProviderTransformer) Transform(g *Graph) error { | |
268 | for _, v := range g.Vertices() { | |
269 | // We only care about the providers | |
270 | if pn, ok := v.(GraphNodeProvider); !ok || pn.ProviderName() == "" { | |
271 | continue | |
272 | } | |
273 | // Does anything depend on this? If not, then prune it. | |
274 | if s := g.UpEdges(v); s.Len() == 0 { | |
275 | if nv, ok := v.(dag.NamedVertex); ok { | |
276 | log.Printf("[DEBUG] Pruning provider with no dependencies: %s", nv.Name()) | |
277 | } | |
278 | g.Remove(v) | |
279 | } | |
280 | } | |
281 | ||
282 | return nil | |
283 | } | |
284 | ||
285 | // providerMapKey is a helper that gives us the key to use for the | |
286 | // maps returned by things such as providerVertexMap. | |
287 | func providerMapKey(k string, v dag.Vertex) string { | |
288 | pathPrefix := "" | |
289 | if sp, ok := v.(GraphNodeSubPath); ok { | |
290 | raw := normalizeModulePath(sp.Path()) | |
291 | if len(raw) > len(rootModulePath) { | |
292 | pathPrefix = modulePrefixStr(raw) + "." | |
293 | } | |
294 | } | |
295 | ||
296 | return pathPrefix + k | |
297 | } | |
298 | ||
299 | func providerVertexMap(g *Graph) map[string]dag.Vertex { | |
300 | m := make(map[string]dag.Vertex) | |
301 | for _, v := range g.Vertices() { | |
302 | if pv, ok := v.(GraphNodeProvider); ok { | |
303 | key := providerMapKey(pv.ProviderName(), v) | |
304 | m[key] = v | |
305 | } | |
306 | } | |
307 | ||
308 | return m | |
309 | } | |
310 | ||
311 | func closeProviderVertexMap(g *Graph) map[string]dag.Vertex { | |
312 | m := make(map[string]dag.Vertex) | |
313 | for _, v := range g.Vertices() { | |
314 | if pv, ok := v.(GraphNodeCloseProvider); ok { | |
315 | m[pv.CloseProviderName()] = v | |
316 | } | |
317 | } | |
318 | ||
319 | return m | |
320 | } | |
321 | ||
322 | type graphNodeCloseProvider struct { | |
323 | ProviderNameValue string | |
324 | } | |
325 | ||
326 | func (n *graphNodeCloseProvider) Name() string { | |
327 | return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue) | |
328 | } | |
329 | ||
330 | // GraphNodeEvalable impl. | |
331 | func (n *graphNodeCloseProvider) EvalTree() EvalNode { | |
332 | return CloseProviderEvalTree(n.ProviderNameValue) | |
333 | } | |
334 | ||
335 | // GraphNodeDependable impl. | |
336 | func (n *graphNodeCloseProvider) DependableName() []string { | |
337 | return []string{n.Name()} | |
338 | } | |
339 | ||
340 | func (n *graphNodeCloseProvider) CloseProviderName() string { | |
341 | return n.ProviderNameValue | |
342 | } | |
343 | ||
344 | // GraphNodeDotter impl. | |
345 | func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { | |
346 | if !opts.Verbose { | |
347 | return nil | |
348 | } | |
349 | return &dag.DotNode{ | |
350 | Name: name, | |
351 | Attrs: map[string]string{ | |
352 | "label": n.Name(), | |
353 | "shape": "diamond", | |
354 | }, | |
355 | } | |
356 | } | |
357 | ||
358 | // RemovableIfNotTargeted | |
359 | func (n *graphNodeCloseProvider) RemoveIfNotTargeted() bool { | |
360 | // We need to add this so that this node will be removed if | |
361 | // it isn't targeted or a dependency of a target. | |
362 | return true | |
363 | } | |
364 | ||
365 | // graphNodeProviderConsumerDummy is a struct that never enters the real | |
366 | // graph (though it could to no ill effect). It implements | |
367 | // GraphNodeProviderConsumer and GraphNodeSubpath as a way to force | |
368 | // certain transformations. | |
369 | type graphNodeProviderConsumerDummy struct { | |
370 | ProviderValue string | |
371 | PathValue []string | |
372 | } | |
373 | ||
374 | func (n *graphNodeProviderConsumerDummy) Path() []string { | |
375 | return n.PathValue | |
376 | } | |
377 | ||
378 | func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string { | |
379 | return []string{n.ProviderValue} | |
380 | } |