github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/transform_provider.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/dag" 10 "github.com/hashicorp/terraform/dot" 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 // DisableProviderTransformer "disables" any providers that are only 36 // depended on by modules. 37 type DisableProviderTransformer struct{} 38 39 func (t *DisableProviderTransformer) Transform(g *Graph) error { 40 for _, v := range g.Vertices() { 41 // We only care about providers 42 pn, ok := v.(GraphNodeProvider) 43 if !ok || pn.ProviderName() == "" { 44 continue 45 } 46 47 // Go through all the up-edges (things that depend on this 48 // provider) and if any is not a module, then ignore this node. 49 nonModule := false 50 for _, sourceRaw := range g.UpEdges(v).List() { 51 source := sourceRaw.(dag.Vertex) 52 cn, ok := source.(graphNodeConfig) 53 if !ok { 54 nonModule = true 55 break 56 } 57 58 if cn.ConfigType() != GraphNodeConfigTypeModule { 59 nonModule = true 60 break 61 } 62 } 63 if nonModule { 64 // We found something that depends on this provider that 65 // isn't a module, so skip it. 66 continue 67 } 68 69 // Disable the provider by replacing it with a "disabled" provider 70 disabled := &graphNodeDisabledProvider{GraphNodeProvider: pn} 71 if !g.Replace(v, disabled) { 72 panic(fmt.Sprintf( 73 "vertex disappeared from under us: %s", 74 dag.VertexName(v))) 75 } 76 } 77 78 return nil 79 } 80 81 // ProviderTransformer is a GraphTransformer that maps resources to 82 // providers within the graph. This will error if there are any resources 83 // that don't map to proper resources. 84 type ProviderTransformer struct{} 85 86 func (t *ProviderTransformer) Transform(g *Graph) error { 87 // Go through the other nodes and match them to providers they need 88 var err error 89 m := providerVertexMap(g) 90 for _, v := range g.Vertices() { 91 if pv, ok := v.(GraphNodeProviderConsumer); ok { 92 for _, p := range pv.ProvidedBy() { 93 target := m[p] 94 if target == nil { 95 err = multierror.Append(err, fmt.Errorf( 96 "%s: provider %s couldn't be found", 97 dag.VertexName(v), p)) 98 continue 99 } 100 101 g.Connect(dag.BasicEdge(v, target)) 102 } 103 } 104 } 105 106 return err 107 } 108 109 // CloseProviderTransformer is a GraphTransformer that adds nodes to the 110 // graph that will close open provider connections that aren't needed anymore. 111 // A provider connection is not needed anymore once all depended resources 112 // in the graph are evaluated. 113 type CloseProviderTransformer struct{} 114 115 func (t *CloseProviderTransformer) Transform(g *Graph) error { 116 pm := providerVertexMap(g) 117 cpm := closeProviderVertexMap(g) 118 var err error 119 for _, v := range g.Vertices() { 120 if pv, ok := v.(GraphNodeProviderConsumer); ok { 121 for _, p := range pv.ProvidedBy() { 122 source := cpm[p] 123 124 if source == nil { 125 // Create a new graphNodeCloseProvider and add it to the graph 126 source = &graphNodeCloseProvider{ProviderNameValue: p} 127 g.Add(source) 128 129 // Close node needs to depend on provider 130 provider, ok := pm[p] 131 if !ok { 132 err = multierror.Append(err, fmt.Errorf( 133 "%s: provider %s couldn't be found for closing", 134 dag.VertexName(v), p)) 135 continue 136 } 137 g.Connect(dag.BasicEdge(source, provider)) 138 139 // Make sure we also add the new graphNodeCloseProvider to the map 140 // so we don't create and add any duplicate graphNodeCloseProviders. 141 cpm[p] = source 142 } 143 144 // Close node depends on all nodes provided by the provider 145 g.Connect(dag.BasicEdge(source, v)) 146 } 147 } 148 } 149 150 return err 151 } 152 153 // MissingProviderTransformer is a GraphTransformer that adds nodes 154 // for missing providers into the graph. Specifically, it creates provider 155 // configuration nodes for all the providers that we support. These are 156 // pruned later during an optimization pass. 157 type MissingProviderTransformer struct { 158 // Providers is the list of providers we support. 159 Providers []string 160 } 161 162 func (t *MissingProviderTransformer) Transform(g *Graph) error { 163 // Create a set of our supported providers 164 supported := make(map[string]struct{}, len(t.Providers)) 165 for _, v := range t.Providers { 166 supported[v] = struct{}{} 167 } 168 169 // Get the map of providers we already have in our graph 170 m := providerVertexMap(g) 171 172 // Go through all the provider consumers and make sure we add 173 // that provider if it is missing. 174 for _, v := range g.Vertices() { 175 pv, ok := v.(GraphNodeProviderConsumer) 176 if !ok { 177 continue 178 } 179 180 for _, p := range pv.ProvidedBy() { 181 if _, ok := m[p]; ok { 182 // This provider already exists as a configure node 183 continue 184 } 185 186 // If the provider has an alias in it, we just want the type 187 ptype := p 188 if idx := strings.IndexRune(p, '.'); idx != -1 { 189 ptype = p[:idx] 190 } 191 192 if _, ok := supported[ptype]; !ok { 193 // If we don't support the provider type, skip it. 194 // Validation later will catch this as an error. 195 continue 196 } 197 198 // Add our own missing provider node to the graph 199 m[p] = g.Add(&graphNodeMissingProvider{ProviderNameValue: p}) 200 } 201 } 202 203 return nil 204 } 205 206 // PruneProviderTransformer is a GraphTransformer that prunes all the 207 // providers that aren't needed from the graph. A provider is unneeded if 208 // no resource or module is using that provider. 209 type PruneProviderTransformer struct{} 210 211 func (t *PruneProviderTransformer) Transform(g *Graph) error { 212 for _, v := range g.Vertices() { 213 // We only care about the providers 214 if pn, ok := v.(GraphNodeProvider); !ok || pn.ProviderName() == "" { 215 continue 216 } 217 218 // Does anything depend on this? If not, then prune it. 219 if s := g.UpEdges(v); s.Len() == 0 { 220 g.Remove(v) 221 } 222 } 223 224 return nil 225 } 226 227 func providerVertexMap(g *Graph) map[string]dag.Vertex { 228 m := make(map[string]dag.Vertex) 229 for _, v := range g.Vertices() { 230 if pv, ok := v.(GraphNodeProvider); ok { 231 m[pv.ProviderName()] = v 232 } 233 } 234 235 return m 236 } 237 238 func closeProviderVertexMap(g *Graph) map[string]dag.Vertex { 239 m := make(map[string]dag.Vertex) 240 for _, v := range g.Vertices() { 241 if pv, ok := v.(GraphNodeCloseProvider); ok { 242 m[pv.CloseProviderName()] = v 243 } 244 } 245 246 return m 247 } 248 249 type graphNodeDisabledProvider struct { 250 GraphNodeProvider 251 } 252 253 // GraphNodeEvalable impl. 254 func (n *graphNodeDisabledProvider) EvalTree() EvalNode { 255 var resourceConfig *ResourceConfig 256 257 return &EvalOpFilter{ 258 Ops: []walkOperation{walkInput, walkValidate, walkRefresh, walkPlan, walkApply, walkDestroy}, 259 Node: &EvalSequence{ 260 Nodes: []EvalNode{ 261 &EvalInterpolate{ 262 Config: n.ProviderConfig(), 263 Output: &resourceConfig, 264 }, 265 &EvalBuildProviderConfig{ 266 Provider: n.ProviderName(), 267 Config: &resourceConfig, 268 Output: &resourceConfig, 269 }, 270 &EvalSetProviderConfig{ 271 Provider: n.ProviderName(), 272 Config: &resourceConfig, 273 }, 274 }, 275 }, 276 } 277 } 278 279 // GraphNodeFlattenable impl. 280 func (n *graphNodeDisabledProvider) Flatten(p []string) (dag.Vertex, error) { 281 return &graphNodeDisabledProviderFlat{ 282 graphNodeDisabledProvider: n, 283 PathValue: p, 284 }, nil 285 } 286 287 func (n *graphNodeDisabledProvider) Name() string { 288 return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider)) 289 } 290 291 // GraphNodeDotter impl. 292 func (n *graphNodeDisabledProvider) DotNode(name string, opts *GraphDotOpts) *dot.Node { 293 return dot.NewNode(name, map[string]string{ 294 "label": n.Name(), 295 "shape": "diamond", 296 }) 297 } 298 299 // GraphNodeDotterOrigin impl. 300 func (n *graphNodeDisabledProvider) DotOrigin() bool { 301 return true 302 } 303 304 // GraphNodeDependable impl. 305 func (n *graphNodeDisabledProvider) DependableName() []string { 306 return []string{"provider." + n.ProviderName()} 307 } 308 309 // GraphNodeProvider impl. 310 func (n *graphNodeDisabledProvider) ProviderName() string { 311 return n.GraphNodeProvider.ProviderName() 312 } 313 314 // GraphNodeProvider impl. 315 func (n *graphNodeDisabledProvider) ProviderConfig() *config.RawConfig { 316 return n.GraphNodeProvider.ProviderConfig() 317 } 318 319 // Same as graphNodeDisabledProvider, but for flattening 320 type graphNodeDisabledProviderFlat struct { 321 *graphNodeDisabledProvider 322 323 PathValue []string 324 } 325 326 func (n *graphNodeDisabledProviderFlat) Name() string { 327 return fmt.Sprintf( 328 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeDisabledProvider.Name()) 329 } 330 331 func (n *graphNodeDisabledProviderFlat) Path() []string { 332 return n.PathValue 333 } 334 335 func (n *graphNodeDisabledProviderFlat) ProviderName() string { 336 return fmt.Sprintf( 337 "%s.%s", modulePrefixStr(n.PathValue), 338 n.graphNodeDisabledProvider.ProviderName()) 339 } 340 341 // GraphNodeDependable impl. 342 func (n *graphNodeDisabledProviderFlat) DependableName() []string { 343 return []string{n.Name()} 344 } 345 346 func (n *graphNodeDisabledProviderFlat) DependentOn() []string { 347 var result []string 348 349 // If we're in a module, then depend on our parent's provider 350 if len(n.PathValue) > 1 { 351 prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) 352 if prefix != "" { 353 prefix += "." 354 } 355 356 result = append(result, fmt.Sprintf( 357 "%s%s", 358 prefix, n.graphNodeDisabledProvider.Name())) 359 } 360 361 return result 362 } 363 364 type graphNodeCloseProvider struct { 365 ProviderNameValue string 366 } 367 368 func (n *graphNodeCloseProvider) Name() string { 369 return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue) 370 } 371 372 // GraphNodeEvalable impl. 373 func (n *graphNodeCloseProvider) EvalTree() EvalNode { 374 return CloseProviderEvalTree(n.ProviderNameValue) 375 } 376 377 // GraphNodeDependable impl. 378 func (n *graphNodeCloseProvider) DependableName() []string { 379 return []string{n.Name()} 380 } 381 382 func (n *graphNodeCloseProvider) CloseProviderName() string { 383 return n.ProviderNameValue 384 } 385 386 // GraphNodeDotter impl. 387 func (n *graphNodeCloseProvider) DotNode(name string, opts *GraphDotOpts) *dot.Node { 388 if !opts.Verbose { 389 return nil 390 } 391 return dot.NewNode(name, map[string]string{ 392 "label": n.Name(), 393 "shape": "diamond", 394 }) 395 } 396 397 type graphNodeMissingProvider struct { 398 ProviderNameValue string 399 } 400 401 func (n *graphNodeMissingProvider) Name() string { 402 return fmt.Sprintf("provider.%s", n.ProviderNameValue) 403 } 404 405 // GraphNodeEvalable impl. 406 func (n *graphNodeMissingProvider) EvalTree() EvalNode { 407 return ProviderEvalTree(n.ProviderNameValue, nil) 408 } 409 410 // GraphNodeDependable impl. 411 func (n *graphNodeMissingProvider) DependableName() []string { 412 return []string{n.Name()} 413 } 414 415 func (n *graphNodeMissingProvider) ProviderName() string { 416 return n.ProviderNameValue 417 } 418 419 func (n *graphNodeMissingProvider) ProviderConfig() *config.RawConfig { 420 return nil 421 } 422 423 // GraphNodeDotter impl. 424 func (n *graphNodeMissingProvider) DotNode(name string, opts *GraphDotOpts) *dot.Node { 425 return dot.NewNode(name, map[string]string{ 426 "label": n.Name(), 427 "shape": "diamond", 428 }) 429 } 430 431 // GraphNodeDotterOrigin impl. 432 func (n *graphNodeMissingProvider) DotOrigin() bool { 433 return true 434 } 435 436 // GraphNodeFlattenable impl. 437 func (n *graphNodeMissingProvider) Flatten(p []string) (dag.Vertex, error) { 438 return &graphNodeMissingProviderFlat{ 439 graphNodeMissingProvider: n, 440 PathValue: p, 441 }, nil 442 } 443 444 // Same as graphNodeMissingProvider, but for flattening 445 type graphNodeMissingProviderFlat struct { 446 *graphNodeMissingProvider 447 448 PathValue []string 449 } 450 451 func (n *graphNodeMissingProviderFlat) Name() string { 452 return fmt.Sprintf( 453 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeMissingProvider.Name()) 454 } 455 456 func (n *graphNodeMissingProviderFlat) Path() []string { 457 return n.PathValue 458 } 459 460 func (n *graphNodeMissingProviderFlat) ProviderName() string { 461 return fmt.Sprintf( 462 "%s.%s", modulePrefixStr(n.PathValue), 463 n.graphNodeMissingProvider.ProviderName()) 464 } 465 466 // GraphNodeDependable impl. 467 func (n *graphNodeMissingProviderFlat) DependableName() []string { 468 return []string{n.Name()} 469 } 470 471 func (n *graphNodeMissingProviderFlat) DependentOn() []string { 472 var result []string 473 474 // If we're in a module, then depend on our parent's provider 475 if len(n.PathValue) > 1 { 476 prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) 477 if prefix != "" { 478 prefix += "." 479 } 480 481 result = append(result, fmt.Sprintf( 482 "%s%s", 483 prefix, n.graphNodeMissingProvider.Name())) 484 } 485 486 return result 487 }