mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-16 22:38:14 +00:00
updateLock: implement a module for a file-based update lock
This update lock library allows an application to take a lockfile in several locations (subdirectories inside a base folder). The user of this library must be able to exclusively create a lockfile in each of the corresponding locations, and if any of the files exist, the locking fails. Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
This commit is contained in:
51
src/lib/update-lock.coffee
Normal file
51
src/lib/update-lock.coffee
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
Promise = require 'bluebird'
|
||||||
|
_ = require 'lodash'
|
||||||
|
TypedError = require 'typed-error'
|
||||||
|
lockFile = Promise.promisifyAll(require('lockfile'))
|
||||||
|
Lock = require 'rwlock'
|
||||||
|
fs = Promise.promisifyAll(require('fs'))
|
||||||
|
|
||||||
|
constants = require './constants'
|
||||||
|
|
||||||
|
ENOENT = (err) -> err.code is 'ENOENT'
|
||||||
|
|
||||||
|
baseLockPath = (appId) ->
|
||||||
|
return "/tmp/resin-supervisor/services/#{appId}"
|
||||||
|
exports.lockPath = (appId, serviceName) ->
|
||||||
|
return "#{baseLockPath(appId)}/#{serviceName}"
|
||||||
|
|
||||||
|
lockFileOnHost = (appId, serviceName) ->
|
||||||
|
return "#{constants.rootMountPoint}#{exports.lockPath(appId, serviceName)}/resin-updates.lock"
|
||||||
|
|
||||||
|
exports.UpdatesLockedError = class UpdatesLockedError extends TypedError
|
||||||
|
|
||||||
|
exports.lock = do ->
|
||||||
|
_lock = new Lock()
|
||||||
|
_writeLock = Promise.promisify(_lock.async.writeLock)
|
||||||
|
return (appId, { force = false } = {}) ->
|
||||||
|
Promise.try ->
|
||||||
|
return if !appId?
|
||||||
|
locksTaken = []
|
||||||
|
dispose = (release) ->
|
||||||
|
Promise.map locksTaken, (lockName) ->
|
||||||
|
lockFile.unlockAsync(lockName)
|
||||||
|
.finally ->
|
||||||
|
release()
|
||||||
|
_writeLock(appId)
|
||||||
|
.tap (release) ->
|
||||||
|
fs.readdirAsync(baseLockPath(appId))
|
||||||
|
.catch ENOENT, -> []
|
||||||
|
.mapSeries (serviceName) ->
|
||||||
|
tmpLockName = lockFileOnHost(appId, serviceName)
|
||||||
|
Promise.try ->
|
||||||
|
lockFile.unlockAsync(tmpLockName) if force == true
|
||||||
|
.then ->
|
||||||
|
lockFile.lockAsync(tmpLockName)
|
||||||
|
.then ->
|
||||||
|
locksTaken.push(tmpLockName)
|
||||||
|
.catch ENOENT, _.noop
|
||||||
|
.catch (err) ->
|
||||||
|
dispose(release)
|
||||||
|
.finally ->
|
||||||
|
throw new exports.UpdatesLockedError("Updates are locked: #{err.message}")
|
||||||
|
.disposer(dispose)
|
Reference in New Issue
Block a user