make command tests pass with new state.Locker

This commit is contained in:
James Bardin 2017-02-14 18:51:32 -05:00
parent cd233fef6a
commit 4f0c465187
7 changed files with 63 additions and 34 deletions

View File

@ -533,11 +533,16 @@ func (m *Meta) backendFromPlan(opts *BackendOpts) (backend.Backend, error) {
} }
// Lock the state if we can // Lock the state if we can
err = clistate.Lock(realMgr, "backend from plan", m.Ui, m.Colorize()) lockInfo := &state.LockInfo{
Operation: "plan",
Info: "backend from plan",
}
lockID, err := clistate.Lock(realMgr, lockInfo, m.Ui, m.Colorize())
if err != nil { if err != nil {
return nil, fmt.Errorf("Error locking state: %s", err) return nil, fmt.Errorf("Error locking state: %s", err)
} }
defer clistate.Unlock(realMgr, m.Ui, m.Colorize()) defer clistate.Unlock(realMgr, lockID, m.Ui, m.Colorize())
if err := realMgr.RefreshState(); err != nil { if err := realMgr.RefreshState(); err != nil {
return nil, fmt.Errorf("Error reading state: %s", err) return nil, fmt.Errorf("Error reading state: %s", err)
@ -986,11 +991,15 @@ func (m *Meta) backend_C_r_s(
} }
// Lock the state if we can // Lock the state if we can
err = clistate.Lock(sMgr, "backend from config", m.Ui, m.Colorize()) lockInfo := &state.LockInfo{
Info: "backend from config",
}
lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize())
if err != nil { if err != nil {
return nil, fmt.Errorf("Error locking state: %s", err) return nil, fmt.Errorf("Error locking state: %s", err)
} }
defer clistate.Unlock(sMgr, m.Ui, m.Colorize()) defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
// Store the metadata in our saved state location // Store the metadata in our saved state location
s := sMgr.State() s := sMgr.State()
@ -1091,11 +1100,14 @@ func (m *Meta) backend_C_r_S_changed(
} }
// Lock the state if we can // Lock the state if we can
err = clistate.Lock(sMgr, "backend from config", m.Ui, m.Colorize()) lockInfo := &state.LockInfo{
Info: "backend from config",
}
lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize())
if err != nil { if err != nil {
return nil, fmt.Errorf("Error locking state: %s", err) return nil, fmt.Errorf("Error locking state: %s", err)
} }
defer clistate.Unlock(sMgr, m.Ui, m.Colorize()) defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
// Update the backend state // Update the backend state
s = sMgr.State() s = sMgr.State()
@ -1249,11 +1261,15 @@ func (m *Meta) backend_C_R_S_unchanged(
} }
// Lock the state if we can // Lock the state if we can
err = clistate.Lock(sMgr, "backend from config", m.Ui, m.Colorize()) lockInfo := &state.LockInfo{
Info: "backend from config",
}
lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize())
if err != nil { if err != nil {
return nil, fmt.Errorf("Error locking state: %s", err) return nil, fmt.Errorf("Error locking state: %s", err)
} }
defer clistate.Unlock(sMgr, m.Ui, m.Colorize()) defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
// Unset the remote state // Unset the remote state
s = sMgr.State() s = sMgr.State()

View File

@ -24,17 +24,25 @@ import (
// //
// This will attempt to lock both states for the migration. // This will attempt to lock both states for the migration.
func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error { func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
err := clistate.Lock(opts.One, "migration source state", m.Ui, m.Colorize()) lockInfoOne := &state.LockInfo{
Info: "migration source state",
}
lockIDOne, err := clistate.Lock(opts.One, lockInfoOne, m.Ui, m.Colorize())
if err != nil { if err != nil {
return fmt.Errorf("Error locking source state: %s", err) return fmt.Errorf("Error locking source state: %s", err)
} }
defer clistate.Unlock(opts.One, m.Ui, m.Colorize()) defer clistate.Unlock(opts.One, lockIDOne, m.Ui, m.Colorize())
err = clistate.Lock(opts.Two, "migration destination state", m.Ui, m.Colorize()) lockInfoTwo := &state.LockInfo{
Info: "migration source state",
}
lockIDTwo, err := clistate.Lock(opts.Two, lockInfoTwo, m.Ui, m.Colorize())
if err != nil { if err != nil {
return fmt.Errorf("Error locking destination state: %s", err) return fmt.Errorf("Error locking destination state: %s", err)
} }
defer clistate.Unlock(opts.Two, m.Ui, m.Colorize()) defer clistate.Unlock(opts.Two, lockIDTwo, m.Ui, m.Colorize())
one := opts.One.State() one := opts.One.State()
two := opts.Two.State() two := opts.Two.State()

View File

@ -6,6 +6,7 @@ import (
"strings" "strings"
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"
) )
@ -73,13 +74,16 @@ func (c *TaintCommand) Run(args []string) int {
} }
if c.Meta.stateLock { if c.Meta.stateLock {
err := clistate.Lock(st, "taint", c.Ui, c.Colorize()) lockInfo := &state.LockInfo{
Operation: "taint",
}
lockID, err := clistate.Lock(st, lockInfo, c.Ui, c.Colorize())
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
return 1 return 1
} }
defer clistate.Unlock(st, c.Ui, c.Colorize()) defer clistate.Unlock(st, lockID, c.Ui, c.Colorize())
} }
// Get the actual state structure // Get the actual state structure

View File

@ -23,7 +23,7 @@ func main() {
Path: os.Args[1], Path: os.Args[1],
} }
err := s.Lock("command test") lockID, err := s.Lock(&state.LockInfo{Operation: "test", Info: "state locker"})
if err != nil { if err != nil {
io.WriteString(os.Stderr, err.Error()) io.WriteString(os.Stderr, err.Error())
return return
@ -33,7 +33,7 @@ func main() {
io.WriteString(os.Stdout, "LOCKED") io.WriteString(os.Stdout, "LOCKED")
defer func() { defer func() {
if err := s.Unlock(); err != nil { if err := s.Unlock(lockID); err != nil {
io.WriteString(os.Stderr, err.Error()) io.WriteString(os.Stderr, err.Error())
} }
}() }()

View File

@ -92,7 +92,8 @@ func (c *UnlockCommand) Run(args []string) int {
} }
} }
if err := s.Unlock(); err != nil { // FIXME: unlock should require the lock ID
if err := s.Unlock(""); 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
} }

View File

@ -6,6 +6,7 @@ import (
"strings" "strings"
clistate "github.com/hashicorp/terraform/command/state" clistate "github.com/hashicorp/terraform/command/state"
"github.com/hashicorp/terraform/state"
) )
// UntaintCommand is a cli.Command implementation that manually untaints // UntaintCommand is a cli.Command implementation that manually untaints
@ -61,13 +62,16 @@ func (c *UntaintCommand) Run(args []string) int {
} }
if c.Meta.stateLock { if c.Meta.stateLock {
err := clistate.Lock(st, "untaint", c.Ui, c.Colorize()) lockInfo := &state.LockInfo{
Operation: "untaint",
}
lockID, err := clistate.Lock(st, lockInfo, c.Ui, c.Colorize())
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
return 1 return 1
} }
defer clistate.Unlock(st, c.Ui, c.Colorize()) defer clistate.Unlock(st, lockID, c.Ui, c.Colorize())
} }
// Get the actual state structure // Get the actual state structure

View File

@ -134,25 +134,25 @@ func (s *LocalState) RefreshState() error {
} }
// Lock implements a local filesystem state.Locker. // Lock implements a local filesystem state.Locker.
func (s *LocalState) Lock(reason string) error { func (s *LocalState) Lock(info *LockInfo) (string, error) {
if s.stateFileOut == nil { if s.stateFileOut == nil {
if err := s.createStateFiles(); err != nil { if err := s.createStateFiles(); err != nil {
return err return "", err
} }
} }
if err := s.lock(); err != nil { if err := s.lock(); err != nil {
if info, err := s.lockInfo(); err == nil { if info, err := s.lockInfo(); err == nil {
return info.Err() return "", info.Err()
} }
return fmt.Errorf("state file %q locked: %s", s.Path, err) return "", fmt.Errorf("state file %q locked: %s", s.Path, err)
} }
return s.writeLockInfo(reason) return "", s.writeLockInfo(info)
} }
func (s *LocalState) Unlock() error { func (s *LocalState) Unlock(id string) error {
// we can't be locked if we don't have a file // we can't be locked if we don't have a file
if s.stateFileOut == nil { if s.stateFileOut == nil {
return nil return nil
@ -232,18 +232,14 @@ func (s *LocalState) lockInfo() (*LockInfo, error) {
} }
// write a new lock info file // write a new lock info file
func (s *LocalState) writeLockInfo(info string) error { func (s *LocalState) writeLockInfo(info *LockInfo) error {
path := s.lockInfoPath() path := s.lockInfoPath()
info.Path = s.Path
info.Created = time.Now().UTC()
lockInfo := &LockInfo{ infoData, err := json.Marshal(info)
Path: s.Path,
Created: time.Now().UTC(),
Info: info,
}
infoData, err := json.Marshal(lockInfo)
if err != nil { if err != nil {
panic(fmt.Sprintf("could not marshal lock info: %#v", lockInfo)) panic(fmt.Sprintf("could not marshal lock info: %#v", info))
} }
err = ioutil.WriteFile(path, infoData, 0600) err = ioutil.WriteFile(path, infoData, 0600)