github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-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/muratcelep/terraform/not-internal/addrs" 10 "github.com/muratcelep/terraform/not-internal/configs/configschema" 11 "github.com/muratcelep/terraform/not-internal/dag" 12 "github.com/muratcelep/terraform/not-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 not-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 // Check any ancestors for transitive dependencies when we're 357 // not pointed directly at a resource. We can't be much more 358 // precise here, since in order to maintain our guarantee that data 359 // sources will wait for explicit dependencies, if those dependencies 360 // happen to be a module, output, or variable, we have to find some 361 // upstream managed resource in order to check for a planned 362 // change. 363 if _, ok := rv.(GraphNodeConfigResource); !ok { 364 ans, _ := g.Ancestors(rv) 365 for _, v := range ans { 366 if isDependableResource(v) { 367 res = append(res, v) 368 } 369 } 370 } 371 } 372 } 373 374 parentDeps, fromParentModule := m.parentModuleDependsOn(g, depender) 375 res = append(res, parentDeps...) 376 377 return res, fromModule || fromParentModule 378 } 379 380 // Return extra depends_on references if this is a data source. 381 // For data sources we implicitly treat references to managed resources as 382 // depends_on entries. If a data source references a managed resource, even if 383 // that reference is resolvable, it stands to reason that the user intends for 384 // the data source to require that resource in some way. 385 func (m ReferenceMap) dataDependsOn(depender graphNodeDependsOn) []*addrs.Reference { 386 var refs []*addrs.Reference 387 if n, ok := depender.(GraphNodeConfigResource); ok && 388 n.ResourceAddr().Resource.Mode == addrs.DataResourceMode { 389 for _, r := range depender.References() { 390 391 var resAddr addrs.Resource 392 switch s := r.Subject.(type) { 393 case addrs.Resource: 394 resAddr = s 395 case addrs.ResourceInstance: 396 resAddr = s.Resource 397 r.Subject = resAddr 398 } 399 400 if resAddr.Mode != addrs.ManagedResourceMode { 401 // We only want to wait on directly referenced managed resources. 402 // Data sources have no external side effects, so normal 403 // references to them in the config will suffice for proper 404 // ordering. 405 continue 406 } 407 408 refs = append(refs, r) 409 } 410 } 411 return refs 412 } 413 414 // parentModuleDependsOn returns the set of vertices that a data sources parent 415 // module references through the module call's depends_on. The bool return 416 // value indicates if depends_on was found in a parent module configuration. 417 func (m ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { 418 var res []dag.Vertex 419 fromModule := false 420 421 // Look for containing modules with DependsOn. 422 // This should be connected directly to the module node, so we only need to 423 // look one step away. 424 for _, v := range g.DownEdges(depender) { 425 // we're only concerned with module expansion nodes here. 426 mod, ok := v.(*nodeExpandModule) 427 if !ok { 428 continue 429 } 430 431 deps, fromParentModule := m.dependsOn(g, mod) 432 for _, dep := range deps { 433 // add the dependency 434 res = append(res, dep) 435 436 // and check any transitive resource dependencies for more resources 437 ans, _ := g.Ancestors(dep) 438 for _, v := range ans { 439 if isDependableResource(v) { 440 res = append(res, v) 441 } 442 } 443 } 444 fromModule = fromModule || fromParentModule 445 } 446 447 return res, fromModule 448 } 449 450 func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string { 451 return fmt.Sprintf("%s|%s", path.String(), addr.String()) 452 } 453 454 // vertexReferenceablePath returns the path in which the given vertex can be 455 // referenced. This is the path that its results from ReferenceableAddrs 456 // are considered to be relative to. 457 // 458 // Only GraphNodeModulePath implementations can be referenced, so this method will 459 // panic if the given vertex does not implement that interface. 460 func vertexReferenceablePath(v dag.Vertex) addrs.Module { 461 sp, ok := v.(GraphNodeModulePath) 462 if !ok { 463 // Only nodes with paths can participate in a reference map. 464 panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeModulePath", sp)) 465 } 466 467 if outside, ok := v.(GraphNodeReferenceOutside); ok { 468 // Vertex is referenced from a different module than where it was 469 // declared. 470 path, _ := outside.ReferenceOutside() 471 return path 472 } 473 474 // Vertex is referenced from the same module as where it was declared. 475 return sp.ModulePath() 476 } 477 478 // vertexReferencePath returns the path in which references _from_ the given 479 // vertex must be interpreted. 480 // 481 // Only GraphNodeModulePath implementations can have references, so this method 482 // will panic if the given vertex does not implement that interface. 483 func vertexReferencePath(v dag.Vertex) addrs.Module { 484 sp, ok := v.(GraphNodeModulePath) 485 if !ok { 486 // Only nodes with paths can participate in a reference map. 487 panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeModulePath", v)) 488 } 489 490 if outside, ok := v.(GraphNodeReferenceOutside); ok { 491 // Vertex makes references to objects in a different module than where 492 // it was declared. 493 _, path := outside.ReferenceOutside() 494 return path 495 } 496 497 // Vertex makes references to objects in the same module as where it 498 // was declared. 499 return sp.ModulePath() 500 } 501 502 // referenceMapKey produces keys for the "edges" map. "referrer" is the vertex 503 // that the reference is from, and "addr" is the address of the object being 504 // referenced. 505 // 506 // The result is an opaque string that includes both the address of the given 507 // object and the address of the module instance that object belongs to. 508 // 509 // Only GraphNodeModulePath implementations can be referrers, so this method will 510 // panic if the given vertex does not implement that interface. 511 func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string { 512 path := vertexReferencePath(referrer) 513 return m.mapKey(path, addr) 514 } 515 516 // NewReferenceMap is used to create a new reference map for the 517 // given set of vertices. 518 func NewReferenceMap(vs []dag.Vertex) ReferenceMap { 519 // Build the lookup table 520 m := make(ReferenceMap) 521 for _, v := range vs { 522 // We're only looking for referenceable nodes 523 rn, ok := v.(GraphNodeReferenceable) 524 if !ok { 525 continue 526 } 527 528 path := vertexReferenceablePath(v) 529 530 // Go through and cache them 531 for _, addr := range rn.ReferenceableAddrs() { 532 key := m.mapKey(path, addr) 533 m[key] = append(m[key], v) 534 } 535 } 536 537 return m 538 } 539 540 // ReferencesFromConfig returns the references that a configuration has 541 // based on the interpolated variables in a configuration. 542 func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference { 543 if body == nil { 544 return nil 545 } 546 refs, _ := lang.ReferencesInBlock(body, schema) 547 return refs 548 }