From 4d55e2604f46da254daa942d385a3c0dfbe38ffd Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 17 May 2021 16:09:24 -0400 Subject: [PATCH] save apply diagnostics on backend failure Make sure to collect apply diagnostics before persisting the backend state so runtime errors are not lost. --- internal/backend/local/backend_apply.go | 2 +- internal/backend/local/backend_apply_test.go | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/internal/backend/local/backend_apply.go b/internal/backend/local/backend_apply.go index 519157419..7d68006be 100644 --- a/internal/backend/local/backend_apply.go +++ b/internal/backend/local/backend_apply.go @@ -165,6 +165,7 @@ func (b *Local) opApply( if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState, op.View) { return } + diags = diags.Append(applyDiags) // Store the final state runningOp.State = applyState @@ -183,7 +184,6 @@ func (b *Local) opApply( return } - diags = diags.Append(applyDiags) if applyDiags.HasErrors() { op.ReportResult(runningOp, diags) return diff --git a/internal/backend/local/backend_apply_test.go b/internal/backend/local/backend_apply_test.go index e2f7314e2..07d5f8a6e 100644 --- a/internal/backend/local/backend_apply_test.go +++ b/internal/backend/local/backend_apply_test.go @@ -212,10 +212,14 @@ func TestLocal_applyBackendFail(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", applyFixtureSchema()) - p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("yes"), - "ami": cty.StringVal("bar"), - })} + + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ + NewState: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("yes"), + "ami": cty.StringVal("bar"), + }), + Diagnostics: tfdiags.Diagnostics.Append(nil, errors.New("error before backend failure")), + } wd, err := os.Getwd() if err != nil { @@ -245,14 +249,19 @@ func TestLocal_applyBackendFail(t *testing.T) { } diagErr := output.Stderr() + if !strings.Contains(diagErr, "Error saving state: fake failure") { t.Fatalf("missing \"fake failure\" message in diags:\n%s", diagErr) } + if !strings.Contains(diagErr, "error before backend failure") { + t.Fatalf("missing 'error before backend failure' diagnostic from apply") + } + // The fallback behavior should've created a file errored.tfstate in the // current working directory. checkState(t, "errored.tfstate", ` -test_instance.foo: +test_instance.foo: (tainted) ID = yes provider = provider["registry.terraform.io/hashicorp/test"] ami = bar