cleanup consul lock entries

This matches the consul cli behavior, where locks are cleaned up after
use.

Return an error from re-locking the state. This isn't required by the
Locker interface, but it's an added sanity check for state operations.
What was incorrect here was returning an empty ID and error, which would
indicate that Lock/Unlock isn't supported.
This commit is contained in:
James Bardin 2017-04-06 14:04:50 -04:00
parent e2a428b43f
commit 5621d97925
2 changed files with 48 additions and 4 deletions

View File

@ -121,16 +121,15 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
default: default:
if c.lockCh != nil { if c.lockCh != nil {
// we have an active lock already // we have an active lock already
return "", nil return "", fmt.Errorf("state %q already locked", c.Path)
} }
} }
if c.consulLock == nil { if c.consulLock == nil {
opts := &consulapi.LockOptions{ opts := &consulapi.LockOptions{
Key: c.Path + lockSuffix, Key: c.Path + lockSuffix,
// We currently don't procide any options to block terraform and // only wait briefly, so terraform has the choice to fail fast or
// retry lock acquisition, but we can wait briefly in case the // retry as needed.
// lock is about to be freed.
LockWaitTime: time.Second, LockWaitTime: time.Second,
LockTryOnce: true, LockTryOnce: true,
} }
@ -191,6 +190,10 @@ func (c *RemoteClient) Unlock(id string) error {
err := c.consulLock.Unlock() err := c.consulLock.Unlock()
c.lockCh = nil c.lockCh = nil
// This is only cleanup, and will fail if the lock was immediately taken by
// another client, so we don't report an error to the user here.
c.consulLock.Destroy()
kv := c.Client.KV() kv := c.Client.KV()
_, delErr := kv.Delete(c.Path+lockInfoSuffix, nil) _, delErr := kv.Delete(c.Path+lockInfoSuffix, nil)
if delErr != nil { if delErr != nil {

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote" "github.com/hashicorp/terraform/state/remote"
) )
@ -98,3 +99,43 @@ func TestConsul_stateLock(t *testing.T) {
remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client) remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client)
} }
func TestConsul_destroyLock(t *testing.T) {
srv := newConsulTestServer(t)
defer srv.Stop()
// Get the backend
b := backend.TestBackendConfig(t, New(), map[string]interface{}{
"address": srv.HTTPAddr,
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
})
// Grab the client
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
}
c := s.(*remote.State).Client.(*RemoteClient)
info := state.NewLockInfo()
id, err := c.Lock(info)
if err != nil {
t.Fatal(err)
}
lockPath := c.Path + lockSuffix
if err := c.Unlock(id); err != nil {
t.Fatal(err)
}
// get the lock val
pair, _, err := c.Client.KV().Get(lockPath, nil)
if err != nil {
t.Fatal(err)
}
if pair != nil {
t.Fatalf("lock key not cleaned up at: %s", pair.Key)
}
}