github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/lang/globalref/analyzer_contributing_resources.go (about) 1 package globalref 2 3 import ( 4 "sort" 5 6 "github.com/hashicorp/terraform/internal/addrs" 7 ) 8 9 // ContributingResources analyzes all of the given references and 10 // for each one tries to walk backwards through any named values to find all 11 // resources whose values contributed either directly or indirectly to any of 12 // them. 13 // 14 // This is a wrapper around ContributingResourceReferences which simplifies 15 // the result to only include distinct resource addresses, not full references. 16 // If the configuration includes several different references to different 17 // parts of a resource, ContributingResources will not preserve that detail. 18 func (a *Analyzer) ContributingResources(refs ...Reference) []addrs.AbsResource { 19 retRefs := a.ContributingResourceReferences(refs...) 20 if len(retRefs) == 0 { 21 return nil 22 } 23 24 uniq := make(map[string]addrs.AbsResource, len(refs)) 25 for _, ref := range retRefs { 26 if addr, ok := resourceForAddr(ref.LocalRef.Subject); ok { 27 moduleAddr := ref.ModuleAddr() 28 absAddr := addr.Absolute(moduleAddr) 29 uniq[absAddr.String()] = absAddr 30 } 31 } 32 ret := make([]addrs.AbsResource, 0, len(uniq)) 33 for _, addr := range uniq { 34 ret = append(ret, addr) 35 } 36 sort.Slice(ret, func(i, j int) bool { 37 // We only have a sorting function for resource _instances_, but 38 // it'll do well enough if we just pretend we have no-key instances. 39 return ret[i].Instance(addrs.NoKey).Less(ret[j].Instance(addrs.NoKey)) 40 }) 41 return ret 42 } 43 44 // ContributingResourceReferences analyzes all of the given references and 45 // for each one tries to walk backwards through any named values to find all 46 // references to resource attributes that contributed either directly or 47 // indirectly to any of them. 48 // 49 // This is a global operation that can be potentially quite expensive for 50 // complex configurations. 51 func (a *Analyzer) ContributingResourceReferences(refs ...Reference) []Reference { 52 // Our methodology here is to keep digging through MetaReferences 53 // until we've visited everything we encounter directly or indirectly, 54 // and keep track of any resources we find along the way. 55 56 // We'll aggregate our result here, using the string representations of 57 // the resources as keys to avoid returning the same one more than once. 58 found := make(map[referenceAddrKey]Reference) 59 60 // We might encounter the same object multiple times as we walk, 61 // but we won't learn anything more by traversing them again and so we'll 62 // just skip them instead. 63 visitedObjects := make(map[referenceAddrKey]struct{}) 64 65 // A queue of objects we still need to visit. 66 // Note that if we find multiple references to the same object then we'll 67 // just arbitrary choose any one of them, because for our purposes here 68 // it's immaterial which reference we actually followed. 69 pendingObjects := make(map[referenceAddrKey]Reference) 70 71 // Initial state: identify any directly-mentioned resources and 72 // queue up any named values we refer to. 73 for _, ref := range refs { 74 if _, ok := resourceForAddr(ref.LocalRef.Subject); ok { 75 found[ref.addrKey()] = ref 76 } 77 pendingObjects[ref.addrKey()] = ref 78 } 79 80 for len(pendingObjects) > 0 { 81 // Note: we modify this map while we're iterating over it, which means 82 // that anything we add might be either visited within a later 83 // iteration of the inner loop or in a later iteration of the outer 84 // loop, but we get the correct result either way because we keep 85 // working until we've fully depleted the queue. 86 for key, ref := range pendingObjects { 87 delete(pendingObjects, key) 88 89 // We do this _before_ the visit below just in case this is an 90 // invalid config with a self-referential local value, in which 91 // case we'll just silently ignore the self reference for our 92 // purposes here, and thus still eventually converge (albeit 93 // with an incomplete answer). 94 visitedObjects[key] = struct{}{} 95 96 moreRefs := a.MetaReferences(ref) 97 for _, newRef := range moreRefs { 98 if _, ok := resourceForAddr(newRef.LocalRef.Subject); ok { 99 found[newRef.addrKey()] = newRef 100 } 101 102 newKey := newRef.addrKey() 103 if _, visited := visitedObjects[newKey]; !visited { 104 pendingObjects[newKey] = newRef 105 } 106 } 107 } 108 } 109 110 if len(found) == 0 { 111 return nil 112 } 113 114 ret := make([]Reference, 0, len(found)) 115 for _, ref := range found { 116 ret = append(ret, ref) 117 } 118 return ret 119 } 120 121 func resourceForAddr(addr addrs.Referenceable) (addrs.Resource, bool) { 122 switch addr := addr.(type) { 123 case addrs.Resource: 124 return addr, true 125 case addrs.ResourceInstance: 126 return addr.Resource, true 127 default: 128 return addrs.Resource{}, false 129 } 130 }