Merge pull request #12777 from hashicorp/b-refresh-empty
backend/local: allow refresh on empty/non-existent state
This commit is contained in:
commit
6921457601
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
@ -22,24 +23,17 @@ func (b *Local) opRefresh(
|
||||||
if b.Backend == nil {
|
if b.Backend == nil {
|
||||||
if _, err := os.Stat(b.StatePath); err != nil {
|
if _, err := os.Stat(b.StatePath); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
runningOp.Err = fmt.Errorf(
|
err = nil
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runningOp.Err = fmt.Errorf(
|
if err != nil {
|
||||||
"There was an error reading the Terraform state that is needed\n"+
|
runningOp.Err = fmt.Errorf(
|
||||||
"for refreshing. The path and error are shown below.\n\n"+
|
"There was an error reading the Terraform state that is needed\n"+
|
||||||
"Path: %s\n\nError: %s",
|
"for refreshing. The path and error are shown below.\n\n"+
|
||||||
b.StatePath, err)
|
"Path: %s\n\nError: %s",
|
||||||
return
|
b.StatePath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +68,12 @@ func (b *Local) opRefresh(
|
||||||
|
|
||||||
// Set our state
|
// Set our state
|
||||||
runningOp.State = opState.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
|
// Perform operation and write the resulting state to the running op
|
||||||
newState, err := tfCtx.Refresh()
|
newState, err := tfCtx.Refresh()
|
||||||
|
@ -93,3 +93,11 @@ func (b *Local) opRefresh(
|
||||||
return
|
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"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/copy"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/cli"
|
"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) {
|
func TestRefresh_lockedState(t *testing.T) {
|
||||||
state := testState()
|
state := testState()
|
||||||
statePath := testStateFile(t, state)
|
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) {
|
func TestRefresh_cwd(t *testing.T) {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
# Hello
|
Loading…
Reference in New Issue