github.com/mehmetalisavas/terraform@v0.7.10/terraform/transform_reference.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/config"
     9  	"github.com/hashicorp/terraform/dag"
    10  )
    11  
    12  // GraphNodeReferenceable must be implemented by any node that represents
    13  // a Terraform thing that can be referenced (resource, module, etc.).
    14  type GraphNodeReferenceable interface {
    15  	// ReferenceableName is the name by which this can be referenced.
    16  	// This can be either just the type, or include the field. Example:
    17  	// "aws_instance.bar" or "aws_instance.bar.id".
    18  	ReferenceableName() []string
    19  }
    20  
    21  // GraphNodeReferencer must be implemented by nodes that reference other
    22  // Terraform items and therefore depend on them.
    23  type GraphNodeReferencer interface {
    24  	// References are the list of things that this node references. This
    25  	// can include fields or just the type, just like GraphNodeReferenceable
    26  	// above.
    27  	References() []string
    28  }
    29  
    30  // GraphNodeReferenceGlobal is an interface that can optionally be
    31  // implemented. If ReferenceGlobal returns true, then the References()
    32  // and ReferenceableName() must be _fully qualified_ with "module.foo.bar"
    33  // etc.
    34  //
    35  // This allows a node to reference and be referenced by a specific name
    36  // that may cross module boundaries. This can be very dangerous so use
    37  // this wisely.
    38  //
    39  // The primary use case for this is module boundaries (variables coming in).
    40  type GraphNodeReferenceGlobal interface {
    41  	// Set to true to signal that references and name are fully
    42  	// qualified. See the above docs for more information.
    43  	ReferenceGlobal() bool
    44  }
    45  
    46  // ReferenceTransformer is a GraphTransformer that connects all the
    47  // nodes that reference each other in order to form the proper ordering.
    48  type ReferenceTransformer struct{}
    49  
    50  func (t *ReferenceTransformer) Transform(g *Graph) error {
    51  	// Build a reference map so we can efficiently look up the references
    52  	vs := g.Vertices()
    53  	m := NewReferenceMap(vs)
    54  
    55  	// Find the things that reference things and connect them
    56  	for _, v := range vs {
    57  		parents, _ := m.References(v)
    58  		parentsDbg := make([]string, len(parents))
    59  		for i, v := range parents {
    60  			parentsDbg[i] = dag.VertexName(v)
    61  		}
    62  		log.Printf(
    63  			"[DEBUG] ReferenceTransformer: %q references: %v",
    64  			dag.VertexName(v), parentsDbg)
    65  
    66  		for _, parent := range parents {
    67  			g.Connect(dag.BasicEdge(v, parent))
    68  		}
    69  	}
    70  
    71  	return nil
    72  }
    73  
    74  // ReferenceMap is a structure that can be used to efficiently check
    75  // for references on a graph.
    76  type ReferenceMap struct {
    77  	// m is the mapping of referenceable name to list of verticies that
    78  	// implement that name. This is built on initialization.
    79  	references   map[string][]dag.Vertex
    80  	referencedBy map[string][]dag.Vertex
    81  }
    82  
    83  // References returns the list of vertices that this vertex
    84  // references along with any missing references.
    85  func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
    86  	rn, ok := v.(GraphNodeReferencer)
    87  	if !ok {
    88  		return nil, nil
    89  	}
    90  
    91  	var matches []dag.Vertex
    92  	var missing []string
    93  	prefix := m.prefix(v)
    94  	for _, ns := range rn.References() {
    95  		found := false
    96  		for _, n := range strings.Split(ns, "/") {
    97  			n = prefix + n
    98  			parents, ok := m.references[n]
    99  			if !ok {
   100  				continue
   101  			}
   102  
   103  			// Mark that we found a match
   104  			found = true
   105  
   106  			// Make sure this isn't a self reference, which isn't included
   107  			selfRef := false
   108  			for _, p := range parents {
   109  				if p == v {
   110  					selfRef = true
   111  					break
   112  				}
   113  			}
   114  			if selfRef {
   115  				continue
   116  			}
   117  
   118  			matches = append(matches, parents...)
   119  			break
   120  		}
   121  
   122  		if !found {
   123  			missing = append(missing, ns)
   124  		}
   125  	}
   126  
   127  	return matches, missing
   128  }
   129  
   130  // ReferencedBy returns the list of vertices that reference the
   131  // vertex passed in.
   132  func (m *ReferenceMap) ReferencedBy(v dag.Vertex) []dag.Vertex {
   133  	rn, ok := v.(GraphNodeReferenceable)
   134  	if !ok {
   135  		return nil
   136  	}
   137  
   138  	var matches []dag.Vertex
   139  	prefix := m.prefix(v)
   140  	for _, n := range rn.ReferenceableName() {
   141  		n = prefix + n
   142  		children, ok := m.referencedBy[n]
   143  		if !ok {
   144  			continue
   145  		}
   146  
   147  		// Make sure this isn't a self reference, which isn't included
   148  		selfRef := false
   149  		for _, p := range children {
   150  			if p == v {
   151  				selfRef = true
   152  				break
   153  			}
   154  		}
   155  		if selfRef {
   156  			continue
   157  		}
   158  
   159  		matches = append(matches, children...)
   160  	}
   161  
   162  	return matches
   163  }
   164  
   165  func (m *ReferenceMap) prefix(v dag.Vertex) string {
   166  	// If the node is stating it is already fully qualified then
   167  	// we don't have to create the prefix!
   168  	if gn, ok := v.(GraphNodeReferenceGlobal); ok && gn.ReferenceGlobal() {
   169  		return ""
   170  	}
   171  
   172  	// Create the prefix based on the path
   173  	var prefix string
   174  	if pn, ok := v.(GraphNodeSubPath); ok {
   175  		if path := normalizeModulePath(pn.Path()); len(path) > 1 {
   176  			prefix = modulePrefixStr(path) + "."
   177  		}
   178  	}
   179  
   180  	return prefix
   181  }
   182  
   183  // NewReferenceMap is used to create a new reference map for the
   184  // given set of vertices.
   185  func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
   186  	var m ReferenceMap
   187  
   188  	// Build the lookup table
   189  	refMap := make(map[string][]dag.Vertex)
   190  	for _, v := range vs {
   191  		// We're only looking for referenceable nodes
   192  		rn, ok := v.(GraphNodeReferenceable)
   193  		if !ok {
   194  			continue
   195  		}
   196  
   197  		// Go through and cache them
   198  		prefix := m.prefix(v)
   199  		for _, n := range rn.ReferenceableName() {
   200  			n = prefix + n
   201  			refMap[n] = append(refMap[n], v)
   202  		}
   203  	}
   204  
   205  	// Build the lookup table for referenced by
   206  	refByMap := make(map[string][]dag.Vertex)
   207  	for _, v := range vs {
   208  		// We're only looking for referenceable nodes
   209  		rn, ok := v.(GraphNodeReferencer)
   210  		if !ok {
   211  			continue
   212  		}
   213  
   214  		// Go through and cache them
   215  		prefix := m.prefix(v)
   216  		for _, n := range rn.References() {
   217  			n = prefix + n
   218  			refByMap[n] = append(refByMap[n], v)
   219  		}
   220  	}
   221  
   222  	m.references = refMap
   223  	m.referencedBy = refByMap
   224  	return &m
   225  }
   226  
   227  // ReferencesFromConfig returns the references that a configuration has
   228  // based on the interpolated variables in a configuration.
   229  func ReferencesFromConfig(c *config.RawConfig) []string {
   230  	var result []string
   231  	for _, v := range c.Variables {
   232  		if r := ReferenceFromInterpolatedVar(v); len(r) > 0 {
   233  			result = append(result, r...)
   234  		}
   235  	}
   236  
   237  	return result
   238  }
   239  
   240  // ReferenceFromInterpolatedVar returns the reference from this variable,
   241  // or an empty string if there is no reference.
   242  func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string {
   243  	switch v := v.(type) {
   244  	case *config.ModuleVariable:
   245  		return []string{fmt.Sprintf("module.%s.output.%s", v.Name, v.Field)}
   246  	case *config.ResourceVariable:
   247  		id := v.ResourceId()
   248  
   249  		// If we have a multi-reference (splat), then we depend on ALL
   250  		// resources with this type/name.
   251  		if v.Multi && v.Index == -1 {
   252  			return []string{fmt.Sprintf("%s.*", id)}
   253  		}
   254  
   255  		// Otherwise, we depend on a specific index.
   256  		idx := v.Index
   257  		if !v.Multi || v.Index == -1 {
   258  			idx = 0
   259  		}
   260  
   261  		// Depend on the index, as well as "N" which represents the
   262  		// un-expanded set of resources.
   263  		return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)}
   264  	case *config.UserVariable:
   265  		return []string{fmt.Sprintf("var.%s", v.Name)}
   266  	default:
   267  		return nil
   268  	}
   269  }