github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/terraform/transform_provider.go (about) 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 }