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