]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "log" | |
bae9f6d2 | 6 | |
107c1cdb ND |
7 | "github.com/hashicorp/hcl2/hcl" |
8 | "github.com/hashicorp/terraform/addrs" | |
9 | "github.com/hashicorp/terraform/configs" | |
bae9f6d2 | 10 | "github.com/hashicorp/terraform/dag" |
107c1cdb | 11 | "github.com/hashicorp/terraform/tfdiags" |
bae9f6d2 JC |
12 | ) |
13 | ||
107c1cdb | 14 | func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, config *configs.Config) GraphTransformer { |
15c0b25d AP |
15 | return GraphTransformMulti( |
16 | // Add providers from the config | |
17 | &ProviderConfigTransformer{ | |
107c1cdb | 18 | Config: config, |
15c0b25d AP |
19 | Providers: providers, |
20 | Concrete: concrete, | |
21 | }, | |
22 | // Add any remaining missing providers | |
23 | &MissingProviderTransformer{ | |
24 | Providers: providers, | |
25 | Concrete: concrete, | |
26 | }, | |
27 | // Connect the providers | |
107c1cdb ND |
28 | &ProviderTransformer{ |
29 | Config: config, | |
30 | }, | |
15c0b25d AP |
31 | // Remove unused providers and proxies |
32 | &PruneProviderTransformer{}, | |
33 | // Connect provider to their parent provider nodes | |
34 | &ParentProviderTransformer{}, | |
35 | ) | |
36 | } | |
37 | ||
bae9f6d2 | 38 | // GraphNodeProvider is an interface that nodes that can be a provider |
15c0b25d | 39 | // must implement. |
107c1cdb ND |
40 | // |
41 | // ProviderAddr returns the address of the provider configuration this | |
42 | // satisfies, which is relative to the path returned by method Path(). | |
43 | // | |
15c0b25d | 44 | // Name returns the full name of the provider in the config. |
bae9f6d2 | 45 | type GraphNodeProvider interface { |
107c1cdb ND |
46 | GraphNodeSubPath |
47 | ProviderAddr() addrs.AbsProviderConfig | |
15c0b25d | 48 | Name() string |
bae9f6d2 JC |
49 | } |
50 | ||
51 | // GraphNodeCloseProvider is an interface that nodes that can be a close | |
52 | // provider must implement. The CloseProviderName returned is the name of | |
53 | // the provider they satisfy. | |
54 | type GraphNodeCloseProvider interface { | |
107c1cdb ND |
55 | GraphNodeSubPath |
56 | CloseProviderAddr() addrs.AbsProviderConfig | |
bae9f6d2 JC |
57 | } |
58 | ||
59 | // GraphNodeProviderConsumer is an interface that nodes that require | |
107c1cdb ND |
60 | // a provider must implement. ProvidedBy must return the address of the provider |
61 | // to use, which will be resolved to a configuration either in the same module | |
62 | // or in an ancestor module, with the resulting absolute address passed to | |
63 | // SetProvider. | |
bae9f6d2 | 64 | type GraphNodeProviderConsumer interface { |
107c1cdb ND |
65 | // ProvidedBy returns the address of the provider configuration the node |
66 | // refers to. If the returned "exact" value is true, this address will | |
67 | // be taken exactly. If "exact" is false, a provider configuration from | |
68 | // an ancestor module may be selected instead. | |
69 | ProvidedBy() (addr addrs.AbsProviderConfig, exact bool) | |
15c0b25d | 70 | // Set the resolved provider address for this resource. |
107c1cdb | 71 | SetProvider(addrs.AbsProviderConfig) |
bae9f6d2 JC |
72 | } |
73 | ||
74 | // ProviderTransformer is a GraphTransformer that maps resources to | |
75 | // providers within the graph. This will error if there are any resources | |
76 | // that don't map to proper resources. | |
107c1cdb ND |
77 | type ProviderTransformer struct { |
78 | Config *configs.Config | |
79 | } | |
bae9f6d2 JC |
80 | |
81 | func (t *ProviderTransformer) Transform(g *Graph) error { | |
107c1cdb ND |
82 | // We need to find a provider configuration address for each resource |
83 | // either directly represented by a node or referenced by a node in | |
84 | // the graph, and then create graph edges from provider to provider user | |
85 | // so that the providers will get initialized first. | |
86 | ||
87 | var diags tfdiags.Diagnostics | |
88 | ||
89 | // To start, we'll collect the _requested_ provider addresses for each | |
90 | // node, which we'll then resolve (handling provider inheritence, etc) in | |
91 | // the next step. | |
92 | // Our "requested" map is from graph vertices to string representations of | |
93 | // provider config addresses (for deduping) to requests. | |
94 | type ProviderRequest struct { | |
95 | Addr addrs.AbsProviderConfig | |
96 | Exact bool // If true, inheritence from parent modules is not attempted | |
97 | } | |
98 | requested := map[dag.Vertex]map[string]ProviderRequest{} | |
99 | needConfigured := map[string]addrs.AbsProviderConfig{} | |
bae9f6d2 | 100 | for _, v := range g.Vertices() { |
107c1cdb ND |
101 | |
102 | // Does the vertex _directly_ use a provider? | |
bae9f6d2 | 103 | if pv, ok := v.(GraphNodeProviderConsumer); ok { |
107c1cdb | 104 | requested[v] = make(map[string]ProviderRequest) |
15c0b25d | 105 | |
107c1cdb ND |
106 | p, exact := pv.ProvidedBy() |
107 | if exact { | |
108 | log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), p) | |
109 | } else { | |
110 | log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), p) | |
111 | } | |
112 | ||
113 | requested[v][p.String()] = ProviderRequest{ | |
114 | Addr: p, | |
115 | Exact: exact, | |
116 | } | |
117 | ||
118 | // Direct references need the provider configured as well as initialized | |
119 | needConfigured[p.String()] = p | |
120 | } | |
121 | } | |
122 | ||
123 | // Now we'll go through all the requested addresses we just collected and | |
124 | // figure out which _actual_ config address each belongs to, after resolving | |
125 | // for provider inheritance and passing. | |
126 | m := providerVertexMap(g) | |
127 | for v, reqs := range requested { | |
128 | for key, req := range reqs { | |
129 | p := req.Addr | |
15c0b25d AP |
130 | target := m[key] |
131 | ||
107c1cdb | 132 | _, ok := v.(GraphNodeSubPath) |
15c0b25d | 133 | if !ok && target == nil { |
107c1cdb ND |
134 | // No target and no path to traverse up from |
135 | diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p)) | |
136 | continue | |
137 | } | |
138 | ||
139 | if target != nil { | |
140 | log.Printf("[TRACE] ProviderTransformer: exact match for %s serving %s", p, dag.VertexName(v)) | |
15c0b25d AP |
141 | } |
142 | ||
107c1cdb ND |
143 | // if we don't have a provider at this level, walk up the path looking for one, |
144 | // unless we were told to be exact. | |
145 | if target == nil && !req.Exact { | |
146 | for pp, ok := p.Inherited(); ok; pp, ok = pp.Inherited() { | |
147 | key := pp.String() | |
148 | target = m[key] | |
149 | if target != nil { | |
150 | log.Printf("[TRACE] ProviderTransformer: %s uses inherited configuration %s", dag.VertexName(v), pp) | |
151 | break | |
152 | } | |
153 | log.Printf("[TRACE] ProviderTransformer: looking for %s to serve %s", pp, dag.VertexName(v)) | |
15c0b25d | 154 | } |
107c1cdb | 155 | } |
15c0b25d | 156 | |
107c1cdb ND |
157 | // If this provider doesn't need to be configured then we can just |
158 | // stub it out with an init-only provider node, which will just | |
159 | // start up the provider and fetch its schema. | |
160 | if _, exists := needConfigured[key]; target == nil && !exists { | |
161 | stubAddr := p.ProviderConfig.Absolute(addrs.RootModuleInstance) | |
162 | stub := &NodeEvalableProvider{ | |
163 | &NodeAbstractProvider{ | |
164 | Addr: stubAddr, | |
165 | }, | |
bae9f6d2 | 166 | } |
107c1cdb ND |
167 | m[stubAddr.String()] = stub |
168 | log.Printf("[TRACE] ProviderTransformer: creating init-only node for %s", stubAddr) | |
169 | target = stub | |
170 | g.Add(target) | |
15c0b25d AP |
171 | } |
172 | ||
173 | if target == nil { | |
107c1cdb ND |
174 | diags = diags.Append(tfdiags.Sourceless( |
175 | tfdiags.Error, | |
176 | "Provider configuration not present", | |
177 | fmt.Sprintf( | |
178 | "To work with %s its original provider configuration at %s is required, but it has been removed. This occurs when a provider configuration is removed while objects created by that provider still exist in the state. Re-add the provider configuration to destroy %s, after which you can remove the provider configuration again.", | |
179 | dag.VertexName(v), p, dag.VertexName(v), | |
180 | ), | |
15c0b25d AP |
181 | )) |
182 | break | |
183 | } | |
bae9f6d2 | 184 | |
15c0b25d AP |
185 | // see if this in an inherited provider |
186 | if p, ok := target.(*graphNodeProxyProvider); ok { | |
187 | g.Remove(p) | |
188 | target = p.Target() | |
107c1cdb | 189 | key = target.(GraphNodeProvider).ProviderAddr().String() |
bae9f6d2 | 190 | } |
15c0b25d | 191 | |
107c1cdb ND |
192 | log.Printf("[DEBUG] ProviderTransformer: %q (%T) needs %s", dag.VertexName(v), v, dag.VertexName(target)) |
193 | if pv, ok := v.(GraphNodeProviderConsumer); ok { | |
194 | pv.SetProvider(target.ProviderAddr()) | |
195 | } | |
15c0b25d | 196 | g.Connect(dag.BasicEdge(v, target)) |
bae9f6d2 JC |
197 | } |
198 | } | |
199 | ||
107c1cdb | 200 | return diags.Err() |
bae9f6d2 JC |
201 | } |
202 | ||
203 | // CloseProviderTransformer is a GraphTransformer that adds nodes to the | |
204 | // graph that will close open provider connections that aren't needed anymore. | |
205 | // A provider connection is not needed anymore once all depended resources | |
206 | // in the graph are evaluated. | |
207 | type CloseProviderTransformer struct{} | |
208 | ||
209 | func (t *CloseProviderTransformer) Transform(g *Graph) error { | |
210 | pm := providerVertexMap(g) | |
15c0b25d | 211 | cpm := make(map[string]*graphNodeCloseProvider) |
bae9f6d2 | 212 | var err error |
bae9f6d2 | 213 | |
15c0b25d AP |
214 | for _, v := range pm { |
215 | p := v.(GraphNodeProvider) | |
107c1cdb | 216 | key := p.ProviderAddr().String() |
15c0b25d AP |
217 | |
218 | // get the close provider of this type if we alread created it | |
107c1cdb | 219 | closer := cpm[key] |
15c0b25d AP |
220 | |
221 | if closer == nil { | |
222 | // create a closer for this provider type | |
107c1cdb | 223 | closer = &graphNodeCloseProvider{Addr: p.ProviderAddr()} |
15c0b25d | 224 | g.Add(closer) |
107c1cdb | 225 | cpm[key] = closer |
15c0b25d AP |
226 | } |
227 | ||
228 | // Close node depends on the provider itself | |
229 | // this is added unconditionally, so it will connect to all instances | |
230 | // of the provider. Extra edges will be removed by transitive | |
231 | // reduction. | |
232 | g.Connect(dag.BasicEdge(closer, p)) | |
233 | ||
234 | // connect all the provider's resources to the close node | |
235 | for _, s := range g.UpEdges(p).List() { | |
236 | if _, ok := s.(GraphNodeProviderConsumer); ok { | |
237 | g.Connect(dag.BasicEdge(closer, s)) | |
bae9f6d2 JC |
238 | } |
239 | } | |
240 | } | |
241 | ||
242 | return err | |
243 | } | |
244 | ||
107c1cdb ND |
245 | // MissingProviderTransformer is a GraphTransformer that adds to the graph |
246 | // a node for each default provider configuration that is referenced by another | |
247 | // node but not already present in the graph. | |
248 | // | |
249 | // These "default" nodes are always added to the root module, regardless of | |
250 | // where they are requested. This is important because our inheritance | |
251 | // resolution behavior in ProviderTransformer will then treat these as a | |
252 | // last-ditch fallback after walking up the tree, rather than preferring them | |
253 | // as it would if they were placed in the same module as the requester. | |
254 | // | |
255 | // This transformer may create extra nodes that are not needed in practice, | |
256 | // due to overriding provider configurations in child modules. | |
257 | // PruneProviderTransformer can then remove these once ProviderTransformer | |
258 | // has resolved all of the inheritence, etc. | |
bae9f6d2 JC |
259 | type MissingProviderTransformer struct { |
260 | // Providers is the list of providers we support. | |
261 | Providers []string | |
262 | ||
bae9f6d2 JC |
263 | // Concrete, if set, overrides how the providers are made. |
264 | Concrete ConcreteProviderNodeFunc | |
265 | } | |
266 | ||
267 | func (t *MissingProviderTransformer) Transform(g *Graph) error { | |
268 | // Initialize factory | |
269 | if t.Concrete == nil { | |
270 | t.Concrete = func(a *NodeAbstractProvider) dag.Vertex { | |
271 | return a | |
272 | } | |
273 | } | |
274 | ||
15c0b25d | 275 | var err error |
bae9f6d2 | 276 | m := providerVertexMap(g) |
15c0b25d | 277 | for _, v := range g.Vertices() { |
bae9f6d2 JC |
278 | pv, ok := v.(GraphNodeProviderConsumer) |
279 | if !ok { | |
280 | continue | |
281 | } | |
282 | ||
107c1cdb ND |
283 | // For our work here we actually care only about the provider type and |
284 | // we plan to place all default providers in the root module, and so | |
285 | // it's safe for us to rely on ProvidedBy here rather than waiting for | |
286 | // the later proper resolution of provider inheritance done by | |
287 | // ProviderTransformer. | |
288 | p, _ := pv.ProvidedBy() | |
289 | if p.ProviderConfig.Alias != "" { | |
290 | // We do not create default aliased configurations. | |
291 | log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p) | |
292 | continue | |
293 | } | |
bae9f6d2 | 294 | |
107c1cdb ND |
295 | // We're going to create an implicit _default_ configuration for the |
296 | // referenced provider type in the _root_ module, ignoring all other | |
297 | // aspects of the resource's declared provider address. | |
298 | defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(p.ProviderConfig.Type) | |
299 | key := defaultAddr.String() | |
15c0b25d | 300 | provider := m[key] |
bae9f6d2 | 301 | |
15c0b25d | 302 | if provider != nil { |
107c1cdb ND |
303 | // There's already an explicit default configuration for this |
304 | // provider type in the root module, so we have nothing to do. | |
15c0b25d AP |
305 | continue |
306 | } | |
bae9f6d2 | 307 | |
107c1cdb | 308 | log.Printf("[DEBUG] adding implicit provider configuration %s, implied first by %s", defaultAddr, dag.VertexName(v)) |
bae9f6d2 | 309 | |
107c1cdb | 310 | // create the missing top-level provider |
15c0b25d | 311 | provider = t.Concrete(&NodeAbstractProvider{ |
107c1cdb ND |
312 | Addr: defaultAddr, |
313 | }).(GraphNodeProvider) | |
15c0b25d | 314 | |
107c1cdb ND |
315 | g.Add(provider) |
316 | m[key] = provider | |
bae9f6d2 JC |
317 | } |
318 | ||
15c0b25d | 319 | return err |
bae9f6d2 JC |
320 | } |
321 | ||
322 | // ParentProviderTransformer connects provider nodes to their parents. | |
323 | // | |
324 | // This works by finding nodes that are both GraphNodeProviders and | |
325 | // GraphNodeSubPath. It then connects the providers to their parent | |
15c0b25d | 326 | // path. The parent provider is always at the root level. |
bae9f6d2 JC |
327 | type ParentProviderTransformer struct{} |
328 | ||
329 | func (t *ParentProviderTransformer) Transform(g *Graph) error { | |
15c0b25d AP |
330 | pm := providerVertexMap(g) |
331 | for _, v := range g.Vertices() { | |
bae9f6d2 JC |
332 | // Only care about providers |
333 | pn, ok := v.(GraphNodeProvider) | |
107c1cdb | 334 | if !ok { |
bae9f6d2 JC |
335 | continue |
336 | } | |
337 | ||
107c1cdb ND |
338 | // Also require non-empty path, since otherwise we're in the root |
339 | // module and so cannot have a parent. | |
340 | if len(pn.Path()) <= 1 { | |
341 | continue | |
bae9f6d2 | 342 | } |
bae9f6d2 | 343 | |
15c0b25d AP |
344 | // this provider may be disabled, but we can only get it's name from |
345 | // the ProviderName string | |
107c1cdb ND |
346 | addr := pn.ProviderAddr() |
347 | parentAddr, ok := addr.Inherited() | |
348 | if ok { | |
349 | parent := pm[parentAddr.String()] | |
350 | if parent != nil { | |
351 | g.Connect(dag.BasicEdge(v, parent)) | |
352 | } | |
bae9f6d2 | 353 | } |
15c0b25d | 354 | } |
bae9f6d2 JC |
355 | return nil |
356 | } | |
357 | ||
15c0b25d AP |
358 | // PruneProviderTransformer removes any providers that are not actually used by |
359 | // anything, and provider proxies. This avoids the provider being initialized | |
360 | // and configured. This both saves resources but also avoids errors since | |
361 | // configuration may imply initialization which may require auth. | |
bae9f6d2 JC |
362 | type PruneProviderTransformer struct{} |
363 | ||
364 | func (t *PruneProviderTransformer) Transform(g *Graph) error { | |
365 | for _, v := range g.Vertices() { | |
15c0b25d | 366 | // We only care about providers |
107c1cdb ND |
367 | _, ok := v.(GraphNodeProvider) |
368 | if !ok { | |
bae9f6d2 JC |
369 | continue |
370 | } | |
15c0b25d AP |
371 | |
372 | // ProxyProviders will have up edges, but we're now done with them in the graph | |
373 | if _, ok := v.(*graphNodeProxyProvider); ok { | |
107c1cdb | 374 | log.Printf("[DEBUG] pruning proxy %s", dag.VertexName(v)) |
15c0b25d AP |
375 | g.Remove(v) |
376 | } | |
377 | ||
378 | // Remove providers with no dependencies. | |
379 | if g.UpEdges(v).Len() == 0 { | |
107c1cdb | 380 | log.Printf("[DEBUG] pruning unused %s", dag.VertexName(v)) |
bae9f6d2 JC |
381 | g.Remove(v) |
382 | } | |
383 | } | |
384 | ||
385 | return nil | |
386 | } | |
387 | ||
107c1cdb ND |
388 | func providerVertexMap(g *Graph) map[string]GraphNodeProvider { |
389 | m := make(map[string]GraphNodeProvider) | |
bae9f6d2 JC |
390 | for _, v := range g.Vertices() { |
391 | if pv, ok := v.(GraphNodeProvider); ok { | |
107c1cdb ND |
392 | addr := pv.ProviderAddr() |
393 | m[addr.String()] = pv | |
bae9f6d2 JC |
394 | } |
395 | } | |
396 | ||
397 | return m | |
398 | } | |
399 | ||
107c1cdb ND |
400 | func closeProviderVertexMap(g *Graph) map[string]GraphNodeCloseProvider { |
401 | m := make(map[string]GraphNodeCloseProvider) | |
bae9f6d2 JC |
402 | for _, v := range g.Vertices() { |
403 | if pv, ok := v.(GraphNodeCloseProvider); ok { | |
107c1cdb ND |
404 | addr := pv.CloseProviderAddr() |
405 | m[addr.String()] = pv | |
bae9f6d2 JC |
406 | } |
407 | } | |
408 | ||
409 | return m | |
410 | } | |
411 | ||
412 | type graphNodeCloseProvider struct { | |
107c1cdb | 413 | Addr addrs.AbsProviderConfig |
bae9f6d2 JC |
414 | } |
415 | ||
107c1cdb ND |
416 | var ( |
417 | _ GraphNodeCloseProvider = (*graphNodeCloseProvider)(nil) | |
418 | ) | |
419 | ||
bae9f6d2 | 420 | func (n *graphNodeCloseProvider) Name() string { |
107c1cdb ND |
421 | return n.Addr.String() + " (close)" |
422 | } | |
423 | ||
424 | // GraphNodeSubPath impl. | |
425 | func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance { | |
426 | return n.Addr.Module | |
bae9f6d2 JC |
427 | } |
428 | ||
429 | // GraphNodeEvalable impl. | |
430 | func (n *graphNodeCloseProvider) EvalTree() EvalNode { | |
107c1cdb | 431 | return CloseProviderEvalTree(n.Addr) |
bae9f6d2 JC |
432 | } |
433 | ||
434 | // GraphNodeDependable impl. | |
435 | func (n *graphNodeCloseProvider) DependableName() []string { | |
436 | return []string{n.Name()} | |
437 | } | |
438 | ||
107c1cdb ND |
439 | func (n *graphNodeCloseProvider) CloseProviderAddr() addrs.AbsProviderConfig { |
440 | return n.Addr | |
bae9f6d2 JC |
441 | } |
442 | ||
443 | // GraphNodeDotter impl. | |
444 | func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { | |
445 | if !opts.Verbose { | |
446 | return nil | |
447 | } | |
448 | return &dag.DotNode{ | |
449 | Name: name, | |
450 | Attrs: map[string]string{ | |
451 | "label": n.Name(), | |
452 | "shape": "diamond", | |
453 | }, | |
454 | } | |
455 | } | |
456 | ||
457 | // RemovableIfNotTargeted | |
458 | func (n *graphNodeCloseProvider) RemoveIfNotTargeted() bool { | |
459 | // We need to add this so that this node will be removed if | |
460 | // it isn't targeted or a dependency of a target. | |
461 | return true | |
462 | } | |
463 | ||
15c0b25d AP |
464 | // graphNodeProxyProvider is a GraphNodeProvider implementation that is used to |
465 | // store the name and value of a provider node for inheritance between modules. | |
466 | // These nodes are only used to store the data while loading the provider | |
467 | // configurations, and are removed after all the resources have been connected | |
468 | // to their providers. | |
469 | type graphNodeProxyProvider struct { | |
107c1cdb ND |
470 | addr addrs.AbsProviderConfig |
471 | target GraphNodeProvider | |
472 | } | |
473 | ||
474 | var ( | |
475 | _ GraphNodeProvider = (*graphNodeProxyProvider)(nil) | |
476 | ) | |
477 | ||
478 | func (n *graphNodeProxyProvider) ProviderAddr() addrs.AbsProviderConfig { | |
479 | return n.addr | |
15c0b25d AP |
480 | } |
481 | ||
107c1cdb ND |
482 | func (n *graphNodeProxyProvider) Path() addrs.ModuleInstance { |
483 | return n.addr.Module | |
15c0b25d AP |
484 | } |
485 | ||
486 | func (n *graphNodeProxyProvider) Name() string { | |
107c1cdb | 487 | return n.addr.String() + " (proxy)" |
15c0b25d AP |
488 | } |
489 | ||
490 | // find the concrete provider instance | |
491 | func (n *graphNodeProxyProvider) Target() GraphNodeProvider { | |
492 | switch t := n.target.(type) { | |
493 | case *graphNodeProxyProvider: | |
494 | return t.Target() | |
495 | default: | |
496 | return n.target | |
497 | } | |
498 | } | |
499 | ||
500 | // ProviderConfigTransformer adds all provider nodes from the configuration and | |
501 | // attaches the configs. | |
502 | type ProviderConfigTransformer struct { | |
503 | Providers []string | |
504 | Concrete ConcreteProviderNodeFunc | |
505 | ||
506 | // each provider node is stored here so that the proxy nodes can look up | |
507 | // their targets by name. | |
508 | providers map[string]GraphNodeProvider | |
509 | // record providers that can be overriden with a proxy | |
510 | proxiable map[string]bool | |
511 | ||
107c1cdb ND |
512 | // Config is the root node of the configuration tree to add providers from. |
513 | Config *configs.Config | |
bae9f6d2 JC |
514 | } |
515 | ||
15c0b25d | 516 | func (t *ProviderConfigTransformer) Transform(g *Graph) error { |
107c1cdb ND |
517 | // If no configuration is given, we don't do anything |
518 | if t.Config == nil { | |
15c0b25d AP |
519 | return nil |
520 | } | |
521 | ||
15c0b25d AP |
522 | t.providers = make(map[string]GraphNodeProvider) |
523 | t.proxiable = make(map[string]bool) | |
524 | ||
525 | // Start the transformation process | |
107c1cdb | 526 | if err := t.transform(g, t.Config); err != nil { |
15c0b25d AP |
527 | return err |
528 | } | |
529 | ||
530 | // finally attach the configs to the new nodes | |
531 | return t.attachProviderConfigs(g) | |
bae9f6d2 JC |
532 | } |
533 | ||
107c1cdb | 534 | func (t *ProviderConfigTransformer) transform(g *Graph, c *configs.Config) error { |
15c0b25d | 535 | // If no config, do nothing |
107c1cdb | 536 | if c == nil { |
15c0b25d AP |
537 | return nil |
538 | } | |
539 | ||
540 | // Add our resources | |
107c1cdb | 541 | if err := t.transformSingle(g, c); err != nil { |
15c0b25d AP |
542 | return err |
543 | } | |
544 | ||
545 | // Transform all the children. | |
107c1cdb ND |
546 | for _, cc := range c.Children { |
547 | if err := t.transform(g, cc); err != nil { | |
15c0b25d AP |
548 | return err |
549 | } | |
550 | } | |
551 | return nil | |
552 | } | |
553 | ||
107c1cdb ND |
554 | func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) error { |
555 | // Get the module associated with this configuration tree node | |
556 | mod := c.Module | |
557 | staticPath := c.Path | |
558 | ||
559 | // We actually need a dynamic module path here, but we've not yet updated | |
560 | // our graph builders enough to support expansion of module calls with | |
561 | // "count" and "for_each" set, so for now we'll shim this by converting to | |
562 | // a dynamic path with no keys. At the time of writing this is the only | |
563 | // possible kind of dynamic path anyway. | |
564 | path := make(addrs.ModuleInstance, len(staticPath)) | |
565 | for i, name := range staticPath { | |
566 | path[i] = addrs.ModuleInstanceStep{ | |
567 | Name: name, | |
568 | } | |
15c0b25d AP |
569 | } |
570 | ||
571 | // add all providers from the configuration | |
107c1cdb ND |
572 | for _, p := range mod.ProviderConfigs { |
573 | relAddr := p.Addr() | |
574 | addr := relAddr.Absolute(path) | |
15c0b25d | 575 | |
107c1cdb ND |
576 | abstract := &NodeAbstractProvider{ |
577 | Addr: addr, | |
578 | } | |
579 | var v dag.Vertex | |
580 | if t.Concrete != nil { | |
581 | v = t.Concrete(abstract) | |
582 | } else { | |
583 | v = abstract | |
584 | } | |
15c0b25d AP |
585 | |
586 | // Add it to the graph | |
587 | g.Add(v) | |
107c1cdb ND |
588 | key := addr.String() |
589 | t.providers[key] = v.(GraphNodeProvider) | |
590 | ||
591 | // A provider configuration is "proxyable" if its configuration is | |
592 | // entirely empty. This means it's standing in for a provider | |
593 | // configuration that must be passed in from the parent module. | |
594 | // We decide this by evaluating the config with an empty schema; | |
595 | // if this succeeds, then we know there's nothing in the body. | |
596 | _, diags := p.Config.Content(&hcl.BodySchema{}) | |
597 | t.proxiable[key] = !diags.HasErrors() | |
15c0b25d AP |
598 | } |
599 | ||
600 | // Now replace the provider nodes with proxy nodes if a provider was being | |
601 | // passed in, and create implicit proxies if there was no config. Any extra | |
602 | // proxies will be removed in the prune step. | |
107c1cdb | 603 | return t.addProxyProviders(g, c) |
15c0b25d AP |
604 | } |
605 | ||
107c1cdb ND |
606 | func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Config) error { |
607 | path := c.Path | |
15c0b25d AP |
608 | |
609 | // can't add proxies at the root | |
610 | if len(path) == 0 { | |
611 | return nil | |
612 | } | |
613 | ||
107c1cdb ND |
614 | parentPath, callAddr := path.Call() |
615 | parent := c.Parent | |
15c0b25d AP |
616 | if parent == nil { |
617 | return nil | |
618 | } | |
619 | ||
107c1cdb ND |
620 | callName := callAddr.Name |
621 | var parentCfg *configs.ModuleCall | |
622 | for name, mod := range parent.Module.ModuleCalls { | |
623 | if name == callName { | |
15c0b25d AP |
624 | parentCfg = mod |
625 | break | |
626 | } | |
627 | } | |
628 | ||
107c1cdb ND |
629 | // We currently don't support count/for_each for modules and so we must |
630 | // shim our path and parentPath into module instances here so that the | |
631 | // rest of Terraform can behave as if we do. This shimming should be | |
632 | // removed later as part of implementing count/for_each for modules. | |
633 | instPath := make(addrs.ModuleInstance, len(path)) | |
634 | for i, name := range path { | |
635 | instPath[i] = addrs.ModuleInstanceStep{Name: name} | |
636 | } | |
637 | parentInstPath := make(addrs.ModuleInstance, len(parentPath)) | |
638 | for i, name := range parentPath { | |
639 | parentInstPath[i] = addrs.ModuleInstanceStep{Name: name} | |
640 | } | |
641 | ||
15c0b25d AP |
642 | if parentCfg == nil { |
643 | // this can't really happen during normal execution. | |
107c1cdb | 644 | return fmt.Errorf("parent module config not found for %s", c.Path.String()) |
15c0b25d AP |
645 | } |
646 | ||
647 | // Go through all the providers the parent is passing in, and add proxies to | |
648 | // the parent provider nodes. | |
107c1cdb ND |
649 | for _, pair := range parentCfg.Providers { |
650 | fullAddr := pair.InChild.Addr().Absolute(instPath) | |
651 | fullParentAddr := pair.InParent.Addr().Absolute(parentInstPath) | |
652 | fullName := fullAddr.String() | |
653 | fullParentName := fullParentAddr.String() | |
15c0b25d AP |
654 | |
655 | parentProvider := t.providers[fullParentName] | |
656 | ||
657 | if parentProvider == nil { | |
658 | return fmt.Errorf("missing provider %s", fullParentName) | |
659 | } | |
660 | ||
661 | proxy := &graphNodeProxyProvider{ | |
107c1cdb ND |
662 | addr: fullAddr, |
663 | target: parentProvider, | |
15c0b25d AP |
664 | } |
665 | ||
666 | concreteProvider := t.providers[fullName] | |
667 | ||
668 | // replace the concrete node with the provider passed in | |
669 | if concreteProvider != nil && t.proxiable[fullName] { | |
670 | g.Replace(concreteProvider, proxy) | |
671 | t.providers[fullName] = proxy | |
672 | continue | |
673 | } | |
674 | ||
107c1cdb ND |
675 | // aliased configurations can't be implicitly passed in |
676 | if fullAddr.ProviderConfig.Alias != "" { | |
15c0b25d AP |
677 | continue |
678 | } | |
679 | ||
680 | // There was no concrete provider, so add this as an implicit provider. | |
681 | // The extra proxy will be pruned later if it's unused. | |
682 | g.Add(proxy) | |
683 | t.providers[fullName] = proxy | |
684 | } | |
685 | return nil | |
686 | } | |
687 | ||
688 | func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error { | |
689 | for _, v := range g.Vertices() { | |
690 | // Only care about GraphNodeAttachProvider implementations | |
691 | apn, ok := v.(GraphNodeAttachProvider) | |
692 | if !ok { | |
693 | continue | |
694 | } | |
695 | ||
696 | // Determine what we're looking for | |
107c1cdb | 697 | addr := apn.ProviderAddr() |
15c0b25d AP |
698 | |
699 | // Get the configuration. | |
107c1cdb ND |
700 | mc := t.Config.DescendentForInstance(addr.Module) |
701 | if mc == nil { | |
702 | log.Printf("[TRACE] ProviderConfigTransformer: no configuration available for %s", addr.String()) | |
15c0b25d AP |
703 | continue |
704 | } | |
705 | ||
706 | // Go through the provider configs to find the matching config | |
107c1cdb ND |
707 | for _, p := range mc.Module.ProviderConfigs { |
708 | if p.Name == addr.ProviderConfig.Type && p.Alias == addr.ProviderConfig.Alias { | |
709 | log.Printf("[TRACE] ProviderConfigTransformer: attaching to %q provider configuration from %s", dag.VertexName(v), p.DeclRange) | |
15c0b25d AP |
710 | apn.AttachProvider(p) |
711 | break | |
712 | } | |
713 | } | |
714 | } | |
715 | ||
716 | return nil | |
bae9f6d2 | 717 | } |