mirror of
https://github.com/restic/restic.git
synced 2026-06-17 14:14:19 +00:00
repository: move lock file handling from restic package
This commit is contained in:
@@ -142,7 +142,7 @@ func runCat(ctx context.Context, gopts global.Options, args []string, term ui.Te
|
||||
printer.S(string(buf))
|
||||
return nil
|
||||
case "lock":
|
||||
lock, err := restic.LoadLock(ctx, repo, id)
|
||||
lock, err := repository.LoadLock(ctx, repo, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func testRunInit(t testing.TB, gopts global.Options) {
|
||||
repository.TestUseLowSecurityKDFParameters(t)
|
||||
restic.TestDisableCheckPolynomial(t)
|
||||
restic.TestSetLockTimeout(t, 0)
|
||||
repository.TestSetLockTimeout(t, 0)
|
||||
|
||||
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
|
||||
return runInit(ctx, InitOptions{}, gopts, nil, gopts.Term)
|
||||
|
||||
+2
-3
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/restic/restic/internal/feature"
|
||||
"github.com/restic/restic/internal/global"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
@@ -198,7 +197,7 @@ func main() {
|
||||
|
||||
var exitMessage string
|
||||
switch {
|
||||
case restic.IsAlreadyLocked(err):
|
||||
case repository.IsAlreadyLocked(err):
|
||||
exitMessage = fmt.Sprintf("%v\nthe `unlock` command can be used to remove stale locks", err)
|
||||
case err == ErrInvalidSourceData:
|
||||
exitMessage = fmt.Sprintf("Warning: %v", err)
|
||||
@@ -228,7 +227,7 @@ func main() {
|
||||
exitCode = 3
|
||||
case errors.Is(err, global.ErrNoRepository):
|
||||
exitCode = 10
|
||||
case restic.IsAlreadyLocked(err):
|
||||
case repository.IsAlreadyLocked(err):
|
||||
exitCode = 11
|
||||
case errors.Is(err, repository.ErrNoKeyFound):
|
||||
exitCode = 12
|
||||
|
||||
+10
-10
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
type lockContext struct {
|
||||
lock *restic.Lock
|
||||
lock *Lock
|
||||
cancel context.CancelFunc
|
||||
refreshWG sync.WaitGroup
|
||||
}
|
||||
@@ -34,7 +34,7 @@ var lockerInst = &locker{
|
||||
refreshInterval: defaultRefreshInterval,
|
||||
// consider a lock refresh failed a bit before the lock actually becomes stale
|
||||
// the difference allows to compensate for a small time drift between clients.
|
||||
refreshabilityTimeout: restic.StaleLockTimeout - defaultRefreshInterval*3/2,
|
||||
refreshabilityTimeout: StaleLockTimeout - defaultRefreshInterval*3/2,
|
||||
}
|
||||
|
||||
func LockRepo(ctx context.Context, repo *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (Unlocker, context.Context, error) {
|
||||
@@ -43,8 +43,8 @@ func LockRepo(ctx context.Context, repo *Repository, exclusive bool, retryLock t
|
||||
|
||||
// Lock wraps the ctx such that it is cancelled when the repository is unlocked
|
||||
// cancelling the original context also stops the lock refresh
|
||||
func (l *locker) Lock(ctx context.Context, r *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (Unlocker, context.Context, error) {
|
||||
var lock *restic.Lock
|
||||
func (l *locker) Lock(ctx context.Context, r *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (*unlocker, context.Context, error) {
|
||||
var lock *Lock
|
||||
var err error
|
||||
|
||||
retrySleep := minDuration(l.retrySleepStart, retryLock)
|
||||
@@ -55,8 +55,8 @@ func (l *locker) Lock(ctx context.Context, r *Repository, exclusive bool, retryL
|
||||
|
||||
retryLoop:
|
||||
for {
|
||||
lock, err = restic.NewLock(ctx, repo, exclusive)
|
||||
if err != nil && restic.IsAlreadyLocked(err) {
|
||||
lock, err = NewLock(ctx, repo, exclusive)
|
||||
if err != nil && IsAlreadyLocked(err) {
|
||||
|
||||
if !retryMessagePrinted {
|
||||
printRetry(fmt.Sprintf("repo already locked, waiting up to %s for the lock\n", retryLock))
|
||||
@@ -72,7 +72,7 @@ retryLoop:
|
||||
case <-retryTimeout:
|
||||
debug.Log("repo already locked, timeout expired")
|
||||
// Last lock attempt
|
||||
lock, err = restic.NewLock(ctx, repo, exclusive)
|
||||
lock, err = NewLock(ctx, repo, exclusive)
|
||||
break retryLoop
|
||||
case <-retrySleepCh:
|
||||
retrySleep = minDuration(retrySleep*2, l.retrySleepMax)
|
||||
@@ -82,7 +82,7 @@ retryLoop:
|
||||
break retryLoop
|
||||
}
|
||||
}
|
||||
if restic.IsInvalidLock(err) {
|
||||
if IsInvalidLock(err) {
|
||||
return nil, ctx, errors.Fatalf("%v\n\nthe `unlock --remove-all` command can be used to remove invalid locks. Make sure that no other restic process is accessing the repository when running the command", err)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -242,7 +242,7 @@ func (l *locker) monitorLockRefresh(ctx context.Context, lockInfo *lockContext,
|
||||
}
|
||||
}
|
||||
|
||||
func tryRefreshStaleLock(ctx context.Context, be backend.Backend, lock *restic.Lock, cancel context.CancelFunc, logger func(format string, args ...interface{})) bool {
|
||||
func tryRefreshStaleLock(ctx context.Context, be backend.Backend, lock *Lock, cancel context.CancelFunc, logger func(format string, args ...interface{})) bool {
|
||||
freeze := backend.AsBackend[backend.FreezeBackend](be)
|
||||
if freeze != nil {
|
||||
debug.Log("freezing backend")
|
||||
@@ -277,7 +277,7 @@ func (l *unlocker) Unlock() {
|
||||
// RemoveStaleLocks deletes all locks detected as stale from the repository.
|
||||
func RemoveStaleLocks(ctx context.Context, repo *Repository) (uint, error) {
|
||||
var processed uint
|
||||
err := restic.ForAllLocks(ctx, repo, nil, func(id restic.ID, lock *restic.Lock, err error) error {
|
||||
err := ForAllLocks(ctx, repo, nil, func(id restic.ID, lock *Lock, err error) error {
|
||||
if err != nil {
|
||||
// ignore locks that cannot be loaded
|
||||
debug.Log("ignore lock %v: %v", id, err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package restic
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
// UnlockCancelDelay bounds the duration how long lock cleanup operations will wait
|
||||
@@ -38,8 +38,8 @@ type Lock struct {
|
||||
UID uint32 `json:"uid,omitempty"`
|
||||
GID uint32 `json:"gid,omitempty"`
|
||||
|
||||
repo Unpacked[FileType]
|
||||
lockID *ID
|
||||
repo restic.Unpacked[restic.FileType]
|
||||
lockID *restic.ID
|
||||
}
|
||||
|
||||
// alreadyLockedError is returned when NewLock or NewExclusiveLock are unable to
|
||||
@@ -102,7 +102,7 @@ func TestSetLockTimeout(t testing.TB, d time.Duration) {
|
||||
// exclusive lock is already held by another process, it returns an error
|
||||
// that satisfies IsAlreadyLocked. If the new lock is exclude, then other
|
||||
// non-exclusive locks also result in an IsAlreadyLocked error.
|
||||
func NewLock(ctx context.Context, repo Unpacked[FileType], exclusive bool) (*Lock, error) {
|
||||
func NewLock(ctx context.Context, repo restic.Unpacked[restic.FileType], exclusive bool) (*Lock, error) {
|
||||
lock := &Lock{
|
||||
Time: time.Now(),
|
||||
PID: os.Getpid(),
|
||||
@@ -147,7 +147,7 @@ func (l *Lock) fillUserInfo() error {
|
||||
}
|
||||
l.Username = usr.Username
|
||||
|
||||
l.UID, l.GID, err = UidGidInt(usr)
|
||||
l.UID, l.GID, err = restic.UidGidInt(usr)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func (l *Lock) fillUserInfo() error {
|
||||
// exclusive lock is found.
|
||||
func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
||||
var err error
|
||||
checkedIDs := NewIDSet()
|
||||
checkedIDs := restic.NewIDSet()
|
||||
if l.lockID != nil {
|
||||
checkedIDs.Insert(*l.lockID)
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
||||
// Store updates in new IDSet to prevent data races
|
||||
var m sync.Mutex
|
||||
newCheckedIDs := checkedIDs.Clone()
|
||||
err = ForAllLocks(ctx, l.repo, checkedIDs, func(id ID, lock *Lock, err error) error {
|
||||
err = ForAllLocks(ctx, l.repo, checkedIDs, func(id restic.ID, lock *Lock, err error) error {
|
||||
if err != nil {
|
||||
// if we cannot load a lock then it is unclear whether it can be ignored
|
||||
// it could either be invalid or just unreadable due to network/permission problems
|
||||
@@ -209,7 +209,7 @@ func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if errors.Is(err, ErrInvalidData) {
|
||||
if errors.Is(err, restic.ErrInvalidData) {
|
||||
return &invalidLockError{err}
|
||||
}
|
||||
return err
|
||||
@@ -228,10 +228,10 @@ func cancelableDelay(ctx context.Context, delay time.Duration) error {
|
||||
}
|
||||
|
||||
// createLock acquires the lock by creating a file in the repository.
|
||||
func (l *Lock) createLock(ctx context.Context) (ID, error) {
|
||||
id, err := SaveJSONUnpacked(ctx, l.repo, LockFile, l)
|
||||
func (l *Lock) createLock(ctx context.Context) (restic.ID, error) {
|
||||
id, err := restic.SaveJSONUnpacked(ctx, l.repo, restic.LockFile, l)
|
||||
if err != nil {
|
||||
return ID{}, err
|
||||
return restic.ID{}, err
|
||||
}
|
||||
|
||||
return id, nil
|
||||
@@ -246,7 +246,7 @@ func (l *Lock) Unlock(ctx context.Context) error {
|
||||
ctx, cancel := delayedCancelContext(ctx, UnlockCancelDelay)
|
||||
defer cancel()
|
||||
|
||||
return l.repo.RemoveUnpacked(ctx, LockFile, *l.lockID)
|
||||
return l.repo.RemoveUnpacked(ctx, restic.LockFile, *l.lockID)
|
||||
}
|
||||
|
||||
var StaleLockTimeout = 30 * time.Minute
|
||||
@@ -326,7 +326,7 @@ func (l *Lock) Refresh(ctx context.Context) error {
|
||||
ctx, cancel := delayedCancelContext(ctx, UnlockCancelDelay)
|
||||
defer cancel()
|
||||
|
||||
return l.repo.RemoveUnpacked(ctx, LockFile, *oldLockID)
|
||||
return l.repo.RemoveUnpacked(ctx, restic.LockFile, *oldLockID)
|
||||
}
|
||||
|
||||
// RefreshStaleLock is an extended variant of Refresh that can also refresh stale lock files.
|
||||
@@ -359,13 +359,13 @@ func (l *Lock) RefreshStaleLock(ctx context.Context) error {
|
||||
|
||||
if err != nil {
|
||||
// cleanup replacement lock
|
||||
_ = l.repo.RemoveUnpacked(ctx, LockFile, id)
|
||||
_ = l.repo.RemoveUnpacked(ctx, restic.LockFile, id)
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// cleanup replacement lock
|
||||
_ = l.repo.RemoveUnpacked(ctx, LockFile, id)
|
||||
_ = l.repo.RemoveUnpacked(ctx, restic.LockFile, id)
|
||||
return ErrRemovedLock
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ func (l *Lock) RefreshStaleLock(ctx context.Context) error {
|
||||
oldLockID := l.lockID
|
||||
l.lockID = &id
|
||||
|
||||
return l.repo.RemoveUnpacked(ctx, LockFile, *oldLockID)
|
||||
return l.repo.RemoveUnpacked(ctx, restic.LockFile, *oldLockID)
|
||||
}
|
||||
|
||||
func (l *Lock) checkExistence(ctx context.Context) (bool, error) {
|
||||
@@ -385,7 +385,7 @@ func (l *Lock) checkExistence(ctx context.Context) (bool, error) {
|
||||
|
||||
exists := false
|
||||
|
||||
err := l.repo.List(ctx, LockFile, func(id ID, _ int64) error {
|
||||
err := l.repo.List(ctx, restic.LockFile, func(id restic.ID, _ int64) error {
|
||||
if id.Equal(*l.lockID) {
|
||||
exists = true
|
||||
}
|
||||
@@ -423,9 +423,9 @@ func init() {
|
||||
}
|
||||
|
||||
// LoadLock loads and unserializes a lock from a repository.
|
||||
func LoadLock(ctx context.Context, repo LoaderUnpacked, id ID) (*Lock, error) {
|
||||
func LoadLock(ctx context.Context, repo restic.LoaderUnpacked, id restic.ID) (*Lock, error) {
|
||||
lock := &Lock{}
|
||||
if err := LoadJSONUnpacked(ctx, repo, LockFile, id, lock); err != nil {
|
||||
if err := restic.LoadJSONUnpacked(ctx, repo, restic.LockFile, id, lock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lock.lockID = &id
|
||||
@@ -437,11 +437,11 @@ func LoadLock(ctx context.Context, repo LoaderUnpacked, id ID) (*Lock, error) {
|
||||
// It is guaranteed that the function is not run concurrently. If the
|
||||
// callback returns an error, this function is cancelled and also returns that error.
|
||||
// If a lock ID is passed via excludeID, it will be ignored.
|
||||
func ForAllLocks(ctx context.Context, repo ListerLoaderUnpacked, excludeIDs IDSet, fn func(ID, *Lock, error) error) error {
|
||||
func ForAllLocks(ctx context.Context, repo restic.ListerLoaderUnpacked, excludeIDs restic.IDSet, fn func(restic.ID, *Lock, error) error) error {
|
||||
var m sync.Mutex
|
||||
|
||||
// For locks decoding is nearly for free, thus just assume were only limited by IO
|
||||
return ParallelList(ctx, repo, LockFile, repo.Connections(), func(ctx context.Context, id ID, size int64) error {
|
||||
return restic.ParallelList(ctx, repo, restic.LockFile, repo.Connections(), func(ctx context.Context, id restic.ID, size int64) error {
|
||||
if excludeIDs.Has(id) {
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package restic_test
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,26 +10,25 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/backend"
|
||||
"github.com/restic/restic/internal/backend/mem"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func TestLock(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
func TestLockFile(t *testing.T) {
|
||||
repo := TestRepository(t)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
lock, err := repository.TestNewLock(t, repo, false)
|
||||
lock, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.OK(t, lock.Unlock(context.TODO()))
|
||||
}
|
||||
|
||||
func TestDoubleUnlock(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
repo := TestRepository(t)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
lock, err := repository.TestNewLock(t, repo, false)
|
||||
lock, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.OK(t, lock.Unlock(context.TODO()))
|
||||
@@ -40,13 +39,13 @@ func TestDoubleUnlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMultipleLock(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
repo := TestRepository(t)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
lock1, err := repository.TestNewLock(t, repo, false)
|
||||
lock1, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
|
||||
lock2, err := repository.TestNewLock(t, repo, false)
|
||||
lock2, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.OK(t, lock1.Unlock(context.TODO()))
|
||||
@@ -66,37 +65,37 @@ func (be *failLockLoadingBackend) Load(ctx context.Context, h backend.Handle, le
|
||||
|
||||
func TestMultipleLockFailure(t *testing.T) {
|
||||
be := &failLockLoadingBackend{Backend: mem.New()}
|
||||
repo, _ := repository.TestRepositoryWithBackend(t, be, 0, repository.Options{})
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
repo, _ := TestRepositoryWithBackend(t, be, 0, Options{})
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
lock1, err := repository.TestNewLock(t, repo, false)
|
||||
lock1, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
|
||||
_, err = repository.TestNewLock(t, repo, false)
|
||||
_, err = NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.Assert(t, err != nil, "unreadable lock file did not result in an error")
|
||||
|
||||
rtest.OK(t, lock1.Unlock(context.TODO()))
|
||||
}
|
||||
|
||||
func TestLockExclusive(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
repo := TestRepository(t)
|
||||
|
||||
elock, err := repository.TestNewLock(t, repo, true)
|
||||
elock, err := NewLock(context.TODO(), &internalRepository{repo}, true)
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, elock.Unlock(context.TODO()))
|
||||
}
|
||||
|
||||
func TestLockOnExclusiveLockedRepo(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
repo := TestRepository(t)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
elock, err := repository.TestNewLock(t, repo, true)
|
||||
elock, err := NewLock(context.TODO(), &internalRepository{repo}, true)
|
||||
rtest.OK(t, err)
|
||||
|
||||
lock, err := repository.TestNewLock(t, repo, false)
|
||||
lock, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.Assert(t, err != nil,
|
||||
"create normal lock with exclusively locked repo didn't return an error")
|
||||
rtest.Assert(t, restic.IsAlreadyLocked(err),
|
||||
rtest.Assert(t, IsAlreadyLocked(err),
|
||||
"create normal lock with exclusively locked repo didn't return the correct error")
|
||||
|
||||
rtest.OK(t, lock.Unlock(context.TODO()))
|
||||
@@ -104,16 +103,16 @@ func TestLockOnExclusiveLockedRepo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExclusiveLockOnLockedRepo(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
repo := TestRepository(t)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
elock, err := repository.TestNewLock(t, repo, false)
|
||||
elock, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
|
||||
lock, err := repository.TestNewLock(t, repo, true)
|
||||
lock, err := NewLock(context.TODO(), &internalRepository{repo}, true)
|
||||
rtest.Assert(t, err != nil,
|
||||
"create normal lock with exclusively locked repo didn't return an error")
|
||||
rtest.Assert(t, restic.IsAlreadyLocked(err),
|
||||
rtest.Assert(t, IsAlreadyLocked(err),
|
||||
"create normal lock with exclusively locked repo didn't return the correct error")
|
||||
|
||||
rtest.OK(t, lock.Unlock(context.TODO()))
|
||||
@@ -159,7 +158,7 @@ func TestLockStale(t *testing.T) {
|
||||
otherHostname := "other-" + hostname
|
||||
|
||||
for i, test := range staleLockTests {
|
||||
lock := restic.Lock{
|
||||
lock := Lock{
|
||||
Time: test.timestamp,
|
||||
PID: test.pid,
|
||||
Hostname: hostname,
|
||||
@@ -195,11 +194,11 @@ func checkSingleLock(t *testing.T, repo restic.Lister) restic.ID {
|
||||
return *lockID
|
||||
}
|
||||
|
||||
func testLockRefresh(t *testing.T, refresh func(lock *restic.Lock) error) {
|
||||
repo := repository.TestRepository(t)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
func testLockRefresh(t *testing.T, refresh func(lock *Lock) error) {
|
||||
repo := TestRepository(t)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
lock, err := repository.TestNewLock(t, repo, false)
|
||||
lock, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
time0 := lock.Time
|
||||
|
||||
@@ -212,7 +211,7 @@ func testLockRefresh(t *testing.T, refresh func(lock *restic.Lock) error) {
|
||||
|
||||
rtest.Assert(t, !lockID.Equal(lockID2),
|
||||
"expected a new ID after lock refresh, got the same")
|
||||
lock2, err := restic.LoadLock(context.TODO(), repo, lockID2)
|
||||
lock2, err := LoadLock(context.TODO(), repo, lockID2)
|
||||
rtest.OK(t, err)
|
||||
rtest.Assert(t, lock2.Time.After(time0),
|
||||
"expected a later timestamp after lock refresh")
|
||||
@@ -220,22 +219,22 @@ func testLockRefresh(t *testing.T, refresh func(lock *restic.Lock) error) {
|
||||
}
|
||||
|
||||
func TestLockRefresh(t *testing.T) {
|
||||
testLockRefresh(t, func(lock *restic.Lock) error {
|
||||
testLockRefresh(t, func(lock *Lock) error {
|
||||
return lock.Refresh(context.TODO())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLockRefreshStale(t *testing.T) {
|
||||
testLockRefresh(t, func(lock *restic.Lock) error {
|
||||
testLockRefresh(t, func(lock *Lock) error {
|
||||
return lock.RefreshStaleLock(context.TODO())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLockRefreshStaleMissing(t *testing.T) {
|
||||
repo, _, be := repository.TestRepositoryWithVersion(t, 0)
|
||||
restic.TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
repo, _, be := TestRepositoryWithVersion(t, 0)
|
||||
TestSetLockTimeout(t, 5*time.Millisecond)
|
||||
|
||||
lock, err := repository.TestNewLock(t, repo, false)
|
||||
lock, err := NewLock(context.TODO(), &internalRepository{repo}, false)
|
||||
rtest.OK(t, err)
|
||||
lockID := checkSingleLock(t, repo)
|
||||
|
||||
@@ -243,5 +242,5 @@ func TestLockRefreshStaleMissing(t *testing.T) {
|
||||
rtest.OK(t, be.Remove(context.TODO(), backend.Handle{Type: restic.LockFile, Name: lockID.String()}))
|
||||
time.Sleep(time.Millisecond)
|
||||
err = lock.RefreshStaleLock(context.TODO())
|
||||
rtest.Assert(t, err == restic.ErrRemovedLock, "unexpected error, expected %v, got %v", restic.ErrRemovedLock, err)
|
||||
rtest.Assert(t, err == ErrRemovedLock, "unexpected error, expected %v, got %v", ErrRemovedLock, err)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//go:build !windows
|
||||
|
||||
package restic
|
||||
package repository
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -1,4 +1,4 @@
|
||||
package restic
|
||||
package repository
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -38,7 +38,7 @@ func checkedLockRepo(ctx context.Context, t *testing.T, repo *Repository, locker
|
||||
lock, wrappedCtx, err := lockerInst.Lock(ctx, repo, false, retryLock, func(msg string) {}, func(format string, args ...interface{}) {})
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, wrappedCtx.Err())
|
||||
if lock.(*unlocker).info.lock.Stale() {
|
||||
if lock.info.lock.Stale() {
|
||||
t.Fatal("lock returned stale lock")
|
||||
}
|
||||
return lock, wrappedCtx
|
||||
@@ -83,7 +83,7 @@ func TestLockConflict(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("second lock should have failed")
|
||||
}
|
||||
rtest.Assert(t, restic.IsAlreadyLocked(err), "unexpected error %v", err)
|
||||
rtest.Assert(t, IsAlreadyLocked(err), "unexpected error %v", err)
|
||||
}
|
||||
|
||||
type writeOnceBackend struct {
|
||||
@@ -309,8 +309,8 @@ func createFakeLock(repo *Repository, t time.Time, pid int) (restic.ID, error) {
|
||||
return restic.ID{}, err
|
||||
}
|
||||
|
||||
newLock := &restic.Lock{Time: t, PID: pid, Hostname: hostname}
|
||||
return restic.SaveJSONUnpacked(context.TODO(), &internalRepository{repo}, restic.LockFile, &newLock)
|
||||
newLock := &Lock{Time: t, PID: pid, Hostname: hostname}
|
||||
return restic.SaveJSONUnpacked(context.TODO(), &internalRepository{repo}, restic.LockFile, newLock)
|
||||
}
|
||||
|
||||
func lockExists(repo restic.Lister, t testing.TB, lockID restic.ID) bool {
|
||||
|
||||
@@ -158,11 +158,6 @@ func BenchmarkAllVersions(b *testing.B, bench VersionedBenchmark) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLock(_ *testing.T, repo *Repository, exclusive bool) (*restic.Lock, error) {
|
||||
// TODO get rid of this test helper
|
||||
return restic.NewLock(context.TODO(), &internalRepository{repo}, exclusive)
|
||||
}
|
||||
|
||||
// TestCheckRepo runs the checker on repo.
|
||||
func TestCheckRepo(t testing.TB, repo *Repository) {
|
||||
chkr := newChecker(repo)
|
||||
|
||||
Reference in New Issue
Block a user