github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/internal/getproviders/legacy_lookup.go (about) 1 package getproviders 2 3 import ( 4 "fmt" 5 6 svchost "github.com/hashicorp/terraform-svchost" 7 8 "github.com/hashicorp/terraform/addrs" 9 ) 10 11 // LookupLegacyProvider attempts to resolve a legacy provider address (whose 12 // registry host and namespace are implied, rather than explicit) into a 13 // fully-qualified provider address, by asking the main Terraform registry 14 // to resolve it. 15 // 16 // If the given address is not a legacy provider address then it will just be 17 // returned verbatim without making any outgoing requests. 18 // 19 // Legacy provider lookup is possible only if the given source is either a 20 // *RegistrySource directly or if it is a MultiSource containing a 21 // *RegistrySource whose selector matching patterns include the 22 // public registry hostname registry.terraform.io. 23 // 24 // This is a backward-compatibility mechanism for compatibility with existing 25 // configurations that don't include explicit provider source addresses. New 26 // configurations should not rely on it, and this fallback mechanism is 27 // likely to be removed altogether in a future Terraform version. 28 func LookupLegacyProvider(addr addrs.Provider, source Source) (addrs.Provider, error) { 29 if addr.Namespace != "-" { 30 return addr, nil 31 } 32 if addr.Hostname != defaultRegistryHost { // condition above assures namespace is also "-" 33 // Legacy providers must always belong to the default registry host. 34 return addrs.Provider{}, fmt.Errorf("invalid provider type %q: legacy provider addresses must always belong to %s", addr, defaultRegistryHost) 35 } 36 37 // Now we need to derive a suitable *RegistrySource from the given source, 38 // either directly or indirectly. This will not be possible if the user 39 // has configured Terraform to disable direct installation from 40 // registry.terraform.io; in that case, fully-qualified provider addresses 41 // are always required. 42 regSource := findLegacyProviderLookupSource(addr.Hostname, source) 43 if regSource == nil { 44 // This error message is assuming that the given Source was produced 45 // based on the CLI configuration, which isn't necessarily true but 46 // is true in all cases where this error message will ultimately be 47 // presented to an end-user, so good enough for now. 48 return addrs.Provider{}, fmt.Errorf("unqualified provider type %q cannot be resolved because direct installation from %s is disabled in the CLI configuration; declare an explicit provider namespace for this provider", addr.Type, addr.Hostname) 49 } 50 51 defaultNamespace, err := regSource.LookupLegacyProviderNamespace(addr.Hostname, addr.Type) 52 if err != nil { 53 return addrs.Provider{}, err 54 } 55 56 return addrs.Provider{ 57 Hostname: addr.Hostname, 58 Namespace: defaultNamespace, 59 Type: addr.Type, 60 }, nil 61 } 62 63 // findLegacyProviderLookupSource tries to find a *RegistrySource that can talk 64 // to the given registry host in the given Source. It might be given directly, 65 // or it might be given indirectly via a MultiSource where the selector 66 // includes a wildcard for registry.terraform.io. 67 // 68 // Returns nil if the given source does not have any configured way to talk 69 // directly to the given host. 70 // 71 // If the given source contains multiple sources that can talk to the given 72 // host directly, the first one in the sequence takes preference. In practice 73 // it's pointless to have two direct installation sources that match the same 74 // hostname anyway, so this shouldn't arise in normal use. 75 func findLegacyProviderLookupSource(host svchost.Hostname, source Source) *RegistrySource { 76 switch source := source.(type) { 77 78 case *RegistrySource: 79 // Easy case: the source is a registry source directly, and so we'll 80 // just use it. 81 return source 82 83 case MultiSource: 84 // Trickier case: if it's a multisource then we need to scan over 85 // its selectors until we find one that is a *RegistrySource _and_ 86 // that is configured to accept arbitrary providers from the 87 // given hostname. 88 89 // For our matching purposes we'll use an address that would not be 90 // valid as a real provider FQN and thus can only match a selector 91 // that has no filters at all or a selector that wildcards everything 92 // except the hostname, like "registry.terraform.io/*/*" 93 matchAddr := addrs.Provider{ 94 Hostname: host, 95 // Other fields are intentionally left empty, to make this invalid 96 // as a specific provider address. 97 } 98 99 for _, selector := range source { 100 // If this source has suitable matching patterns to install from 101 // the given hostname then we'll recursively search inside it 102 // for *RegistrySource objects. 103 if selector.CanHandleProvider(matchAddr) { 104 ret := findLegacyProviderLookupSource(host, selector.Source) 105 if ret != nil { 106 return ret 107 } 108 } 109 } 110 111 // If we get here then there were no selectors that are both configured 112 // to handle modules from the given hostname and that are registry 113 // sources, so we fail. 114 return nil 115 116 default: 117 // This source cannot be and cannot contain a *RegistrySource, so 118 // we fail. 119 return nil 120 } 121 }