]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/transform_reference.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_reference.go
index be8c7f96c394e43468f84e9e6822871ed930c365..23bc8cd208240bf1370e2b9ecbc1508a1ba73d09 100644 (file)
@@ -3,8 +3,12 @@ package terraform
 import (
        "fmt"
        "log"
-       "strings"
 
+       "github.com/hashicorp/hcl2/hcl"
+       "github.com/hashicorp/terraform/configs/configschema"
+       "github.com/hashicorp/terraform/lang"
+
+       "github.com/hashicorp/terraform/addrs"
        "github.com/hashicorp/terraform/config"
        "github.com/hashicorp/terraform/dag"
 )
@@ -17,35 +21,46 @@ import (
 // be referenced and other methods of referencing may still be possible (such
 // as by path!)
 type GraphNodeReferenceable interface {
-       // ReferenceableName is the name by which this can be referenced.
-       // This can be either just the type, or include the field. Example:
-       // "aws_instance.bar" or "aws_instance.bar.id".
-       ReferenceableName() []string
+       GraphNodeSubPath
+
+       // ReferenceableAddrs returns a list of addresses through which this can be
+       // referenced.
+       ReferenceableAddrs() []addrs.Referenceable
 }
 
 // GraphNodeReferencer must be implemented by nodes that reference other
 // Terraform items and therefore depend on them.
 type GraphNodeReferencer interface {
-       // References are the list of things that this node references. This
-       // can include fields or just the type, just like GraphNodeReferenceable
-       // above.
-       References() []string
+       GraphNodeSubPath
+
+       // References returns a list of references made by this node, which
+       // include both a referenced address and source location information for
+       // the reference.
+       References() []*addrs.Reference
 }
 
-// GraphNodeReferenceGlobal is an interface that can optionally be
-// implemented. If ReferenceGlobal returns true, then the References()
-// and ReferenceableName() must be _fully qualified_ with "module.foo.bar"
-// etc.
+// GraphNodeReferenceOutside is an interface that can optionally be implemented.
+// A node that implements it can specify that its own referenceable addresses
+// and/or the addresses it references are in a different module than the
+// node itself.
+//
+// Any referenceable addresses returned by ReferenceableAddrs are interpreted
+// relative to the returned selfPath.
 //
-// This allows a node to reference and be referenced by a specific name
-// that may cross module boundaries. This can be very dangerous so use
-// this wisely.
+// Any references returned by References are interpreted relative to the
+// returned referencePath.
 //
-// The primary use case for this is module boundaries (variables coming in).
-type GraphNodeReferenceGlobal interface {
-       // Set to true to signal that references and name are fully
-       // qualified. See the above docs for more information.
-       ReferenceGlobal() bool
+// It is valid but not required for either of these paths to match what is
+// returned by method Path, though if both match the main Path then there
+// is no reason to implement this method.
+//
+// The primary use-case for this is the nodes representing module input
+// variables, since their expressions are resolved in terms of their calling
+// module, but they are still referenced from their own module.
+type GraphNodeReferenceOutside interface {
+       // ReferenceOutside returns a path in which any references from this node
+       // are resolved.
+       ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance)
 }
 
 // ReferenceTransformer is a GraphTransformer that connects all the
@@ -158,75 +173,91 @@ func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
 // ReferenceMap is a structure that can be used to efficiently check
 // for references on a graph.
 type ReferenceMap struct {
-       // m is the mapping of referenceable name to list of verticies that
-       // implement that name. This is built on initialization.
-       references   map[string][]dag.Vertex
-       referencedBy map[string][]dag.Vertex
+       // vertices is a map from internal reference keys (as produced by the
+       // mapKey method) to one or more vertices that are identified by each key.
+       //
+       // A particular reference key might actually identify multiple vertices,
+       // e.g. in situations where one object is contained inside another.
+       vertices map[string][]dag.Vertex
+
+       // edges is a map whose keys are a subset of the internal reference keys
+       // from "vertices", and whose values are the nodes that refer to each
+       // key. The values in this map are the referrers, while values in
+       // "verticies" are the referents. The keys in both cases are referents.
+       edges map[string][]dag.Vertex
 }
 
-// References returns the list of vertices that this vertex
-// references along with any missing references.
-func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
+// References returns the set of vertices that the given vertex refers to,
+// and any referenced addresses that do not have corresponding vertices.
+func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []addrs.Referenceable) {
        rn, ok := v.(GraphNodeReferencer)
        if !ok {
                return nil, nil
        }
+       if _, ok := v.(GraphNodeSubPath); !ok {
+               return nil, nil
+       }
 
        var matches []dag.Vertex
-       var missing []string
-       prefix := m.prefix(v)
-
-       for _, ns := range rn.References() {
-               found := false
-               for _, n := range strings.Split(ns, "/") {
-                       n = prefix + n
-                       parents, ok := m.references[n]
-                       if !ok {
-                               continue
+       var missing []addrs.Referenceable
+
+       for _, ref := range rn.References() {
+               subject := ref.Subject
+
+               key := m.referenceMapKey(v, subject)
+               if _, exists := m.vertices[key]; !exists {
+                       // If what we were looking for was a ResourceInstance then we
+                       // might be in a resource-oriented graph rather than an
+                       // instance-oriented graph, and so we'll see if we have the
+                       // resource itself instead.
+                       switch ri := subject.(type) {
+                       case addrs.ResourceInstance:
+                               subject = ri.ContainingResource()
+                       case addrs.ResourceInstancePhase:
+                               subject = ri.ContainingResource()
                        }
+                       key = m.referenceMapKey(v, subject)
+               }
 
-                       // Mark that we found a match
-                       found = true
-
-                       for _, p := range parents {
-                               // don't include self-references
-                               if p == v {
-                                       continue
-                               }
-                               matches = append(matches, p)
+               vertices := m.vertices[key]
+               for _, rv := range vertices {
+                       // don't include self-references
+                       if rv == v {
+                               continue
                        }
-
-                       break
+                       matches = append(matches, rv)
                }
-
-               if !found {
-                       missing = append(missing, ns)
+               if len(vertices) == 0 {
+                       missing = append(missing, ref.Subject)
                }
        }
 
        return matches, missing
 }
 
-// ReferencedBy returns the list of vertices that reference the
-// vertex passed in.
-func (m *ReferenceMap) ReferencedBy(v dag.Vertex) []dag.Vertex {
+// Referrers returns the set of vertices that refer to the given vertex.
+func (m *ReferenceMap) Referrers(v dag.Vertex) []dag.Vertex {
        rn, ok := v.(GraphNodeReferenceable)
        if !ok {
                return nil
        }
+       sp, ok := v.(GraphNodeSubPath)
+       if !ok {
+               return nil
+       }
 
        var matches []dag.Vertex
-       prefix := m.prefix(v)
-       for _, n := range rn.ReferenceableName() {
-               n = prefix + n
-               children, ok := m.referencedBy[n]
+       for _, addr := range rn.ReferenceableAddrs() {
+               key := m.mapKey(sp.Path(), addr)
+               referrers, ok := m.edges[key]
                if !ok {
                        continue
                }
 
-               // Make sure this isn't a self reference, which isn't included
+               // If the referrer set includes our own given vertex then we skip,
+               // since we don't want to return self-references.
                selfRef := false
-               for _, p := range children {
+               for _, p := range referrers {
                        if p == v {
                                selfRef = true
                                break
@@ -236,28 +267,77 @@ func (m *ReferenceMap) ReferencedBy(v dag.Vertex) []dag.Vertex {
                        continue
                }
 
-               matches = append(matches, children...)
+               matches = append(matches, referrers...)
        }
 
        return matches
 }
 
-func (m *ReferenceMap) prefix(v dag.Vertex) string {
-       // If the node is stating it is already fully qualified then
-       // we don't have to create the prefix!
-       if gn, ok := v.(GraphNodeReferenceGlobal); ok && gn.ReferenceGlobal() {
-               return ""
+func (m *ReferenceMap) mapKey(path addrs.ModuleInstance, addr addrs.Referenceable) string {
+       return fmt.Sprintf("%s|%s", path.String(), addr.String())
+}
+
+// vertexReferenceablePath returns the path in which the given vertex can be
+// referenced. This is the path that its results from ReferenceableAddrs
+// are considered to be relative to.
+//
+// Only GraphNodeSubPath implementations can be referenced, so this method will
+// panic if the given vertex does not implement that interface.
+func (m *ReferenceMap) vertexReferenceablePath(v dag.Vertex) addrs.ModuleInstance {
+       sp, ok := v.(GraphNodeSubPath)
+       if !ok {
+               // Only nodes with paths can participate in a reference map.
+               panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeSubPath", sp))
        }
 
-       // Create the prefix based on the path
-       var prefix string
-       if pn, ok := v.(GraphNodeSubPath); ok {
-               if path := normalizeModulePath(pn.Path()); len(path) > 1 {
-                       prefix = modulePrefixStr(path) + "."
-               }
+       if outside, ok := v.(GraphNodeReferenceOutside); ok {
+               // Vertex is referenced from a different module than where it was
+               // declared.
+               path, _ := outside.ReferenceOutside()
+               return path
+       }
+
+       // Vertex is referenced from the same module as where it was declared.
+       return sp.Path()
+}
+
+// vertexReferencePath returns the path in which references _from_ the given
+// vertex must be interpreted.
+//
+// Only GraphNodeSubPath implementations can have references, so this method
+// will panic if the given vertex does not implement that interface.
+func vertexReferencePath(referrer dag.Vertex) addrs.ModuleInstance {
+       sp, ok := referrer.(GraphNodeSubPath)
+       if !ok {
+               // Only nodes with paths can participate in a reference map.
+               panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeSubPath", sp))
+       }
+
+       var path addrs.ModuleInstance
+       if outside, ok := referrer.(GraphNodeReferenceOutside); ok {
+               // Vertex makes references to objects in a different module than where
+               // it was declared.
+               _, path = outside.ReferenceOutside()
+               return path
        }
 
-       return prefix
+       // Vertex makes references to objects in the same module as where it
+       // was declared.
+       return sp.Path()
+}
+
+// referenceMapKey produces keys for the "edges" map. "referrer" is the vertex
+// that the reference is from, and "addr" is the address of the object being
+// referenced.
+//
+// The result is an opaque string that includes both the address of the given
+// object and the address of the module instance that object belongs to.
+//
+// Only GraphNodeSubPath implementations can be referrers, so this method will
+// panic if the given vertex does not implement that interface.
+func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string {
+       path := vertexReferencePath(referrer)
+       return m.mapKey(path, addr)
 }
 
 // NewReferenceMap is used to create a new reference map for the
@@ -266,83 +346,82 @@ func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
        var m ReferenceMap
 
        // Build the lookup table
-       refMap := make(map[string][]dag.Vertex)
+       vertices := make(map[string][]dag.Vertex)
        for _, v := range vs {
+               _, ok := v.(GraphNodeSubPath)
+               if !ok {
+                       // Only nodes with paths can participate in a reference map.
+                       continue
+               }
+
                // We're only looking for referenceable nodes
                rn, ok := v.(GraphNodeReferenceable)
                if !ok {
                        continue
                }
 
+               path := m.vertexReferenceablePath(v)
+
                // Go through and cache them
-               prefix := m.prefix(v)
-               for _, n := range rn.ReferenceableName() {
-                       n = prefix + n
-                       refMap[n] = append(refMap[n], v)
+               for _, addr := range rn.ReferenceableAddrs() {
+                       key := m.mapKey(path, addr)
+                       vertices[key] = append(vertices[key], v)
                }
 
-               // If there is a path, it is always referenceable by that. For
-               // example, if this is a referenceable thing at path []string{"foo"},
-               // then it can be referenced at "module.foo"
-               if pn, ok := v.(GraphNodeSubPath); ok {
-                       for _, p := range ReferenceModulePath(pn.Path()) {
-                               refMap[p] = append(refMap[p], v)
-                       }
+               // Any node can be referenced by the address of the module it belongs
+               // to or any of that module's ancestors.
+               for _, addr := range path.Ancestors()[1:] {
+                       // Can be referenced either as the specific call instance (with
+                       // an instance key) or as the bare module call itself (the "module"
+                       // block in the parent module that created the instance).
+                       callPath, call := addr.Call()
+                       callInstPath, callInst := addr.CallInstance()
+                       callKey := m.mapKey(callPath, call)
+                       callInstKey := m.mapKey(callInstPath, callInst)
+                       vertices[callKey] = append(vertices[callKey], v)
+                       vertices[callInstKey] = append(vertices[callInstKey], v)
                }
        }
 
        // Build the lookup table for referenced by
-       refByMap := make(map[string][]dag.Vertex)
+       edges := make(map[string][]dag.Vertex)
        for _, v := range vs {
-               // We're only looking for referenceable nodes
+               _, ok := v.(GraphNodeSubPath)
+               if !ok {
+                       // Only nodes with paths can participate in a reference map.
+                       continue
+               }
+
                rn, ok := v.(GraphNodeReferencer)
                if !ok {
+                       // We're only looking for referenceable nodes
                        continue
                }
 
                // Go through and cache them
-               prefix := m.prefix(v)
-               for _, n := range rn.References() {
-                       n = prefix + n
-                       refByMap[n] = append(refByMap[n], v)
+               for _, ref := range rn.References() {
+                       if ref.Subject == nil {
+                               // Should never happen
+                               panic(fmt.Sprintf("%T.References returned reference with nil subject", rn))
+                       }
+                       key := m.referenceMapKey(v, ref.Subject)
+                       edges[key] = append(edges[key], v)
                }
        }
 
-       m.references = refMap
-       m.referencedBy = refByMap
+       m.vertices = vertices
+       m.edges = edges
        return &m
 }
 
-// Returns the reference name for a module path. The path "foo" would return
-// "module.foo". If this is a deeply nested module, it will be every parent
-// as well. For example: ["foo", "bar"] would return both "module.foo" and
-// "module.foo.module.bar"
-func ReferenceModulePath(p []string) []string {
-       p = normalizeModulePath(p)
-       if len(p) == 1 {
-               // Root, no name
-               return nil
-       }
-
-       result := make([]string, 0, len(p)-1)
-       for i := len(p); i > 1; i-- {
-               result = append(result, modulePrefixStr(p[:i]))
-       }
-
-       return result
-}
-
 // ReferencesFromConfig returns the references that a configuration has
 // based on the interpolated variables in a configuration.
-func ReferencesFromConfig(c *config.RawConfig) []string {
-       var result []string
-       for _, v := range c.Variables {
-               if r := ReferenceFromInterpolatedVar(v); len(r) > 0 {
-                       result = append(result, r...)
-               }
+func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference {
+       if body == nil {
+               return nil
        }
-
-       return result
+       refs, _ := lang.ReferencesInBlock(body, schema)
+       return refs
 }
 
 // ReferenceFromInterpolatedVar returns the reference from this variable,
@@ -378,18 +457,31 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string {
        }
 }
 
-func modulePrefixStr(p []string) string {
-       // strip "root"
-       if len(p) > 0 && p[0] == rootModulePath[0] {
-               p = p[1:]
-       }
-
-       parts := make([]string, 0, len(p)*2)
-       for _, p := range p {
-               parts = append(parts, "module", p)
+// appendResourceDestroyReferences identifies resource and resource instance
+// references in the given slice and appends to it the "destroy-phase"
+// equivalents of those references, returning the result.
+//
+// This can be used in the References implementation for a node which must also
+// depend on the destruction of anything it references.
+func appendResourceDestroyReferences(refs []*addrs.Reference) []*addrs.Reference {
+       given := refs
+       for _, ref := range given {
+               switch tr := ref.Subject.(type) {
+               case addrs.Resource:
+                       newRef := *ref // shallow copy
+                       newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy)
+                       refs = append(refs, &newRef)
+               case addrs.ResourceInstance:
+                       newRef := *ref // shallow copy
+                       newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy)
+                       refs = append(refs, &newRef)
+               }
        }
+       return refs
+}
 
-       return strings.Join(parts, ".")
+func modulePrefixStr(p addrs.ModuleInstance) string {
+       return p.String()
 }
 
 func modulePrefixList(result []string, prefix string) []string {