github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/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)] = 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.Provider.Name) 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.Provider.Name, n.Provider.RawConfig) 205 } 206 207 // GraphNodeProvider implementation 208 func (n *GraphNodeConfigProvider) ProviderName() string { 209 return n.Provider.Name 210 } 211 212 // GraphNodeProvider implementation 213 func (n *GraphNodeConfigProvider) ProviderConfig() *config.RawConfig { 214 return n.Provider.RawConfig 215 } 216 217 // GraphNodeDotter impl. 218 func (n *GraphNodeConfigProvider) Dot(name string) string { 219 return fmt.Sprintf( 220 "\"%s\" [\n"+ 221 "\tlabel=\"%s\"\n"+ 222 "\tshape=diamond\n"+ 223 "];", 224 name, 225 n.Name()) 226 } 227 228 // GraphNodeConfigResource represents a resource within the config graph. 229 type GraphNodeConfigResource struct { 230 Resource *config.Resource 231 232 // If this is set to anything other than destroyModeNone, then this 233 // resource represents a resource that will be destroyed in some way. 234 DestroyMode GraphNodeDestroyMode 235 236 // Used during DynamicExpand to target indexes 237 Targets []ResourceAddress 238 } 239 240 func (n *GraphNodeConfigResource) ConfigType() GraphNodeConfigType { 241 return GraphNodeConfigTypeResource 242 } 243 244 func (n *GraphNodeConfigResource) DependableName() []string { 245 return []string{n.Resource.Id()} 246 } 247 248 // GraphNodeDependent impl. 249 func (n *GraphNodeConfigResource) DependentOn() []string { 250 result := make([]string, len(n.Resource.DependsOn), 251 (len(n.Resource.RawCount.Variables)+ 252 len(n.Resource.RawConfig.Variables)+ 253 len(n.Resource.DependsOn))*2) 254 copy(result, n.Resource.DependsOn) 255 256 for _, v := range n.Resource.RawCount.Variables { 257 if vn := varNameForVar(v); vn != "" { 258 result = append(result, vn) 259 } 260 } 261 for _, v := range n.Resource.RawConfig.Variables { 262 if vn := varNameForVar(v); vn != "" { 263 result = append(result, vn) 264 } 265 } 266 for _, p := range n.Resource.Provisioners { 267 for _, v := range p.ConnInfo.Variables { 268 if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() { 269 result = append(result, vn) 270 } 271 } 272 for _, v := range p.RawConfig.Variables { 273 if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() { 274 result = append(result, vn) 275 } 276 } 277 } 278 279 return result 280 } 281 282 func (n *GraphNodeConfigResource) Name() string { 283 result := n.Resource.Id() 284 switch n.DestroyMode { 285 case DestroyNone: 286 case DestroyPrimary: 287 result += " (destroy)" 288 case DestroyTainted: 289 result += " (destroy tainted)" 290 default: 291 result += " (unknown destroy type)" 292 } 293 294 return result 295 } 296 297 // GraphNodeDotter impl. 298 func (n *GraphNodeConfigResource) Dot(name string) string { 299 if n.DestroyMode != DestroyNone { 300 return "" 301 } 302 303 return fmt.Sprintf( 304 "\"%s\" [\n"+ 305 "\tlabel=\"%s\"\n"+ 306 "\tshape=box\n"+ 307 "];", 308 name, 309 n.Name()) 310 } 311 312 // GraphNodeDynamicExpandable impl. 313 func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) { 314 state, lock := ctx.State() 315 lock.RLock() 316 defer lock.RUnlock() 317 318 // Start creating the steps 319 steps := make([]GraphTransformer, 0, 5) 320 321 // Primary and non-destroy modes are responsible for creating/destroying 322 // all the nodes, expanding counts. 323 switch n.DestroyMode { 324 case DestroyNone: 325 fallthrough 326 case DestroyPrimary: 327 steps = append(steps, &ResourceCountTransformer{ 328 Resource: n.Resource, 329 Destroy: n.DestroyMode != DestroyNone, 330 Targets: n.Targets, 331 }) 332 } 333 334 // Additional destroy modifications. 335 switch n.DestroyMode { 336 case DestroyPrimary: 337 // If we're destroying the primary instance, then we want to 338 // expand orphans, which have all the same semantics in a destroy 339 // as a primary. 340 steps = append(steps, &OrphanTransformer{ 341 State: state, 342 View: n.Resource.Id(), 343 Targeting: (len(n.Targets) > 0), 344 }) 345 346 steps = append(steps, &DeposedTransformer{ 347 State: state, 348 View: n.Resource.Id(), 349 }) 350 case DestroyTainted: 351 // If we're only destroying tainted resources, then we only 352 // want to find tainted resources and destroy them here. 353 steps = append(steps, &TaintedTransformer{ 354 State: state, 355 View: n.Resource.Id(), 356 }) 357 } 358 359 // Always end with the root being added 360 steps = append(steps, &RootTransformer{}) 361 362 // Build the graph 363 b := &BasicGraphBuilder{Steps: steps} 364 return b.Build(ctx.Path()) 365 } 366 367 // GraphNodeAddressable impl. 368 func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress { 369 return &ResourceAddress{ 370 // Indicates no specific index; will match on other three fields 371 Index: -1, 372 InstanceType: TypePrimary, 373 Name: n.Resource.Name, 374 Type: n.Resource.Type, 375 } 376 } 377 378 // GraphNodeTargetable impl. 379 func (n *GraphNodeConfigResource) SetTargets(targets []ResourceAddress) { 380 n.Targets = targets 381 } 382 383 // GraphNodeEvalable impl. 384 func (n *GraphNodeConfigResource) EvalTree() EvalNode { 385 return &EvalSequence{ 386 Nodes: []EvalNode{ 387 &EvalInterpolate{Config: n.Resource.RawCount}, 388 &EvalOpFilter{ 389 Ops: []walkOperation{walkValidate}, 390 Node: &EvalValidateCount{Resource: n.Resource}, 391 }, 392 &EvalCountFixZeroOneBoundary{Resource: n.Resource}, 393 }, 394 } 395 } 396 397 // GraphNodeProviderConsumer 398 func (n *GraphNodeConfigResource) ProvidedBy() []string { 399 return []string{resourceProvider(n.Resource.Type)} 400 } 401 402 // GraphNodeProvisionerConsumer 403 func (n *GraphNodeConfigResource) ProvisionedBy() []string { 404 result := make([]string, len(n.Resource.Provisioners)) 405 for i, p := range n.Resource.Provisioners { 406 result[i] = p.Type 407 } 408 409 return result 410 } 411 412 // GraphNodeDestroyable 413 func (n *GraphNodeConfigResource) DestroyNode(mode GraphNodeDestroyMode) GraphNodeDestroy { 414 // If we're already a destroy node, then don't do anything 415 if n.DestroyMode != DestroyNone { 416 return nil 417 } 418 419 result := &graphNodeResourceDestroy{ 420 GraphNodeConfigResource: *n, 421 Original: n, 422 } 423 result.DestroyMode = mode 424 return result 425 } 426 427 // graphNodeResourceDestroy represents the logical destruction of a 428 // resource. This node doesn't mean it will be destroyed for sure, but 429 // instead that if a destroy were to happen, it must happen at this point. 430 type graphNodeResourceDestroy struct { 431 GraphNodeConfigResource 432 Original *GraphNodeConfigResource 433 } 434 435 func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool { 436 // CBD is enabled if the resource enables it in addition to us 437 // being responsible for destroying the primary state. The primary 438 // state destroy node is the only destroy node that needs to be 439 // "shuffled" according to the CBD rules, since tainted resources 440 // don't have the same inverse dependencies. 441 return n.Original.Resource.Lifecycle.CreateBeforeDestroy && 442 n.DestroyMode == DestroyPrimary 443 } 444 445 func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex { 446 return n.Original 447 } 448 449 func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool { 450 switch n.DestroyMode { 451 case DestroyPrimary: 452 return n.destroyIncludePrimary(d, s) 453 case DestroyTainted: 454 return n.destroyIncludeTainted(d, s) 455 default: 456 return true 457 } 458 } 459 460 func (n *graphNodeResourceDestroy) destroyIncludeTainted( 461 d *ModuleDiff, s *ModuleState) bool { 462 // If there is no state, there can't by any tainted. 463 if s == nil { 464 return false 465 } 466 467 // Grab the ID which is the prefix (in the case count > 0 at some point) 468 prefix := n.Original.Resource.Id() 469 470 // Go through the resources and find any with our prefix. If there 471 // are any tainted, we need to keep it. 472 for k, v := range s.Resources { 473 if !strings.HasPrefix(k, prefix) { 474 continue 475 } 476 477 if len(v.Tainted) > 0 { 478 return true 479 } 480 } 481 482 // We didn't find any tainted nodes, return 483 return false 484 } 485 486 func (n *graphNodeResourceDestroy) destroyIncludePrimary( 487 d *ModuleDiff, s *ModuleState) bool { 488 // Get the count, and specifically the raw value of the count 489 // (with interpolations and all). If the count is NOT a static "1", 490 // then we keep the destroy node no matter what. 491 // 492 // The reasoning for this is complicated and not intuitively obvious, 493 // but I attempt to explain it below. 494 // 495 // The destroy transform works by generating the worst case graph, 496 // with worst case being the case that every resource already exists 497 // and needs to be destroy/created (force-new). There is a single important 498 // edge case where this actually results in a real-life cycle: if a 499 // create-before-destroy (CBD) resource depends on a non-CBD resource. 500 // Imagine a EC2 instance "foo" with CBD depending on a security 501 // group "bar" without CBD, and conceptualize the worst case destroy 502 // order: 503 // 504 // 1.) SG must be destroyed (non-CBD) 505 // 2.) SG must be created/updated 506 // 3.) EC2 instance must be created (CBD, requires the SG be made) 507 // 4.) EC2 instance must be destroyed (requires SG be destroyed) 508 // 509 // Except, #1 depends on #4, since the SG can't be destroyed while 510 // an EC2 instance is using it (AWS API requirements). As you can see, 511 // this is a real life cycle that can't be automatically reconciled 512 // except under two conditions: 513 // 514 // 1.) SG is also CBD. This doesn't work 100% of the time though 515 // since the non-CBD resource might not support CBD. To make matters 516 // worse, the entire transitive closure of dependencies must be 517 // CBD (if the SG depends on a VPC, you have the same problem). 518 // 2.) EC2 must not CBD. This can't happen automatically because CBD 519 // is used as a way to ensure zero (or minimal) downtime Terraform 520 // applies, and it isn't acceptable for TF to ignore this request, 521 // since it can result in unexpected downtime. 522 // 523 // Therefore, we compromise with this edge case here: if there is 524 // a static count of "1", we prune the diff to remove cycles during a 525 // graph optimization path if we don't see the resource in the diff. 526 // If the count is set to ANYTHING other than a static "1" (variable, 527 // computed attribute, static number greater than 1), then we keep the 528 // destroy, since it is required for dynamic graph expansion to find 529 // orphan/tainted count objects. 530 // 531 // This isn't ideal logic, but its strictly better without introducing 532 // new impossibilities. It breaks the cycle in practical cases, and the 533 // cycle comes back in no cases we've found to be practical, but just 534 // as the cycle would already exist without this anyways. 535 count := n.Original.Resource.RawCount 536 if raw := count.Raw[count.Key]; raw != "1" { 537 return true 538 } 539 540 // Okay, we're dealing with a static count. There are a few ways 541 // to include this resource. 542 prefix := n.Original.Resource.Id() 543 544 // If we're present in the diff proper, then keep it. 545 if d != nil { 546 for k, _ := range d.Resources { 547 if strings.HasPrefix(k, prefix) { 548 return true 549 } 550 } 551 } 552 553 // If we're in the state as a primary in any form, then keep it. 554 // This does a prefix check so it will also catch orphans on count 555 // decreases to "1". 556 if s != nil { 557 for k, v := range s.Resources { 558 // Ignore exact matches 559 if k == prefix { 560 continue 561 } 562 563 // Ignore anything that doesn't have a "." afterwards so that 564 // we only get our own resource and any counts on it. 565 if !strings.HasPrefix(k, prefix+".") { 566 continue 567 } 568 569 // Ignore exact matches and the 0'th index. We only care 570 // about if there is a decrease in count. 571 if k == prefix+".0" { 572 continue 573 } 574 575 if v.Primary != nil { 576 return true 577 } 578 } 579 580 // If we're in the state as _both_ "foo" and "foo.0", then 581 // keep it, since we treat the latter as an orphan. 582 _, okOne := s.Resources[prefix] 583 _, okTwo := s.Resources[prefix+".0"] 584 if okOne && okTwo { 585 return true 586 } 587 } 588 589 return false 590 } 591 592 // graphNodeModuleExpanded represents a module where the graph has 593 // been expanded. It stores the graph of the module as well as a reference 594 // to the map of variables. 595 type graphNodeModuleExpanded struct { 596 Original dag.Vertex 597 Graph *Graph 598 InputConfig *config.RawConfig 599 600 // Variables is a map of the input variables. This reference should 601 // be shared with ModuleInputTransformer in order to create a connection 602 // where the variables are set properly. 603 Variables map[string]string 604 } 605 606 func (n *graphNodeModuleExpanded) Name() string { 607 return fmt.Sprintf("%s (expanded)", dag.VertexName(n.Original)) 608 } 609 610 func (n *graphNodeModuleExpanded) ConfigType() GraphNodeConfigType { 611 return GraphNodeConfigTypeModule 612 } 613 614 // GraphNodeDotter impl. 615 func (n *graphNodeModuleExpanded) Dot(name string) string { 616 return fmt.Sprintf( 617 "\"%s\" [\n"+ 618 "\tlabel=\"%s\"\n"+ 619 "\tshape=component\n"+ 620 "];", 621 name, 622 dag.VertexName(n.Original)) 623 } 624 625 // GraphNodeEvalable impl. 626 func (n *graphNodeModuleExpanded) EvalTree() EvalNode { 627 var resourceConfig *ResourceConfig 628 return &EvalSequence{ 629 Nodes: []EvalNode{ 630 &EvalInterpolate{ 631 Config: n.InputConfig, 632 Output: &resourceConfig, 633 }, 634 635 &EvalVariableBlock{ 636 Config: &resourceConfig, 637 Variables: n.Variables, 638 }, 639 640 &EvalOpFilter{ 641 Ops: []walkOperation{walkPlanDestroy}, 642 Node: &EvalSequence{ 643 Nodes: []EvalNode{ 644 &EvalDiffDestroyModule{Path: n.Graph.Path}, 645 }, 646 }, 647 }, 648 }, 649 } 650 } 651 652 // GraphNodeSubgraph impl. 653 func (n *graphNodeModuleExpanded) Subgraph() *Graph { 654 return n.Graph 655 }