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 }