8 "github.com/hashicorp/go-multierror"
9 "github.com/hashicorp/terraform/dag"
12 // GraphNodeProvider is an interface that nodes that can be a provider
13 // must implement. The ProviderName returned is the name of the provider
15 type GraphNodeProvider interface {
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
26 // GraphNodeProviderConsumer is an interface that nodes that require
27 // a provider must implement. ProvidedBy must return the name of the provider
29 type GraphNodeProviderConsumer interface {
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{}
38 func (t *ProviderTransformer) Transform(g *Graph) error {
39 // Go through the other nodes and match them to providers they need
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)]
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))
54 g.Connect(dag.BasicEdge(v, target))
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{}
68 func (t *CloseProviderTransformer) Transform(g *Graph) error {
69 pm := providerVertexMap(g)
70 cpm := closeProviderVertexMap(g)
72 for _, v := range g.Vertices() {
73 if pv, ok := v.(GraphNodeProviderConsumer); ok {
74 for _, p := range pv.ProvidedBy() {
79 // Create a new graphNodeCloseProvider and add it to the graph
80 source = &graphNodeCloseProvider{ProviderNameValue: p}
83 // Close node needs to depend on provider
84 provider, ok := pm[key]
86 err = multierror.Append(err, fmt.Errorf(
87 "%s: provider %s couldn't be found for closing",
88 dag.VertexName(v), p))
91 g.Connect(dag.BasicEdge(source, provider))
93 // Make sure we also add the new graphNodeCloseProvider to the map
94 // so we don't create and add any duplicate graphNodeCloseProviders.
98 // Close node depends on all nodes provided by the provider
99 g.Connect(dag.BasicEdge(source, v))
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.
115 // AllowAny will not check that a provider is supported before adding
119 // Concrete, if set, overrides how the providers are made.
120 Concrete ConcreteProviderNodeFunc
123 func (t *MissingProviderTransformer) Transform(g *Graph) error {
124 // Initialize factory
125 if t.Concrete == nil {
126 t.Concrete = func(a *NodeAbstractProvider) dag.Vertex {
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{}{}
137 // Get the map of providers we already have in our graph
138 m := providerVertexMap(g)
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++ {
147 pv, ok := v.(GraphNodeProviderConsumer)
152 // If this node has a subpath, then we use that as a prefix
153 // into our map to check for an existing provider.
155 if sp, ok := pv.(GraphNodeSubPath); ok {
156 raw := normalizeModulePath(sp.Path())
157 if len(raw) > len(rootModulePath) {
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
169 // If the provider has an alias in it, we just want the type
171 if idx := strings.IndexRune(p, '.'); idx != -1 {
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.
183 // Add the missing provider node to the graph
184 v := t.Concrete(&NodeAbstractProvider{
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{
194 PathValue: path[:len(path)-1],
205 // ParentProviderTransformer connects provider nodes to their parents.
207 // This works by finding nodes that are both GraphNodeProviders and
208 // GraphNodeSubPath. It then connects the providers to their parent
210 type ParentProviderTransformer struct{}
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)
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
224 // Only care about providers
225 pn, ok := v.(GraphNodeProvider)
226 if !ok || pn.ProviderName() == "" {
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.
234 if pn, ok := raw.(GraphNodeSubPath); ok {
237 path = normalizeModulePath(path)
239 // Build the key with path.name i.e. "child.subchild.aws"
240 key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
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.
246 path = path[:len(path)-1]
247 key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
253 for v, key := range parentMap {
254 if parent, ok := m[key]; ok {
255 g.Connect(dag.BasicEdge(v, parent))
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{}
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() == "" {
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())
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 {
289 if sp, ok := v.(GraphNodeSubPath); ok {
290 raw := normalizeModulePath(sp.Path())
291 if len(raw) > len(rootModulePath) {
292 pathPrefix = modulePrefixStr(raw) + "."
296 return pathPrefix + k
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)
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
322 type graphNodeCloseProvider struct {
323 ProviderNameValue string
326 func (n *graphNodeCloseProvider) Name() string {
327 return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue)
330 // GraphNodeEvalable impl.
331 func (n *graphNodeCloseProvider) EvalTree() EvalNode {
332 return CloseProviderEvalTree(n.ProviderNameValue)
335 // GraphNodeDependable impl.
336 func (n *graphNodeCloseProvider) DependableName() []string {
337 return []string{n.Name()}
340 func (n *graphNodeCloseProvider) CloseProviderName() string {
341 return n.ProviderNameValue
344 // GraphNodeDotter impl.
345 func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
351 Attrs: map[string]string{
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.
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 {
374 func (n *graphNodeProviderConsumerDummy) Path() []string {
378 func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string {
379 return []string{n.ProviderValue}