diff --git a/backend/remote-state/azure/backend_state.go b/backend/remote-state/azure/backend_state.go index e7d316287..9017690e3 100644 --- a/backend/remote-state/azure/backend_state.go +++ b/backend/remote-state/azure/backend_state.go @@ -95,9 +95,13 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { stateMgr := &remote.State{Client: client} + // Grab the value + if err := stateMgr.RefreshState(); err != nil { + return nil, err + } //if this isn't the default state name, we need to create the object so //it's listed by States. - if name != backend.DefaultStateName { + if v := stateMgr.State(); v == nil { // take a lock on this state while we write it lockInfo := statemgr.NewLockInfo() lockInfo.Operation = "init" @@ -119,9 +123,10 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { err = lockUnlock(err) return nil, err } - - // If we have no state, we have to create an empty state + //if this isn't the default state name, we need to create the object so + //it's listed by States. if v := stateMgr.State(); v == nil { + // If we have no state, we have to create an empty state if err := stateMgr.WriteState(states.NewState()); err != nil { err = lockUnlock(err) return nil, err @@ -130,13 +135,12 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { err = lockUnlock(err) return nil, err } - } - // Unlock, the state should now be initialized - if err := lockUnlock(nil); err != nil { - return nil, err + // Unlock, the state should now be initialized + if err := lockUnlock(nil); err != nil { + return nil, err + } } - } return stateMgr, nil diff --git a/backend/remote-state/azure/backend_test.go b/backend/remote-state/azure/backend_test.go index 9d9d2d3ae..a0eb1f5ae 100644 --- a/backend/remote-state/azure/backend_test.go +++ b/backend/remote-state/azure/backend_test.go @@ -258,6 +258,9 @@ func TestBackendAccessKeyLocked(t *testing.T) { backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateForceUnlock(t, b1, b2) + + backend.TestBackendStateLocksInWS(t, b1, b2, "foo") + backend.TestBackendStateForceUnlockInWS(t, b1, b2, "foo") } func TestBackendServicePrincipalLocked(t *testing.T) { @@ -301,4 +304,7 @@ func TestBackendServicePrincipalLocked(t *testing.T) { backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateForceUnlock(t, b1, b2) + + backend.TestBackendStateLocksInWS(t, b1, b2, "foo") + backend.TestBackendStateForceUnlockInWS(t, b1, b2, "foo") } diff --git a/backend/testing.go b/backend/testing.go index b1d78ea61..1e0fd3b11 100644 --- a/backend/testing.go +++ b/backend/testing.go @@ -279,7 +279,27 @@ func TestBackendStateForceUnlock(t *testing.T, b1, b2 Backend) { testLocks(t, b1, b2, true) } +// TestBackendStateLocksInWS will test the locking functionality of the remote +// state backend. +func TestBackendStateLocksInWS(t *testing.T, b1, b2 Backend, ws string) { + t.Helper() + testLocksInWorkspace(t, b1, b2, false, ws) +} + +// TestBackendStateForceUnlockInWS 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 TestBackendStateForceUnlockInWS(t *testing.T, b1, b2 Backend, ws string) { + t.Helper() + testLocksInWorkspace(t, b1, b2, true, ws) +} + func testLocks(t *testing.T, b1, b2 Backend, testForceUnlock bool) { + testLocksInWorkspace(t, b1, b2, testForceUnlock, DefaultStateName) +} + +func testLocksInWorkspace(t *testing.T, b1, b2 Backend, testForceUnlock bool, workspace string) { t.Helper() // Get the default state for each