diff --git a/.changes/backported/BUG FIXES-20250206-145217.yaml b/.changes/backported/BUG FIXES-20250206-145217.yaml new file mode 100644 index 000000000000..c685869a442d --- /dev/null +++ b/.changes/backported/BUG FIXES-20250206-145217.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'backends: Fix crash when interrupting during interactive prompt for values' +time: 2025-02-06T14:52:17.033964+01:00 +custom: + Issue: "36448" diff --git a/internal/backend/backendbase/base.go b/internal/backend/backendbase/base.go index 98890d2a179f..4dcce8db9c1b 100644 --- a/internal/backend/backendbase/base.go +++ b/internal/backend/backendbase/base.go @@ -57,6 +57,13 @@ func (b Base) ConfigSchema() *configschema.Block { func (b Base) PrepareConfig(configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics + if configVal.IsNull() { + // We expect the backend configuration to be an object, so if it's + // null for some reason (e.g. because of an interrupt), we'll turn + // it into an empty object so that we can still coerce it + configVal = cty.EmptyObjectVal + } + schema := b.Schema v, err := schema.CoerceValue(configVal) diff --git a/internal/backend/backendbase/base_test.go b/internal/backend/backendbase/base_test.go index 800bf7901b19..1ee4fda2ac13 100644 --- a/internal/backend/backendbase/base_test.go +++ b/internal/backend/backendbase/base_test.go @@ -214,3 +214,41 @@ func TestBase_deprecatedArg(t *testing.T) { } }) } + +func TestBase_nullCrash(t *testing.T) { + // This test ensures that we don't crash while applying defaults to + // a null value + + b := Base{ + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + SDKLikeDefaults: SDKLikeDefaults{ + "foo": { + Fallback: "fallback", + }, + }, + } + + t.Run("error", func(t *testing.T) { + // We pass an explicit null value here to simulate an interrupt + _, gotDiags := b.PrepareConfig(cty.NullVal(cty.Object(map[string]cty.Type{ + "foo": cty.String, + }))) + var wantDiags tfdiags.Diagnostics + wantDiags = wantDiags.Append( + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid backend configuration", + Detail: "The backend configuration is incorrect: attribute \"foo\" is required.", + }) + if diff := cmp.Diff(wantDiags.ForRPC(), gotDiags.ForRPC()); diff != "" { + t.Errorf("wrong diagnostics\n%s", diff) + } + }) +}