state/remote: export ClientLocker, test for implementation

This adds unit tests (that will fail at compile time) if various structs
don't implement the right interfaces for locking
This commit is contained in:
Mitchell Hashimoto 2017-02-15 14:20:59 -08:00
parent 6798cd5911
commit efe754183b
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
5 changed files with 25 additions and 14 deletions

View File

@ -13,6 +13,7 @@ import (
func TestRemoteClient_impl(t *testing.T) { func TestRemoteClient_impl(t *testing.T) {
var _ remote.Client = new(RemoteClient) var _ remote.Client = new(RemoteClient)
var _ remote.ClientLocker = new(RemoteClient)
} }
func TestRemoteClient(t *testing.T) { func TestRemoteClient(t *testing.T) {

View File

@ -6,6 +6,10 @@ import (
"testing" "testing"
) )
func TestBackupState_locker(t *testing.T) {
var _ Locker = new(BackupState)
}
func TestBackupState(t *testing.T) { func TestBackupState(t *testing.T) {
f, err := ioutil.TempFile("", "tf") f, err := ioutil.TempFile("", "tf")
if err != nil { if err != nil {

View File

@ -2,6 +2,8 @@ package remote
import ( import (
"fmt" "fmt"
"github.com/hashicorp/terraform/state"
) )
// Client is the interface that must be implemented for a remote state // Client is the interface that must be implemented for a remote state
@ -13,6 +15,15 @@ type Client interface {
Delete() error Delete() error
} }
// ClientLocker is an optional interface that allows a remote state
// backend to enable state lock/unlock.
type ClientLocker interface {
Client
Lock(*state.LockInfo) (string, error)
Unlock(string) error
}
// Payload is the return value from the remote state storage. // Payload is the return value from the remote state storage.
type Payload struct { type Payload struct {
MD5 []byte MD5 []byte

View File

@ -3,6 +3,7 @@ package remote
import ( import (
"bytes" "bytes"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -62,24 +63,17 @@ func (s *State) PersistState() error {
} }
// Lock calls the Client's Lock method if it's implemented. // Lock calls the Client's Lock method if it's implemented.
func (s *State) Lock(reason string) error { func (s *State) Lock(info *state.LockInfo) (string, error) {
if c, ok := s.Client.(stateLocker); ok { if c, ok := s.Client.(ClientLocker); ok {
return c.Lock(reason) return c.Lock(info)
} }
return nil return "", nil
} }
// Unlock calls the Client's Unlock method if it's implemented. // Unlock calls the Client's Unlock method if it's implemented.
func (s *State) Unlock() error { func (s *State) Unlock(id string) error {
if c, ok := s.Client.(stateLocker); ok { if c, ok := s.Client.(ClientLocker); ok {
return c.Unlock() return c.Unlock(id)
} }
return nil return nil
} }
// stateLocker mirrors the state.Locker interface. This can be implemented by
// Clients to provide methods for locking and unlocking remote state.
type stateLocker interface {
Lock(reason string) error
Unlock() error
}

View File

@ -24,4 +24,5 @@ func TestState_impl(t *testing.T) {
var _ state.StateWriter = new(State) var _ state.StateWriter = new(State)
var _ state.StatePersister = new(State) var _ state.StatePersister = new(State)
var _ state.StateRefresher = new(State) var _ state.StateRefresher = new(State)
var _ state.Locker = new(State)
} }