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  }