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