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  }