github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/transform_reference.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/hcl/v2" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 10 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 11 "github.com/hashicorp/terraform-plugin-sdk/internal/lang" 12 ) 13 14 // GraphNodeReferenceable must be implemented by any node that represents 15 // a Terraform thing that can be referenced (resource, module, etc.). 16 // 17 // Even if the thing has no name, this should return an empty list. By 18 // implementing this and returning a non-nil result, you say that this CAN 19 // be referenced and other methods of referencing may still be possible (such 20 // as by path!) 21 type GraphNodeReferenceable interface { 22 GraphNodeSubPath 23 24 // ReferenceableAddrs returns a list of addresses through which this can be 25 // referenced. 26 ReferenceableAddrs() []addrs.Referenceable 27 } 28 29 // GraphNodeReferencer must be implemented by nodes that reference other 30 // Terraform items and therefore depend on them. 31 type GraphNodeReferencer interface { 32 GraphNodeSubPath 33 34 // References returns a list of references made by this node, which 35 // include both a referenced address and source location information for 36 // the reference. 37 References() []*addrs.Reference 38 } 39 40 // GraphNodeReferenceOutside is an interface that can optionally be implemented. 41 // A node that implements it can specify that its own referenceable addresses 42 // and/or the addresses it references are in a different module than the 43 // node itself. 44 // 45 // Any referenceable addresses returned by ReferenceableAddrs are interpreted 46 // relative to the returned selfPath. 47 // 48 // Any references returned by References are interpreted relative to the 49 // returned referencePath. 50 // 51 // It is valid but not required for either of these paths to match what is 52 // returned by method Path, though if both match the main Path then there 53 // is no reason to implement this method. 54 // 55 // The primary use-case for this is the nodes representing module input 56 // variables, since their expressions are resolved in terms of their calling 57 // module, but they are still referenced from their own module. 58 type GraphNodeReferenceOutside interface { 59 // ReferenceOutside returns a path in which any references from this node 60 // are resolved. 61 ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) 62 } 63 64 // ReferenceTransformer is a GraphTransformer that connects all the 65 // nodes that reference each other in order to form the proper ordering. 66 type ReferenceTransformer struct{} 67 68 func (t *ReferenceTransformer) Transform(g *Graph) error { 69 // Build a reference map so we can efficiently look up the references 70 vs := g.Vertices() 71 m := NewReferenceMap(vs) 72 73 // Find the things that reference things and connect them 74 for _, v := range vs { 75 parents, _ := m.References(v) 76 parentsDbg := make([]string, len(parents)) 77 for i, v := range parents { 78 parentsDbg[i] = dag.VertexName(v) 79 } 80 log.Printf( 81 "[DEBUG] ReferenceTransformer: %q references: %v", 82 dag.VertexName(v), parentsDbg) 83 84 for _, parent := range parents { 85 g.Connect(dag.BasicEdge(v, parent)) 86 } 87 } 88 89 return nil 90 } 91 92 // DestroyReferenceTransformer is a GraphTransformer that reverses the edges 93 // for locals and outputs that depend on other nodes which will be 94 // removed during destroy. If a destroy node is evaluated before the local or 95 // output value, it will be removed from the state, and the later interpolation 96 // will fail. 97 type DestroyValueReferenceTransformer struct{} 98 99 func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error { 100 vs := g.Vertices() 101 for _, v := range vs { 102 switch v.(type) { 103 case *NodeApplyableOutput, *NodeLocal: 104 // OK 105 default: 106 continue 107 } 108 109 // reverse any outgoing edges so that the value is evaluated first. 110 for _, e := range g.EdgesFrom(v) { 111 target := e.Target() 112 113 // only destroy nodes will be evaluated in reverse 114 if _, ok := target.(GraphNodeDestroyer); !ok { 115 continue 116 } 117 118 log.Printf("[TRACE] output dep: %s", dag.VertexName(target)) 119 120 g.RemoveEdge(e) 121 g.Connect(&DestroyEdge{S: target, T: v}) 122 } 123 } 124 125 return nil 126 } 127 128 // PruneUnusedValuesTransformer is s GraphTransformer that removes local and 129 // output values which are not referenced in the graph. Since outputs and 130 // locals always need to be evaluated, if they reference a resource that is not 131 // available in the state the interpolation could fail. 132 type PruneUnusedValuesTransformer struct{} 133 134 func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error { 135 // this might need multiple runs in order to ensure that pruning a value 136 // doesn't effect a previously checked value. 137 for removed := 0; ; removed = 0 { 138 for _, v := range g.Vertices() { 139 switch v.(type) { 140 case *NodeApplyableOutput, *NodeLocal: 141 // OK 142 default: 143 continue 144 } 145 146 dependants := g.UpEdges(v) 147 148 switch dependants.Len() { 149 case 0: 150 // nothing at all depends on this 151 g.Remove(v) 152 removed++ 153 case 1: 154 // because an output's destroy node always depends on the output, 155 // we need to check for the case of a single destroy node. 156 d := dependants.List()[0] 157 if _, ok := d.(*NodeDestroyableOutput); ok { 158 g.Remove(v) 159 removed++ 160 } 161 } 162 } 163 if removed == 0 { 164 break 165 } 166 } 167 168 return nil 169 } 170 171 // ReferenceMap is a structure that can be used to efficiently check 172 // for references on a graph. 173 type ReferenceMap struct { 174 // vertices is a map from internal reference keys (as produced by the 175 // mapKey method) to one or more vertices that are identified by each key. 176 // 177 // A particular reference key might actually identify multiple vertices, 178 // e.g. in situations where one object is contained inside another. 179 vertices map[string][]dag.Vertex 180 181 // edges is a map whose keys are a subset of the internal reference keys 182 // from "vertices", and whose values are the nodes that refer to each 183 // key. The values in this map are the referrers, while values in 184 // "verticies" are the referents. The keys in both cases are referents. 185 edges map[string][]dag.Vertex 186 } 187 188 // References returns the set of vertices that the given vertex refers to, 189 // and any referenced addresses that do not have corresponding vertices. 190 func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []addrs.Referenceable) { 191 rn, ok := v.(GraphNodeReferencer) 192 if !ok { 193 return nil, nil 194 } 195 if _, ok := v.(GraphNodeSubPath); !ok { 196 return nil, nil 197 } 198 199 var matches []dag.Vertex 200 var missing []addrs.Referenceable 201 202 for _, ref := range rn.References() { 203 subject := ref.Subject 204 205 key := m.referenceMapKey(v, subject) 206 if _, exists := m.vertices[key]; !exists { 207 // If what we were looking for was a ResourceInstance then we 208 // might be in a resource-oriented graph rather than an 209 // instance-oriented graph, and so we'll see if we have the 210 // resource itself instead. 211 switch ri := subject.(type) { 212 case addrs.ResourceInstance: 213 subject = ri.ContainingResource() 214 case addrs.ResourceInstancePhase: 215 subject = ri.ContainingResource() 216 } 217 key = m.referenceMapKey(v, subject) 218 } 219 220 vertices := m.vertices[key] 221 for _, rv := range vertices { 222 // don't include self-references 223 if rv == v { 224 continue 225 } 226 matches = append(matches, rv) 227 } 228 if len(vertices) == 0 { 229 missing = append(missing, ref.Subject) 230 } 231 } 232 233 return matches, missing 234 } 235 236 // Referrers returns the set of vertices that refer to the given vertex. 237 func (m *ReferenceMap) Referrers(v dag.Vertex) []dag.Vertex { 238 rn, ok := v.(GraphNodeReferenceable) 239 if !ok { 240 return nil 241 } 242 sp, ok := v.(GraphNodeSubPath) 243 if !ok { 244 return nil 245 } 246 247 var matches []dag.Vertex 248 for _, addr := range rn.ReferenceableAddrs() { 249 key := m.mapKey(sp.Path(), addr) 250 referrers, ok := m.edges[key] 251 if !ok { 252 continue 253 } 254 255 // If the referrer set includes our own given vertex then we skip, 256 // since we don't want to return self-references. 257 selfRef := false 258 for _, p := range referrers { 259 if p == v { 260 selfRef = true 261 break 262 } 263 } 264 if selfRef { 265 continue 266 } 267 268 matches = append(matches, referrers...) 269 } 270 271 return matches 272 } 273 274 func (m *ReferenceMap) mapKey(path addrs.ModuleInstance, addr addrs.Referenceable) string { 275 return fmt.Sprintf("%s|%s", path.String(), addr.String()) 276 } 277 278 // vertexReferenceablePath returns the path in which the given vertex can be 279 // referenced. This is the path that its results from ReferenceableAddrs 280 // are considered to be relative to. 281 // 282 // Only GraphNodeSubPath implementations can be referenced, so this method will 283 // panic if the given vertex does not implement that interface. 284 func (m *ReferenceMap) vertexReferenceablePath(v dag.Vertex) addrs.ModuleInstance { 285 sp, ok := v.(GraphNodeSubPath) 286 if !ok { 287 // Only nodes with paths can participate in a reference map. 288 panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeSubPath", sp)) 289 } 290 291 if outside, ok := v.(GraphNodeReferenceOutside); ok { 292 // Vertex is referenced from a different module than where it was 293 // declared. 294 path, _ := outside.ReferenceOutside() 295 return path 296 } 297 298 // Vertex is referenced from the same module as where it was declared. 299 return sp.Path() 300 } 301 302 // vertexReferencePath returns the path in which references _from_ the given 303 // vertex must be interpreted. 304 // 305 // Only GraphNodeSubPath implementations can have references, so this method 306 // will panic if the given vertex does not implement that interface. 307 func vertexReferencePath(referrer dag.Vertex) addrs.ModuleInstance { 308 sp, ok := referrer.(GraphNodeSubPath) 309 if !ok { 310 // Only nodes with paths can participate in a reference map. 311 panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeSubPath", sp)) 312 } 313 314 var path addrs.ModuleInstance 315 if outside, ok := referrer.(GraphNodeReferenceOutside); ok { 316 // Vertex makes references to objects in a different module than where 317 // it was declared. 318 _, path = outside.ReferenceOutside() 319 return path 320 } 321 322 // Vertex makes references to objects in the same module as where it 323 // was declared. 324 return sp.Path() 325 } 326 327 // referenceMapKey produces keys for the "edges" map. "referrer" is the vertex 328 // that the reference is from, and "addr" is the address of the object being 329 // referenced. 330 // 331 // The result is an opaque string that includes both the address of the given 332 // object and the address of the module instance that object belongs to. 333 // 334 // Only GraphNodeSubPath implementations can be referrers, so this method will 335 // panic if the given vertex does not implement that interface. 336 func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string { 337 path := vertexReferencePath(referrer) 338 return m.mapKey(path, addr) 339 } 340 341 // NewReferenceMap is used to create a new reference map for the 342 // given set of vertices. 343 func NewReferenceMap(vs []dag.Vertex) *ReferenceMap { 344 var m ReferenceMap 345 346 // Build the lookup table 347 vertices := make(map[string][]dag.Vertex) 348 for _, v := range vs { 349 _, ok := v.(GraphNodeSubPath) 350 if !ok { 351 // Only nodes with paths can participate in a reference map. 352 continue 353 } 354 355 // We're only looking for referenceable nodes 356 rn, ok := v.(GraphNodeReferenceable) 357 if !ok { 358 continue 359 } 360 361 path := m.vertexReferenceablePath(v) 362 363 // Go through and cache them 364 for _, addr := range rn.ReferenceableAddrs() { 365 key := m.mapKey(path, addr) 366 vertices[key] = append(vertices[key], v) 367 } 368 369 // Any node can be referenced by the address of the module it belongs 370 // to or any of that module's ancestors. 371 for _, addr := range path.Ancestors()[1:] { 372 // Can be referenced either as the specific call instance (with 373 // an instance key) or as the bare module call itself (the "module" 374 // block in the parent module that created the instance). 375 callPath, call := addr.Call() 376 callInstPath, callInst := addr.CallInstance() 377 callKey := m.mapKey(callPath, call) 378 callInstKey := m.mapKey(callInstPath, callInst) 379 vertices[callKey] = append(vertices[callKey], v) 380 vertices[callInstKey] = append(vertices[callInstKey], v) 381 } 382 } 383 384 // Build the lookup table for referenced by 385 edges := make(map[string][]dag.Vertex) 386 for _, v := range vs { 387 _, ok := v.(GraphNodeSubPath) 388 if !ok { 389 // Only nodes with paths can participate in a reference map. 390 continue 391 } 392 393 rn, ok := v.(GraphNodeReferencer) 394 if !ok { 395 // We're only looking for referenceable nodes 396 continue 397 } 398 399 // Go through and cache them 400 for _, ref := range rn.References() { 401 if ref.Subject == nil { 402 // Should never happen 403 panic(fmt.Sprintf("%T.References returned reference with nil subject", rn)) 404 } 405 key := m.referenceMapKey(v, ref.Subject) 406 edges[key] = append(edges[key], v) 407 } 408 } 409 410 m.vertices = vertices 411 m.edges = edges 412 return &m 413 } 414 415 // ReferencesFromConfig returns the references that a configuration has 416 // based on the interpolated variables in a configuration. 417 func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference { 418 if body == nil { 419 return nil 420 } 421 refs, _ := lang.ReferencesInBlock(body, schema) 422 return refs 423 } 424 425 // appendResourceDestroyReferences identifies resource and resource instance 426 // references in the given slice and appends to it the "destroy-phase" 427 // equivalents of those references, returning the result. 428 // 429 // This can be used in the References implementation for a node which must also 430 // depend on the destruction of anything it references. 431 func appendResourceDestroyReferences(refs []*addrs.Reference) []*addrs.Reference { 432 given := refs 433 for _, ref := range given { 434 switch tr := ref.Subject.(type) { 435 case addrs.Resource: 436 newRef := *ref // shallow copy 437 newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) 438 refs = append(refs, &newRef) 439 case addrs.ResourceInstance: 440 newRef := *ref // shallow copy 441 newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) 442 refs = append(refs, &newRef) 443 } 444 } 445 return refs 446 }