mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
httpclient: Add OTel tracing automatically when needed (#2772)
Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
@@ -181,7 +181,7 @@ func realMain() int {
|
||||
}
|
||||
services.SetUserAgent(httpclient.OpenTofuUserAgent(version.String()))
|
||||
|
||||
modulePkgFetcher := remoteModulePackageFetcher(config.OCICredentialsPolicy)
|
||||
modulePkgFetcher := remoteModulePackageFetcher(ctx, config.OCICredentialsPolicy)
|
||||
|
||||
providerSrc, diags := providerSource(config.ProviderInstallation, services, config.OCICredentialsPolicy)
|
||||
if len(diags) > 0 {
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/getmodules"
|
||||
)
|
||||
|
||||
func remoteModulePackageFetcher(getOCICredsPolicy ociCredsPolicyBuilder) *getmodules.PackageFetcher {
|
||||
func remoteModulePackageFetcher(ctx context.Context, getOCICredsPolicy ociCredsPolicyBuilder) *getmodules.PackageFetcher {
|
||||
// TODO: Pass in a real getmodules.PackageFetcherEnvironment here,
|
||||
// which knows how to make use of the OCI authentication policy.
|
||||
return getmodules.NewPackageFetcher(&modulePackageFetcherEnvironment{
|
||||
return getmodules.NewPackageFetcher(ctx, &modulePackageFetcherEnvironment{
|
||||
getOCICredsPolicy: getOCICredsPolicy,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func getOCIRepositoryORASClient(ctx context.Context, registryDomain, repositoryN
|
||||
return nil, fmt.Errorf("finding credentials for %q: %w", registryDomain, err)
|
||||
}
|
||||
return &orasAuth.Client{
|
||||
Client: httpclient.New(), // the underlying HTTP client to use, preconfigured with OpenTofu's User-Agent string
|
||||
Client: httpclient.New(ctx), // the underlying HTTP client to use, preconfigured with OpenTofu's User-Agent string
|
||||
Credential: func(ctx context.Context, hostport string) (orasAuth.Credential, error) {
|
||||
if hostport != registryDomain {
|
||||
// We should not send the credentials we selected to any registry
|
||||
|
||||
@@ -158,7 +158,7 @@ func testModuleWithSnapshot(t *testing.T, name string) (*configs.Config, *config
|
||||
// Test modules usually do not refer to remote sources, and for local
|
||||
// sources only this ultimately just records all of the module paths
|
||||
// in a JSON file so that we can load them below.
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil), nil)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, instDiags := inst.InstallModules(context.Background(), dir, "tests", true, false, initwd.ModuleInstallHooksImpl{}, configs.RootModuleCallForTesting())
|
||||
if instDiags.HasErrors() {
|
||||
t.Fatal(instDiags.Err())
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestGet_cancel(t *testing.T) {
|
||||
|
||||
// This test needs a real module package fetcher instance because
|
||||
// we want to attempt installing a module package from our server.
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(nil),
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ func TestInit_fromModule_cwdDest(t *testing.T) {
|
||||
// treating an absolute filesystem path as if it were a "remote"
|
||||
// source address, and so we need a real package fetcher but the
|
||||
// way we use it here does not cause it to make network requests.
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(nil),
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func TestInit_fromModule_dstInSrc(t *testing.T) {
|
||||
// treating an absolute filesystem path as if it were a "remote"
|
||||
// source address, and so we need a real package fetcher but the
|
||||
// way we use it here does not cause it to make network requests.
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(nil),
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1866,7 +1866,7 @@ func TestInit_cancelModules(t *testing.T) {
|
||||
// actually making a request to this, but we still need to provide
|
||||
// the fetcher so that it will _attempt_ to make a network request
|
||||
// that can then fail with a cancellation error.
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(nil),
|
||||
ModulePackageFetcher: getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
}
|
||||
c := &InitCommand{
|
||||
Meta: m,
|
||||
|
||||
@@ -50,6 +50,8 @@ type LoginCommand struct {
|
||||
|
||||
// Run implements cli.Command.
|
||||
func (c *LoginCommand) Run(args []string) int {
|
||||
ctx := c.CommandContext()
|
||||
|
||||
args = c.Meta.process(args)
|
||||
cmdFlags := c.Meta.extendedFlagSet("login")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
@@ -192,12 +194,12 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
switch {
|
||||
case clientConfig.SupportedGrantTypes.Has(disco.OAuthAuthzCodeGrant):
|
||||
// We prefer an OAuth code grant if the server supports it.
|
||||
oauthToken, tokenDiags = c.interactiveGetTokenByCode(hostname, credsCtx, clientConfig)
|
||||
oauthToken, tokenDiags = c.interactiveGetTokenByCode(ctx, hostname, credsCtx, clientConfig)
|
||||
case clientConfig.SupportedGrantTypes.Has(disco.OAuthOwnerPasswordGrant) && hostname == svchost.Hostname(tfeHost):
|
||||
// The password grant type is allowed only for Terraform Cloud SaaS.
|
||||
// Note this case is purely theoretical at this point, as TFC currently uses
|
||||
// its own bespoke login protocol (tfe)
|
||||
oauthToken, tokenDiags = c.interactiveGetTokenByPassword(hostname, credsCtx, clientConfig)
|
||||
oauthToken, tokenDiags = c.interactiveGetTokenByPassword(ctx, hostname, credsCtx, clientConfig)
|
||||
default:
|
||||
tokenDiags = tokenDiags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
@@ -209,7 +211,7 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
token = svcauth.HostCredentialsToken(oauthToken.AccessToken)
|
||||
}
|
||||
} else if tfeservice != nil {
|
||||
token, tokenDiags = c.interactiveGetTokenByUI(hostname, credsCtx, tfeservice)
|
||||
token, tokenDiags = c.interactiveGetTokenByUI(ctx, hostname, credsCtx, tfeservice)
|
||||
}
|
||||
|
||||
diags = diags.Append(tokenDiags)
|
||||
@@ -260,7 +262,7 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+token.Token())
|
||||
|
||||
resp, err := httpclient.New().Do(req)
|
||||
resp, err := httpclient.New(ctx).Do(req)
|
||||
if err != nil {
|
||||
c.logMOTDError(err)
|
||||
c.outputDefaultTFCLoginSuccess()
|
||||
@@ -367,9 +369,9 @@ func (c *LoginCommand) defaultOutputFile() string {
|
||||
return filepath.Join(c.CLIConfigDir, "credentials.tfrc.json")
|
||||
}
|
||||
|
||||
func (c *LoginCommand) interactiveGetTokenByCode(hostname svchost.Hostname, credsCtx *loginCredentialsContext, clientConfig *disco.OAuthClient) (*oauth2.Token, tfdiags.Diagnostics) {
|
||||
func (c *LoginCommand) interactiveGetTokenByCode(ctx context.Context, hostname svchost.Hostname, credsCtx *loginCredentialsContext, clientConfig *disco.OAuthClient) (*oauth2.Token, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
confirm, confirmDiags := c.interactiveContextConsent(hostname, disco.OAuthAuthzCodeGrant, credsCtx)
|
||||
confirm, confirmDiags := c.interactiveContextConsent(ctx, hostname, disco.OAuthAuthzCodeGrant, credsCtx)
|
||||
diags = diags.Append(confirmDiags)
|
||||
if !confirm {
|
||||
diags = diags.Append(errors.New("Login cancelled"))
|
||||
@@ -538,7 +540,7 @@ func (c *LoginCommand) interactiveGetTokenByCode(hostname svchost.Hostname, cred
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpclient.New())
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpclient.New(ctx))
|
||||
token, err := oauthConfig.Exchange(
|
||||
ctx, code,
|
||||
oauth2.SetAuthURLParam("code_verifier", proofKey),
|
||||
@@ -555,10 +557,10 @@ func (c *LoginCommand) interactiveGetTokenByCode(hostname svchost.Hostname, cred
|
||||
return token, diags
|
||||
}
|
||||
|
||||
func (c *LoginCommand) interactiveGetTokenByPassword(hostname svchost.Hostname, credsCtx *loginCredentialsContext, clientConfig *disco.OAuthClient) (*oauth2.Token, tfdiags.Diagnostics) {
|
||||
func (c *LoginCommand) interactiveGetTokenByPassword(ctx context.Context, hostname svchost.Hostname, credsCtx *loginCredentialsContext, clientConfig *disco.OAuthClient) (*oauth2.Token, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
confirm, confirmDiags := c.interactiveContextConsent(hostname, disco.OAuthOwnerPasswordGrant, credsCtx)
|
||||
confirm, confirmDiags := c.interactiveContextConsent(ctx, hostname, disco.OAuthOwnerPasswordGrant, credsCtx)
|
||||
diags = diags.Append(confirmDiags)
|
||||
if !confirm {
|
||||
diags = diags.Append(errors.New("Login cancelled"))
|
||||
@@ -568,7 +570,7 @@ func (c *LoginCommand) interactiveGetTokenByPassword(hostname svchost.Hostname,
|
||||
c.Ui.Output("\n---------------------------------------------------------------------------------\n")
|
||||
c.Ui.Output("OpenTofu must temporarily use your password to request an API token.\nThis password will NOT be saved locally.\n")
|
||||
|
||||
username, err := c.UIInput().Input(context.Background(), &tofu.InputOpts{
|
||||
username, err := c.UIInput().Input(ctx, &tofu.InputOpts{
|
||||
Id: "username",
|
||||
Query: fmt.Sprintf("Username for %s:", hostname.ForDisplay()),
|
||||
})
|
||||
@@ -576,7 +578,7 @@ func (c *LoginCommand) interactiveGetTokenByPassword(hostname svchost.Hostname,
|
||||
diags = diags.Append(fmt.Errorf("Failed to request username: %w", err))
|
||||
return nil, diags
|
||||
}
|
||||
password, err := c.UIInput().Input(context.Background(), &tofu.InputOpts{
|
||||
password, err := c.UIInput().Input(ctx, &tofu.InputOpts{
|
||||
Id: "password",
|
||||
Query: fmt.Sprintf("Password for %s:", hostname.ForDisplay()),
|
||||
Secret: true,
|
||||
@@ -591,7 +593,7 @@ func (c *LoginCommand) interactiveGetTokenByPassword(hostname svchost.Hostname,
|
||||
Endpoint: clientConfig.Endpoint(),
|
||||
Scopes: clientConfig.Scopes,
|
||||
}
|
||||
token, err := oauthConfig.PasswordCredentialsToken(context.Background(), username, password)
|
||||
token, err := oauthConfig.PasswordCredentialsToken(ctx, username, password)
|
||||
if err != nil {
|
||||
// FIXME: The OAuth2 library generates errors that are not appropriate
|
||||
// for a Terraform end-user audience, so once we have more experience
|
||||
@@ -607,10 +609,10 @@ func (c *LoginCommand) interactiveGetTokenByPassword(hostname svchost.Hostname,
|
||||
return token, diags
|
||||
}
|
||||
|
||||
func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsCtx *loginCredentialsContext, service *url.URL) (svcauth.HostCredentialsToken, tfdiags.Diagnostics) {
|
||||
func (c *LoginCommand) interactiveGetTokenByUI(ctx context.Context, hostname svchost.Hostname, credsCtx *loginCredentialsContext, service *url.URL) (svcauth.HostCredentialsToken, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
confirm, confirmDiags := c.interactiveContextConsent(hostname, disco.OAuthGrantType(""), credsCtx)
|
||||
confirm, confirmDiags := c.interactiveContextConsent(ctx, hostname, disco.OAuthGrantType(""), credsCtx)
|
||||
diags = diags.Append(confirmDiags)
|
||||
if !confirm {
|
||||
diags = diags.Append(errors.New("Login cancelled"))
|
||||
@@ -659,7 +661,7 @@ func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsC
|
||||
}
|
||||
}
|
||||
|
||||
token, err := c.UIInput().Input(context.Background(), &tofu.InputOpts{
|
||||
token, err := c.UIInput().Input(ctx, &tofu.InputOpts{
|
||||
Id: "token",
|
||||
Query: fmt.Sprintf("Token for %s:", hostname.ForDisplay()),
|
||||
Secret: true,
|
||||
@@ -685,7 +687,7 @@ func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsC
|
||||
diags = diags.Append(fmt.Errorf("Failed to create API client: %w", err))
|
||||
return "", diags
|
||||
}
|
||||
user, err := client.Users.ReadCurrent(context.Background())
|
||||
user, err := client.Users.ReadCurrent(ctx)
|
||||
if err == tfe.ErrUnauthorized {
|
||||
diags = diags.Append(fmt.Errorf("Token is invalid: %w", err))
|
||||
return "", diags
|
||||
@@ -698,7 +700,7 @@ func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsC
|
||||
return svcauth.HostCredentialsToken(token), nil
|
||||
}
|
||||
|
||||
func (c *LoginCommand) interactiveContextConsent(hostname svchost.Hostname, grantType disco.OAuthGrantType, credsCtx *loginCredentialsContext) (bool, tfdiags.Diagnostics) {
|
||||
func (c *LoginCommand) interactiveContextConsent(ctx context.Context, hostname svchost.Hostname, grantType disco.OAuthGrantType, credsCtx *loginCredentialsContext) (bool, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
mechanism := "OAuth"
|
||||
if grantType == "" {
|
||||
@@ -724,7 +726,7 @@ func (c *LoginCommand) interactiveContextConsent(hostname svchost.Hostname, gran
|
||||
}
|
||||
}
|
||||
|
||||
v, err := c.UIInput().Input(context.Background(), &tofu.InputOpts{
|
||||
v, err := c.UIInput().Input(ctx, &tofu.InputOpts{
|
||||
Id: "approve",
|
||||
Query: "Do you want to proceed?",
|
||||
Description: `Only 'yes' will be accepted to confirm.`,
|
||||
|
||||
@@ -287,7 +287,7 @@ func (m *Meta) installModules(ctx context.Context, rootDir, testsDir string, upg
|
||||
return true, diags
|
||||
}
|
||||
|
||||
inst := initwd.NewModuleInstaller(m.modulesDir(), loader, m.registryClient(), m.ModulePackageFetcher)
|
||||
inst := initwd.NewModuleInstaller(m.modulesDir(), loader, m.registryClient(ctx), m.ModulePackageFetcher)
|
||||
|
||||
call, vDiags := m.rootModuleCall(ctx, rootDir)
|
||||
diags = diags.Append(vDiags)
|
||||
@@ -324,7 +324,7 @@ func (m *Meta) initDirFromModule(ctx context.Context, targetDir string, addr str
|
||||
}
|
||||
|
||||
targetDir = m.normalizePath(targetDir)
|
||||
moreDiags := initwd.DirFromModule(ctx, loader, targetDir, m.modulesDir(), addr, m.registryClient(), m.ModulePackageFetcher, hooks)
|
||||
moreDiags := initwd.DirFromModule(ctx, loader, targetDir, m.modulesDir(), addr, m.registryClient(ctx), m.ModulePackageFetcher, hooks)
|
||||
diags = diags.Append(moreDiags)
|
||||
if ctx.Err() == context.Canceled {
|
||||
m.showDiagnostics(diags)
|
||||
@@ -441,7 +441,6 @@ func (m *Meta) initConfigLoader() (*configload.Loader, error) {
|
||||
if m.configLoader == nil {
|
||||
loader, err := configload.NewLoader(&configload.Config{
|
||||
ModulesDir: m.modulesDir(),
|
||||
Services: m.Services,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -456,8 +455,8 @@ func (m *Meta) initConfigLoader() (*configload.Loader, error) {
|
||||
}
|
||||
|
||||
// registryClient instantiates and returns a new Registry client.
|
||||
func (m *Meta) registryClient() *registry.Client {
|
||||
return registry.NewClient(m.Services, nil)
|
||||
func (m *Meta) registryClient(ctx context.Context) *registry.Client {
|
||||
return registry.NewClient(ctx, m.Services, nil)
|
||||
}
|
||||
|
||||
// configValueFromCLI parses a configuration value that was provided in a
|
||||
|
||||
@@ -119,7 +119,7 @@ func (c *ProvidersMirrorCommand) Run(args []string) int {
|
||||
// generality of go-getter but it's still handy to use the HTTP getter
|
||||
// as an easy way to download over HTTP into a file on disk.
|
||||
httpGetter := getter.HttpGetter{
|
||||
Client: httpclient.New(),
|
||||
Client: httpclient.New(ctx),
|
||||
Netrc: true,
|
||||
XTerraformGetDisabled: true,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
//
|
||||
// See the documentation of MockLauncher itself for more information.
|
||||
func NewMockLauncher(ctx context.Context) *MockLauncher {
|
||||
client := httpclient.New()
|
||||
client := httpclient.New(ctx)
|
||||
return &MockLauncher{
|
||||
Client: client,
|
||||
Context: ctx,
|
||||
|
||||
@@ -10,10 +10,9 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/terraform-svchost/disco"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/registry"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
)
|
||||
|
||||
// A Loader instance is the main entry-point for loading configurations via
|
||||
@@ -39,12 +38,6 @@ type Config struct {
|
||||
// .terraform/modules directory, in the common case where this package
|
||||
// is being loaded from the main OpenTofu CLI package.)
|
||||
ModulesDir string
|
||||
|
||||
// Services is the service discovery client to use when locating remote
|
||||
// module registry endpoints. If this is nil then registry sources are
|
||||
// not supported, which should be true only in specialized circumstances
|
||||
// such as in tests.
|
||||
Services *disco.Disco
|
||||
}
|
||||
|
||||
// NewLoader creates and returns a loader that reads configuration from the
|
||||
@@ -56,7 +49,6 @@ type Config struct {
|
||||
func NewLoader(config *Config) (*Loader, error) {
|
||||
fs := afero.NewOsFs()
|
||||
parser := configs.NewParser(fs)
|
||||
reg := registry.NewClient(config.Services, nil)
|
||||
|
||||
ret := &Loader{
|
||||
parser: parser,
|
||||
@@ -64,8 +56,6 @@ func NewLoader(config *Config) (*Loader, error) {
|
||||
FS: afero.Afero{Fs: fs},
|
||||
CanInstall: true,
|
||||
Dir: config.ModulesDir,
|
||||
Services: config.Services,
|
||||
Registry: reg,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/terraform-svchost/disco"
|
||||
"github.com/opentofu/opentofu/internal/modsdir"
|
||||
"github.com/opentofu/opentofu/internal/registry"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/modsdir"
|
||||
)
|
||||
|
||||
type moduleMgr struct {
|
||||
@@ -28,15 +27,6 @@ type moduleMgr struct {
|
||||
// Dir is the path where descendent modules are (or will be) installed.
|
||||
Dir string
|
||||
|
||||
// Services is a service discovery client that will be used to find
|
||||
// remote module registry endpoints. This object may be pre-loaded with
|
||||
// cached discovery information.
|
||||
Services *disco.Disco
|
||||
|
||||
// Registry is a client for the module registry protocol, which is used
|
||||
// when a module is requested from a registry source.
|
||||
Registry *registry.Client
|
||||
|
||||
// manifest tracks the currently-installed modules for this manager.
|
||||
//
|
||||
// The loader may read this. Only the installer may write to it, and
|
||||
|
||||
@@ -9,14 +9,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/copy"
|
||||
)
|
||||
@@ -106,22 +103,12 @@ var goGetterGetters = map[string]getter.Getter{
|
||||
"gcs": new(getter.GCSGetter),
|
||||
"git": new(getter.GitGetter),
|
||||
"hg": new(getter.HgGetter),
|
||||
"http": getterHTTPGetter,
|
||||
"https": getterHTTPGetter,
|
||||
"http": nil, // configured dynamically in NewPackageFetcher
|
||||
"https": nil, // configured dynamically in NewPackageFetcher
|
||||
"oci": nil, // configured dynamically using [PackageFetcherEnvironment.OCIRepositoryStore]
|
||||
"s3": new(getter.S3Getter),
|
||||
}
|
||||
|
||||
var getterHTTPClient = &http.Client{
|
||||
Transport: otelhttp.NewTransport(cleanhttp.DefaultTransport()),
|
||||
}
|
||||
|
||||
var getterHTTPGetter = &getter.HttpGetter{
|
||||
Client: getterHTTPClient,
|
||||
Netrc: true,
|
||||
XTerraformGetLimit: 10,
|
||||
}
|
||||
|
||||
// A reusingGetter is a helper for the module installer that remembers
|
||||
// the final resolved addresses of all of the sources it has already been
|
||||
// asked to install, and will copy from a prior installation directory if
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
"github.com/opentofu/opentofu/internal/httpclient"
|
||||
"github.com/opentofu/opentofu/internal/tracing"
|
||||
)
|
||||
|
||||
@@ -40,18 +42,35 @@ type PackageFetcher struct {
|
||||
// It's valid to set "env" to nil, but that will make certain module
|
||||
// package source types unavailable for use and so that concession is
|
||||
// intended only for use in unit tests.
|
||||
func NewPackageFetcher(env PackageFetcherEnvironment) *PackageFetcher {
|
||||
func NewPackageFetcher(ctx context.Context, env PackageFetcherEnvironment) *PackageFetcher {
|
||||
env = preparePackageFetcherEnvironment(env)
|
||||
|
||||
var httpClient = httpclient.New(ctx)
|
||||
|
||||
// We use goGetterGetters as our starting point for the available
|
||||
// getters, but some need to be instantiated dynamically based on
|
||||
// the given "env". We shallow-copy the source map so that multiple
|
||||
// instances of PackageFetcher don't clobber each other's getters.
|
||||
getters := maps.Clone(goGetterGetters)
|
||||
|
||||
// The OCI Distribution getter needs to acquire credentials based on
|
||||
// centrally-configured policy, encapsulated in env.OCIRepositoryStore.
|
||||
getters["oci"] = &ociDistributionGetter{
|
||||
getOCIRepositoryStore: env.OCIRepositoryStore,
|
||||
}
|
||||
|
||||
// The HTTP getter (used for both "http" and "https" schemes) uses
|
||||
// the HTTP client we instantiated above, whose behavior can be
|
||||
// incluenced by the ctx argument we passed to it, such as by
|
||||
// enabling OpenTelemetry tracing when appropriate.
|
||||
var httpGetter = &getter.HttpGetter{
|
||||
Client: httpClient,
|
||||
Netrc: true,
|
||||
XTerraformGetLimit: 10,
|
||||
}
|
||||
getters["http"] = httpGetter
|
||||
getters["https"] = httpGetter
|
||||
|
||||
return &PackageFetcher{
|
||||
getter: newReusingGetter(getters),
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ func findLegacyProviderLookupSource(host svchost.Hostname, source Source) *Regis
|
||||
// depend on it, and this fallback mechanism will likely be removed altogether
|
||||
// in a future OpenTofu version.
|
||||
func (s *RegistrySource) lookupLegacyProviderNamespace(ctx context.Context, hostname svchost.Hostname, typeName string) (string, string, error) {
|
||||
client, err := s.registryClient(hostname)
|
||||
client, err := s.registryClient(ctx, hostname)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ var _ Source = (*HTTPMirrorSource)(nil)
|
||||
// UI/config layer's responsibility to validate this and return a suitable
|
||||
// error message for the end-user audience.)
|
||||
func NewHTTPMirrorSource(baseURL *url.URL, creds svcauth.CredentialsSource) *HTTPMirrorSource {
|
||||
httpClient := httpclient.New()
|
||||
httpClient := httpclient.New(context.TODO())
|
||||
httpClient.Timeout = requestTimeout
|
||||
httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
// If we get redirected more than five times we'll assume we're
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
"github.com/hashicorp/go-getter"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
@@ -52,8 +51,7 @@ func (p PackageHTTPURL) InstallProviderPackage(ctx context.Context, meta Package
|
||||
// files that already exist, etc.)
|
||||
|
||||
retryableClient := retryablehttp.NewClient()
|
||||
retryableClient.HTTPClient = httpclient.New()
|
||||
retryableClient.HTTPClient.Transport = otelhttp.NewTransport(retryableClient.HTTPClient.Transport)
|
||||
retryableClient.HTTPClient = httpclient.New(ctx)
|
||||
retryableClient.RetryMax = maxHTTPPackageRetryCount
|
||||
retryableClient.RequestLogHook = func(logger retryablehttp.Logger, _ *http.Request, i int) {
|
||||
if i > 0 {
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
svchost "github.com/hashicorp/terraform-svchost"
|
||||
svcauth "github.com/hashicorp/terraform-svchost/auth"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
otelAttr "go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -78,8 +77,8 @@ type registryClient struct {
|
||||
httpClient *retryablehttp.Client
|
||||
}
|
||||
|
||||
func newRegistryClient(baseURL *url.URL, creds svcauth.HostCredentials) *registryClient {
|
||||
httpClient := httpclient.New()
|
||||
func newRegistryClient(ctx context.Context, baseURL *url.URL, creds svcauth.HostCredentials) *registryClient {
|
||||
httpClient := httpclient.New(ctx)
|
||||
httpClient.Timeout = requestTimeout
|
||||
|
||||
retryableClient := retryablehttp.NewClient()
|
||||
@@ -88,8 +87,6 @@ func newRegistryClient(baseURL *url.URL, creds svcauth.HostCredentials) *registr
|
||||
retryableClient.RequestLogHook = requestLogHook
|
||||
retryableClient.ErrorHandler = maxRetryErrorHandler
|
||||
|
||||
retryableClient.HTTPClient.Transport = otelhttp.NewTransport(retryableClient.HTTPClient.Transport)
|
||||
|
||||
retryableClient.Logger = log.New(logging.LogOutput(), "", log.Flags())
|
||||
|
||||
return ®istryClient{
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package getproviders
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -29,7 +28,7 @@ func TestConfigureDiscoveryRetry(t *testing.T) {
|
||||
t.Fatalf("expected retry %q, got %q", registryClientDefaultRetry, discoveryRetry)
|
||||
}
|
||||
|
||||
rc := newRegistryClient(nil, nil)
|
||||
rc := newRegistryClient(t.Context(), nil, nil)
|
||||
if rc.httpClient.RetryMax != registryClientDefaultRetry {
|
||||
t.Fatalf("expected client retry %q, got %q",
|
||||
registryClientDefaultRetry, rc.httpClient.RetryMax)
|
||||
@@ -49,7 +48,7 @@ func TestConfigureDiscoveryRetry(t *testing.T) {
|
||||
expected, discoveryRetry)
|
||||
}
|
||||
|
||||
rc := newRegistryClient(nil, nil)
|
||||
rc := newRegistryClient(t.Context(), nil, nil)
|
||||
if rc.httpClient.RetryMax != expected {
|
||||
t.Fatalf("expected client retry %q, got %q",
|
||||
expected, rc.httpClient.RetryMax)
|
||||
@@ -64,7 +63,7 @@ func TestConfigureRegistryClientTimeout(t *testing.T) {
|
||||
defaultRequestTimeout.String(), requestTimeout.String())
|
||||
}
|
||||
|
||||
rc := newRegistryClient(nil, nil)
|
||||
rc := newRegistryClient(t.Context(), nil, nil)
|
||||
if rc.httpClient.HTTPClient.Timeout != defaultRequestTimeout {
|
||||
t.Fatalf("expected client timeout %q, got %q",
|
||||
defaultRequestTimeout.String(), rc.httpClient.HTTPClient.Timeout.String())
|
||||
@@ -84,7 +83,7 @@ func TestConfigureRegistryClientTimeout(t *testing.T) {
|
||||
expected, requestTimeout.String())
|
||||
}
|
||||
|
||||
rc := newRegistryClient(nil, nil)
|
||||
rc := newRegistryClient(t.Context(), nil, nil)
|
||||
if rc.httpClient.HTTPClient.Timeout != expected {
|
||||
t.Fatalf("expected client timeout %q, got %q",
|
||||
expected, rc.httpClient.HTTPClient.Timeout.String())
|
||||
@@ -342,12 +341,12 @@ func TestProviderVersions(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.provider.String(), func(t *testing.T) {
|
||||
client, err := source.registryClient(test.provider.Hostname)
|
||||
client, err := source.registryClient(t.Context(), test.provider.Hostname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
gotVersions, _, err := client.ProviderVersions(context.Background(), test.provider)
|
||||
gotVersions, _, err := client.ProviderVersions(t.Context(), test.provider)
|
||||
|
||||
if err != nil {
|
||||
if test.wantErr == "" {
|
||||
@@ -427,12 +426,12 @@ func TestFindClosestProtocolCompatibleVersion(t *testing.T) {
|
||||
}
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
client, err := source.registryClient(test.provider.Hostname)
|
||||
client, err := source.registryClient(t.Context(), test.provider.Hostname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := client.findClosestProtocolCompatibleVersion(context.Background(), test.provider, test.version)
|
||||
got, err := client.findClosestProtocolCompatibleVersion(t.Context(), test.provider, test.version)
|
||||
|
||||
if err != nil {
|
||||
if test.wantErr == "" {
|
||||
|
||||
@@ -39,7 +39,7 @@ func NewRegistrySource(services *disco.Disco) *RegistrySource {
|
||||
// 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)
|
||||
client, err := s.registryClient(ctx, provider.Hostname)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func (s *RegistrySource) AvailableVersions(ctx context.Context, provider addrs.P
|
||||
// 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)
|
||||
client, err := s.registryClient(ctx, provider.Hostname)
|
||||
if err != nil {
|
||||
return PackageMeta{}, err
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (s *RegistrySource) PackageMeta(ctx context.Context, provider addrs.Provide
|
||||
return client.PackageMeta(ctx, provider, version, target)
|
||||
}
|
||||
|
||||
func (s *RegistrySource) registryClient(hostname svchost.Hostname) (*registryClient, error) {
|
||||
func (s *RegistrySource) registryClient(ctx context.Context, hostname svchost.Hostname) (*registryClient, error) {
|
||||
host, err := s.services.Discover(hostname)
|
||||
if err != nil {
|
||||
return nil, ErrHostUnreachable{
|
||||
@@ -147,7 +147,7 @@ func (s *RegistrySource) registryClient(hostname svchost.Hostname) (*registryCli
|
||||
return nil, fmt.Errorf("failed to retrieve credentials for %s: %w", hostname, err)
|
||||
}
|
||||
|
||||
return newRegistryClient(url, creds), nil
|
||||
return newRegistryClient(ctx, url, creds), nil
|
||||
}
|
||||
|
||||
func (s *RegistrySource) ForDisplay(provider addrs.Provider) string {
|
||||
|
||||
@@ -6,19 +6,61 @@
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
otelTrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/opentofu/opentofu/version"
|
||||
)
|
||||
|
||||
// New returns the DefaultPooledClient from the cleanhttp
|
||||
// package that will also send a OpenTofu User-Agent string.
|
||||
func New() *http.Client {
|
||||
//
|
||||
// If the given context has an active OpenTelemetry trace span associated with
|
||||
// it then the returned client is also configured to collect traces for
|
||||
// outgoing requests. However, those traces will be children of the span
|
||||
// associated with the context passed _in each individual request_, rather
|
||||
// than of the span in the context passed to this function; this function
|
||||
// only checks for the presence of any span as a heuristic for whether the
|
||||
// caller is in a part of the codebase that has OpenTelemetry plumbing in
|
||||
// place, and does not actually make use of any information from that span.
|
||||
func New(ctx context.Context) *http.Client {
|
||||
cli := cleanhttp.DefaultPooledClient()
|
||||
cli.Transport = &userAgentRoundTripper{
|
||||
userAgent: OpenTofuUserAgent(version.Version),
|
||||
inner: cli.Transport,
|
||||
}
|
||||
|
||||
if span := otelTrace.SpanFromContext(ctx); span != nil && span.IsRecording() {
|
||||
// We consider the presence of an active span -- that is, one whose
|
||||
// presence is going to be reported to a trace collector outside of
|
||||
// the OpenTofu process -- as sufficient signal that generating
|
||||
// spans for requests made with the returned client will be useful.
|
||||
//
|
||||
// The following has two important implications:
|
||||
// - Any request made using the returned client will generate an
|
||||
// OpenTelemetry tracing span using the standard semantic conventions
|
||||
// for an outgoing HTTP request. Therefore all requests made with
|
||||
// this client must also be passed a context.Context carrying a
|
||||
// suitable parent span that the request will be reported as a child
|
||||
// of.
|
||||
// - The outgoing request will include trace context metadata using
|
||||
// the conventions from following specification, which would allow
|
||||
// the recieving server to contribute its own child spans to the
|
||||
// trace if it has access to the same collector:
|
||||
//
|
||||
// https://www.w3.org/TR/trace-context/
|
||||
//
|
||||
// We do this only when there seems to be an active span because
|
||||
// otherwise each HTTP request without an active trace context will
|
||||
// cause a separate trace to begin, containing only that HTTP request,
|
||||
// which would create confusing noise for whoever is consuming the
|
||||
// traces.
|
||||
cli.Transport = otelhttp.NewTransport(cli.Transport)
|
||||
}
|
||||
|
||||
return cli
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestNew_userAgent(t *testing.T) {
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%d %s", i, c.expected), func(t *testing.T) {
|
||||
actualUserAgent = ""
|
||||
cli := New()
|
||||
cli := New(t.Context())
|
||||
err := c.request(cli)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestDirFromModule_registry(t *testing.T) {
|
||||
|
||||
hooks := &testInstallHooks{}
|
||||
|
||||
reg := registry.NewClient(nil, nil)
|
||||
reg := registry.NewClient(t.Context(), nil, nil)
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
diags := DirFromModule(context.Background(), loader, dir, modsDir, "hashicorp/module-installer-acctest/aws//examples/main", reg, nil, hooks)
|
||||
assertNoDiagnostics(t, diags)
|
||||
@@ -173,7 +173,7 @@ func TestDirFromModule_submodules(t *testing.T) {
|
||||
// treating an absolute filesystem path as if it were a "remote"
|
||||
// source address, and so we need a real package fetcher but the
|
||||
// way we use it here does not cause it to make network requests.
|
||||
getmodules.NewPackageFetcher(nil),
|
||||
getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
hooks,
|
||||
)
|
||||
assertNoDiagnostics(t, diags)
|
||||
@@ -257,7 +257,7 @@ func TestDirFromModule_submodulesWithProvider(t *testing.T) {
|
||||
// treating an absolute filesystem path as if it were a "remote"
|
||||
// source address, and so we need a real package fetcher but the
|
||||
// way we use it here does not cause it to make network requests.
|
||||
getmodules.NewPackageFetcher(nil),
|
||||
getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
hooks,
|
||||
)
|
||||
|
||||
@@ -325,7 +325,7 @@ func TestDirFromModule_rel_submodules(t *testing.T) {
|
||||
// treating an absolute filesystem path as if it were a "remote"
|
||||
// source address, and so we need a real package fetcher but the
|
||||
// way we use it here does not cause it to make network requests.
|
||||
getmodules.NewPackageFetcher(nil),
|
||||
getmodules.NewPackageFetcher(t.Context(), nil),
|
||||
hooks,
|
||||
)
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
@@ -144,7 +144,7 @@ func TestModuleInstaller_invalidModuleName(t *testing.T) {
|
||||
modulesDir := filepath.Join(dir, ".terraform/modules")
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil), nil)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("expected error")
|
||||
@@ -183,7 +183,7 @@ func TestModuleInstaller_packageEscapeError(t *testing.T) {
|
||||
// the esoteric legacy support for treating an absolute filesystem path
|
||||
// as if it were a "remote package". This should not use any of the
|
||||
// truly-"remote" module sources, even though it technically has access to.
|
||||
inst := NewModuleInstaller(modulesDir, loader, nil, getmodules.NewPackageFetcher(nil))
|
||||
inst := NewModuleInstaller(modulesDir, loader, nil, getmodules.NewPackageFetcher(t.Context(), nil))
|
||||
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
|
||||
if !diags.HasErrors() {
|
||||
@@ -223,7 +223,7 @@ func TestModuleInstaller_explicitPackageBoundary(t *testing.T) {
|
||||
// the esoteric legacy support for treating an absolute filesystem path
|
||||
// as if it were a "remote package". This should not use any of the
|
||||
// truly-"remote" module sources, even though it technically has access to.
|
||||
inst := NewModuleInstaller(modulesDir, loader, nil, getmodules.NewPackageFetcher(nil))
|
||||
inst := NewModuleInstaller(modulesDir, loader, nil, getmodules.NewPackageFetcher(t.Context(), nil))
|
||||
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
|
||||
if diags.HasErrors() {
|
||||
@@ -307,7 +307,7 @@ func TestModuleInstaller_Prerelease(t *testing.T) {
|
||||
modulesDir := filepath.Join(dir, ".terraform/modules")
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil), nil)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
cfg, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
|
||||
if tc.shouldError {
|
||||
@@ -482,7 +482,7 @@ func TestLoaderInstallModules_registry(t *testing.T) {
|
||||
modulesDir := filepath.Join(dir, ".terraform/modules")
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil), nil)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
@@ -643,7 +643,7 @@ func TestLoaderInstallModules_goGetter(t *testing.T) {
|
||||
modulesDir := filepath.Join(dir, ".terraform/modules")
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil), nil)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
@@ -813,7 +813,7 @@ func TestLoadInstallModules_registryFromTest(t *testing.T) {
|
||||
modulesDir := filepath.Join(dir, ".terraform/modules")
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil), nil)
|
||||
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks, configs.RootModuleCallForTesting())
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func LoadConfigForTests(t testing.TB, rootDir string, testsDir string) (*configs
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil), nil)
|
||||
inst := NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
|
||||
call := configs.RootModuleCallForTesting()
|
||||
_, moreDiags := inst.InstallModules(t.Context(), rootDir, testsDir, true, false, ModuleInstallHooksImpl{}, call)
|
||||
|
||||
@@ -25,7 +25,7 @@ func testAnalyzer(t *testing.T, fixtureName string) *Analyzer {
|
||||
configDir := filepath.Join("testdata", fixtureName)
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil), nil)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, instDiags := inst.InstallModules(context.Background(), configDir, "tests", true, false, initwd.ModuleInstallHooksImpl{}, configs.RootModuleCallForTesting())
|
||||
if instDiags.HasErrors() {
|
||||
t.Fatalf("unexpected module installation errors: %s", instDiags.Err().Error())
|
||||
|
||||
@@ -539,7 +539,7 @@ func loadRefactoringFixture(t *testing.T, dir string) (*configs.Config, instance
|
||||
t.Helper()
|
||||
|
||||
loader := configload.NewLoaderForTests(t)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil), nil)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, instDiags := inst.InstallModules(context.Background(), dir, "tests", true, false, initwd.ModuleInstallHooksImpl{}, configs.RootModuleCallForTesting())
|
||||
if instDiags.HasErrors() {
|
||||
t.Fatal(instDiags.Err())
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
svchost "github.com/hashicorp/terraform-svchost"
|
||||
"github.com/hashicorp/terraform-svchost/disco"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
otelAttr "go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
@@ -80,13 +79,13 @@ type Client struct {
|
||||
}
|
||||
|
||||
// NewClient returns a new initialized registry client.
|
||||
func NewClient(services *disco.Disco, client *http.Client) *Client {
|
||||
func NewClient(ctx context.Context, services *disco.Disco, client *http.Client) *Client {
|
||||
if services == nil {
|
||||
services = disco.New()
|
||||
}
|
||||
|
||||
if client == nil {
|
||||
client = httpclient.New()
|
||||
client = httpclient.New(ctx)
|
||||
client.Timeout = requestTimeout
|
||||
}
|
||||
retryableClient := retryablehttp.NewClient()
|
||||
@@ -95,8 +94,6 @@ func NewClient(services *disco.Disco, client *http.Client) *Client {
|
||||
retryableClient.RequestLogHook = requestLogHook
|
||||
retryableClient.ErrorHandler = maxRetryErrorHandler
|
||||
|
||||
retryableClient.HTTPClient.Transport = otelhttp.NewTransport(retryableClient.HTTPClient.Transport)
|
||||
|
||||
logOutput := logging.LogOutput()
|
||||
retryableClient.Logger = log.New(logOutput, "", log.Flags())
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestConfigureDiscoveryRetry(t *testing.T) {
|
||||
t.Fatalf("expected retry %q, got %q", defaultRetry, discoveryRetry)
|
||||
}
|
||||
|
||||
rc := NewClient(nil, nil)
|
||||
rc := NewClient(t.Context(), nil, nil)
|
||||
if rc.client.RetryMax != defaultRetry {
|
||||
t.Fatalf("expected client retry %q, got %q",
|
||||
defaultRetry, rc.client.RetryMax)
|
||||
@@ -52,7 +52,7 @@ func TestConfigureDiscoveryRetry(t *testing.T) {
|
||||
expected, discoveryRetry)
|
||||
}
|
||||
|
||||
rc := NewClient(nil, nil)
|
||||
rc := NewClient(t.Context(), nil, nil)
|
||||
if rc.client.RetryMax != expected {
|
||||
t.Fatalf("expected client retry %q, got %q",
|
||||
expected, rc.client.RetryMax)
|
||||
@@ -67,7 +67,7 @@ func TestConfigureRegistryClientTimeout(t *testing.T) {
|
||||
defaultRequestTimeout.String(), requestTimeout.String())
|
||||
}
|
||||
|
||||
rc := NewClient(nil, nil)
|
||||
rc := NewClient(t.Context(), nil, nil)
|
||||
if rc.client.HTTPClient.Timeout != defaultRequestTimeout {
|
||||
t.Fatalf("expected client timeout %q, got %q",
|
||||
defaultRequestTimeout.String(), rc.client.HTTPClient.Timeout.String())
|
||||
@@ -87,7 +87,7 @@ func TestConfigureRegistryClientTimeout(t *testing.T) {
|
||||
expected, requestTimeout.String())
|
||||
}
|
||||
|
||||
rc := NewClient(nil, nil)
|
||||
rc := NewClient(t.Context(), nil, nil)
|
||||
if rc.client.HTTPClient.Timeout != expected {
|
||||
t.Fatalf("expected client timeout %q, got %q",
|
||||
expected, rc.client.HTTPClient.Timeout.String())
|
||||
@@ -99,7 +99,7 @@ func TestLookupModuleVersions(t *testing.T) {
|
||||
server := test.Registry()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
// test with and without a hostname
|
||||
for _, src := range []string{
|
||||
@@ -143,7 +143,7 @@ func TestInvalidRegistry(t *testing.T) {
|
||||
server := test.Registry()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
src := "non-existent.localhost.localdomain/test-versions/name/provider"
|
||||
modsrc, err := regsrc.ParseModuleSource(src)
|
||||
@@ -160,7 +160,7 @@ func TestRegistryAuth(t *testing.T) {
|
||||
server := test.Registry()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
src := "private/name/provider"
|
||||
mod, err := regsrc.ParseModuleSource(src)
|
||||
@@ -195,7 +195,7 @@ func TestLookupModuleLocationRelative(t *testing.T) {
|
||||
server := test.Registry()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
src := "relative/foo/bar"
|
||||
mod, err := regsrc.ParseModuleSource(src)
|
||||
@@ -231,7 +231,7 @@ func TestAccLookupModuleVersions(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := NewClient(regDisco, nil)
|
||||
s := NewClient(t.Context(), regDisco, nil)
|
||||
resp, err := s.ModuleVersions(context.Background(), modsrc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -265,7 +265,7 @@ func TestLookupLookupModuleError(t *testing.T) {
|
||||
server := test.Registry()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
// this should not be found in the registry
|
||||
src := "bad/local/path"
|
||||
@@ -300,7 +300,7 @@ func TestLookupModuleRetryError(t *testing.T) {
|
||||
server := test.RegistryRetryableErrorsServer()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
src := "example.com/test-versions/name/provider"
|
||||
modsrc, err := regsrc.ParseModuleSource(src)
|
||||
@@ -329,7 +329,7 @@ func TestLookupModuleNoRetryError(t *testing.T) {
|
||||
server := test.RegistryRetryableErrorsServer()
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
src := "example.com/test-versions/name/provider"
|
||||
modsrc, err := regsrc.ParseModuleSource(src)
|
||||
@@ -352,7 +352,7 @@ func TestLookupModuleNoRetryError(t *testing.T) {
|
||||
|
||||
func TestLookupModuleNetworkError(t *testing.T) {
|
||||
server := test.RegistryRetryableErrorsServer()
|
||||
client := NewClient(test.Disco(server), nil)
|
||||
client := NewClient(t.Context(), test.Disco(server), nil)
|
||||
|
||||
// Shut down the server to simulate network failure
|
||||
server.Close()
|
||||
@@ -481,7 +481,7 @@ func TestModuleLocation_readRegistryResponse(t *testing.T) {
|
||||
transport := &testTransport{
|
||||
mockURL: mockServer.URL,
|
||||
}
|
||||
client := NewClient(test.Disco(registryServer), &http.Client{
|
||||
client := NewClient(t.Context(), test.Disco(registryServer), &http.Client{
|
||||
Transport: transport,
|
||||
})
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ func testModuleWithSnapshot(t testing.TB, name string) (*configs.Config, *config
|
||||
// Test modules usually do not refer to remote sources, and for local
|
||||
// sources only this ultimately just records all of the module paths
|
||||
// in a JSON file so that we can load them below.
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil), nil)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, instDiags := inst.InstallModules(context.Background(), dir, "tests", true, false, initwd.ModuleInstallHooksImpl{}, configs.RootModuleCallForTesting())
|
||||
if instDiags.HasErrors() {
|
||||
t.Fatal(instDiags.Err())
|
||||
@@ -121,7 +121,7 @@ func testModuleInline(t testing.TB, sources map[string]string) *configs.Config {
|
||||
// Test modules usually do not refer to remote sources, and for local
|
||||
// sources only this ultimately just records all of the module paths
|
||||
// in a JSON file so that we can load them below.
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil), nil)
|
||||
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(t.Context(), nil, nil), nil)
|
||||
_, instDiags := inst.InstallModules(context.Background(), cfgPath, "tests", true, false, initwd.ModuleInstallHooksImpl{}, configs.RootModuleCallForTesting())
|
||||
if instDiags.HasErrors() {
|
||||
t.Fatal(instDiags.Err())
|
||||
|
||||
Reference in New Issue
Block a user