github.com/alexissmirnov/terraform@v0.4.3-0.20150423153700-1ef9731a2f14/terraform/graph_config_node.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/terraform/config" 8 "github.com/hashicorp/terraform/config/module" 9 "github.com/hashicorp/terraform/dag" 10 ) 11 12 // graphNodeConfig is an interface that all graph nodes for the 13 // configuration graph need to implement in order to build the variable 14 // dependencies properly. 15 type graphNodeConfig interface { 16 dag.NamedVertex 17 18 // All graph nodes should be dependent on other things, and able to 19 // be depended on. 20 GraphNodeDependable 21 GraphNodeDependent 22 23 // ConfigType returns the type of thing in the configuration that 24 // this node represents, such as a resource, module, etc. 25 ConfigType() GraphNodeConfigType 26 } 27 28 // GraphNodeAddressable is an interface that all graph nodes for the 29 // configuration graph need to implement in order to be be addressed / targeted 30 // properly. 31 type GraphNodeAddressable interface { 32 graphNodeConfig 33 34 ResourceAddress() *ResourceAddress 35 } 36 37 // GraphNodeTargetable is an interface for graph nodes to implement when they 38 // need to be told about incoming targets. This is useful for nodes that need 39 // to respect targets as they dynamically expand. Note that the list of targets 40 // provided will contain every target provided, and each implementing graph 41 // node must filter this list to targets considered relevant. 42 type GraphNodeTargetable interface { 43 GraphNodeAddressable 44 45 SetTargets([]ResourceAddress) 46 } 47 48 // GraphNodeConfigModule represents a module within the configuration graph. 49 type GraphNodeConfigModule struct { 50 Path []string 51 Module *config.Module 52 Tree *module.Tree 53 } 54 55 func (n *GraphNodeConfigModule) ConfigType() GraphNodeConfigType { 56 return GraphNodeConfigTypeModule 57 } 58 59 func (n *GraphNodeConfigModule) DependableName() []string { 60 return []string{n.Name()} 61 } 62 63 func (n *GraphNodeConfigModule) DependentOn() []string { 64 vars := n.Module.RawConfig.Variables 65 result := make([]string, 0, len(vars)) 66 for _, v := range vars { 67 if vn := varNameForVar(v); vn != "" { 68 result = append(result, vn) 69 } 70 } 71 72 return result 73 } 74 75 func (n *GraphNodeConfigModule) Name() string { 76 return fmt.Sprintf("module.%s", n.Module.Name) 77 } 78 79 // GraphNodeExpandable 80 func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) { 81 // Build the graph first 82 graph, err := b.Build(n.Path) 83 if err != nil { 84 return nil, err 85 } 86 87 // Add the parameters node to the module 88 t := &ModuleInputTransformer{Variables: make(map[string]string)} 89 if err := t.Transform(graph); err != nil { 90 return nil, err 91 } 92 93 // Build the actual subgraph node 94 return &graphNodeModuleExpanded{ 95 Original: n, 96 Graph: graph, 97 InputConfig: n.Module.RawConfig, 98 Variables: t.Variables, 99 }, nil 100 } 101 102 // GraphNodeExpandable 103 func (n *GraphNodeConfigModule) ProvidedBy() []string { 104 // Build up the list of providers by simply going over our configuration 105 // to find the providers that are configured there as well as the 106 // providers that the resources use. 107 config := n.Tree.Config() 108 providers := make(map[string]struct{}) 109 for _, p := range config.ProviderConfigs { 110 providers[p.Name] = struct{}{} 111 } 112 for _, r := range config.Resources { 113 providers[resourceProvider(r.Type, r.Provider)] = struct{}{} 114 } 115 116 // Turn the map into a string. This makes sure that the list is 117 // de-dupped since we could be going over potentially many resources. 118 result := make([]string, 0, len(providers)) 119 for p, _ := range providers { 120 result = append(result, p) 121 } 122 123 return result 124 } 125 126 // GraphNodeConfigOutput represents an output configured within the 127 // configuration. 128 type GraphNodeConfigOutput struct { 129 Output *config.Output 130 } 131 132 func (n *GraphNodeConfigOutput) Name() string { 133 return fmt.Sprintf("output.%s", n.Output.Name) 134 } 135 136 func (n *GraphNodeConfigOutput) ConfigType() GraphNodeConfigType { 137 return GraphNodeConfigTypeOutput 138 } 139 140 func (n *GraphNodeConfigOutput) DependableName() []string { 141 return []string{n.Name()} 142 } 143 144 func (n *GraphNodeConfigOutput) DependentOn() []string { 145 vars := n.Output.RawConfig.Variables 146 result := make([]string, 0, len(vars)) 147 for _, v := range vars { 148 if vn := varNameForVar(v); vn != "" { 149 result = append(result, vn) 150 } 151 } 152 153 return result 154 } 155 156 // GraphNodeEvalable impl. 157 func (n *GraphNodeConfigOutput) EvalTree() EvalNode { 158 return &EvalOpFilter{ 159 Ops: []walkOperation{walkRefresh, walkPlan, walkApply}, 160 Node: &EvalSequence{ 161 Nodes: []EvalNode{ 162 &EvalWriteOutput{ 163 Name: n.Output.Name, 164 Value: n.Output.RawConfig, 165 }, 166 }, 167 }, 168 } 169 } 170 171 // GraphNodeConfigProvider represents a configured provider within the 172 // configuration graph. These are only immediately in the graph when an 173 // explicit `provider` configuration block is in the configuration. 174 type GraphNodeConfigProvider struct { 175 Provider *config.ProviderConfig 176 } 177 178 func (n *GraphNodeConfigProvider) Name() string { 179 return fmt.Sprintf("provider.%s", n.ProviderName()) 180 } 181 182 func (n *GraphNodeConfigProvider) ConfigType() GraphNodeConfigType { 183 return GraphNodeConfigTypeProvider 184 } 185 186 func (n *GraphNodeConfigProvider) DependableName() []string { 187 return []string{n.Name()} 188 } 189 190 func (n *GraphNodeConfigProvider) DependentOn() []string { 191 vars := n.Provider.RawConfig.Variables 192 result := make([]string, 0, len(vars)) 193 for _, v := range vars { 194 if vn := varNameForVar(v); vn != "" { 195 result = append(result, vn) 196 } 197 } 198 199 return result 200 } 201 202 // GraphNodeEvalable impl. 203 func (n *GraphNodeConfigProvider) EvalTree() EvalNode { 204 return ProviderEvalTree(n.ProviderName(), n.Provider.RawConfig) 205 } 206 207 // GraphNodeProvider implementation 208 func (n *GraphNodeConfigProvider) ProviderName() string { 209 if n.Provider.Alias == "" { 210 return n.Provider.Name 211 } else { 212 return fmt.Sprintf("%s.%s", n.Provider.Name, n.Provider.Alias) 213 } 214 } 215 216 // GraphNodeProvider implementation 217 func (n *GraphNodeConfigProvider) ProviderConfig() *config.RawConfig { 218 return n.Provider.RawConfig 219 } 220 221 // GraphNodeDotter impl. 222 func (n *GraphNodeConfigProvider) Dot(name string) string { 223 return fmt.Sprintf( 224 "\"%s\" [\n"+ 225 "\tlabel=\"%s\"\n"+ 226 "\tshape=diamond\n"+ 227 "];", 228 name, 229 n.Name()) 230 } 231 232 // GraphNodeConfigResource represents a resource within the config graph. 233 type GraphNodeConfigResource struct { 234 Resource *config.Resource 235 236 // If this is set to anything other than destroyModeNone, then this 237 // resource represents a resource that will be destroyed in some way. 238 DestroyMode GraphNodeDestroyMode 239 240 // Used during DynamicExpand to target indexes 241 Targets []ResourceAddress 242 } 243 244 func (n *GraphNodeConfigResource) ConfigType() GraphNodeConfigType { 245 return GraphNodeConfigTypeResource 246 } 247 248 func (n *GraphNodeConfigResource) DependableName() []string { 249 return []string{n.Resource.Id()} 250 } 251 252 // GraphNodeDependent impl. 253 func (n *GraphNodeConfigResource) DependentOn() []string { 254 result := make([]string, len(n.Resource.DependsOn), 255 (len(n.Resource.RawCount.Variables)+ 256 len(n.Resource.RawConfig.Variables)+ 257 len(n.Resource.DependsOn))*2) 258 copy(result, n.Resource.DependsOn) 259 260 for _, v := range n.Resource.RawCount.Variables { 261 if vn := varNameForVar(v); vn != "" { 262 result = append(result, vn) 263 } 264 } 265 for _, v := range n.Resource.RawConfig.Variables { 266 if vn := varNameForVar(v); vn != "" { 267 result = append(result, vn) 268 } 269 } 270 for _, p := range n.Resource.Provisioners { 271 for _, v := range p.ConnInfo.Variables { 272 if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() { 273 result = append(result, vn) 274 } 275 } 276 for _, v := range p.RawConfig.Variables { 277 if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() { 278 result = append(result, vn) 279 } 280 } 281 } 282 283 return result 284 } 285 286 // VarWalk calls a callback for all the variables that this resource 287 // depends on. 288 func (n *GraphNodeConfigResource) VarWalk(fn func(config.InterpolatedVariable)) { 289 for _, v := range n.Resource.RawCount.Variables { 290 fn(v) 291 } 292 for _, v := range n.Resource.RawConfig.Variables { 293 fn(v) 294 } 295 for _, p := range n.Resource.Provisioners { 296 for _, v := range p.ConnInfo.Variables { 297 fn(v) 298 } 299 for _, v := range p.RawConfig.Variables { 300 fn(v) 301 } 302 } 303 } 304 305 func (n *GraphNodeConfigResource) Name() string { 306 result := n.Resource.Id() 307 switch n.DestroyMode { 308 case DestroyNone: 309 case DestroyPrimary: 310 result += " (destroy)" 311 case DestroyTainted: 312 result += " (destroy tainted)" 313 default: 314 result += " (unknown destroy type)" 315 } 316 317 return result 318 } 319 320 // GraphNodeDotter impl. 321 func (n *GraphNodeConfigResource) Dot(name string) string { 322 if n.DestroyMode != DestroyNone { 323 return "" 324 } 325 326 return fmt.Sprintf( 327 "\"%s\" [\n"+ 328 "\tlabel=\"%s\"\n"+ 329 "\tshape=box\n"+ 330 "];", 331 name, 332 n.Name()) 333 } 334 335 // GraphNodeDynamicExpandable impl. 336 func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) { 337 state, lock := ctx.State() 338 lock.RLock() 339 defer lock.RUnlock() 340 341 // Start creating the steps 342 steps := make([]GraphTransformer, 0, 5) 343 344 // Primary and non-destroy modes are responsible for creating/destroying 345 // all the nodes, expanding counts. 346 switch n.DestroyMode { 347 case DestroyNone: 348 fallthrough 349 case DestroyPrimary: 350 steps = append(steps, &ResourceCountTransformer{ 351 Resource: n.Resource, 352 Destroy: n.DestroyMode != DestroyNone, 353 Targets: n.Targets, 354 }) 355 } 356 357 // Additional destroy modifications. 358 switch n.DestroyMode { 359 case DestroyPrimary: 360 // If we're destroying the primary instance, then we want to 361 // expand orphans, which have all the same semantics in a destroy 362 // as a primary. 363 steps = append(steps, &OrphanTransformer{ 364 State: state, 365 View: n.Resource.Id(), 366 Targeting: (len(n.Targets) > 0), 367 }) 368 369 steps = append(steps, &DeposedTransformer{ 370 State: state, 371 View: n.Resource.Id(), 372 }) 373 case DestroyTainted: 374 // If we're only destroying tainted resources, then we only 375 // want to find tainted resources and destroy them here. 376 steps = append(steps, &TaintedTransformer{ 377 State: state, 378 View: n.Resource.Id(), 379 }) 380 } 381 382 // Always end with the root being added 383 steps = append(steps, &RootTransformer{}) 384 385 // Build the graph 386 b := &BasicGraphBuilder{Steps: steps} 387 return b.Build(ctx.Path()) 388 } 389 390 // GraphNodeAddressable impl. 391 func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress { 392 return &ResourceAddress{ 393 // Indicates no specific index; will match on other three fields 394 Index: -1, 395 InstanceType: TypePrimary, 396 Name: n.Resource.Name, 397 Type: n.Resource.Type, 398 } 399 } 400 401 // GraphNodeTargetable impl. 402 func (n *GraphNodeConfigResource) SetTargets(targets []ResourceAddress) { 403 n.Targets = targets 404 } 405 406 // GraphNodeEvalable impl. 407 func (n *GraphNodeConfigResource) EvalTree() EvalNode { 408 return &EvalSequence{ 409 Nodes: []EvalNode{ 410 &EvalInterpolate{Config: n.Resource.RawCount}, 411 &EvalOpFilter{ 412 Ops: []walkOperation{walkValidate}, 413 Node: &EvalValidateCount{Resource: n.Resource}, 414 }, 415 &EvalCountFixZeroOneBoundary{Resource: n.Resource}, 416 }, 417 } 418 } 419 420 // GraphNodeProviderConsumer 421 func (n *GraphNodeConfigResource) ProvidedBy() []string { 422 return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)} 423 } 424 425 // GraphNodeProvisionerConsumer 426 func (n *GraphNodeConfigResource) ProvisionedBy() []string { 427 result := make([]string, len(n.Resource.Provisioners)) 428 for i, p := range n.Resource.Provisioners { 429 result[i] = p.Type 430 } 431 432 return result 433 } 434 435 // GraphNodeDestroyable 436 func (n *GraphNodeConfigResource) DestroyNode(mode GraphNodeDestroyMode) GraphNodeDestroy { 437 // If we're already a destroy node, then don't do anything 438 if n.DestroyMode != DestroyNone { 439 return nil 440 } 441 442 result := &graphNodeResourceDestroy{ 443 GraphNodeConfigResource: *n, 444 Original: n, 445 } 446 result.DestroyMode = mode 447 return result 448 } 449 450 // graphNodeResourceDestroy represents the logical destruction of a 451 // resource. This node doesn't mean it will be destroyed for sure, but 452 // instead that if a destroy were to happen, it must happen at this point. 453 type graphNodeResourceDestroy struct { 454 GraphNodeConfigResource 455 Original *GraphNodeConfigResource 456 } 457 458 func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool { 459 // CBD is enabled if the resource enables it in addition to us 460 // being responsible for destroying the primary state. The primary 461 // state destroy node is the only destroy node that needs to be 462 // "shuffled" according to the CBD rules, since tainted resources 463 // don't have the same inverse dependencies. 464 return n.Original.Resource.Lifecycle.CreateBeforeDestroy && 465 n.DestroyMode == DestroyPrimary 466 } 467 468 func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex { 469 return n.Original 470 } 471 472 func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool { 473 switch n.DestroyMode { 474 case DestroyPrimary: 475 return n.destroyIncludePrimary(d, s) 476 case DestroyTainted: 477 return n.destroyIncludeTainted(d, s) 478 default: 479 return true 480 } 481 } 482 483 func (n *graphNodeResourceDestroy) destroyIncludeTainted( 484 d *ModuleDiff, s *ModuleState) bool { 485 // If there is no state, there can't by any tainted. 486 if s == nil { 487 return false 488 } 489 490 // Grab the ID which is the prefix (in the case count > 0 at some point) 491 prefix := n.Original.Resource.Id() 492 493 // Go through the resources and find any with our prefix. If there 494 // are any tainted, we need to keep it. 495 for k, v := range s.Resources { 496 if !strings.HasPrefix(k, prefix) { 497 continue 498 } 499 500 if len(v.Tainted) > 0 { 501 return true 502 } 503 } 504 505 // We didn't find any tainted nodes, return 506 return false 507 } 508 509 func (n *graphNodeResourceDestroy) destroyIncludePrimary( 510 d *ModuleDiff, s *ModuleState) bool { 511 // Get the count, and specifically the raw value of the count 512 // (with interpolations and all). If the count is NOT a static "1", 513 // then we keep the destroy node no matter what. 514 // 515 // The reasoning for this is complicated and not intuitively obvious, 516 // but I attempt to explain it below. 517 // 518 // The destroy transform works by generating the worst case graph, 519 // with worst case being the case that every resource already exists 520 // and needs to be destroy/created (force-new). There is a single important 521 // edge case where this actually results in a real-life cycle: if a 522 // create-before-destroy (CBD) resource depends on a non-CBD resource. 523 // Imagine a EC2 instance "foo" with CBD depending on a security 524 // group "bar" without CBD, and conceptualize the worst case destroy 525 // order: 526 // 527 // 1.) SG must be destroyed (non-CBD) 528 // 2.) SG must be created/updated 529 // 3.) EC2 instance must be created (CBD, requires the SG be made) 530 // 4.) EC2 instance must be destroyed (requires SG be destroyed) 531 // 532 // Except, #1 depends on #4, since the SG can't be destroyed while 533 // an EC2 instance is using it (AWS API requirements). As you can see, 534 // this is a real life cycle that can't be automatically reconciled 535 // except under two conditions: 536 // 537 // 1.) SG is also CBD. This doesn't work 100% of the time though 538 // since the non-CBD resource might not support CBD. To make matters 539 // worse, the entire transitive closure of dependencies must be 540 // CBD (if the SG depends on a VPC, you have the same problem). 541 // 2.) EC2 must not CBD. This can't happen automatically because CBD 542 // is used as a way to ensure zero (or minimal) downtime Terraform 543 // applies, and it isn't acceptable for TF to ignore this request, 544 // since it can result in unexpected downtime. 545 // 546 // Therefore, we compromise with this edge case here: if there is 547 // a static count of "1", we prune the diff to remove cycles during a 548 // graph optimization path if we don't see the resource in the diff. 549 // If the count is set to ANYTHING other than a static "1" (variable, 550 // computed attribute, static number greater than 1), then we keep the 551 // destroy, since it is required for dynamic graph expansion to find 552 // orphan/tainted count objects. 553 // 554 // This isn't ideal logic, but its strictly better without introducing 555 // new impossibilities. It breaks the cycle in practical cases, and the 556 // cycle comes back in no cases we've found to be practical, but just 557 // as the cycle would already exist without this anyways. 558 count := n.Original.Resource.RawCount 559 if raw := count.Raw[count.Key]; raw != "1" { 560 return true 561 } 562 563 // Okay, we're dealing with a static count. There are a few ways 564 // to include this resource. 565 prefix := n.Original.Resource.Id() 566 567 // If we're present in the diff proper, then keep it. 568 if d != nil { 569 for k, _ := range d.Resources { 570 if strings.HasPrefix(k, prefix) { 571 return true 572 } 573 } 574 } 575 576 // If we're in the state as a primary in any form, then keep it. 577 // This does a prefix check so it will also catch orphans on count 578 // decreases to "1". 579 if s != nil { 580 for k, v := range s.Resources { 581 // Ignore exact matches 582 if k == prefix { 583 continue 584 } 585 586 // Ignore anything that doesn't have a "." afterwards so that 587 // we only get our own resource and any counts on it. 588 if !strings.HasPrefix(k, prefix+".") { 589 continue 590 } 591 592 // Ignore exact matches and the 0'th index. We only care 593 // about if there is a decrease in count. 594 if k == prefix+".0" { 595 continue 596 } 597 598 if v.Primary != nil { 599 return true 600 } 601 } 602 603 // If we're in the state as _both_ "foo" and "foo.0", then 604 // keep it, since we treat the latter as an orphan. 605 _, okOne := s.Resources[prefix] 606 _, okTwo := s.Resources[prefix+".0"] 607 if okOne && okTwo { 608 return true 609 } 610 } 611 612 return false 613 } 614 615 // graphNodeModuleExpanded represents a module where the graph has 616 // been expanded. It stores the graph of the module as well as a reference 617 // to the map of variables. 618 type graphNodeModuleExpanded struct { 619 Original dag.Vertex 620 Graph *Graph 621 InputConfig *config.RawConfig 622 623 // Variables is a map of the input variables. This reference should 624 // be shared with ModuleInputTransformer in order to create a connection 625 // where the variables are set properly. 626 Variables map[string]string 627 } 628 629 func (n *graphNodeModuleExpanded) Name() string { 630 return fmt.Sprintf("%s (expanded)", dag.VertexName(n.Original)) 631 } 632 633 func (n *graphNodeModuleExpanded) ConfigType() GraphNodeConfigType { 634 return GraphNodeConfigTypeModule 635 } 636 637 // GraphNodeDotter impl. 638 func (n *graphNodeModuleExpanded) Dot(name string) string { 639 return fmt.Sprintf( 640 "\"%s\" [\n"+ 641 "\tlabel=\"%s\"\n"+ 642 "\tshape=component\n"+ 643 "];", 644 name, 645 dag.VertexName(n.Original)) 646 } 647 648 // GraphNodeEvalable impl. 649 func (n *graphNodeModuleExpanded) EvalTree() EvalNode { 650 var resourceConfig *ResourceConfig 651 return &EvalSequence{ 652 Nodes: []EvalNode{ 653 &EvalInterpolate{ 654 Config: n.InputConfig, 655 Output: &resourceConfig, 656 }, 657 658 &EvalVariableBlock{ 659 Config: &resourceConfig, 660 Variables: n.Variables, 661 }, 662 663 &EvalOpFilter{ 664 Ops: []walkOperation{walkPlanDestroy}, 665 Node: &EvalSequence{ 666 Nodes: []EvalNode{ 667 &EvalDiffDestroyModule{Path: n.Graph.Path}, 668 }, 669 }, 670 }, 671 }, 672 } 673 } 674 675 // GraphNodeSubgraph impl. 676 func (n *graphNodeModuleExpanded) Subgraph() *Graph { 677 return n.Graph 678 }