Skip to content

Commit c783438

Browse files
committed
Allow init commands to succeed if using PSS and a reattached provider.
1 parent 2cf7e93 commit c783438

File tree

1 file changed

+67
-18
lines changed

1 file changed

+67
-18
lines changed

internal/command/meta_backend.go

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"fmt"
1515
"log"
1616
"maps"
17+
"os"
1718
"path/filepath"
1819
"slices"
1920
"strconv"
@@ -1604,33 +1605,58 @@ func (m *Meta) stateStore_C_s(c *configs.StateStore, stateStoreHash int, provide
16041605
// If we're handling the builtin "terraform" provider then there's no version information to store in the dependency lock file, so don't access it.
16051606
// We must record a value into the backend state file, and we cannot include a value that changes (e.g. the Terraform core binary version) as migration
16061607
// is impossible with builtin providers.
1607-
// So, we use a hardcoded version number of 42.
1608-
var err error
1609-
pVersion, err = version.NewVersion("0.42.0")
1608+
// So, we use an arbitrary stand-in version.
1609+
standInVersion, err := version.NewVersion("0.0.1")
16101610
if err != nil {
1611-
diags = diags.Append(fmt.Errorf("Error when creating a backend state file containing a builtin provider. This is a bug in Terraform and should be reported: %w",
1611+
diags = diags.Append(fmt.Errorf("Error when creating a backend state file. This is a bug in Terraform and should be reported: %w",
16121612
err))
16131613
return nil, diags
16141614
}
1615+
pVersion = standInVersion
16151616
} else {
1616-
pLock := opts.Locks.Provider(c.ProviderAddr)
1617-
if pLock == nil {
1618-
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.",
1619-
c.Provider.Name,
1620-
c.ProviderAddr,
1621-
c.Type))
1622-
return nil, diags
1623-
}
1624-
var err error
1625-
pVersion, err = providerreqs.GoVersionFromVersion(pLock.Version())
1617+
isReattached, err := isProviderReattached(c.ProviderAddr)
16261618
if err != nil {
1627-
diags = diags.Append(fmt.Errorf("Failed obtain the in-use version of provider %s (%q) when recording backend state for state store %q. This is a bug in Terraform and should be reported: %w",
1628-
c.Provider.Name,
1629-
c.ProviderAddr,
1630-
c.Type,
1619+
diags = diags.Append(fmt.Errorf("Error determining if the state storage provider is reattached or not. This is a bug in Terraform and should be reported: %w",
16311620
err))
16321621
return nil, diags
16331622
}
1623+
if isReattached {
1624+
// If the provider is unmanaged then it won't be in the locks.
1625+
// If there are no locks then there's no version information to for us to access and use when creating the backend state file.
1626+
// So, we use an arbitrary stand-in version.
1627+
diags = diags.Append(&hcl.Diagnostic{
1628+
Severity: hcl.DiagWarning,
1629+
Summary: "State storage provider is not managed by Terraform",
1630+
Detail: "Terraform is using a provider supplied via TF_REATTACH_PROVIDERS for initializing state storage. This will affect Terraform's ability to detect when state migrations are required.",
1631+
})
1632+
standInVersion, err := version.NewVersion("0.0.1")
1633+
if err != nil {
1634+
diags = diags.Append(fmt.Errorf("Error when creating a backend state file. This is a bug in Terraform and should be reported: %w",
1635+
err))
1636+
return nil, diags
1637+
}
1638+
pVersion = standInVersion
1639+
} else {
1640+
// The provider is not built in and is being managed by Terraform
1641+
// This is the most common scenario, by far.
1642+
pLock := opts.Locks.Provider(c.ProviderAddr)
1643+
if pLock == nil {
1644+
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.",
1645+
c.Provider.Name,
1646+
c.ProviderAddr,
1647+
c.Type))
1648+
return nil, diags
1649+
}
1650+
pVersion, err = providerreqs.GoVersionFromVersion(pLock.Version())
1651+
if err != nil {
1652+
diags = diags.Append(fmt.Errorf("Failed obtain the in-use version of provider %s (%q) when recording backend state for state store %q. This is a bug in Terraform and should be reported: %w",
1653+
c.Provider.Name,
1654+
c.ProviderAddr,
1655+
c.Type,
1656+
err))
1657+
return nil, diags
1658+
}
1659+
}
16341660
}
16351661
s.StateStore = &workdir.StateStoreConfigState{
16361662
Type: c.Type,
@@ -1715,6 +1741,29 @@ func (m *Meta) stateStore_C_s(c *configs.StateStore, stateStoreHash int, provide
17151741
return b, diags
17161742
}
17171743

1744+
// isProviderReattached determines if a given provider is being supplied to Terraform via the TF_REATTACH_PROVIDERS
1745+
// environment variable.
1746+
func isProviderReattached(provider addrs.Provider) (bool, error) {
1747+
in := os.Getenv("TF_REATTACH_PROVIDERS")
1748+
if in != "" {
1749+
var m map[string]any
1750+
err := json.Unmarshal([]byte(in), &m)
1751+
if err != nil {
1752+
return false, fmt.Errorf("Invalid format for TF_REATTACH_PROVIDERS: %w", err)
1753+
}
1754+
for p, _ := range m {
1755+
a, diags := addrs.ParseProviderSourceString(p)
1756+
if diags.HasErrors() {
1757+
return false, fmt.Errorf("Error parsing %q as a provider address: %w", a, diags.Err())
1758+
}
1759+
if a.Equals(provider) {
1760+
return true, nil
1761+
}
1762+
}
1763+
}
1764+
return false, nil
1765+
}
1766+
17181767
// createDefaultWorkspace receives a backend made using a pluggable state store, and details about that store's config,
17191768
// and persists an empty state file in the default workspace. By creating this artifact we ensure that the default
17201769
// workspace is created and usable by Terraform in later operations.

0 commit comments

Comments
 (0)