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  }