Skip to content

Commit 0169b7e

Browse files
committed
Protect against provider version data missing when using an un-reattached, un-builtin provider. Add test coverage.
1 parent ddb6287 commit 0169b7e

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

internal/configs/state_store.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ package configs
55

66
import (
77
"fmt"
8+
"os"
89

910
version "github.com/hashicorp/go-version"
1011
"github.com/hashicorp/hcl/v2"
1112
"github.com/hashicorp/hcl/v2/hcldec"
1213
tfaddr "github.com/hashicorp/terraform-registry-address"
1314
"github.com/hashicorp/terraform/internal/addrs"
1415
"github.com/hashicorp/terraform/internal/configs/configschema"
16+
"github.com/hashicorp/terraform/internal/getproviders/reattach"
1517
"github.com/hashicorp/terraform/internal/tfdiags"
1618
"github.com/zclconf/go-cty/cty"
1719
)
@@ -206,10 +208,25 @@ func (b *StateStore) Hash(stateStoreSchema *configschema.Block, providerSchema *
206208
}
207209

208210
var providerVersionString string
209-
if stateStoreProviderVersion != nil {
210-
providerVersionString = stateStoreProviderVersion.String()
211+
if stateStoreProviderVersion == nil {
212+
isReattached, err := reattach.IsProviderReattached(b.ProviderAddr, os.Getenv("TF_REATTACH_PROVIDERS"))
213+
if err != nil {
214+
return 0, diags.Append(fmt.Errorf("Unable to determine if state storage provider is reattached while hashing state store configuration. This is a bug in Terraform and should be reported: %w", err))
215+
}
216+
217+
if (b.ProviderAddr.Hostname != tfaddr.BuiltInProviderHost) &&
218+
!isReattached {
219+
return 0, diags.Append(&hcl.Diagnostic{
220+
Severity: hcl.DiagError,
221+
Summary: "Unable to calculate hash of state store configuration",
222+
Detail: "Provider version data was missing during hash generation. This is a bug in Terraform and should be reported.",
223+
})
224+
}
225+
226+
// Version information can be empty but only if the provider is builtin or unmanaged by Terraform
227+
providerVersionString = ""
211228
} else {
212-
providerVersionString = "" // It's valid for no version to be present if the provider is builtin or reattached
229+
providerVersionString = stateStoreProviderVersion.String()
213230
}
214231

215232
toHash := cty.TupleVal([]cty.Value{

internal/configs/state_store_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,17 @@ func TestStateStore_Hash_errorConditions(t *testing.T) {
269269
},
270270
},
271271
}
272+
exampleProviderVersion := version.Must(version.NewSemver("1.2.3"))
272273

273274
// Cases where an error would occur
274275
cases := map[string]struct {
275276
config hcl.Body
276277
stateStoreSchema *configschema.Block
278+
providerVersion *version.Version
277279
wantErrorString string
278280
}{
279281
"returns errors when the state_store config doesn't match the schema": {
282+
providerVersion: exampleProviderVersion,
280283
stateStoreSchema: exampleStateStoreSchema,
281284
config: configBodyForTest(t, `state_store "foobar_fs" {
282285
provider "foobar" {
@@ -292,6 +295,7 @@ func TestStateStore_Hash_errorConditions(t *testing.T) {
292295
wantErrorString: "Unsupported argument",
293296
},
294297
"returns errors when the provider config doesn't match the schema": {
298+
providerVersion: exampleProviderVersion,
295299
stateStoreSchema: exampleStateStoreSchema,
296300
config: configBodyForTest(t, `state_store "foobar_fs" {
297301
provider "foobar" {
@@ -307,6 +311,7 @@ func TestStateStore_Hash_errorConditions(t *testing.T) {
307311
wantErrorString: "Unsupported argument",
308312
},
309313
"returns an error if the state_store schema includes a provider block": {
314+
providerVersion: exampleProviderVersion,
310315
stateStoreSchema: &configschema.Block{
311316
BlockTypes: map[string]*configschema.NestedBlock{
312317
"provider": {
@@ -332,6 +337,7 @@ func TestStateStore_Hash_errorConditions(t *testing.T) {
332337
wantErrorString: `Protected block name "provider" in state store schema`,
333338
},
334339
"returns an error if the state_store schema includes a provider attribute": {
340+
providerVersion: exampleProviderVersion,
335341
stateStoreSchema: &configschema.Block{
336342
Attributes: map[string]*configschema.Attribute{
337343
"provider": {
@@ -349,6 +355,18 @@ func TestStateStore_Hash_errorConditions(t *testing.T) {
349355
}`),
350356
wantErrorString: `Protected argument name "provider" in state store schema`,
351357
},
358+
"returns an error if the provider version is missing when using a non-builtin, non-reattached provider": {
359+
providerVersion: nil, // No value provided in this test case
360+
stateStoreSchema: exampleStateStoreSchema,
361+
config: configBodyForTest(t, `state_store "foobar_fs" {
362+
provider "foobar" {
363+
foobar = "foobar"
364+
}
365+
path = "mystate.tfstate"
366+
workspace_dir = "foobar"
367+
}`),
368+
wantErrorString: `Provider version data was missing during hash generation`,
369+
},
352370
}
353371

354372
for tn, tc := range cases {
@@ -366,7 +384,7 @@ func TestStateStore_Hash_errorConditions(t *testing.T) {
366384
s.ProviderAddr = tfaddr.NewProvider(tfaddr.DefaultProviderRegistryHost, "hashicorp", "foobar")
367385

368386
// Test Hash method.
369-
_, diags := s.Hash(tc.stateStoreSchema, exampleProviderSchema, version.Must(version.NewSemver("1.2.3")))
387+
_, diags := s.Hash(tc.stateStoreSchema, exampleProviderSchema, tc.providerVersion)
370388
if !diags.HasErrors() {
371389
t.Fatal("expected error but got none")
372390
}

0 commit comments

Comments
 (0)