mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
59b116f7bf
We no longer need to support 0.12-and-earlier-style provider addresses because users should've upgraded their existing configurations and states on Terraform 0.13 already. For now this is only checked in the "init" command, because various test shims are still relying on the idea of legacy providers the core layer. However, rejecting these during init is sufficient grounds to avoid supporting legacy provider addresses in the new dependency lock file format, and thus sets the stage for a more severe removal of legacy provider support in a later commit.
151 lines
5.3 KiB
Go
151 lines
5.3 KiB
Go
package getproviders
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
svchost "github.com/hashicorp/terraform-svchost"
|
|
disco "github.com/hashicorp/terraform-svchost/disco"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
)
|
|
|
|
// RegistrySource is a Source that knows how to find and install providers from
|
|
// their originating provider registries.
|
|
type RegistrySource struct {
|
|
services *disco.Disco
|
|
}
|
|
|
|
var _ Source = (*RegistrySource)(nil)
|
|
|
|
// NewRegistrySource creates and returns a new source that will install
|
|
// providers from their originating provider registries.
|
|
func NewRegistrySource(services *disco.Disco) *RegistrySource {
|
|
return &RegistrySource{
|
|
services: services,
|
|
}
|
|
}
|
|
|
|
// AvailableVersions returns all of the versions available for the provider
|
|
// with the given address, or an error if that result cannot be determined.
|
|
//
|
|
// If the request fails, the returned error might be an value of
|
|
// ErrHostNoProviders, ErrHostUnreachable, ErrUnauthenticated,
|
|
// ErrProviderNotKnown, or ErrQueryFailed. Callers must be defensive and
|
|
// expect errors of other types too, to allow for future expansion.
|
|
func (s *RegistrySource) AvailableVersions(ctx context.Context, provider addrs.Provider) (VersionList, Warnings, error) {
|
|
client, err := s.registryClient(provider.Hostname)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
versionsResponse, warnings, err := client.ProviderVersions(ctx, provider)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if len(versionsResponse) == 0 {
|
|
return nil, warnings, nil
|
|
}
|
|
|
|
// We ignore protocols here because our goal is to find out which versions
|
|
// are available _at all_. Which ones are compatible with the current
|
|
// Terraform becomes relevant only once we've selected one, at which point
|
|
// we'll return an error if the selected one is incompatible.
|
|
//
|
|
// We intentionally produce an error on incompatibility, rather than
|
|
// silently ignoring an incompatible version, in order to give the user
|
|
// explicit feedback about why their selection wasn't valid and allow them
|
|
// to decide whether to fix that by changing the selection or by some other
|
|
// action such as upgrading Terraform, using a different OS to run
|
|
// Terraform, etc. Changes that affect compatibility are considered breaking
|
|
// changes from a provider API standpoint, so provider teams should change
|
|
// compatibility only in new major versions.
|
|
ret := make(VersionList, 0, len(versionsResponse))
|
|
for str := range versionsResponse {
|
|
v, err := ParseVersion(str)
|
|
if err != nil {
|
|
return nil, nil, ErrQueryFailed{
|
|
Provider: provider,
|
|
Wrapped: fmt.Errorf("registry response includes invalid version string %q: %s", str, err),
|
|
}
|
|
}
|
|
ret = append(ret, v)
|
|
}
|
|
ret.Sort() // lowest precedence first, preserving order when equal precedence
|
|
return ret, warnings, nil
|
|
}
|
|
|
|
// PackageMeta returns metadata about the location and capabilities of
|
|
// a distribution package for a particular provider at a particular version
|
|
// targeting a particular platform.
|
|
//
|
|
// Callers of PackageMeta should first call AvailableVersions and pass
|
|
// one of the resulting versions to this function. This function cannot
|
|
// distinguish between a version that is not available and an unsupported
|
|
// target platform, so if it encounters either case it will return an error
|
|
// suggesting that the target platform isn't supported under the assumption
|
|
// that the caller already checked that the version is available at all.
|
|
//
|
|
// To find a package suitable for the platform where the provider installation
|
|
// process is running, set the "target" argument to
|
|
// getproviders.CurrentPlatform.
|
|
//
|
|
// If the request fails, the returned error might be an value of
|
|
// ErrHostNoProviders, ErrHostUnreachable, ErrUnauthenticated,
|
|
// ErrPlatformNotSupported, or ErrQueryFailed. Callers must be defensive and
|
|
// expect errors of other types too, to allow for future expansion.
|
|
func (s *RegistrySource) PackageMeta(ctx context.Context, provider addrs.Provider, version Version, target Platform) (PackageMeta, error) {
|
|
client, err := s.registryClient(provider.Hostname)
|
|
if err != nil {
|
|
return PackageMeta{}, err
|
|
}
|
|
|
|
return client.PackageMeta(ctx, provider, version, target)
|
|
}
|
|
|
|
func (s *RegistrySource) registryClient(hostname svchost.Hostname) (*registryClient, error) {
|
|
host, err := s.services.Discover(hostname)
|
|
if err != nil {
|
|
return nil, ErrHostUnreachable{
|
|
Hostname: hostname,
|
|
Wrapped: err,
|
|
}
|
|
}
|
|
|
|
url, err := host.ServiceURL("providers.v1")
|
|
switch err := err.(type) {
|
|
case nil:
|
|
// okay! We'll fall through and return below.
|
|
case *disco.ErrServiceNotProvided:
|
|
return nil, ErrHostNoProviders{
|
|
Hostname: hostname,
|
|
}
|
|
case *disco.ErrVersionNotSupported:
|
|
return nil, ErrHostNoProviders{
|
|
Hostname: hostname,
|
|
HasOtherVersion: true,
|
|
}
|
|
default:
|
|
return nil, ErrHostUnreachable{
|
|
Hostname: hostname,
|
|
Wrapped: err,
|
|
}
|
|
}
|
|
|
|
// Check if we have credentials configured for this hostname.
|
|
creds, err := s.services.CredentialsForHost(hostname)
|
|
if err != nil {
|
|
// This indicates that a credentials helper failed, which means we
|
|
// can't do anything better than just pass through the helper's
|
|
// own error message.
|
|
return nil, fmt.Errorf("failed to retrieve credentials for %s: %s", hostname, err)
|
|
}
|
|
|
|
return newRegistryClient(url, creds), nil
|
|
}
|
|
|
|
func (s *RegistrySource) ForDisplay(provider addrs.Provider) string {
|
|
return fmt.Sprintf("registry %s", provider.Hostname.ForDisplay())
|
|
}
|