restic/internal/restic/lock_test.go

301 lines
7.9 KiB
Go
Raw Normal View History

2015-06-24 16:17:01 +00:00
package restic_test
import (
2017-06-05 21:56:59 +00:00
"context"
"fmt"
"io"
2015-06-24 16:17:01 +00:00
"os"
"testing"
"time"
"github.com/restic/restic/internal/backend/mem"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/repository"
2017-07-24 15:42:25 +00:00
"github.com/restic/restic/internal/restic"
2017-10-02 13:06:39 +00:00
rtest "github.com/restic/restic/internal/test"
2015-06-24 16:17:01 +00:00
)
func TestLock(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
lock, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock.Unlock())
2015-06-24 16:17:01 +00:00
}
func TestDoubleUnlock(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
lock, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock.Unlock())
2015-06-24 16:17:01 +00:00
err = lock.Unlock()
2017-10-02 13:06:39 +00:00
rtest.Assert(t, err != nil,
2015-06-24 16:17:01 +00:00
"double unlock didn't return an error, got %v", err)
}
func TestMultipleLock(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
lock1, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
lock2, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock1.Unlock())
rtest.OK(t, lock2.Unlock())
2015-06-24 16:17:01 +00:00
}
type failLockLoadingBackend struct {
restic.Backend
}
func (be *failLockLoadingBackend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
if h.Type == restic.LockFile {
return fmt.Errorf("error loading lock")
}
return be.Backend.Load(ctx, h, length, offset, fn)
}
func TestMultipleLockFailure(t *testing.T) {
be := &failLockLoadingBackend{Backend: mem.New()}
repo, cleanup := repository.TestRepositoryWithBackend(t, be, 0)
defer cleanup()
lock1, err := restic.NewLock(context.TODO(), repo)
rtest.OK(t, err)
_, err = restic.NewLock(context.TODO(), repo)
rtest.Assert(t, err != nil, "unreadable lock file did not result in an error")
rtest.OK(t, lock1.Unlock())
}
2015-06-24 16:17:01 +00:00
func TestLockExclusive(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
elock, err := restic.NewExclusiveLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
rtest.OK(t, elock.Unlock())
2015-06-24 16:17:01 +00:00
}
func TestLockOnExclusiveLockedRepo(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
elock, err := restic.NewExclusiveLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
lock, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.Assert(t, err != nil,
2015-06-24 16:17:01 +00:00
"create normal lock with exclusively locked repo didn't return an error")
2017-10-02 13:06:39 +00:00
rtest.Assert(t, restic.IsAlreadyLocked(err),
"create normal lock with exclusively locked repo didn't return the correct error")
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock.Unlock())
rtest.OK(t, elock.Unlock())
2015-06-24 16:17:01 +00:00
}
func TestExclusiveLockOnLockedRepo(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
elock, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-06-05 21:56:59 +00:00
lock, err := restic.NewExclusiveLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.Assert(t, err != nil,
"create normal lock with exclusively locked repo didn't return an error")
2017-10-02 13:06:39 +00:00
rtest.Assert(t, restic.IsAlreadyLocked(err),
"create normal lock with exclusively locked repo didn't return the correct error")
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock.Unlock())
rtest.OK(t, elock.Unlock())
2015-06-24 16:17:01 +00:00
}
2016-08-31 18:29:54 +00:00
func createFakeLock(repo restic.Repository, t time.Time, pid int) (restic.ID, error) {
hostname, err := os.Hostname()
if err != nil {
2016-08-31 18:29:54 +00:00
return restic.ID{}, err
}
newLock := &restic.Lock{Time: t, PID: pid, Hostname: hostname}
return restic.SaveJSONUnpacked(context.TODO(), repo, restic.LockFile, &newLock)
2015-06-24 16:17:01 +00:00
}
2016-08-31 18:29:54 +00:00
func removeLock(repo restic.Repository, id restic.ID) error {
h := restic.Handle{Type: restic.LockFile, Name: id.String()}
2017-06-05 21:56:59 +00:00
return repo.Backend().Remove(context.TODO(), h)
2015-06-24 16:17:01 +00:00
}
var staleLockTests = []struct {
timestamp time.Time
stale bool
staleOnOtherHost bool
pid int
2015-06-24 16:17:01 +00:00
}{
{
timestamp: time.Now(),
stale: false,
staleOnOtherHost: false,
pid: os.Getpid(),
2015-06-24 16:17:01 +00:00
},
{
timestamp: time.Now().Add(-time.Hour),
stale: true,
staleOnOtherHost: true,
pid: os.Getpid(),
2015-06-24 16:17:01 +00:00
},
{
timestamp: time.Now().Add(3 * time.Minute),
stale: false,
staleOnOtherHost: false,
pid: os.Getpid(),
2015-06-24 16:17:01 +00:00
},
{
timestamp: time.Now(),
stale: true,
staleOnOtherHost: false,
pid: os.Getpid() + 500000,
2015-06-24 16:17:01 +00:00
},
}
func TestLockStale(t *testing.T) {
hostname, err := os.Hostname()
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
otherHostname := "other-" + hostname
2015-06-24 16:17:01 +00:00
for i, test := range staleLockTests {
lock := restic.Lock{
Time: test.timestamp,
PID: test.pid,
Hostname: hostname,
2015-06-24 16:17:01 +00:00
}
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lock.Stale() == test.stale,
2015-06-24 16:17:01 +00:00
"TestStaleLock: test %d failed: expected stale: %v, got %v",
i, test.stale, !test.stale)
lock.Hostname = otherHostname
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lock.Stale() == test.staleOnOtherHost,
"TestStaleLock: test %d failed: expected staleOnOtherHost: %v, got %v",
i, test.staleOnOtherHost, !test.staleOnOtherHost)
2015-06-24 16:17:01 +00:00
}
}
2016-08-31 18:29:54 +00:00
func lockExists(repo restic.Repository, t testing.TB, id restic.ID) bool {
h := restic.Handle{Type: restic.LockFile, Name: id.String()}
2017-06-05 21:56:59 +00:00
exists, err := repo.Backend().Test(context.TODO(), h)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
return exists
}
func TestLockWithStaleLock(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-06-24 16:17:01 +00:00
id1, err := createFakeLock(repo, time.Now().Add(-time.Hour), os.Getpid())
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
id2, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid())
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
id3, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()+500000)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
processed, err := restic.RemoveStaleLocks(context.TODO(), repo)
rtest.OK(t, err)
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lockExists(repo, t, id1) == false,
2015-06-24 16:17:01 +00:00
"stale lock still exists after RemoveStaleLocks was called")
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lockExists(repo, t, id2) == true,
2015-06-24 16:17:01 +00:00
"non-stale lock was removed by RemoveStaleLocks")
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lockExists(repo, t, id3) == false,
2015-06-24 16:17:01 +00:00
"stale lock still exists after RemoveStaleLocks was called")
rtest.Assert(t, processed == 2,
"number of locks removed does not match: expected %d, got %d",
2, processed)
2015-06-24 16:17:01 +00:00
2017-10-02 13:06:39 +00:00
rtest.OK(t, removeLock(repo, id2))
2015-06-24 16:17:01 +00:00
}
2015-06-27 12:26:33 +00:00
func TestRemoveAllLocks(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
id1, err := createFakeLock(repo, time.Now().Add(-time.Hour), os.Getpid())
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
id2, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid())
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
id3, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()+500000)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
processed, err := restic.RemoveAllLocks(context.TODO(), repo)
rtest.OK(t, err)
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lockExists(repo, t, id1) == false,
"lock still exists after RemoveAllLocks was called")
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lockExists(repo, t, id2) == false,
"lock still exists after RemoveAllLocks was called")
2017-10-02 13:06:39 +00:00
rtest.Assert(t, lockExists(repo, t, id3) == false,
"lock still exists after RemoveAllLocks was called")
rtest.Assert(t, processed == 3,
"number of locks removed does not match: expected %d, got %d",
3, processed)
}
2015-07-12 19:02:00 +00:00
func TestLockRefresh(t *testing.T) {
2016-09-04 11:24:51 +00:00
repo, cleanup := repository.TestRepository(t)
2016-09-04 10:52:43 +00:00
defer cleanup()
2015-07-12 19:02:00 +00:00
2017-06-05 21:56:59 +00:00
lock, err := restic.NewLock(context.TODO(), repo)
2017-10-02 13:06:39 +00:00
rtest.OK(t, err)
2019-09-04 18:38:35 +00:00
time0 := lock.Time
2015-07-12 19:02:00 +00:00
2016-08-31 18:29:54 +00:00
var lockID *restic.ID
err = repo.List(context.TODO(), restic.LockFile, func(id restic.ID, size int64) error {
2015-07-12 19:02:00 +00:00
if lockID != nil {
t.Error("more than one lock found")
}
lockID = &id
return nil
})
if err != nil {
t.Fatal(err)
2015-07-12 19:02:00 +00:00
}
2019-09-04 18:38:35 +00:00
time.Sleep(time.Millisecond)
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock.Refresh(context.TODO()))
2015-07-12 19:02:00 +00:00
2016-08-31 18:29:54 +00:00
var lockID2 *restic.ID
err = repo.List(context.TODO(), restic.LockFile, func(id restic.ID, size int64) error {
2015-07-12 19:02:00 +00:00
if lockID2 != nil {
t.Error("more than one lock found")
}
lockID2 = &id
return nil
})
if err != nil {
t.Fatal(err)
2015-07-12 19:02:00 +00:00
}
2017-10-02 13:06:39 +00:00
rtest.Assert(t, !lockID.Equal(*lockID2),
2015-07-12 19:02:00 +00:00
"expected a new ID after lock refresh, got the same")
2019-09-04 18:38:35 +00:00
lock2, err := restic.LoadLock(context.TODO(), repo, *lockID2)
rtest.OK(t, err)
rtest.Assert(t, lock2.Time.After(time0),
"expected a later timestamp after lock refresh")
2017-10-02 13:06:39 +00:00
rtest.OK(t, lock.Unlock())
2015-07-12 19:02:00 +00:00
}