* refactor: Check that the state storage provider is present when beginning to initialise a state store for use in a non-init command. Ensure reattached providers can be used.
Previously we passed all required providers into backend options to be used within `stateStoreConfig`, which is invoked via (Meta).Backend. The new approach enforces that the provider is present while assembling the backend options passed to (Meta).Backend from (Meta).backend, which is non-init specific. As this code is defending against users running non-init commands before an init, this place feels appropriate and isn't able to impact the init command.
* fix: Reattached PSS providers should return early when checking locks, and an empty locks file is only bad if there isn't a reattached PSS provider
* test: Assert that running init with reattached PSS provider is ok, via an E2E test that uses the reattach feature.
* fix: Allow builtin or reattached providers to be used for state stores when generating a plan file
* test: Expand E2E test to show using a reattached provider can be used for a workflow of init, plan with -out, and apply.
* chore: Replace 'io/ioutil' and format code in unmanaged e2e tests
// This should never happen as the user would've already hit
// an error earlier prompting them to run init
diags=diags.Append(fmt.Errorf("The provider %s (%q) is not present in the lockfile, despite being used for state store %q. This is a bug in Terraform and should be reported.",
returnnil,fmt.Errorf("Unable to determine if state storage provider is reattached while saving state store data to a plan file. This is a bug in Terraform and should be reported: %w",err)
}
varproviderVersion*version.Version
ifs.Provider.Source.IsBuiltIn()||isReattached{
// For built-in providers and reattached providers, we don't require version information to be present in the state file, so we should be tolerant of it being missing. In this case we can just use a placeholder version that will never actually be used for anything, but allows us to avoid returning an error when trying to save state store data to a plan file.
// The "overridden" case is for unusual special situations like
// dev overrides, so we'll explicitly note it in the logs just in
// case we see bug reports with these active and it helps us
// understand why we ended up using the "wrong" plugin.
log.Printf("[DEBUG] StateStore.VerifyDependencySelections: skipping %s because it's overridden by a special configuration setting",providerAddr)
continue
}
if!depsfile.ProviderIsLockable(ss.ProviderAddr){
// If it's not lockable we don't raise errors about it not being in the lock file!
returndiags
}
varlock*depsfile.ProviderLock
ifdepLocks!=nil{// Should always be true in main code, but unfortunately sometimes not true in old tests that don't fill out arguments completely
lock=depLocks.Provider(providerAddr)
}
iflock==nil{
log.Printf("[TRACE] StateStore.VerifyDependencySelections: provider %s has no lock file entry to satisfy %q",providerAddr,providerreqs.VersionConstraintsString(constraints))
errs=append(errs,fmt.Errorf("provider %s: required by this configuration but no version is selected",providerAddr))
continue
}
ifdepLocks.ProviderIsOverridden(ss.ProviderAddr){
// The "overridden" case is for unusual special situations like
// dev overrides, so we'll explicitly note it in the logs just in
// case we see bug reports with these active and it helps us
// understand why we ended up using the "wrong" plugin.
log.Printf("[DEBUG] StateStore.VerifyDependencySelection: skipping %s because it's overridden by a special configuration setting",ss.ProviderAddr)
log.Printf("[TRACE] StateStore.VerifyDependencySelections: provider %s has %s to satisfy %q",providerAddr,selectedVersion.String(),providerreqs.VersionConstraintsString(constraints))
if!allowedVersions.Has(selectedVersion){
// The most likely cause of this is that the author of a module
// has changed its constraints, but this could also happen in
// some other unusual situations, such as the user directly
// editing the lock file to record something invalid. We'll
// distinguish those cases here in order to avoid the more
// specific error message potentially being a red herring in
errs=append(errs,fmt.Errorf("provider %s: locked version selection %s doesn't match the updated version constraints %q",providerAddr,selectedVersion.String(),currentConstraints))
default:
errs=append(errs,fmt.Errorf("provider %s: version constraints %q don't match the locked version selection %s",providerAddr,currentConstraints,selectedVersion.String()))
returndiags.Append(fmt.Errorf("Unable to determine if state storage provider is reattached while verifying required_providers are available to launch a state store. This is a bug in Terraform and should be reported: %w",err))
}
ifisReattached{
// Having an empty lock file may be valid if the only provider used is a re-attached provider in use for the state store that's receiver for this method.
// An empty lock file might be an issue if other providers are used, but we'll let existing downstream code handle that.
//
// Note this in the logs to help with any bug reports.
log.Printf("[DEBUG] StateStore.VerifyDependencySelection: skipping %s because it's not managed by Terraform",ss.ProviderAddr)
returndiags
}
// Return multiple errors in an arbitrary-but-deterministic order.
sort.Slice(errs,func(i,jint)bool{
returnerrs[i].Error()<errs[j].Error()
})
returnerrs
// From this point on the state storage provider should be present in the lock file, and the lock file should not be empty or missing.
// The provider used for state storage is not in the required providers list.
// This should have been identified when the block was parsed, so if we get here
// it suggests that upstream code is swallowing that error.
panic("State store provider is missing from required providers but this was not caught during config parsing, which is a bug in Terraform; please report it!")
}
// Is the provider in the lock file, and is it an appropriate version matching the constraints in required_providers?
log.Printf("[TRACE] StateStore.VerifyDependencySelections: provider %s has no lock file entry to satisfy %q",ss.ProviderAddr,providerreqs.VersionConstraintsString(constraints))
log.Printf("[TRACE] StateStore.VerifyDependencySelection: provider %s has %s to satisfy %q",ss.ProviderAddr,selectedVersion.String(),providerreqs.VersionConstraintsString(constraints))