github.com/biggiemac/terraform@v0.6.12-0.20160217180759-34b7665af0d6/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 we have no module diff, we're certainly a noop. This is because 287 // it means there is a diff, and that the module we're in just isn't 288 // in it, meaning we're not doing anything. 289 if opts.ModDiff == nil || opts.ModDiff.Empty() { 290 return true 291 } 292 293 // Grab the ID which is the prefix (in the case count > 0 at some point) 294 prefix := n.Resource.Id() 295 296 // Go through the diff and if there are any with our name on it, keep us 297 found := false 298 for k, _ := range opts.ModDiff.Resources { 299 if strings.HasPrefix(k, prefix) { 300 found = true 301 break 302 } 303 } 304 305 return !found 306 } 307 308 // Same as GraphNodeConfigResource, but for flattening 309 type GraphNodeConfigResourceFlat struct { 310 *GraphNodeConfigResource 311 312 PathValue []string 313 } 314 315 func (n *GraphNodeConfigResourceFlat) Name() string { 316 return fmt.Sprintf( 317 "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigResource.Name()) 318 } 319 320 func (n *GraphNodeConfigResourceFlat) Path() []string { 321 return n.PathValue 322 } 323 324 func (n *GraphNodeConfigResourceFlat) DependableName() []string { 325 return modulePrefixList( 326 n.GraphNodeConfigResource.DependableName(), 327 modulePrefixStr(n.PathValue)) 328 } 329 330 func (n *GraphNodeConfigResourceFlat) DependentOn() []string { 331 prefix := modulePrefixStr(n.PathValue) 332 return modulePrefixList( 333 n.GraphNodeConfigResource.DependentOn(), 334 prefix) 335 } 336 337 func (n *GraphNodeConfigResourceFlat) ProvidedBy() []string { 338 prefix := modulePrefixStr(n.PathValue) 339 return modulePrefixList( 340 n.GraphNodeConfigResource.ProvidedBy(), 341 prefix) 342 } 343 344 func (n *GraphNodeConfigResourceFlat) ProvisionedBy() []string { 345 prefix := modulePrefixStr(n.PathValue) 346 return modulePrefixList( 347 n.GraphNodeConfigResource.ProvisionedBy(), 348 prefix) 349 } 350 351 // GraphNodeDestroyable impl. 352 func (n *GraphNodeConfigResourceFlat) DestroyNode(mode GraphNodeDestroyMode) GraphNodeDestroy { 353 // Get our parent destroy node. If we don't have any, just return 354 raw := n.GraphNodeConfigResource.DestroyNode(mode) 355 if raw == nil { 356 return nil 357 } 358 359 node, ok := raw.(*graphNodeResourceDestroy) 360 if !ok { 361 panic(fmt.Sprintf("unknown destroy node: %s %T", dag.VertexName(raw), raw)) 362 } 363 364 // Otherwise, wrap it so that it gets the proper module treatment. 365 return &graphNodeResourceDestroyFlat{ 366 graphNodeResourceDestroy: node, 367 PathValue: n.PathValue, 368 FlatCreateNode: n, 369 } 370 } 371 372 type graphNodeResourceDestroyFlat struct { 373 *graphNodeResourceDestroy 374 375 PathValue []string 376 377 // Needs to be able to properly yield back a flattened create node to prevent 378 FlatCreateNode *GraphNodeConfigResourceFlat 379 } 380 381 func (n *graphNodeResourceDestroyFlat) Name() string { 382 return fmt.Sprintf( 383 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeResourceDestroy.Name()) 384 } 385 386 func (n *graphNodeResourceDestroyFlat) Path() []string { 387 return n.PathValue 388 } 389 390 func (n *graphNodeResourceDestroyFlat) CreateNode() dag.Vertex { 391 return n.FlatCreateNode 392 } 393 394 func (n *graphNodeResourceDestroyFlat) ProvidedBy() []string { 395 prefix := modulePrefixStr(n.PathValue) 396 return modulePrefixList( 397 n.GraphNodeConfigResource.ProvidedBy(), 398 prefix) 399 } 400 401 // graphNodeResourceDestroy represents the logical destruction of a 402 // resource. This node doesn't mean it will be destroyed for sure, but 403 // instead that if a destroy were to happen, it must happen at this point. 404 type graphNodeResourceDestroy struct { 405 GraphNodeConfigResource 406 Original *GraphNodeConfigResource 407 } 408 409 func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool { 410 // CBD is enabled if the resource enables it in addition to us 411 // being responsible for destroying the primary state. The primary 412 // state destroy node is the only destroy node that needs to be 413 // "shuffled" according to the CBD rules, since tainted resources 414 // don't have the same inverse dependencies. 415 return n.Original.Resource.Lifecycle.CreateBeforeDestroy && 416 n.DestroyMode == DestroyPrimary 417 } 418 419 func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex { 420 return n.Original 421 } 422 423 func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool { 424 switch n.DestroyMode { 425 case DestroyPrimary: 426 return n.destroyIncludePrimary(d, s) 427 case DestroyTainted: 428 return n.destroyIncludeTainted(d, s) 429 default: 430 return true 431 } 432 } 433 434 func (n *graphNodeResourceDestroy) destroyIncludeTainted( 435 d *ModuleDiff, s *ModuleState) bool { 436 // If there is no state, there can't by any tainted. 437 if s == nil { 438 return false 439 } 440 441 // Grab the ID which is the prefix (in the case count > 0 at some point) 442 prefix := n.Original.Resource.Id() 443 444 // Go through the resources and find any with our prefix. If there 445 // are any tainted, we need to keep it. 446 for k, v := range s.Resources { 447 if !strings.HasPrefix(k, prefix) { 448 continue 449 } 450 451 if len(v.Tainted) > 0 { 452 return true 453 } 454 } 455 456 // We didn't find any tainted nodes, return 457 return false 458 } 459 460 func (n *graphNodeResourceDestroy) destroyIncludePrimary( 461 d *ModuleDiff, s *ModuleState) bool { 462 // Get the count, and specifically the raw value of the count 463 // (with interpolations and all). If the count is NOT a static "1", 464 // then we keep the destroy node no matter what. 465 // 466 // The reasoning for this is complicated and not intuitively obvious, 467 // but I attempt to explain it below. 468 // 469 // The destroy transform works by generating the worst case graph, 470 // with worst case being the case that every resource already exists 471 // and needs to be destroy/created (force-new). There is a single important 472 // edge case where this actually results in a real-life cycle: if a 473 // create-before-destroy (CBD) resource depends on a non-CBD resource. 474 // Imagine a EC2 instance "foo" with CBD depending on a security 475 // group "bar" without CBD, and conceptualize the worst case destroy 476 // order: 477 // 478 // 1.) SG must be destroyed (non-CBD) 479 // 2.) SG must be created/updated 480 // 3.) EC2 instance must be created (CBD, requires the SG be made) 481 // 4.) EC2 instance must be destroyed (requires SG be destroyed) 482 // 483 // Except, #1 depends on #4, since the SG can't be destroyed while 484 // an EC2 instance is using it (AWS API requirements). As you can see, 485 // this is a real life cycle that can't be automatically reconciled 486 // except under two conditions: 487 // 488 // 1.) SG is also CBD. This doesn't work 100% of the time though 489 // since the non-CBD resource might not support CBD. To make matters 490 // worse, the entire transitive closure of dependencies must be 491 // CBD (if the SG depends on a VPC, you have the same problem). 492 // 2.) EC2 must not CBD. This can't happen automatically because CBD 493 // is used as a way to ensure zero (or minimal) downtime Terraform 494 // applies, and it isn't acceptable for TF to ignore this request, 495 // since it can result in unexpected downtime. 496 // 497 // Therefore, we compromise with this edge case here: if there is 498 // a static count of "1", we prune the diff to remove cycles during a 499 // graph optimization path if we don't see the resource in the diff. 500 // If the count is set to ANYTHING other than a static "1" (variable, 501 // computed attribute, static number greater than 1), then we keep the 502 // destroy, since it is required for dynamic graph expansion to find 503 // orphan/tainted count objects. 504 // 505 // This isn't ideal logic, but its strictly better without introducing 506 // new impossibilities. It breaks the cycle in practical cases, and the 507 // cycle comes back in no cases we've found to be practical, but just 508 // as the cycle would already exist without this anyways. 509 count := n.Original.Resource.RawCount 510 if raw := count.Raw[count.Key]; raw != "1" { 511 return true 512 } 513 514 // Okay, we're dealing with a static count. There are a few ways 515 // to include this resource. 516 prefix := n.Original.Resource.Id() 517 518 // If we're present in the diff proper, then keep it. We're looking 519 // only for resources in the diff that match our resource or a count-index 520 // of our resource that are marked for destroy. 521 if d != nil { 522 for k, d := range d.Resources { 523 match := k == prefix || strings.HasPrefix(k, prefix+".") 524 if match && d.Destroy { 525 return true 526 } 527 } 528 } 529 530 // If we're in the state as a primary in any form, then keep it. 531 // This does a prefix check so it will also catch orphans on count 532 // decreases to "1". 533 if s != nil { 534 for k, v := range s.Resources { 535 // Ignore exact matches 536 if k == prefix { 537 continue 538 } 539 540 // Ignore anything that doesn't have a "." afterwards so that 541 // we only get our own resource and any counts on it. 542 if !strings.HasPrefix(k, prefix+".") { 543 continue 544 } 545 546 // Ignore exact matches and the 0'th index. We only care 547 // about if there is a decrease in count. 548 if k == prefix+".0" { 549 continue 550 } 551 552 if v.Primary != nil { 553 return true 554 } 555 } 556 557 // If we're in the state as _both_ "foo" and "foo.0", then 558 // keep it, since we treat the latter as an orphan. 559 _, okOne := s.Resources[prefix] 560 _, okTwo := s.Resources[prefix+".0"] 561 if okOne && okTwo { 562 return true 563 } 564 } 565 566 return false 567 }