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