Merge pull request #27786 from hashicorp/alisdair/refresh-empty-diag

backend/local: Return diag for refresh empty state
This commit is contained in:
Alisdair McDiarmid 2021-02-16 09:05:50 -05:00 committed by GitHub
commit 1301e38caa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 18 deletions

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"strings"
"github.com/hashicorp/errwrap" "github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
@ -66,14 +65,11 @@ func (b *Local) opRefresh(
// Set our state // Set our state
runningOp.State = opState.State() runningOp.State = opState.State()
if !runningOp.State.HasResources() { if !runningOp.State.HasResources() {
if b.CLI != nil { diags = diags.Append(tfdiags.Sourceless(
diags = diags.Append(tfdiags.Sourceless( tfdiags.Warning,
tfdiags.Warning, "Empty or non-existent state",
"Empty or non-existent state", "There are currently no resources tracked in the state, so there is nothing to refresh.",
"There are currently no resources tracked in the state, so there is nothing to refresh.", ))
))
b.CLI.Output(b.Colorize().Color(strings.TrimSpace(refreshNoState) + "\n"))
}
} }
// Perform the refresh in a goroutine so we can be interrupted // Perform the refresh in a goroutine so we can be interrupted
@ -90,7 +86,7 @@ func (b *Local) opRefresh(
return return
} }
// write the resulting state to the running op // Write the resulting state to the running op
runningOp.State = newState runningOp.State = newState
diags = diags.Append(refreshDiags) diags = diags.Append(refreshDiags)
if refreshDiags.HasErrors() { if refreshDiags.HasErrors() {
@ -104,12 +100,7 @@ func (b *Local) opRefresh(
op.ReportResult(runningOp, diags) op.ReportResult(runningOp, diags)
return return
} }
// Show any remaining warnings before exiting
op.ReportResult(runningOp, diags)
} }
const refreshNoState = `
[reset][bold][yellow]Empty or non-existent state file.[reset][yellow]
Refresh will do nothing. Refresh does not error or return an erroneous
exit status because many automation scripts use refresh, plan, then apply
and may not have a state file yet for the first run.
`

View File

@ -3,6 +3,7 @@ package local
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
@ -214,6 +215,42 @@ func TestLocal_refresh_context_error(t *testing.T) {
assertBackendStateUnlocked(t, b) assertBackendStateUnlocked(t, b)
} }
func TestLocal_refreshEmptyState(t *testing.T) {
b, cleanup := TestLocal(t)
defer cleanup()
p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
testStateFile(t, b.StatePath, states.NewState())
p.ReadResourceFn = nil
p.ReadResourceResponse = &providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yes"),
})}
op, configCleanup := testOperationRefresh(t, "./testdata/refresh")
defer configCleanup()
record, playback := testRecordDiagnostics(t)
op.ShowDiagnostics = record
run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("bad: %s", err)
}
<-run.Done()
diags := playback()
if diags.HasErrors() {
t.Fatalf("expected only warning diags, got errors: %s", diags.Err())
}
if got, want := diags.ErrWithWarnings().Error(), "Empty or non-existent state"; !strings.Contains(got, want) {
t.Errorf("wrong diags\n got: %s\nwant: %s", got, want)
}
// the backend should be unlocked after a run
assertBackendStateUnlocked(t, b)
}
func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, func()) { func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, func()) {
t.Helper() t.Helper()