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