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  }