Merge pull request #17397 from hashicorp/jbardin/gcs-lock-info
Fix reported GCS state lock ID
This commit is contained in:
commit
7c6072c2a0
|
@ -23,7 +23,8 @@ func TestLocal_impl(t *testing.T) {
|
|||
func TestLocal_backend(t *testing.T) {
|
||||
defer testTmpDir(t)()
|
||||
b := &Local{}
|
||||
backend.TestBackend(t, b, b)
|
||||
backend.TestBackendStates(t, b)
|
||||
backend.TestBackendStateLocks(t, b, b)
|
||||
}
|
||||
|
||||
func checkState(t *testing.T, path, expected string) {
|
||||
|
|
|
@ -64,7 +64,7 @@ func TestBackend(t *testing.T) {
|
|||
"access_key": res.accessKey,
|
||||
}).(*Backend)
|
||||
|
||||
backend.TestBackend(t, b, nil)
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendLocked(t *testing.T) {
|
||||
|
@ -88,7 +88,8 @@ func TestBackendLocked(t *testing.T) {
|
|||
"access_key": res.accessKey,
|
||||
}).(*Backend)
|
||||
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
backend.TestBackendStateForceUnlock(t, b1, b2)
|
||||
}
|
||||
|
||||
type testResources struct {
|
||||
|
|
|
@ -63,7 +63,8 @@ func TestBackend(t *testing.T) {
|
|||
})
|
||||
|
||||
// Test
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStates(t, b1)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestBackend_lockDisabled(t *testing.T) {
|
||||
|
@ -83,7 +84,8 @@ func TestBackend_lockDisabled(t *testing.T) {
|
|||
})
|
||||
|
||||
// Test
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStates(t, b1)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestBackend_gzip(t *testing.T) {
|
||||
|
@ -95,5 +97,5 @@ func TestBackend_gzip(t *testing.T) {
|
|||
})
|
||||
|
||||
// Test
|
||||
backend.TestBackend(t, b, nil)
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
|
|
@ -66,7 +66,9 @@ func TestBackend(t *testing.T) {
|
|||
})
|
||||
|
||||
// Test
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStates(t, b1)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
backend.TestBackendStateForceUnlock(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestBackend_lockDisabled(t *testing.T) {
|
||||
|
@ -89,5 +91,5 @@ func TestBackend_lockDisabled(t *testing.T) {
|
|||
})
|
||||
|
||||
// Test
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
}
|
||||
|
|
|
@ -136,8 +136,11 @@ func TestBackend(t *testing.T) {
|
|||
|
||||
be1 := setupBackend(t, bucket, noPrefix, noEncryptionKey)
|
||||
|
||||
backend.TestBackend(t, be0, be1)
|
||||
backend.TestBackendStates(t, be0)
|
||||
backend.TestBackendStateLocks(t, be0, be1)
|
||||
backend.TestBackendStateForceUnlock(t, be0, be1)
|
||||
}
|
||||
|
||||
func TestBackendWithPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -149,7 +152,8 @@ func TestBackendWithPrefix(t *testing.T) {
|
|||
|
||||
be1 := setupBackend(t, bucket, prefix+"/", noEncryptionKey)
|
||||
|
||||
backend.TestBackend(t, be0, be1)
|
||||
backend.TestBackendStates(t, be0)
|
||||
backend.TestBackendStateLocks(t, be0, be1)
|
||||
}
|
||||
func TestBackendWithEncryption(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -161,7 +165,8 @@ func TestBackendWithEncryption(t *testing.T) {
|
|||
|
||||
be1 := setupBackend(t, bucket, noPrefix, encryptionKey)
|
||||
|
||||
backend.TestBackend(t, be0, be1)
|
||||
backend.TestBackendStates(t, be0)
|
||||
backend.TestBackendStateLocks(t, be0, be1)
|
||||
}
|
||||
|
||||
// setupBackend returns a new GCS backend.
|
||||
|
|
|
@ -80,6 +80,10 @@ func (c *remoteClient) Delete() error {
|
|||
// Lock writes to a lock file, ensuring file creation. Returns the generation
|
||||
// number, which must be passed to Unlock().
|
||||
func (c *remoteClient) Lock(info *state.LockInfo) (string, error) {
|
||||
// update the path we're using
|
||||
// we can't set the ID until the info is written
|
||||
info.Path = c.lockFileURL()
|
||||
|
||||
infoJson, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -93,12 +97,12 @@ func (c *remoteClient) Lock(info *state.LockInfo) (string, error) {
|
|||
}
|
||||
return w.Close()
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return "", c.lockError(fmt.Errorf("writing %q failed: %v", c.lockFileURL(), err))
|
||||
}
|
||||
|
||||
info.ID = strconv.FormatInt(w.Attrs().Generation, 10)
|
||||
info.Path = c.lockFileURL()
|
||||
|
||||
return info.ID, nil
|
||||
}
|
||||
|
@ -149,6 +153,15 @@ func (c *remoteClient) lockInfo() (*state.LockInfo, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// We use the Generation as the ID, so overwrite the ID in the json.
|
||||
// This can't be written into the Info, since the generation isn't known
|
||||
// until it's written.
|
||||
attrs, err := c.lockFile().Attrs(c.storageContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.ID = strconv.FormatInt(attrs.Generation, 10)
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ func TestBackendConfig(t *testing.T) {
|
|||
func TestBackend(t *testing.T) {
|
||||
defer Reset()
|
||||
b := backend.TestBackendConfig(t, New(), nil).(*Backend)
|
||||
backend.TestBackend(t, b, nil)
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendLocked(t *testing.T) {
|
||||
|
@ -48,7 +48,7 @@ func TestBackendLocked(t *testing.T) {
|
|||
b1 := backend.TestBackendConfig(t, New(), nil).(*Backend)
|
||||
b2 := backend.TestBackendConfig(t, New(), nil).(*Backend)
|
||||
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
}
|
||||
|
||||
// use the this backen to test the remote.State implementation
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestBackend(t *testing.T) {
|
|||
createMantaFolder(t, b.storageClient, directory)
|
||||
defer deleteMantaFolder(t, b.storageClient, directory)
|
||||
|
||||
backend.TestBackend(t, b, nil)
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendLocked(t *testing.T) {
|
||||
|
@ -60,7 +60,8 @@ func TestBackendLocked(t *testing.T) {
|
|||
createMantaFolder(t, b1.storageClient, directory)
|
||||
defer deleteMantaFolder(t, b1.storageClient, directory)
|
||||
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
backend.TestBackendStateForceUnlock(t, b1, b2)
|
||||
}
|
||||
|
||||
func createMantaFolder(t *testing.T, mantaClient *storage.StorageClient, directoryName string) {
|
||||
|
|
|
@ -103,7 +103,7 @@ func TestBackend(t *testing.T) {
|
|||
createS3Bucket(t, b.s3Client, bucketName)
|
||||
defer deleteS3Bucket(t, b.s3Client, bucketName)
|
||||
|
||||
backend.TestBackend(t, b, nil)
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendLocked(t *testing.T) {
|
||||
|
@ -131,7 +131,8 @@ func TestBackendLocked(t *testing.T) {
|
|||
createDynamoDBTable(t, b1.dynClient, bucketName)
|
||||
defer deleteDynamoDBTable(t, b1.dynClient, bucketName)
|
||||
|
||||
backend.TestBackend(t, b1, b2)
|
||||
backend.TestBackendStateLocks(t, b1, b2)
|
||||
backend.TestBackendStateForceUnlock(t, b1, b2)
|
||||
}
|
||||
|
||||
// add some extra junk in S3 to try and confuse the env listing.
|
||||
|
@ -334,9 +335,9 @@ func TestKeyEnv(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
backend.TestBackend(t, b0, nil)
|
||||
backend.TestBackend(t, b1, nil)
|
||||
backend.TestBackend(t, b2, nil)
|
||||
backend.TestBackendStates(t, b0)
|
||||
backend.TestBackendStates(t, b1)
|
||||
backend.TestBackendStates(t, b2)
|
||||
}
|
||||
|
||||
func testGetWorkspaceForKey(b *Backend, key string, expected string) error {
|
||||
|
|
|
@ -67,7 +67,7 @@ func TestBackend(t *testing.T) {
|
|||
|
||||
defer deleteSwiftContainer(t, b.client, container)
|
||||
|
||||
backend.TestBackend(t, b, nil)
|
||||
backend.TestBackendStates(t, b)
|
||||
}
|
||||
|
||||
func TestBackendPath(t *testing.T) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"sort"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
|
@ -43,20 +44,7 @@ func TestBackendConfig(t *testing.T, b Backend, c map[string]interface{}) Backen
|
|||
// assumed to already be configured. This will test state functionality.
|
||||
// If the backend reports it doesn't support multi-state by returning the
|
||||
// error ErrNamedStatesNotSupported, then it will not test that.
|
||||
//
|
||||
// If you want to test locking, two backends must be given. If b2 is nil,
|
||||
// then state locking won't be tested.
|
||||
func TestBackend(t *testing.T, b1, b2 Backend) {
|
||||
t.Helper()
|
||||
|
||||
testBackendStates(t, b1)
|
||||
|
||||
if b2 != nil {
|
||||
testBackendStateLock(t, b1, b2)
|
||||
}
|
||||
}
|
||||
|
||||
func testBackendStates(t *testing.T, b Backend) {
|
||||
func TestBackendStates(t *testing.T, b Backend) {
|
||||
t.Helper()
|
||||
|
||||
states, err := b.States()
|
||||
|
@ -236,7 +224,23 @@ func testBackendStates(t *testing.T, b Backend) {
|
|||
}
|
||||
}
|
||||
|
||||
func testBackendStateLock(t *testing.T, b1, b2 Backend) {
|
||||
// TestBackendStateLocks will test the locking functionality of the remote
|
||||
// state backend.
|
||||
func TestBackendStateLocks(t *testing.T, b1, b2 Backend) {
|
||||
t.Helper()
|
||||
testLocks(t, b1, b2, false)
|
||||
}
|
||||
|
||||
// TestBackendStateForceUnlock verifies that the lock error is the expected
|
||||
// type, and the lock can be unlocked using the ID reported in the error.
|
||||
// Remote state backends that support -force-unlock should call this in at
|
||||
// least one of the acceptance tests.
|
||||
func TestBackendStateForceUnlock(t *testing.T, b1, b2 Backend) {
|
||||
t.Helper()
|
||||
testLocks(t, b1, b2, true)
|
||||
}
|
||||
|
||||
func testLocks(t *testing.T, b1, b2 Backend, testForceUnlock bool) {
|
||||
t.Helper()
|
||||
|
||||
// Get the default state for each
|
||||
|
@ -286,7 +290,7 @@ func testBackendStateLock(t *testing.T, b1, b2 Backend) {
|
|||
// backend, and as a remote state.
|
||||
_, err = b2.State(DefaultStateName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read locked state from another backend instance: %s", err)
|
||||
t.Errorf("failed to read locked state from another backend instance: %s", err)
|
||||
}
|
||||
|
||||
// If the lock ID is blank, assume locking is disabled
|
||||
|
@ -311,11 +315,51 @@ func testBackendStateLock(t *testing.T, b1, b2 Backend) {
|
|||
}
|
||||
|
||||
if lockIDB == lockIDA {
|
||||
t.Fatalf("duplicate lock IDs: %q", lockIDB)
|
||||
t.Errorf("duplicate lock IDs: %q", lockIDB)
|
||||
}
|
||||
|
||||
if err = lockerB.Unlock(lockIDB); err != nil {
|
||||
t.Fatal("error unlocking client B:", err)
|
||||
}
|
||||
|
||||
// test the equivalent of -force-unlock, by using the id from the error
|
||||
// output.
|
||||
if !testForceUnlock {
|
||||
return
|
||||
}
|
||||
|
||||
// get a new ID
|
||||
infoA.ID, err = uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
lockIDA, err = lockerA.Lock(infoA)
|
||||
if err != nil {
|
||||
t.Fatal("unable to get re lock A:", err)
|
||||
}
|
||||
unlock := func() {
|
||||
err := lockerA.Unlock(lockIDA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = lockerB.Lock(infoB)
|
||||
if err == nil {
|
||||
unlock()
|
||||
t.Fatal("client B obtained lock while held by client A")
|
||||
}
|
||||
|
||||
infoErr, ok := err.(*state.LockError)
|
||||
if !ok {
|
||||
unlock()
|
||||
t.Fatalf("expected type *state.LockError, got : %#v", err)
|
||||
}
|
||||
|
||||
// try to unlock with the second unlocker, using the ID from the error
|
||||
if err := lockerB.Unlock(infoErr.Info.ID); err != nil {
|
||||
unlock()
|
||||
t.Fatalf("could not unlock with the reported ID %q: %s", infoErr.Info.ID, err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue