github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/transform_reference.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "sort" 7 8 "github.com/hashicorp/hcl/v2" 9 "github.com/hashicorp/terraform/internal/addrs" 10 "github.com/hashicorp/terraform/internal/configs/configschema" 11 "github.com/hashicorp/terraform/internal/dag" 12 "github.com/hashicorp/terraform/internal/lang" 13 ) 14 15 // GraphNodeReferenceable must be implemented by any node that represents 16 // a Terraform thing that can be referenced (resource, module, etc.). 17 // 18 // Even if the thing has no name, this should return an empty list. By 19 // implementing this and returning a non-nil result, you say that this CAN 20 // be referenced and other methods of referencing may still be possible (such 21 // as by path!) 22 type GraphNodeReferenceable interface { 23 GraphNodeModulePath 24 25 // ReferenceableAddrs returns a list of addresses through which this can be 26 // referenced. 27 ReferenceableAddrs() []addrs.Referenceable 28 } 29 30 // GraphNodeReferencer must be implemented by nodes that reference other 31 // Terraform items and therefore depend on them. 32 type GraphNodeReferencer interface { 33 GraphNodeModulePath 34 35 // References returns a list of references made by this node, which 36 // include both a referenced address and source location information for 37 // the reference. 38 References() []*addrs.Reference 39 } 40 41 type GraphNodeAttachDependencies interface { 42 GraphNodeConfigResource 43 AttachDependencies([]addrs.ConfigResource) 44 } 45 46 // graphNodeDependsOn is implemented by resources that need to expose any 47 // references set via DependsOn in their configuration. 48 type graphNodeDependsOn interface { 49 GraphNodeReferencer 50 DependsOn() []*addrs.Reference 51 } 52 53 // graphNodeAttachDataResourceDependsOn records all resources that are transitively 54 // referenced through depends_on in the configuration. This is used by data 55 // resources to determine if they can be read during the plan, or if they need 56 // to be further delayed until apply. 57 // We can only use an addrs.ConfigResource address here, because modules are 58 // not yet expended in the graph. While this will cause some extra data 59 // resources to show in the plan when their depends_on references may be in 60 // unrelated module instances, the fact that it only happens when there are any 61 // resource updates pending means we can still avoid the problem of the 62 // "perpetual diff" 63 type graphNodeAttachDataResourceDependsOn interface { 64 GraphNodeConfigResource 65 graphNodeDependsOn 66 67 // AttachDataResourceDependsOn stores the discovered dependencies in the 68 // resource node for evaluation later. 69 // 70 // The force parameter indicates that even if there are no dependencies, 71 // force the data source to act as though there are for refresh purposes. 72 // This is needed because yet-to-be-created resources won't be in the 73 // initial refresh graph, but may still be referenced through depends_on. 74 AttachDataResourceDependsOn(deps []addrs.ConfigResource, force bool) 75 } 76 77 // GraphNodeReferenceOutside is an interface that can optionally be implemented. 78 // A node that implements it can specify that its own referenceable addresses 79 // and/or the addresses it references are in a different module than the 80 // node itself. 81 // 82 // Any referenceable addresses returned by ReferenceableAddrs are interpreted 83 // relative to the returned selfPath. 84 // 85 // Any references returned by References are interpreted relative to the 86 // returned referencePath. 87 // 88 // It is valid but not required for either of these paths to match what is 89 // returned by method Path, though if both match the main Path then there 90 // is no reason to implement this method. 91 // 92 // The primary use-case for this is the nodes representing module input 93 // variables, since their expressions are resolved in terms of their calling 94 // module, but they are still referenced from their own module. 95 type GraphNodeReferenceOutside interface { 96 // ReferenceOutside returns a path in which any references from this node 97 // are resolved. 98 ReferenceOutside() (selfPath, referencePath addrs.Module) 99 } 100 101 // ReferenceTransformer is a GraphTransformer that connects all the 102 // nodes that reference each other in order to form the proper ordering. 103 type ReferenceTransformer struct{} 104 105 func (t *ReferenceTransformer) Transform(g *Graph) error { 106 // Build a reference map so we can efficiently look up the references 107 vs := g.Vertices() 108 m := NewReferenceMap(vs) 109 110 // Find the things that reference things and connect them 111 for _, v := range vs { 112 if _, ok := v.(GraphNodeDestroyer); ok { 113 // destroy nodes references are not connected, since they can only 114 // use their own state. 115 continue 116 } 117 118 parents := m.References(v) 119 parentsDbg := make([]string, len(parents)) 120 for i, v := range parents { 121 parentsDbg[i] = dag.VertexName(v) 122 } 123 log.Printf( 124 "[DEBUG] ReferenceTransformer: %q references: %v", 125 dag.VertexName(v), parentsDbg) 126 127 for _, parent := range parents { 128 // A destroy plan relies solely on the state, so we only need to 129 // ensure that temporary values are connected to get the evaluation 130 // order correct. Any references to destroy nodes will cause 131 // cycles, because they are connected in reverse order. 132 if _, ok := parent.(GraphNodeDestroyer); ok { 133 continue 134 } 135 136 if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(v, parent) { 137 g.Connect(dag.BasicEdge(v, parent)) 138 } else { 139 log.Printf("[TRACE] ReferenceTransformer: skipping %s => %s inter-module-instance dependency", v, parent) 140 } 141 } 142 143 if len(parents) > 0 { 144 continue 145 } 146 } 147 148 return nil 149 } 150 151 type depMap map[string]addrs.ConfigResource 152 153 // add stores the vertex if it represents a resource in the 154 // graph. 155 func (m depMap) add(v dag.Vertex) { 156 // we're only concerned with resources which may have changes that 157 // need to be applied. 158 switch v := v.(type) { 159 case GraphNodeResourceInstance: 160 instAddr := v.ResourceInstanceAddr() 161 addr := instAddr.ContainingResource().Config() 162 m[addr.String()] = addr 163 case GraphNodeConfigResource: 164 addr := v.ResourceAddr() 165 m[addr.String()] = addr 166 } 167 } 168 169 // attachDataResourceDependsOnTransformer records all resources transitively 170 // referenced through a configuration depends_on. 171 type attachDataResourceDependsOnTransformer struct { 172 } 173 174 func (t attachDataResourceDependsOnTransformer) Transform(g *Graph) error { 175 // First we need to make a map of referenceable addresses to their vertices. 176 // This is very similar to what's done in ReferenceTransformer, but we keep 177 // implementation separate as they may need to change independently. 178 vertices := g.Vertices() 179 refMap := NewReferenceMap(vertices) 180 181 for _, v := range vertices { 182 depender, ok := v.(graphNodeAttachDataResourceDependsOn) 183 if !ok { 184 continue 185 } 186 187 // Only data need to attach depends_on, so they can determine if they 188 // are eligible to be read during plan. 189 if depender.ResourceAddr().Resource.Mode != addrs.DataResourceMode { 190 continue 191 } 192 193 // depMap will only add resource references then dedupe 194 deps := make(depMap) 195 dependsOnDeps, fromModule := refMap.dependsOn(g, depender) 196 for _, dep := range dependsOnDeps { 197 // any the dependency 198 deps.add(dep) 199 } 200 201 res := make([]addrs.ConfigResource, 0, len(deps)) 202 for _, d := range deps { 203 res = append(res, d) 204 } 205 206 log.Printf("[TRACE] attachDataDependenciesTransformer: %s depends on %s", depender.ResourceAddr(), res) 207 depender.AttachDataResourceDependsOn(res, fromModule) 208 } 209 210 return nil 211 } 212 213 // AttachDependenciesTransformer records all resource dependencies for each 214 // instance, and attaches the addresses to the node itself. Managed resource 215 // will record these in the state for proper ordering of destroy operations. 216 type AttachDependenciesTransformer struct { 217 } 218 219 func (t AttachDependenciesTransformer) Transform(g *Graph) error { 220 for _, v := range g.Vertices() { 221 attacher, ok := v.(GraphNodeAttachDependencies) 222 if !ok { 223 continue 224 } 225 selfAddr := attacher.ResourceAddr() 226 227 ans, err := g.Ancestors(v) 228 if err != nil { 229 return err 230 } 231 232 // dedupe addrs when there's multiple instances involved, or 233 // multiple paths in the un-reduced graph 234 depMap := map[string]addrs.ConfigResource{} 235 for _, d := range ans { 236 var addr addrs.ConfigResource 237 238 switch d := d.(type) { 239 case GraphNodeResourceInstance: 240 instAddr := d.ResourceInstanceAddr() 241 addr = instAddr.ContainingResource().Config() 242 case GraphNodeConfigResource: 243 addr = d.ResourceAddr() 244 default: 245 continue 246 } 247 248 if addr.Equal(selfAddr) { 249 continue 250 } 251 depMap[addr.String()] = addr 252 } 253 254 deps := make([]addrs.ConfigResource, 0, len(depMap)) 255 for _, d := range depMap { 256 deps = append(deps, d) 257 } 258 sort.Slice(deps, func(i, j int) bool { 259 return deps[i].String() < deps[j].String() 260 }) 261 262 log.Printf("[TRACE] AttachDependenciesTransformer: %s depends on %s", attacher.ResourceAddr(), deps) 263 attacher.AttachDependencies(deps) 264 } 265 266 return nil 267 } 268 269 func isDependableResource(v dag.Vertex) bool { 270 switch v.(type) { 271 case GraphNodeResourceInstance: 272 return true 273 case GraphNodeConfigResource: 274 return true 275 } 276 return false 277 } 278 279 // ReferenceMap is a structure that can be used to efficiently check 280 // for references on a graph, mapping internal reference keys (as produced by 281 // the mapKey method) to one or more vertices that are identified by each key. 282 type ReferenceMap map[string][]dag.Vertex 283 284 // References returns the set of vertices that the given vertex refers to, 285 // and any referenced addresses that do not have corresponding vertices. 286 func (m ReferenceMap) References(v dag.Vertex) []dag.Vertex { 287 rn, ok := v.(GraphNodeReferencer) 288 if !ok { 289 return nil 290 } 291 292 var matches []dag.Vertex 293 294 for _, ref := range rn.References() { 295 subject := ref.Subject 296 297 key := m.referenceMapKey(v, subject) 298 if _, exists := m[key]; !exists { 299 // If what we were looking for was a ResourceInstance then we 300 // might be in a resource-oriented graph rather than an 301 // instance-oriented graph, and so we'll see if we have the 302 // resource itself instead. 303 switch ri := subject.(type) { 304 case addrs.ResourceInstance: 305 subject = ri.ContainingResource() 306 case addrs.ResourceInstancePhase: 307 subject = ri.ContainingResource() 308 case addrs.ModuleCallInstanceOutput: 309 subject = ri.ModuleCallOutput() 310 case addrs.ModuleCallInstance: 311 subject = ri.Call 312 default: 313 log.Printf("[INFO] ReferenceTransformer: reference not found: %q", subject) 314 continue 315 } 316 key = m.referenceMapKey(v, subject) 317 } 318 vertices := m[key] 319 for _, rv := range vertices { 320 // don't include self-references 321 if rv == v { 322 continue 323 } 324 matches = append(matches, rv) 325 } 326 } 327 328 return matches 329 } 330 331 // dependsOn returns the set of vertices that the given vertex refers to from 332 // the configured depends_on. The bool return value indicates if depends_on was 333 // found in a parent module configuration. 334 func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { 335 var res []dag.Vertex 336 fromModule := false 337 338 refs := depender.DependsOn() 339 340 // get any implied dependencies for data sources 341 refs = append(refs, m.dataDependsOn(depender)...) 342 343 // This is where we record that a module has depends_on configured. 344 if _, ok := depender.(*nodeExpandModule); ok && len(refs) > 0 { 345 fromModule = true 346 } 347 348 for _, ref := range refs { 349 subject := ref.Subject 350 351 key := m.referenceMapKey(depender, subject) 352 vertices, ok := m[key] 353 if !ok { 354 // the ReferenceMap generates all possible keys, so any warning 355 // here is probably not useful for this implementation. 356 continue 357 } 358 for _, rv := range vertices { 359 // don't include self-references 360 if rv == depender { 361 continue 362 } 363 res = append(res, rv) 364 365 // Check any ancestors for transitive dependencies when we're 366 // not pointed directly at a resource. We can't be much more 367 // precise here, since in order to maintain our guarantee that data 368 // sources will wait for explicit dependencies, if those dependencies 369 // happen to be a module, output, or variable, we have to find some 370 // upstream managed resource in order to check for a planned 371 // change. 372 if _, ok := rv.(GraphNodeConfigResource); !ok { 373 ans, _ := g.Ancestors(rv) 374 for _, v := range ans { 375 if isDependableResource(v) { 376 res = append(res, v) 377 } 378 } 379 } 380 } 381 } 382 383 parentDeps, fromParentModule := m.parentModuleDependsOn(g, depender) 384 res = append(res, parentDeps...) 385 386 return res, fromModule || fromParentModule 387 } 388 389 // Return extra depends_on references if this is a data source. 390 // For data sources we implicitly treat references to managed resources as 391 // depends_on entries. If a data source references a managed resource, even if 392 // that reference is resolvable, it stands to reason that the user intends for 393 // the data source to require that resource in some way. 394 func (m ReferenceMap) dataDependsOn(depender graphNodeDependsOn) []*addrs.Reference { 395 var refs []*addrs.Reference 396 if n, ok := depender.(GraphNodeConfigResource); ok && 397 n.ResourceAddr().Resource.Mode == addrs.DataResourceMode { 398 for _, r := range depender.References() { 399 400 var resAddr addrs.Resource 401 switch s := r.Subject.(type) { 402 case addrs.Resource: 403 resAddr = s 404 case addrs.ResourceInstance: 405 resAddr = s.Resource 406 r.Subject = resAddr 407 } 408 409 if resAddr.Mode != addrs.ManagedResourceMode { 410 // We only want to wait on directly referenced managed resources. 411 // Data sources have no external side effects, so normal 412 // references to them in the config will suffice for proper 413 // ordering. 414 continue 415 } 416 417 refs = append(refs, r) 418 } 419 } 420 return refs 421 } 422 423 // parentModuleDependsOn returns the set of vertices that a data sources parent 424 // module references through the module call's depends_on. The bool return 425 // value indicates if depends_on was found in a parent module configuration. 426 func (m ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { 427 var res []dag.Vertex 428 fromModule := false 429 430 // Look for containing modules with DependsOn. 431 // This should be connected directly to the module node, so we only need to 432 // look one step away. 433 for _, v := range g.DownEdges(depender) { 434 // we're only concerned with module expansion nodes here. 435 mod, ok := v.(*nodeExpandModule) 436 if !ok { 437 continue 438 } 439 440 deps, fromParentModule := m.dependsOn(g, mod) 441 for _, dep := range deps { 442 // add the dependency 443 res = append(res, dep) 444 445 // and check any transitive resource dependencies for more resources 446 ans, _ := g.Ancestors(dep) 447 for _, v := range ans { 448 if isDependableResource(v) { 449 res = append(res, v) 450 } 451 } 452 } 453 fromModule = fromModule || fromParentModule 454 } 455 456 return res, fromModule 457 } 458 459 func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string { 460 return fmt.Sprintf("%s|%s", path.String(), addr.String()) 461 } 462 463 // vertexReferenceablePath returns the path in which the given vertex can be 464 // referenced. This is the path that its results from ReferenceableAddrs 465 // are considered to be relative to. 466 // 467 // Only GraphNodeModulePath implementations can be referenced, so this method will 468 // panic if the given vertex does not implement that interface. 469 func vertexReferenceablePath(v dag.Vertex) addrs.Module { 470 sp, ok := v.(GraphNodeModulePath) 471 if !ok { 472 // Only nodes with paths can participate in a reference map. 473 panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeModulePath", sp)) 474 } 475 476 if outside, ok := v.(GraphNodeReferenceOutside); ok { 477 // Vertex is referenced from a different module than where it was 478 // declared. 479 path, _ := outside.ReferenceOutside() 480 return path 481 } 482 483 // Vertex is referenced from the same module as where it was declared. 484 return sp.ModulePath() 485 } 486 487 // vertexReferencePath returns the path in which references _from_ the given 488 // vertex must be interpreted. 489 // 490 // Only GraphNodeModulePath implementations can have references, so this method 491 // will panic if the given vertex does not implement that interface. 492 func vertexReferencePath(v dag.Vertex) addrs.Module { 493 sp, ok := v.(GraphNodeModulePath) 494 if !ok { 495 // Only nodes with paths can participate in a reference map. 496 panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeModulePath", v)) 497 } 498 499 if outside, ok := v.(GraphNodeReferenceOutside); ok { 500 // Vertex makes references to objects in a different module than where 501 // it was declared. 502 _, path := outside.ReferenceOutside() 503 return path 504 } 505 506 // Vertex makes references to objects in the same module as where it 507 // was declared. 508 return sp.ModulePath() 509 } 510 511 // referenceMapKey produces keys for the "edges" map. "referrer" is the vertex 512 // that the reference is from, and "addr" is the address of the object being 513 // referenced. 514 // 515 // The result is an opaque string that includes both the address of the given 516 // object and the address of the module instance that object belongs to. 517 // 518 // Only GraphNodeModulePath implementations can be referrers, so this method will 519 // panic if the given vertex does not implement that interface. 520 func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string { 521 path := vertexReferencePath(referrer) 522 return m.mapKey(path, addr) 523 } 524 525 // NewReferenceMap is used to create a new reference map for the 526 // given set of vertices. 527 func NewReferenceMap(vs []dag.Vertex) ReferenceMap { 528 // Build the lookup table 529 m := make(ReferenceMap) 530 for _, v := range vs { 531 // We're only looking for referenceable nodes 532 rn, ok := v.(GraphNodeReferenceable) 533 if !ok { 534 continue 535 } 536 537 path := vertexReferenceablePath(v) 538 539 // Go through and cache them 540 for _, addr := range rn.ReferenceableAddrs() { 541 key := m.mapKey(path, addr) 542 m[key] = append(m[key], v) 543 } 544 } 545 546 return m 547 } 548 549 // ReferencesFromConfig returns the references that a configuration has 550 // based on the interpolated variables in a configuration. 551 func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference { 552 if body == nil { 553 return nil 554 } 555 refs, _ := lang.ReferencesInBlock(body, schema) 556 return refs 557 }