save apply diagnostics on backend failure

Make sure to collect apply diagnostics before persisting the backend
state so runtime errors are not lost.
This commit is contained in:
James Bardin 2021-05-17 16:09:24 -04:00
parent d35bc05312
commit 4d55e2604f
2 changed files with 15 additions and 6 deletions

View File

@ -165,6 +165,7 @@ func (b *Local) opApply(
if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState, op.View) { if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState, op.View) {
return return
} }
diags = diags.Append(applyDiags)
// Store the final state // Store the final state
runningOp.State = applyState runningOp.State = applyState
@ -183,7 +184,6 @@ func (b *Local) opApply(
return return
} }
diags = diags.Append(applyDiags)
if applyDiags.HasErrors() { if applyDiags.HasErrors() {
op.ReportResult(runningOp, diags) op.ReportResult(runningOp, diags)
return return

View File

@ -212,10 +212,14 @@ func TestLocal_applyBackendFail(t *testing.T) {
defer cleanup() defer cleanup()
p := TestLocalProvider(t, b, "test", applyFixtureSchema()) p := TestLocalProvider(t, b, "test", applyFixtureSchema())
p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yes"), p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{
"ami": cty.StringVal("bar"), 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() wd, err := os.Getwd()
if err != nil { if err != nil {
@ -245,14 +249,19 @@ func TestLocal_applyBackendFail(t *testing.T) {
} }
diagErr := output.Stderr() diagErr := output.Stderr()
if !strings.Contains(diagErr, "Error saving state: fake failure") { if !strings.Contains(diagErr, "Error saving state: fake failure") {
t.Fatalf("missing \"fake failure\" message in diags:\n%s", diagErr) 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 // The fallback behavior should've created a file errored.tfstate in the
// current working directory. // current working directory.
checkState(t, "errored.tfstate", ` checkState(t, "errored.tfstate", `
test_instance.foo: test_instance.foo: (tainted)
ID = yes ID = yes
provider = provider["registry.terraform.io/hashicorp/test"] provider = provider["registry.terraform.io/hashicorp/test"]
ami = bar ami = bar