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