github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/lang/globalref/reference.go (about)

     1  package globalref
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/hashicorp/terraform/internal/addrs"
     8  	"github.com/hashicorp/terraform/internal/tfdiags"
     9  	"github.com/zclconf/go-cty/cty"
    10  )
    11  
    12  // Reference combines an addrs.Reference with the address of the module
    13  // instance or resource instance where it was found.
    14  //
    15  // Because of the design of the Terraform language, our main model of
    16  // references only captures the module-local part of the reference and assumes
    17  // that it's always clear from context which module a reference belongs to.
    18  // That's not true for globalref because our whole purpose is to work across
    19  // module boundaries, and so this package in particular has its own
    20  // representation of references.
    21  type Reference struct {
    22  	// ContainerAddr is always either addrs.ModuleInstance or
    23  	// addrs.AbsResourceInstance. The latter is required if LocalRef's
    24  	// subject is either an addrs.CountAddr or addrs.ForEachAddr, so
    25  	// we can know which resource's repetition expression it's
    26  	// referring to.
    27  	ContainerAddr addrs.Targetable
    28  
    29  	// LocalRef is a reference that would be resolved in the context
    30  	// of the module instance or resource instance given in ContainerAddr.
    31  	LocalRef *addrs.Reference
    32  }
    33  
    34  func absoluteRef(containerAddr addrs.Targetable, localRef *addrs.Reference) Reference {
    35  	ret := Reference{
    36  		ContainerAddr: containerAddr,
    37  		LocalRef:      localRef,
    38  	}
    39  	// For simplicity's sake, we always reduce the ContainerAddr to be
    40  	// just the module address unless it's a count.index, each.key, or
    41  	// each.value reference, because for anything else it's immaterial
    42  	// which resource it belongs to.
    43  	switch localRef.Subject.(type) {
    44  	case addrs.CountAttr, addrs.ForEachAttr:
    45  		// nothing to do
    46  	default:
    47  		ret.ContainerAddr = ret.ModuleAddr()
    48  	}
    49  	return ret
    50  }
    51  
    52  func absoluteRefs(containerAddr addrs.Targetable, refs []*addrs.Reference) []Reference {
    53  	if len(refs) == 0 {
    54  		return nil
    55  	}
    56  
    57  	ret := make([]Reference, len(refs))
    58  	for i, ref := range refs {
    59  		ret[i] = absoluteRef(containerAddr, ref)
    60  	}
    61  	return ret
    62  }
    63  
    64  // ModuleAddr returns the address of the module where the reference would
    65  // be resolved.
    66  //
    67  // This is either ContainerAddr directly if it's already just a module
    68  // instance, or the module instance part of it if it's a resource instance.
    69  func (r Reference) ModuleAddr() addrs.ModuleInstance {
    70  	switch addr := r.ContainerAddr.(type) {
    71  	case addrs.ModuleInstance:
    72  		return addr
    73  	case addrs.AbsResourceInstance:
    74  		return addr.Module
    75  	default:
    76  		// NOTE: We're intentionally using only a subset of possible
    77  		// addrs.Targetable implementations here, so anything else
    78  		// is invalid.
    79  		panic(fmt.Sprintf("reference has invalid container address type %T", addr))
    80  	}
    81  }
    82  
    83  // ResourceInstance returns the address of the resource where the reference
    84  // would be resolved, if there is one.
    85  //
    86  // Because not all references belong to resources, the extra boolean return
    87  // value indicates whether the returned address is valid.
    88  func (r Reference) ResourceInstance() (addrs.AbsResourceInstance, bool) {
    89  	switch container := r.ContainerAddr.(type) {
    90  	case addrs.ModuleInstance:
    91  		moduleInstance := container
    92  
    93  		switch ref := r.LocalRef.Subject.(type) {
    94  		case addrs.Resource:
    95  			return ref.Instance(addrs.NoKey).Absolute(moduleInstance), true
    96  		case addrs.ResourceInstance:
    97  			return ref.Absolute(moduleInstance), true
    98  		}
    99  
   100  		return addrs.AbsResourceInstance{}, false
   101  
   102  	case addrs.AbsResourceInstance:
   103  		return container, true
   104  	default:
   105  		// NOTE: We're intentionally using only a subset of possible
   106  		// addrs.Targetable implementations here, so anything else
   107  		// is invalid.
   108  		panic(fmt.Sprintf("reference has invalid container address type %T", container))
   109  	}
   110  }
   111  
   112  // DebugString returns an internal (but still somewhat Terraform-language-like)
   113  // compact string representation of the reciever, which isn't an address that
   114  // any of our usual address parsers could accept but still captures the
   115  // essence of what the reference represents.
   116  //
   117  // The DebugString result is not suitable for end-user-oriented messages.
   118  //
   119  // DebugString is also not suitable for use as a unique key for a reference,
   120  // because it's ambiguous (between a no-key resource instance and a resource)
   121  // and because it discards the source location information in the LocalRef.
   122  func (r Reference) DebugString() string {
   123  	// As the doc comment insinuates, we don't have any real syntax for
   124  	// "absolute references": references are always local, and targets are
   125  	// always absolute but only include modules and resources.
   126  	return r.ContainerAddr.String() + "::" + r.LocalRef.DisplayString()
   127  }
   128  
   129  // ResourceAttr converts the Reference value to a more specific ResourceAttr
   130  // value.
   131  //
   132  // Because not all references belong to resources, the extra boolean return
   133  // value indicates whether the returned address is valid.
   134  func (r Reference) ResourceAttr() (ResourceAttr, bool) {
   135  	res, ok := r.ResourceInstance()
   136  	if !ok {
   137  		return ResourceAttr{}, ok
   138  	}
   139  
   140  	traversal := r.LocalRef.Remaining
   141  
   142  	path := make(cty.Path, len(traversal))
   143  	for si, step := range traversal {
   144  		switch ts := step.(type) {
   145  		case hcl.TraverseRoot:
   146  			path[si] = cty.GetAttrStep{
   147  				Name: ts.Name,
   148  			}
   149  		case hcl.TraverseAttr:
   150  			path[si] = cty.GetAttrStep{
   151  				Name: ts.Name,
   152  			}
   153  		case hcl.TraverseIndex:
   154  			path[si] = cty.IndexStep{
   155  				Key: ts.Key,
   156  			}
   157  		default:
   158  			panic(fmt.Sprintf("unsupported traversal step %#v", step))
   159  		}
   160  	}
   161  
   162  	return ResourceAttr{
   163  		Resource: res,
   164  		Attr:     path,
   165  	}, true
   166  }
   167  
   168  // addrKey returns the referenceAddrKey value for the item that
   169  // this reference refers to, discarding any source location information.
   170  //
   171  // See the referenceAddrKey doc comment for more information on what this
   172  // is suitable for.
   173  func (r Reference) addrKey() referenceAddrKey {
   174  	// This is a pretty arbitrary bunch of stuff. We include the type here
   175  	// just to differentiate between no-key resource instances and resources.
   176  	return referenceAddrKey(fmt.Sprintf("%s(%T)%s", r.ContainerAddr.String(), r.LocalRef.Subject, r.LocalRef.DisplayString()))
   177  }
   178  
   179  // referenceAddrKey is a special string type which conventionally contains
   180  // a unique string representation of the object that a reference refers to,
   181  // although not of the reference itself because it ignores the information
   182  // that would differentiate two different references to the same object.
   183  //
   184  // The actual content of a referenceAddrKey is arbitrary, for internal use
   185  // only. and subject to change in future. We use a named type here only to
   186  // make it easier to see when we're intentionally using strings to uniquely
   187  // identify absolute reference addresses.
   188  type referenceAddrKey string
   189  
   190  // ResourceAttr represents a global resource and attribute reference.
   191  // This is a more specific form of the Reference type since it can only refer
   192  // to a specific AbsResource and one of its attributes.
   193  type ResourceAttr struct {
   194  	Resource addrs.AbsResourceInstance
   195  	Attr     cty.Path
   196  }
   197  
   198  func (r ResourceAttr) DebugString() string {
   199  	return r.Resource.String() + tfdiags.FormatCtyPath(r.Attr)
   200  }