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/terraform/backend"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -35,10 +36,17 @@ func (b *Local) opApply(
|
|||
return
|
||||
}
|
||||
|
||||
// If we're locking state, unlock when we're done
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"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)
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/hashicorp/terraform/command/format"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -59,10 +60,17 @@ func (b *Local) opPlan(
|
|||
return
|
||||
}
|
||||
|
||||
// If we're locking state, unlock when we're done
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
)
|
||||
|
||||
func (b *Local) opRefresh(
|
||||
|
@ -48,10 +49,17 @@ func (b *Local) opRefresh(
|
|||
return
|
||||
}
|
||||
|
||||
// If we're locking state, unlock when we're done
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -45,6 +45,7 @@ func TestApply_destroy(t *testing.T) {
|
|||
testFixturePath("apply"),
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Log(ui.OutputWriter.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)
|
||||
}
|
||||
|
||||
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, nil
|
||||
|
|
|
@ -34,7 +34,7 @@ func main() {
|
|||
}
|
||||
|
||||
// signal to the caller that we're locked
|
||||
io.WriteString(os.Stdout, "LOCKED")
|
||||
io.WriteString(os.Stdout, "LOCKID "+lockID)
|
||||
|
||||
defer func() {
|
||||
if err := s.Unlock(lockID); err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
// UnlockCommand is a cli.Command implementation that manually unlocks
|
||||
|
@ -25,9 +26,21 @@ func (c *UnlockCommand) Run(args []string) int {
|
|||
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
|
||||
// required.
|
||||
configPath, err := ModulePath(cmdFlags.Args())
|
||||
configPath, err := ModulePath(args)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
|
@ -93,7 +106,7 @@ func (c *UnlockCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// 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))
|
||||
return 1
|
||||
}
|
||||
|
@ -104,7 +117,7 @@ func (c *UnlockCommand) Run(args []string) int {
|
|||
|
||||
func (c *UnlockCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform force-unlock [DIR]
|
||||
Usage: terraform force-unlock LOCK_ID [DIR]
|
||||
|
||||
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 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
args := []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