]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/resource_address.go
Merge branch 'fix_read_test' of github.com:alexandreFre/terraform-provider-statuscake
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / resource_address.go
index a8a0c95530fe7d256988dc196c0ded0845d6a201..156ecf5c0eafdcade14aedda3331db80511ca8d5 100644 (file)
@@ -7,7 +7,10 @@ import (
        "strconv"
        "strings"
 
+       "github.com/hashicorp/terraform/addrs"
+
        "github.com/hashicorp/terraform/config"
+       "github.com/hashicorp/terraform/configs"
 )
 
 // ResourceAddress is a way of identifying an individual resource (or,
@@ -41,9 +44,9 @@ func (r *ResourceAddress) Copy() *ResourceAddress {
                Type:         r.Type,
                Mode:         r.Mode,
        }
-       for _, p := range r.Path {
-               n.Path = append(n.Path, p)
-       }
+
+       n.Path = append(n.Path, r.Path...)
+
        return n
 }
 
@@ -89,6 +92,68 @@ func (r *ResourceAddress) String() string {
        return strings.Join(result, ".")
 }
 
+// HasResourceSpec returns true if the address has a resource spec, as
+// defined in the documentation:
+//    https://www.terraform.io/docs/internals/resource-addressing.html
+// In particular, this returns false if the address contains only
+// a module path, thus addressing the entire module.
+func (r *ResourceAddress) HasResourceSpec() bool {
+       return r.Type != "" && r.Name != ""
+}
+
+// WholeModuleAddress returns the resource address that refers to all
+// resources in the same module as the receiver address.
+func (r *ResourceAddress) WholeModuleAddress() *ResourceAddress {
+       return &ResourceAddress{
+               Path:            r.Path,
+               Index:           -1,
+               InstanceTypeSet: false,
+       }
+}
+
+// MatchesResourceConfig returns true if the receiver matches the given
+// configuration resource within the given _static_ module path. Note that
+// the module path in a resource address is a _dynamic_ module path, and
+// multiple dynamic resource paths may map to a single static path if
+// count and for_each are in use on module calls.
+//
+// Since resource configuration blocks represent all of the instances of
+// a multi-instance resource, the index of the address (if any) is not
+// considered.
+func (r *ResourceAddress) MatchesResourceConfig(path addrs.Module, rc *configs.Resource) bool {
+       if r.HasResourceSpec() {
+               // FIXME: Some ugliness while we are between worlds. Functionality
+               // in "addrs" should eventually replace this ResourceAddress idea
+               // completely, but for now we'll need to translate to the old
+               // way of representing resource modes.
+               switch r.Mode {
+               case config.ManagedResourceMode:
+                       if rc.Mode != addrs.ManagedResourceMode {
+                               return false
+                       }
+               case config.DataResourceMode:
+                       if rc.Mode != addrs.DataResourceMode {
+                               return false
+                       }
+               }
+               if r.Type != rc.Type || r.Name != rc.Name {
+                       return false
+               }
+       }
+
+       addrPath := r.Path
+
+       // normalize
+       if len(addrPath) == 0 {
+               addrPath = nil
+       }
+       if len(path) == 0 {
+               path = nil
+       }
+       rawPath := []string(path)
+       return reflect.DeepEqual(addrPath, rawPath)
+}
+
 // stateId returns the ID that this resource should be entered with
 // in the state. This is also used for diffs. In the future, we'd like to
 // move away from this string field so I don't export this.
@@ -185,7 +250,10 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) {
 
        // not allowed to say "data." without a type following
        if mode == config.DataResourceMode && matches["type"] == "" {
-               return nil, fmt.Errorf("must target specific data instance")
+               return nil, fmt.Errorf(
+                       "invalid resource address %q: must target specific data instance",
+                       s,
+               )
        }
 
        return &ResourceAddress{
@@ -199,6 +267,213 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) {
        }, nil
 }
 
+// ParseResourceAddressForInstanceDiff creates a ResourceAddress for a
+// resource name as described in a module diff.
+//
+// For historical reasons a different addressing format is used in this
+// context. The internal format should not be shown in the UI and instead
+// this function should be used to translate to a ResourceAddress and
+// then, where appropriate, use the String method to produce a canonical
+// resource address string for display in the UI.
+//
+// The given path slice must be empty (or nil) for the root module, and
+// otherwise consist of a sequence of module names traversing down into
+// the module tree. If a non-nil path is provided, the caller must not
+// modify its underlying array after passing it to this function.
+func ParseResourceAddressForInstanceDiff(path []string, key string) (*ResourceAddress, error) {
+       addr, err := parseResourceAddressInternal(key)
+       if err != nil {
+               return nil, err
+       }
+       addr.Path = path
+       return addr, nil
+}
+
+// NewLegacyResourceAddress creates a ResourceAddress from a new-style
+// addrs.AbsResource value.
+//
+// This is provided for shimming purposes so that we can still easily call into
+// older functions that expect the ResourceAddress type.
+func NewLegacyResourceAddress(addr addrs.AbsResource) *ResourceAddress {
+       ret := &ResourceAddress{
+               Type: addr.Resource.Type,
+               Name: addr.Resource.Name,
+       }
+
+       switch addr.Resource.Mode {
+       case addrs.ManagedResourceMode:
+               ret.Mode = config.ManagedResourceMode
+       case addrs.DataResourceMode:
+               ret.Mode = config.DataResourceMode
+       default:
+               panic(fmt.Errorf("cannot shim %s to legacy config.ResourceMode value", addr.Resource.Mode))
+       }
+
+       path := make([]string, len(addr.Module))
+       for i, step := range addr.Module {
+               if step.InstanceKey != addrs.NoKey {
+                       // At the time of writing this can't happen because we don't
+                       // ket generate keyed module instances. This legacy codepath must
+                       // be removed before we can support "count" and "for_each" for
+                       // modules.
+                       panic(fmt.Errorf("cannot shim module instance step with key %#v to legacy ResourceAddress.Path", step.InstanceKey))
+               }
+
+               path[i] = step.Name
+       }
+       ret.Path = path
+       ret.Index = -1
+
+       return ret
+}
+
+// NewLegacyResourceInstanceAddress creates a ResourceAddress from a new-style
+// addrs.AbsResource value.
+//
+// This is provided for shimming purposes so that we can still easily call into
+// older functions that expect the ResourceAddress type.
+func NewLegacyResourceInstanceAddress(addr addrs.AbsResourceInstance) *ResourceAddress {
+       ret := &ResourceAddress{
+               Type: addr.Resource.Resource.Type,
+               Name: addr.Resource.Resource.Name,
+       }
+
+       switch addr.Resource.Resource.Mode {
+       case addrs.ManagedResourceMode:
+               ret.Mode = config.ManagedResourceMode
+       case addrs.DataResourceMode:
+               ret.Mode = config.DataResourceMode
+       default:
+               panic(fmt.Errorf("cannot shim %s to legacy config.ResourceMode value", addr.Resource.Resource.Mode))
+       }
+
+       path := make([]string, len(addr.Module))
+       for i, step := range addr.Module {
+               if step.InstanceKey != addrs.NoKey {
+                       // At the time of writing this can't happen because we don't
+                       // ket generate keyed module instances. This legacy codepath must
+                       // be removed before we can support "count" and "for_each" for
+                       // modules.
+                       panic(fmt.Errorf("cannot shim module instance step with key %#v to legacy ResourceAddress.Path", step.InstanceKey))
+               }
+
+               path[i] = step.Name
+       }
+       ret.Path = path
+
+       if addr.Resource.Key == addrs.NoKey {
+               ret.Index = -1
+       } else if ik, ok := addr.Resource.Key.(addrs.IntKey); ok {
+               ret.Index = int(ik)
+       } else {
+               panic(fmt.Errorf("cannot shim resource instance with key %#v to legacy ResourceAddress.Index", addr.Resource.Key))
+       }
+
+       return ret
+}
+
+// AbsResourceInstanceAddr converts the receiver, a legacy resource address, to
+// the new resource address type addrs.AbsResourceInstance.
+//
+// This method can be used only on an address that has a resource specification.
+// It will panic if called on a module-path-only ResourceAddress. Use
+// method HasResourceSpec to check before calling, in contexts where it is
+// unclear.
+//
+// addrs.AbsResourceInstance does not represent the "tainted" and "deposed"
+// states, and so if these are present on the receiver then they are discarded.
+//
+// This is provided for shimming purposes so that we can easily adapt functions
+// that are returning the legacy ResourceAddress type, for situations where
+// the new type is required.
+func (addr *ResourceAddress) AbsResourceInstanceAddr() addrs.AbsResourceInstance {
+       if !addr.HasResourceSpec() {
+               panic("AbsResourceInstanceAddr called on ResourceAddress with no resource spec")
+       }
+
+       ret := addrs.AbsResourceInstance{
+               Module: addr.ModuleInstanceAddr(),
+               Resource: addrs.ResourceInstance{
+                       Resource: addrs.Resource{
+                               Type: addr.Type,
+                               Name: addr.Name,
+                       },
+               },
+       }
+
+       switch addr.Mode {
+       case config.ManagedResourceMode:
+               ret.Resource.Resource.Mode = addrs.ManagedResourceMode
+       case config.DataResourceMode:
+               ret.Resource.Resource.Mode = addrs.DataResourceMode
+       default:
+               panic(fmt.Errorf("cannot shim %s to addrs.ResourceMode value", addr.Mode))
+       }
+
+       if addr.Index != -1 {
+               ret.Resource.Key = addrs.IntKey(addr.Index)
+       }
+
+       return ret
+}
+
+// ModuleInstanceAddr returns the module path portion of the receiver as a
+// addrs.ModuleInstance value.
+func (addr *ResourceAddress) ModuleInstanceAddr() addrs.ModuleInstance {
+       path := make(addrs.ModuleInstance, len(addr.Path))
+       for i, name := range addr.Path {
+               path[i] = addrs.ModuleInstanceStep{Name: name}
+       }
+       return path
+}
+
+// Contains returns true if and only if the given node is contained within
+// the receiver.
+//
+// Containment is defined in terms of the module and resource heirarchy:
+// a resource is contained within its module and any ancestor modules,
+// an indexed resource instance is contained with the unindexed resource, etc.
+func (addr *ResourceAddress) Contains(other *ResourceAddress) bool {
+       ourPath := addr.Path
+       givenPath := other.Path
+       if len(givenPath) < len(ourPath) {
+               return false
+       }
+       for i := range ourPath {
+               if ourPath[i] != givenPath[i] {
+                       return false
+               }
+       }
+
+       // If the receiver is a whole-module address then the path prefix
+       // matching is all we need.
+       if !addr.HasResourceSpec() {
+               return true
+       }
+
+       if addr.Type != other.Type || addr.Name != other.Name || addr.Mode != other.Mode {
+               return false
+       }
+
+       if addr.Index != -1 && addr.Index != other.Index {
+               return false
+       }
+
+       if addr.InstanceTypeSet && (addr.InstanceTypeSet != other.InstanceTypeSet || addr.InstanceType != other.InstanceType) {
+               return false
+       }
+
+       return true
+}
+
+// Equals returns true if the receiver matches the given address.
+//
+// The name of this method is a misnomer, since it doesn't test for exact
+// equality. Instead, it tests that the _specified_ parts of each
+// address match, treating any unspecified parts as wildcards.
+//
+// See also Contains, which takes a more heirarchical approach to comparing
+// addresses.
 func (addr *ResourceAddress) Equals(raw interface{}) bool {
        other, ok := raw.(*ResourceAddress)
        if !ok {
@@ -233,6 +508,59 @@ func (addr *ResourceAddress) Equals(raw interface{}) bool {
                modeMatch
 }
 
+// Less returns true if and only if the receiver should be sorted before
+// the given address when presenting a list of resource addresses to
+// an end-user.
+//
+// This sort uses lexicographic sorting for most components, but uses
+// numeric sort for indices, thus causing index 10 to sort after
+// index 9, rather than after index 1.
+func (addr *ResourceAddress) Less(other *ResourceAddress) bool {
+
+       switch {
+
+       case len(addr.Path) != len(other.Path):
+               return len(addr.Path) < len(other.Path)
+
+       case !reflect.DeepEqual(addr.Path, other.Path):
+               // If the two paths are the same length but don't match, we'll just
+               // cheat and compare the string forms since it's easier than
+               // comparing all of the path segments in turn, and lexicographic
+               // comparison is correct for the module path portion.
+               addrStr := addr.String()
+               otherStr := other.String()
+               return addrStr < otherStr
+
+       case addr.Mode != other.Mode:
+               return addr.Mode == config.DataResourceMode
+
+       case addr.Type != other.Type:
+               return addr.Type < other.Type
+
+       case addr.Name != other.Name:
+               return addr.Name < other.Name
+
+       case addr.Index != other.Index:
+               // Since "Index" is -1 for an un-indexed address, this also conveniently
+               // sorts unindexed addresses before indexed ones, should they both
+               // appear for some reason.
+               return addr.Index < other.Index
+
+       case addr.InstanceTypeSet != other.InstanceTypeSet:
+               return !addr.InstanceTypeSet
+
+       case addr.InstanceType != other.InstanceType:
+               // InstanceType is actually an enum, so this is just an arbitrary
+               // sort based on the enum numeric values, and thus not particularly
+               // meaningful.
+               return addr.InstanceType < other.InstanceType
+
+       default:
+               return false
+
+       }
+}
+
 func ParseResourceIndex(s string) (int, error) {
        if s == "" {
                return -1, nil
@@ -275,7 +603,7 @@ func tokenizeResourceAddress(s string) (map[string]string, error) {
        // string "aws_instance.web.tainted[1]"
        re := regexp.MustCompile(`\A` +
                // "module.foo.module.bar" (optional)
-               `(?P<path>(?:module\.[^.]+\.?)*)` +
+               `(?P<path>(?:module\.(?P<module_name>[^.]+)\.?)*)` +
                // possibly "data.", if targeting is a data resource
                `(?P<data_prefix>(?:data\.)?)` +
                // "aws_instance.web" (optional when module path specified)
@@ -289,7 +617,7 @@ func tokenizeResourceAddress(s string) (map[string]string, error) {
        groupNames := re.SubexpNames()
        rawMatches := re.FindAllStringSubmatch(s, -1)
        if len(rawMatches) != 1 {
-               return nil, fmt.Errorf("Problem parsing address: %q", s)
+               return nil, fmt.Errorf("invalid resource address %q", s)
        }
 
        matches := make(map[string]string)