Skip to content

Commit 8aa3860

Browse files
committed
Introduce yet another file locker
1 parent 08ddeab commit 8aa3860

File tree

2 files changed

+93
-12
lines changed

2 files changed

+93
-12
lines changed

internal/pkg/agent/application/upgrade/step_mark.go

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package upgrade
66

77
import (
8+
"context"
89
"encoding/json"
910
"fmt"
1011
"os"
@@ -229,7 +230,7 @@ func markUpgrade(log *logger.Logger, dataDirPath string, agent, previousAgent ag
229230
return fmt.Errorf("failed locking marker file: %w", err)
230231
}
231232

232-
defer func(markerFileLock *flock.Flock) {
233+
defer func(markerFileLock Locker) {
233234
err := markerFileLock.Unlock()
234235
if err != nil {
235236
log.Warnw("Failed to unlock marker file", "file.path", markerPath, "err", err)
@@ -338,15 +339,95 @@ func markerFilePath(dataDirPath string) string {
338339
return filepath.Join(dataDirPath, markerFilename)
339340
}
340341

341-
func lockMarkerFile(markerFileName string) (*flock.Flock, error) {
342-
fLock := flock.New(markerFileName + ".lock")
343-
locked, err := fLock.TryLock()
342+
func fileLockName(markerFileName string) string {
343+
return markerFileName + ".lock"
344+
}
345+
346+
func lockMarkerFile(markerFileName string) (Locker, error) {
347+
locker, err := NewFileLocker(fileLockName(markerFileName), WithTimeout(10*time.Second))
348+
if err != nil {
349+
return nil, fmt.Errorf("instantiating file locker: %w", err)
350+
}
351+
352+
err = locker.Lock()
344353
if err != nil {
345-
return nil, fmt.Errorf("locking %s: %w", fLock, err)
354+
return nil, fmt.Errorf("locking: %w", err)
355+
}
356+
return locker, nil
357+
}
358+
359+
type Locker interface {
360+
Lock() error
361+
Unlock() error
362+
}
363+
364+
type FileLocker struct {
365+
fileLock *flock.Flock
366+
blocking bool
367+
timeout time.Duration
368+
customNotLockedError error
369+
}
370+
371+
type FileLockerOption func(locker *FileLocker) error
372+
373+
func WithCustomNotLockedError(customError error) FileLockerOption {
374+
return func(locker *FileLocker) error {
375+
locker.customNotLockedError = customError
376+
return nil
377+
}
378+
}
379+
380+
var ErrZeroTimeout = errors.New("must specify a non-zero timeout for a blocking file locker")
381+
var ErrNotLocked = errors.New("file not locked")
382+
383+
func WithTimeout(timeout time.Duration) FileLockerOption {
384+
return func(locker *FileLocker) error {
385+
386+
if timeout == 0 {
387+
return ErrZeroTimeout
388+
}
389+
390+
locker.blocking = true
391+
locker.timeout = timeout
392+
393+
return nil
394+
}
395+
}
396+
397+
func NewFileLocker(lockFilePath string, opts ...FileLockerOption) (*FileLocker, error) {
398+
flocker := &FileLocker{fileLock: flock.New(lockFilePath)}
399+
for _, opt := range opts {
400+
if err := opt(flocker); err != nil {
401+
return nil, fmt.Errorf("applying options to new file locker: %w", err)
402+
}
403+
}
404+
return flocker, nil
405+
}
406+
407+
func (fl *FileLocker) Lock() error {
408+
var locked bool
409+
var err error
410+
411+
if fl.blocking {
412+
timeoutCtx, cancel := context.WithTimeout(context.TODO(), fl.timeout)
413+
defer cancel()
414+
locked, err = fl.fileLock.TryLockContext(timeoutCtx, time.Second)
415+
} else {
416+
locked, err = fl.fileLock.TryLock()
417+
}
418+
419+
if err != nil {
420+
return fmt.Errorf("locking %s: %w", fl.fileLock.Path(), err)
346421
}
347422
if !locked {
348-
return nil, fmt.Errorf("failed locking %s", fLock)
423+
if fl.customNotLockedError != nil {
424+
return fmt.Errorf("failed locking %s: %w", fl.fileLock.Path(), fl.customNotLockedError)
425+
}
426+
return fmt.Errorf("failed locking %s: %w", fl.fileLock.Path(), ErrNotLocked)
349427
}
428+
return nil
429+
}
350430

351-
return fLock, nil
431+
func (fl *FileLocker) Unlock() error {
432+
return fl.fileLock.Unlock()
352433
}

internal/pkg/agent/application/upgrade/step_mark_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,11 @@ func Test_markUpgradeLocking(t *testing.T) {
373373
},
374374
{
375375
name: "Update marker creation should fail if marker is already locked by another process", args: args{
376-
agent: newAgent456,
377-
previousAgent: prevAgent123,
378-
action: nil,
379-
upgradeDetails: nil,
380-
},
376+
agent: newAgent456,
377+
previousAgent: prevAgent123,
378+
action: nil,
379+
upgradeDetails: nil,
380+
},
381381
beforeUpdateMarkerCreation: func(t *testing.T, dataDir string) {
382382
// write some fake data in update marker file
383383
updateMarkerFilePath := markerFilePath(dataDir)

0 commit comments

Comments
 (0)