Have backend operations properly unlock state
Make sure unlock is called with the correct LockID during operations
This commit is contained in:
parent
08cff7cc13
commit
f2e496a14c
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
clistate "github.com/hashicorp/terraform/command/state"
|
||||||
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,10 +36,17 @@ func (b *Local) opApply(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're locking state, unlock when we're done
|
|
||||||
if op.LockState {
|
if op.LockState {
|
||||||
|
lockInfo := state.NewLockInfo()
|
||||||
|
lockInfo.Operation = op.Type.String()
|
||||||
|
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := clistate.Unlock(opState, "", b.CLI, b.Colorize()); err != nil {
|
if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil {
|
||||||
runningOp.Err = multierror.Append(runningOp.Err, err)
|
runningOp.Err = multierror.Append(runningOp.Err, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -29,15 +28,6 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, state.State,
|
||||||
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
|
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.LockState {
|
|
||||||
lockInfo := state.NewLockInfo()
|
|
||||||
lockInfo.Operation = op.Type.String()
|
|
||||||
_, err := clistate.Lock(s, lockInfo, b.CLI, b.Colorize())
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errwrap.Wrapf("Error locking state: {{err}}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.RefreshState(); err != nil {
|
if err := s.RefreshState(); err != nil {
|
||||||
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
|
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/command/format"
|
"github.com/hashicorp/terraform/command/format"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
clistate "github.com/hashicorp/terraform/command/state"
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,10 +60,17 @@ func (b *Local) opPlan(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're locking state, unlock when we're done
|
|
||||||
if op.LockState {
|
if op.LockState {
|
||||||
|
lockInfo := state.NewLockInfo()
|
||||||
|
lockInfo.Operation = op.Type.String()
|
||||||
|
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := clistate.Unlock(opState, "", b.CLI, b.Colorize()); err != nil {
|
if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil {
|
||||||
runningOp.Err = multierror.Append(runningOp.Err, err)
|
runningOp.Err = multierror.Append(runningOp.Err, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
clistate "github.com/hashicorp/terraform/command/state"
|
||||||
|
"github.com/hashicorp/terraform/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Local) opRefresh(
|
func (b *Local) opRefresh(
|
||||||
|
@ -48,10 +49,17 @@ func (b *Local) opRefresh(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're locking state, unlock when we're done
|
|
||||||
if op.LockState {
|
if op.LockState {
|
||||||
|
lockInfo := state.NewLockInfo()
|
||||||
|
lockInfo.Operation = op.Type.String()
|
||||||
|
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := clistate.Unlock(opState, "", b.CLI, b.Colorize()); err != nil {
|
if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil {
|
||||||
runningOp.Err = multierror.Append(runningOp.Err, err)
|
runningOp.Err = multierror.Append(runningOp.Err, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -45,6 +45,7 @@ func TestApply_destroy(t *testing.T) {
|
||||||
testFixturePath("apply"),
|
testFixturePath("apply"),
|
||||||
}
|
}
|
||||||
if code := c.Run(args); code != 0 {
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Log(ui.OutputWriter.String())
|
||||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -583,7 +583,8 @@ func testLockState(sourceDir, path string) (func(), error) {
|
||||||
return deferFunc, fmt.Errorf("read from statelocker returned: %s", err)
|
return deferFunc, fmt.Errorf("read from statelocker returned: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(buf[:n]) != "LOCKED" {
|
output := string(buf[:n])
|
||||||
|
if !strings.HasPrefix(output, "LOCKID") {
|
||||||
return deferFunc, fmt.Errorf("statelocker wrote: %s", string(buf[:n]))
|
return deferFunc, fmt.Errorf("statelocker wrote: %s", string(buf[:n]))
|
||||||
}
|
}
|
||||||
return deferFunc, nil
|
return deferFunc, nil
|
||||||
|
|
|
@ -34,7 +34,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// signal to the caller that we're locked
|
// signal to the caller that we're locked
|
||||||
io.WriteString(os.Stdout, "LOCKED")
|
io.WriteString(os.Stdout, "LOCKID "+lockID)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := s.Unlock(lockID); err != nil {
|
if err := s.Unlock(lockID); err != nil {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UnlockCommand is a cli.Command implementation that manually unlocks
|
// UnlockCommand is a cli.Command implementation that manually unlocks
|
||||||
|
@ -25,9 +26,21 @@ func (c *UnlockCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args = cmdFlags.Args()
|
||||||
|
if len(args) == 0 {
|
||||||
|
c.Ui.Error("unlock requires a lock id argument")
|
||||||
|
return cli.RunResultHelp
|
||||||
|
}
|
||||||
|
|
||||||
|
lockID := args[0]
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
args = args[1:]
|
||||||
|
}
|
||||||
|
|
||||||
// assume everything is initialized. The user can manually init if this is
|
// assume everything is initialized. The user can manually init if this is
|
||||||
// required.
|
// required.
|
||||||
configPath, err := ModulePath(cmdFlags.Args())
|
configPath, err := ModulePath(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(err.Error())
|
c.Ui.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
|
@ -93,7 +106,7 @@ func (c *UnlockCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: unlock should require the lock ID
|
// FIXME: unlock should require the lock ID
|
||||||
if err := s.Unlock(""); err != nil {
|
if err := s.Unlock(lockID); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -104,7 +117,7 @@ func (c *UnlockCommand) Run(args []string) int {
|
||||||
|
|
||||||
func (c *UnlockCommand) Help() string {
|
func (c *UnlockCommand) Help() string {
|
||||||
helpText := `
|
helpText := `
|
||||||
Usage: terraform force-unlock [DIR]
|
Usage: terraform force-unlock LOCK_ID [DIR]
|
||||||
|
|
||||||
Manually unlock the state for the defined configuration.
|
Manually unlock the state for the defined configuration.
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,12 @@ func TestUnlock(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if code := c.Run([]string{"-force"}); code != 0 {
|
args := []string{
|
||||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
"-force",
|
||||||
|
"LOCK_ID",
|
||||||
|
}
|
||||||
|
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("bad: %d\n%s\n%s", code, ui.OutputWriter.String(), ui.ErrorWriter.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue