* refactor: Replace use of prepareInstallerEvents method. This will allow finer control of callbacks when implementing security related features
* feat: Users are prompted to approve a provider used for PSS on first use, and only if downloaded via HTTP.
Prompts include signer details and key ID data.
* test: Users see "Authentication: unauthenticated" in prompt if network mirror doesn't include hashes
They'll see authentication data in all other prompt scenarios. There's no auth when using an fs mirror, but when those are in use we trust the providers already and no prompts are raised.
* refactor: Simplify how we prepare installation event callbacks by defining reused callbacks
* refactor: Remove unused parameters from `getProvidersFromState`
// The provider was not processed in the FetchPackageBegin callback.
// A provider that wasn't downloaded during this init could be because:
// * It was already present from a previous installation.
// * If upgrading, no newer version was available that matched version constraints.
// * Or, the provider is unmanaged/reattached and so download was skipped.
log.Printf("[TRACE] init (getProvidersFromConfig): the state storage provider %s (%q) will not be changed in the dependency lock file after provider installation. Either it was already present and/or there was no available upgrade version that matched version constraints.",config.Module.StateStore.ProviderAddr.Type,config.Module.StateStore.ProviderAddr)
safeInitAction=SafeInitActionProceed
}else{
// The provider was processed in the FetchPackageBegin callback, so either it's being downloaded for the first time, or upgraded.
log.Printf("[TRACE] init (getProvidersFromConfig): the state storage provider %s (%q) will be changed in the dependency lock file during provider installation.",config.Module.StateStore.ProviderAddr.Type,config.Module.StateStore.ProviderAddr)
// If the provider is downloaded from a local source we assume it's safe.
// We don't require presence of the -safe-init flag, or require input from the user to approve its usage.
log.Printf("[TRACE] init (getProvidersFromConfig): the state storage provider %s (%q) is downloaded from a local source, so we consider it safe.",config.Module.StateStore.ProviderAddr.Type,config.Module.StateStore.ProviderAddr)
safeInitAction=SafeInitActionProceed
casegetproviders.PackageHTTPURL:
log.Printf("[DEBUG] init (getProvidersFromConfig): the state storage provider %s (%q) is downloaded via HTTP, so we consider it potentially unsafe.",config.Module.StateStore.ProviderAddr.Type,config.Module.StateStore.ProviderAddr)
safeInitAction=SafeInitActionPromptForInput
default:
panic(fmt.Sprintf("init (getProvidersFromConfig): unexpected provider location type for state storage provider %q: %T",config.Module.StateStore.ProviderAddr,location))
// prepareInstallerEvents returns an instance of *providercache.InstallerEvents. This struct defines callback functions that will be executed
// when a specific type of event occurs during provider installation.
// The calling code needs to provide a tfdiags.Diagnostics collection, so that provider installation code returns diags to the calling code using closures
// We might be able to suggest an alternative provider to use
// instead of this one.
suggestion:=fmt.Sprintf("\n\nAll modules should specify their required_providers so that external consumers will get the correct providers when using a module. To see which modules are currently depending on %s, run the following command:\n terraform providers",provider.ForDisplay())
"\n\nDid you intend to use %s? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on %s, run the following command:\n terraform providers",
alternative.ForDisplay(),provider.ForDisplay(),
)
}
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to query available provider packages",
fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s",
// If a user copies the URL of a GitHub repository into
// the source argument and removes the schema to make it
// provider-address-shaped then that's one way we can end up
// here. We'll use a specialized error message in anticipation
// of that mistake. We only do this if github.com isn't a
// provider registry, to allow for the (admittedly currently
// rather unlikely) possibility that github.com starts being
// a real Terraform provider registry in the future.
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider registry host",
fmt.Sprintf("The given source address %q specifies a GitHub repository rather than a Terraform provider. Refer to the documentation of the provider to find the correct source address to use.",
provider.String(),
),
))
caseerrorTy.HasOtherVersion:
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider registry host",
fmt.Sprintf("The host %q given in provider source address %q does not offer a Terraform provider registry that is compatible with this Terraform version, but it may be compatible with a different Terraform version.",
errorTy.Hostname,provider.String(),
),
))
default:
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider registry host",
fmt.Sprintf("The host %q given in provider source address %q does not offer a Terraform provider registry.",
errorTy.Hostname,provider.String(),
),
))
}
casegetproviders.ErrRequestCanceled:
// We don't attribute cancellation to any particular operation,
// but rather just emit a single general message about it at
// the end, by checking ctx.Err().
default:
suggestion:=fmt.Sprintf("\n\nTo see which modules are currently depending on %s and what versions are specified, run the following command:\n terraform providers",provider.ForDisplay())
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to query available provider packages",
fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s",
// If we're installing from a mirror then it may just be
// the mirror lacking the package, rather than it being
// unavailable from upstream.
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
summaryIncompatible,
fmt.Sprintf(
"Your chosen provider mirror at %s does not have a %s v%s package available for your current platform, %s.\n\nProvider releases are separate from Terraform CLI releases, so this provider might not support your current platform. Alternatively, the mirror itself might have only a subset of the plugin packages available in the origin registry, at %s.",
"Provider %s v%s does not have a package available for your current platform, %s.\n\nProvider releases are separate from Terraform CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.",
err.Provider,err.Version,err.Platform,
),
))
}
casegetproviders.ErrRequestCanceled:
// We don't attribute cancellation to any particular operation,
// but rather just emit a single general message about it at
// the end, by checking ctx.Err().
default:
// We can potentially end up in here under cancellation too,
// in spite of our getproviders.ErrRequestCanceled case above,
// because not all of the outgoing requests we do under the
// "fetch package" banner are source metadata requests.
// In that case we will emit a redundant error here about
// the request being cancelled, but we'll still detect it
// as a cancellation after the installer returns and do the
// normal cancellation handling.
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to install provider",
fmt.Sprintf("Error while installing %s v%s: %s",provider.ForDisplay(),version,err),
// We might be able to suggest an alternative provider to use
// instead of this one.
suggestion:=fmt.Sprintf("\n\nAll modules should specify their required_providers so that external consumers will get the correct providers when using a module. To see which modules are currently depending on %s, run the following command:\n terraform providers",provider.ForDisplay())
"\n\nDid you intend to use %s? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on %s, run the following command:\n terraform providers",
alternative.ForDisplay(),provider.ForDisplay(),
)
}
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to query available provider packages",
fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s",
// If a user copies the URL of a GitHub repository into
// the source argument and removes the schema to make it
// provider-address-shaped then that's one way we can end up
// here. We'll use a specialized error message in anticipation
// of that mistake. We only do this if github.com isn't a
// provider registry, to allow for the (admittedly currently
// rather unlikely) possibility that github.com starts being
// a real Terraform provider registry in the future.
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider registry host",
fmt.Sprintf("The given source address %q specifies a GitHub repository rather than a Terraform provider. Refer to the documentation of the provider to find the correct source address to use.",
provider.String(),
),
))
caseerrorTy.HasOtherVersion:
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider registry host",
fmt.Sprintf("The host %q given in provider source address %q does not offer a Terraform provider registry that is compatible with this Terraform version, but it may be compatible with a different Terraform version.",
errorTy.Hostname,provider.String(),
),
))
default:
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider registry host",
fmt.Sprintf("The host %q given in provider source address %q does not offer a Terraform provider registry.",
errorTy.Hostname,provider.String(),
),
))
}
casegetproviders.ErrRequestCanceled:
// We don't attribute cancellation to any particular operation,
// but rather just emit a single general message about it at
// the end, by checking ctx.Err().
default:
suggestion:=fmt.Sprintf("\n\nTo see which modules are currently depending on %s and what versions are specified, run the following command:\n terraform providers",provider.ForDisplay())
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to query available provider packages",
fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s",
provider.ForDisplay(),err,suggestion,
),
))
}
}
}
// Returns a reused callback function for the QueryPackagesWarning event in a providercache.InstallerEvents struct.
// If we're installing from a mirror then it may just be
// the mirror lacking the package, rather than it being
// unavailable from upstream.
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
summaryIncompatible,
fmt.Sprintf(
"Your chosen provider mirror at %s does not have a %s v%s package available for your current platform, %s.\n\nProvider releases are separate from Terraform CLI releases, so this provider might not support your current platform. Alternatively, the mirror itself might have only a subset of the plugin packages available in the origin registry, at %s.",
"Provider %s v%s does not have a package available for your current platform, %s.\n\nProvider releases are separate from Terraform CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.",
err.Provider,err.Version,err.Platform,
),
))
}
casegetproviders.ErrRequestCanceled:
// We don't attribute cancellation to any particular operation,
// but rather just emit a single general message about it at
// the end, by checking ctx.Err().
default:
// We can potentially end up in here under cancellation too,
// in spite of our getproviders.ErrRequestCanceled case above,
// because not all of the outgoing requests we do under the
// "fetch package" banner are source metadata requests.
// In that case we will emit a redundant error here about
// the request being cancelled, but we'll still detect it
// as a cancellation after the installer returns and do the
// normal cancellation handling.
*diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to install provider",
fmt.Sprintf("Error while installing %s v%s: %s",provider.ForDisplay(),version,err),
))
}
}
}
// Returns a reused callback function for the FetchPackageSuccess event in a providercache.InstallerEvents struct.
// Handle SafeInitActionInvalid or unexpected action types
panic(fmt.Sprintf("When installing providers described in the config Terraform couldn't determine what 'safe init' action should be taken and returned action type %T. This is a bug in Terraform and should be reported.",safeInitAction))
}
// The init command is not allowed to upgrade the provider used for state storage (unless we're reconfiguring the state store).
// Unless users choose to reconfigure, they must upgrade the state store provider separately using `terraform state migrate -upgrade`.
Pleaseuse\"terraform state migrate -upgrade\" to upgrade the state store provider and navigate migrating your state between the two versions. You can then re-attempt \"terraform init -upgrade\"toupgradetherestofyourproviders.
@ -332,7 +356,7 @@ If you do not intend to upgrade the state store provider, please update your con