github.com/markdia/terraform@v0.5.1-0.20150508012022-f1ae920aa970/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 } 315 } 316 317 type graphNodeResourceDestroyFlat struct { 318 *graphNodeResourceDestroy 319 320 PathValue []string 321 } 322 323 func (n *graphNodeResourceDestroyFlat) Name() string { 324 return fmt.Sprintf( 325 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeResourceDestroy.Name()) 326 } 327 328 func (n *graphNodeResourceDestroyFlat) Path() []string { 329 return n.PathValue 330 } 331 332 // graphNodeResourceDestroy represents the logical destruction of a 333 // resource. This node doesn't mean it will be destroyed for sure, but 334 // instead that if a destroy were to happen, it must happen at this point. 335 type graphNodeResourceDestroy struct { 336 GraphNodeConfigResource 337 Original *GraphNodeConfigResource 338 } 339 340 func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool { 341 // CBD is enabled if the resource enables it in addition to us 342 // being responsible for destroying the primary state. The primary 343 // state destroy node is the only destroy node that needs to be 344 // "shuffled" according to the CBD rules, since tainted resources 345 // don't have the same inverse dependencies. 346 return n.Original.Resource.Lifecycle.CreateBeforeDestroy && 347 n.DestroyMode == DestroyPrimary 348 } 349 350 func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex { 351 return n.Original 352 } 353 354 func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool { 355 switch n.DestroyMode { 356 case DestroyPrimary: 357 return n.destroyIncludePrimary(d, s) 358 case DestroyTainted: 359 return n.destroyIncludeTainted(d, s) 360 default: 361 return true 362 } 363 } 364 365 func (n *graphNodeResourceDestroy) destroyIncludeTainted( 366 d *ModuleDiff, s *ModuleState) bool { 367 // If there is no state, there can't by any tainted. 368 if s == nil { 369 return false 370 } 371 372 // Grab the ID which is the prefix (in the case count > 0 at some point) 373 prefix := n.Original.Resource.Id() 374 375 // Go through the resources and find any with our prefix. If there 376 // are any tainted, we need to keep it. 377 for k, v := range s.Resources { 378 if !strings.HasPrefix(k, prefix) { 379 continue 380 } 381 382 if len(v.Tainted) > 0 { 383 return true 384 } 385 } 386 387 // We didn't find any tainted nodes, return 388 return false 389 } 390 391 func (n *graphNodeResourceDestroy) destroyIncludePrimary( 392 d *ModuleDiff, s *ModuleState) bool { 393 // Get the count, and specifically the raw value of the count 394 // (with interpolations and all). If the count is NOT a static "1", 395 // then we keep the destroy node no matter what. 396 // 397 // The reasoning for this is complicated and not intuitively obvious, 398 // but I attempt to explain it below. 399 // 400 // The destroy transform works by generating the worst case graph, 401 // with worst case being the case that every resource already exists 402 // and needs to be destroy/created (force-new). There is a single important 403 // edge case where this actually results in a real-life cycle: if a 404 // create-before-destroy (CBD) resource depends on a non-CBD resource. 405 // Imagine a EC2 instance "foo" with CBD depending on a security 406 // group "bar" without CBD, and conceptualize the worst case destroy 407 // order: 408 // 409 // 1.) SG must be destroyed (non-CBD) 410 // 2.) SG must be created/updated 411 // 3.) EC2 instance must be created (CBD, requires the SG be made) 412 // 4.) EC2 instance must be destroyed (requires SG be destroyed) 413 // 414 // Except, #1 depends on #4, since the SG can't be destroyed while 415 // an EC2 instance is using it (AWS API requirements). As you can see, 416 // this is a real life cycle that can't be automatically reconciled 417 // except under two conditions: 418 // 419 // 1.) SG is also CBD. This doesn't work 100% of the time though 420 // since the non-CBD resource might not support CBD. To make matters 421 // worse, the entire transitive closure of dependencies must be 422 // CBD (if the SG depends on a VPC, you have the same problem). 423 // 2.) EC2 must not CBD. This can't happen automatically because CBD 424 // is used as a way to ensure zero (or minimal) downtime Terraform 425 // applies, and it isn't acceptable for TF to ignore this request, 426 // since it can result in unexpected downtime. 427 // 428 // Therefore, we compromise with this edge case here: if there is 429 // a static count of "1", we prune the diff to remove cycles during a 430 // graph optimization path if we don't see the resource in the diff. 431 // If the count is set to ANYTHING other than a static "1" (variable, 432 // computed attribute, static number greater than 1), then we keep the 433 // destroy, since it is required for dynamic graph expansion to find 434 // orphan/tainted count objects. 435 // 436 // This isn't ideal logic, but its strictly better without introducing 437 // new impossibilities. It breaks the cycle in practical cases, and the 438 // cycle comes back in no cases we've found to be practical, but just 439 // as the cycle would already exist without this anyways. 440 count := n.Original.Resource.RawCount 441 if raw := count.Raw[count.Key]; raw != "1" { 442 return true 443 } 444 445 // Okay, we're dealing with a static count. There are a few ways 446 // to include this resource. 447 prefix := n.Original.Resource.Id() 448 449 // If we're present in the diff proper, then keep it. We're looking 450 // only for resources in the diff that match our resource or a count-index 451 // of our resource that are marked for destroy. 452 if d != nil { 453 for k, d := range d.Resources { 454 match := k == prefix || strings.HasPrefix(k, prefix+".") 455 if match && d.Destroy { 456 return true 457 } 458 } 459 } 460 461 // If we're in the state as a primary in any form, then keep it. 462 // This does a prefix check so it will also catch orphans on count 463 // decreases to "1". 464 if s != nil { 465 for k, v := range s.Resources { 466 // Ignore exact matches 467 if k == prefix { 468 continue 469 } 470 471 // Ignore anything that doesn't have a "." afterwards so that 472 // we only get our own resource and any counts on it. 473 if !strings.HasPrefix(k, prefix+".") { 474 continue 475 } 476 477 // Ignore exact matches and the 0'th index. We only care 478 // about if there is a decrease in count. 479 if k == prefix+".0" { 480 continue 481 } 482 483 if v.Primary != nil { 484 return true 485 } 486 } 487 488 // If we're in the state as _both_ "foo" and "foo.0", then 489 // keep it, since we treat the latter as an orphan. 490 _, okOne := s.Resources[prefix] 491 _, okTwo := s.Resources[prefix+".0"] 492 if okOne && okTwo { 493 return true 494 } 495 } 496 497 return false 498 }