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