|
5 | 5 | package upgrade
|
6 | 6 |
|
7 | 7 | import (
|
| 8 | + "context" |
8 | 9 | "encoding/json"
|
9 | 10 | "fmt"
|
10 | 11 | "os"
|
@@ -229,7 +230,7 @@ func markUpgrade(log *logger.Logger, dataDirPath string, agent, previousAgent ag
|
229 | 230 | return fmt.Errorf("failed locking marker file: %w", err)
|
230 | 231 | }
|
231 | 232 |
|
232 |
| - defer func(markerFileLock *flock.Flock) { |
| 233 | + defer func(markerFileLock Locker) { |
233 | 234 | err := markerFileLock.Unlock()
|
234 | 235 | if err != nil {
|
235 | 236 | log.Warnw("Failed to unlock marker file", "file.path", markerPath, "err", err)
|
@@ -338,15 +339,95 @@ func markerFilePath(dataDirPath string) string {
|
338 | 339 | return filepath.Join(dataDirPath, markerFilename)
|
339 | 340 | }
|
340 | 341 |
|
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() |
344 | 353 | 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) |
346 | 421 | }
|
347 | 422 | 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) |
349 | 427 | }
|
| 428 | + return nil |
| 429 | +} |
350 | 430 |
|
351 |
| - return fLock, nil |
| 431 | +func (fl *FileLocker) Unlock() error { |
| 432 | + return fl.fileLock.Unlock() |
352 | 433 | }
|
0 commit comments