github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/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 parents := m.References(v) 118 parentsDbg := make([]string, len(parents)) 119 for i, v := range parents { 120 parentsDbg[i] = dag.VertexName(v) 121 } 122 log.Printf( 123 "[DEBUG] ReferenceTransformer: %q references: %v", 124 dag.VertexName(v), parentsDbg) 125 126 for _, parent := range parents { 127 if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(v, parent) { 128 g.Connect(dag.BasicEdge(v, parent)) 129 } else { 130 log.Printf("[TRACE] ReferenceTransformer: skipping %s => %s inter-module-instance dependency", v, parent) 131 } 132 } 133 134 if len(parents) > 0 { 135 continue 136 } 137 } 138 139 return nil 140 } 141 142 type depMap map[string]addrs.ConfigResource 143 144 // add stores the vertex if it represents a resource in the 145 // graph. 146 func (m depMap) add(v dag.Vertex) { 147 // we're only concerned with resources which may have changes that 148 // need to be applied. 149 switch v := v.(type) { 150 case GraphNodeResourceInstance: 151 instAddr := v.ResourceInstanceAddr() 152 addr := instAddr.ContainingResource().Config() 153 m[addr.String()] = addr 154 case GraphNodeConfigResource: 155 addr := v.ResourceAddr() 156 m[addr.String()] = addr 157 } 158 } 159 160 // attachDataResourceDependsOnTransformer records all resources transitively 161 // referenced through a configuration depends_on. 162 type attachDataResourceDependsOnTransformer struct { 163 } 164 165 func (t attachDataResourceDependsOnTransformer) Transform(g *Graph) error { 166 // First we need to make a map of referenceable addresses to their vertices. 167 // This is very similar to what's done in ReferenceTransformer, but we keep 168 // implementation separate as they may need to change independently. 169 vertices := g.Vertices() 170 refMap := NewReferenceMap(vertices) 171 172 for _, v := range vertices { 173 depender, ok := v.(graphNodeAttachDataResourceDependsOn) 174 if !ok { 175 continue 176 } 177 178 // Only data need to attach depends_on, so they can determine if they 179 // are eligible to be read during plan. 180 if depender.ResourceAddr().Resource.Mode != addrs.DataResourceMode { 181 continue 182 } 183 184 // depMap will only add resource references then dedupe 185 deps := make(depMap) 186 dependsOnDeps, fromModule := refMap.dependsOn(g, depender) 187 for _, dep := range dependsOnDeps { 188 // any the dependency 189 deps.add(dep) 190 } 191 192 res := make([]addrs.ConfigResource, 0, len(deps)) 193 for _, d := range deps { 194 res = append(res, d) 195 } 196 197 log.Printf("[TRACE] attachDataDependenciesTransformer: %s depends on %s", depender.ResourceAddr(), res) 198 depender.AttachDataResourceDependsOn(res, fromModule) 199 } 200 201 return nil 202 } 203 204 // AttachDependenciesTransformer records all resource dependencies for each 205 // instance, and attaches the addresses to the node itself. Managed resource 206 // will record these in the state for proper ordering of destroy operations. 207 type AttachDependenciesTransformer struct { 208 } 209 210 func (t AttachDependenciesTransformer) Transform(g *Graph) error { 211 for _, v := range g.Vertices() { 212 attacher, ok := v.(GraphNodeAttachDependencies) 213 if !ok { 214 continue 215 } 216 selfAddr := attacher.ResourceAddr() 217 218 ans, err := g.Ancestors(v) 219 if err != nil { 220 return err 221 } 222 223 // dedupe addrs when there's multiple instances involved, or 224 // multiple paths in the un-reduced graph 225 depMap := map[string]addrs.ConfigResource{} 226 for _, d := range ans { 227 var addr addrs.ConfigResource 228 229 switch d := d.(type) { 230 case GraphNodeResourceInstance: 231 instAddr := d.ResourceInstanceAddr() 232 addr = instAddr.ContainingResource().Config() 233 case GraphNodeConfigResource: 234 addr = d.ResourceAddr() 235 default: 236 continue 237 } 238 239 if addr.Equal(selfAddr) { 240 continue 241 } 242 depMap[addr.String()] = addr 243 } 244 245 deps := make([]addrs.ConfigResource, 0, len(depMap)) 246 for _, d := range depMap { 247 deps = append(deps, d) 248 } 249 sort.Slice(deps, func(i, j int) bool { 250 return deps[i].String() < deps[j].String() 251 }) 252 253 log.Printf("[TRACE] AttachDependenciesTransformer: %s depends on %s", attacher.ResourceAddr(), deps) 254 attacher.AttachDependencies(deps) 255 } 256 257 return nil 258 } 259 260 func isDependableResource(v dag.Vertex) bool { 261 switch v.(type) { 262 case GraphNodeResourceInstance: 263 return true 264 case GraphNodeConfigResource: 265 return true 266 } 267 return false 268 } 269 270 // ReferenceMap is a structure that can be used to efficiently check 271 // for references on a graph, mapping internal reference keys (as produced by 272 // the mapKey method) to one or more vertices that are identified by each key. 273 type ReferenceMap map[string][]dag.Vertex 274 275 // References returns the set of vertices that the given vertex refers to, 276 // and any referenced addresses that do not have corresponding vertices. 277 func (m ReferenceMap) References(v dag.Vertex) []dag.Vertex { 278 rn, ok := v.(GraphNodeReferencer) 279 if !ok { 280 return nil 281 } 282 283 var matches []dag.Vertex 284 285 for _, ref := range rn.References() { 286 subject := ref.Subject 287 288 key := m.referenceMapKey(v, subject) 289 if _, exists := m[key]; !exists { 290 // If what we were looking for was a ResourceInstance then we 291 // might be in a resource-oriented graph rather than an 292 // instance-oriented graph, and so we'll see if we have the 293 // resource itself instead. 294 switch ri := subject.(type) { 295 case addrs.ResourceInstance: 296 subject = ri.ContainingResource() 297 case addrs.ResourceInstancePhase: 298 subject = ri.ContainingResource() 299 case addrs.ModuleCallInstanceOutput: 300 subject = ri.ModuleCallOutput() 301 case addrs.ModuleCallInstance: 302 subject = ri.Call 303 default: 304 log.Printf("[INFO] ReferenceTransformer: reference not found: %q", subject) 305 continue 306 } 307 key = m.referenceMapKey(v, subject) 308 } 309 vertices := m[key] 310 for _, rv := range vertices { 311 // don't include self-references 312 if rv == v { 313 continue 314 } 315 matches = append(matches, rv) 316 } 317 } 318 319 return matches 320 } 321 322 // dependsOn returns the set of vertices that the given vertex refers to from 323 // the configured depends_on. The bool return value indicates if depends_on was 324 // found in a parent module configuration. 325 func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { 326 var res []dag.Vertex 327 fromModule := false 328 329 refs := depender.DependsOn() 330 331 // get any implied dependencies for data sources 332 refs = append(refs, m.dataDependsOn(depender)...) 333 334 // This is where we record that a module has depends_on configured. 335 if _, ok := depender.(*nodeExpandModule); ok && len(refs) > 0 { 336 fromModule = true 337 } 338 339 for _, ref := range refs { 340 subject := ref.Subject 341 342 key := m.referenceMapKey(depender, subject) 343 vertices, ok := m[key] 344 if !ok { 345 // the ReferenceMap generates all possible keys, so any warning 346 // here is probably not useful for this implementation. 347 continue 348 } 349 for _, rv := range vertices { 350 // don't include self-references 351 if rv == depender { 352 continue 353 } 354 res = append(res, rv) 355 356 // and check any ancestors for transitive dependencies 357 ans, _ := g.Ancestors(rv) 358 for _, v := range ans { 359 if isDependableResource(v) { 360 res = append(res, v) 361 } 362 } 363 } 364 } 365 366 parentDeps, fromParentModule := m.parentModuleDependsOn(g, depender) 367 res = append(res, parentDeps...) 368 369 return res, fromModule || fromParentModule 370 } 371 372 // Return extra depends_on references if this is a data source. 373 // For data sources we implicitly treat references to managed resources as 374 // depends_on entries. If a data source references a managed resource, even if 375 // that reference is resolvable, it stands to reason that the user intends for 376 // the data source to require that resource in some way. 377 func (m ReferenceMap) dataDependsOn(depender graphNodeDependsOn) []*addrs.Reference { 378 var refs []*addrs.Reference 379 if n, ok := depender.(GraphNodeConfigResource); ok && 380 n.ResourceAddr().Resource.Mode == addrs.DataResourceMode { 381 for _, r := range depender.References() { 382 383 var resAddr addrs.Resource 384 switch s := r.Subject.(type) { 385 case addrs.Resource: 386 resAddr = s 387 case addrs.ResourceInstance: 388 resAddr = s.Resource 389 r.Subject = resAddr 390 } 391 392 if resAddr.Mode != addrs.ManagedResourceMode { 393 // We only want to wait on directly referenced managed resources. 394 // Data sources have no external side effects, so normal 395 // references to them in the config will suffice for proper 396 // ordering. 397 continue 398 } 399 400 refs = append(refs, r) 401 } 402 } 403 return refs 404 } 405 406 // parentModuleDependsOn returns the set of vertices that a data sources parent 407 // module references through the module call's depends_on. The bool return 408 // value indicates if depends_on was found in a parent module configuration. 409 func (m ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { 410 var res []dag.Vertex 411 fromModule := false 412 413 // Look for containing modules with DependsOn. 414 // This should be connected directly to the module node, so we only need to 415 // look one step away. 416 for _, v := range g.DownEdges(depender) { 417 // we're only concerned with module expansion nodes here. 418 mod, ok := v.(*nodeExpandModule) 419 if !ok { 420 continue 421 } 422 423 deps, fromParentModule := m.dependsOn(g, mod) 424 for _, dep := range deps { 425 // add the dependency 426 res = append(res, dep) 427 428 // and check any transitive resource dependencies for more resources 429 ans, _ := g.Ancestors(dep) 430 for _, v := range ans { 431 if isDependableResource(v) { 432 res = append(res, v) 433 } 434 } 435 } 436 fromModule = fromModule || fromParentModule 437 } 438 439 return res, fromModule 440 } 441 442 func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string { 443 return fmt.Sprintf("%s|%s", path.String(), addr.String()) 444 } 445 446 // vertexReferenceablePath returns the path in which the given vertex can be 447 // referenced. This is the path that its results from ReferenceableAddrs 448 // are considered to be relative to. 449 // 450 // Only GraphNodeModulePath implementations can be referenced, so this method will 451 // panic if the given vertex does not implement that interface. 452 func vertexReferenceablePath(v dag.Vertex) addrs.Module { 453 sp, ok := v.(GraphNodeModulePath) 454 if !ok { 455 // Only nodes with paths can participate in a reference map. 456 panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeModulePath", sp)) 457 } 458 459 if outside, ok := v.(GraphNodeReferenceOutside); ok { 460 // Vertex is referenced from a different module than where it was 461 // declared. 462 path, _ := outside.ReferenceOutside() 463 return path 464 } 465 466 // Vertex is referenced from the same module as where it was declared. 467 return sp.ModulePath() 468 } 469 470 // vertexReferencePath returns the path in which references _from_ the given 471 // vertex must be interpreted. 472 // 473 // Only GraphNodeModulePath implementations can have references, so this method 474 // will panic if the given vertex does not implement that interface. 475 func vertexReferencePath(v dag.Vertex) addrs.Module { 476 sp, ok := v.(GraphNodeModulePath) 477 if !ok { 478 // Only nodes with paths can participate in a reference map. 479 panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeModulePath", v)) 480 } 481 482 if outside, ok := v.(GraphNodeReferenceOutside); ok { 483 // Vertex makes references to objects in a different module than where 484 // it was declared. 485 _, path := outside.ReferenceOutside() 486 return path 487 } 488 489 // Vertex makes references to objects in the same module as where it 490 // was declared. 491 return sp.ModulePath() 492 } 493 494 // referenceMapKey produces keys for the "edges" map. "referrer" is the vertex 495 // that the reference is from, and "addr" is the address of the object being 496 // referenced. 497 // 498 // The result is an opaque string that includes both the address of the given 499 // object and the address of the module instance that object belongs to. 500 // 501 // Only GraphNodeModulePath implementations can be referrers, so this method will 502 // panic if the given vertex does not implement that interface. 503 func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string { 504 path := vertexReferencePath(referrer) 505 return m.mapKey(path, addr) 506 } 507 508 // NewReferenceMap is used to create a new reference map for the 509 // given set of vertices. 510 func NewReferenceMap(vs []dag.Vertex) ReferenceMap { 511 // Build the lookup table 512 m := make(ReferenceMap) 513 for _, v := range vs { 514 // We're only looking for referenceable nodes 515 rn, ok := v.(GraphNodeReferenceable) 516 if !ok { 517 continue 518 } 519 520 path := vertexReferenceablePath(v) 521 522 // Go through and cache them 523 for _, addr := range rn.ReferenceableAddrs() { 524 key := m.mapKey(path, addr) 525 m[key] = append(m[key], v) 526 } 527 } 528 529 return m 530 } 531 532 // ReferencesFromConfig returns the references that a configuration has 533 // based on the interpolated variables in a configuration. 534 func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference { 535 if body == nil { 536 return nil 537 } 538 refs, _ := lang.ReferencesInBlock(body, schema) 539 return refs 540 }