backend/local: allow refresh on empty/non-existent state
This allows a refresh on a non-existent or empty state file. We changed this in 0.9.0 to error which seemed reasonable but it turns out this complicates automation that runs refresh since it now needed to determine if the state file was empty before running. Its easier to just revert this into a warning with exit code zero. The reason this changed is because in 0.8.x and earlier, the output would be simply empty with exit code zero which seemed odd.
This commit is contained in:
parent
ead7a3758b
commit
2be1f55cbb
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
@ -22,24 +23,17 @@ func (b *Local) opRefresh(
|
|||
if b.Backend == nil {
|
||||
if _, err := os.Stat(b.StatePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
runningOp.Err = fmt.Errorf(
|
||||
"The Terraform state file for your infrastructure does not\n"+
|
||||
"exist. The 'refresh' command only works and only makes sense\n"+
|
||||
"when there is existing state that Terraform is managing. Please\n"+
|
||||
"double-check the value given below and try again. If you\n"+
|
||||
"haven't created infrastructure with Terraform yet, use the\n"+
|
||||
"'terraform apply' command.\n\n"+
|
||||
"Path: %s",
|
||||
b.StatePath)
|
||||
return
|
||||
err = nil
|
||||
}
|
||||
|
||||
runningOp.Err = fmt.Errorf(
|
||||
"There was an error reading the Terraform state that is needed\n"+
|
||||
"for refreshing. The path and error are shown below.\n\n"+
|
||||
"Path: %s\n\nError: %s",
|
||||
b.StatePath, err)
|
||||
return
|
||||
if err != nil {
|
||||
runningOp.Err = fmt.Errorf(
|
||||
"There was an error reading the Terraform state that is needed\n"+
|
||||
"for refreshing. The path and error are shown below.\n\n"+
|
||||
"Path: %s\n\nError: %s",
|
||||
b.StatePath, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,6 +68,12 @@ func (b *Local) opRefresh(
|
|||
|
||||
// Set our state
|
||||
runningOp.State = opState.State()
|
||||
if runningOp.State.Empty() || !runningOp.State.HasResources() {
|
||||
if b.CLI != nil {
|
||||
b.CLI.Output(b.Colorize().Color(
|
||||
strings.TrimSpace(refreshNoState) + "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
// Perform operation and write the resulting state to the running op
|
||||
newState, err := tfCtx.Refresh()
|
||||
|
@ -93,3 +93,11 @@ func (b *Local) opRefresh(
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
`
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/copy"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
@ -59,6 +60,37 @@ func TestRefresh(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRefresh_empty(t *testing.T) {
|
||||
// Create a temporary working directory that is empty
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("refresh-empty"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &RefreshCommand{
|
||||
Meta: Meta{
|
||||
ContextOpts: testCtxConfig(p),
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
p.RefreshFn = nil
|
||||
p.RefreshReturn = &terraform.InstanceState{ID: "yes"}
|
||||
|
||||
args := []string{
|
||||
td,
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
if p.RefreshCalled {
|
||||
t.Fatal("refresh should not be called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefresh_lockedState(t *testing.T) {
|
||||
state := testState()
|
||||
statePath := testStateFile(t, state)
|
||||
|
@ -96,25 +128,6 @@ func TestRefresh_lockedState(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRefresh_badState(t *testing.T) {
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &RefreshCommand{
|
||||
Meta: Meta{
|
||||
ContextOpts: testCtxConfig(p),
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-state", "i-should-not-exist-ever",
|
||||
testFixturePath("refresh"),
|
||||
}
|
||||
if code := c.Run(args); code != 1 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefresh_cwd(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# Hello
|
Loading…
Reference in New Issue