mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-22 10:46:31 +00:00
Merge pull request #2326 from resin-io/resin-update-lock
feat(gui): Add resin update lock based on inactivity
This commit is contained in:
commit
196f16b941
@ -46,6 +46,7 @@ const selectionState = require('../../shared/models/selection-state')
|
|||||||
const driveScanner = require('./modules/drive-scanner')
|
const driveScanner = require('./modules/drive-scanner')
|
||||||
const osDialog = require('./os/dialog')
|
const osDialog = require('./os/dialog')
|
||||||
const exceptionReporter = require('./modules/exception-reporter')
|
const exceptionReporter = require('./modules/exception-reporter')
|
||||||
|
const updateLock = require('./modules/update-lock')
|
||||||
|
|
||||||
/* eslint-disable lodash/prefer-lodash-method */
|
/* eslint-disable lodash/prefer-lodash-method */
|
||||||
|
|
||||||
@ -284,6 +285,23 @@ app.run(($window) => {
|
|||||||
popupExists = false
|
popupExists = false
|
||||||
}).catch(exceptionReporter.report)
|
}).catch(exceptionReporter.report)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Helper fn for events
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @example
|
||||||
|
* window.addEventListener('click', extendLock)
|
||||||
|
*/
|
||||||
|
const extendLock = () => {
|
||||||
|
updateLock.extend()
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.addEventListener('click', extendLock)
|
||||||
|
$window.addEventListener('touchstart', extendLock)
|
||||||
|
|
||||||
|
// Initial update lock acquisition
|
||||||
|
extendLock()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.run(($rootScope) => {
|
app.run(($rootScope) => {
|
||||||
|
@ -29,6 +29,7 @@ const errors = require('../../../shared/errors')
|
|||||||
const permissions = require('../../../shared/permissions')
|
const permissions = require('../../../shared/permissions')
|
||||||
const windowProgress = require('../os/window-progress')
|
const windowProgress = require('../os/window-progress')
|
||||||
const analytics = require('../modules/analytics')
|
const analytics = require('../modules/analytics')
|
||||||
|
const updateLock = require('./update-lock')
|
||||||
const packageJSON = require('../../../../package.json')
|
const packageJSON = require('../../../../package.json')
|
||||||
const selectionState = require('../../../shared/models/selection-state')
|
const selectionState = require('../../../shared/models/selection-state')
|
||||||
|
|
||||||
@ -269,6 +270,9 @@ exports.performWrite = (image, drives, onProgress) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Clear the update lock timer to prevent longer
|
||||||
|
// flashing timing it out, and releasing the lock
|
||||||
|
updateLock.pause()
|
||||||
ipc.server.start()
|
ipc.server.start()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -351,6 +355,9 @@ exports.cancel = () => {
|
|||||||
validateWriteOnSuccess: settings.get('validateWriteOnSuccess')
|
validateWriteOnSuccess: settings.get('validateWriteOnSuccess')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Re-enable lock release on inactivity
|
||||||
|
updateLock.resume()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [ socket ] = ipc.server.sockets
|
const [ socket ] = ipc.server.sockets
|
||||||
ipc.server.emit(socket, 'cancel')
|
ipc.server.emit(socket, 'cancel')
|
||||||
|
213
lib/gui/app/modules/update-lock.js
Normal file
213
lib/gui/app/modules/update-lock.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 resin.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const electron = require('electron')
|
||||||
|
const EventEmitter = require('events')
|
||||||
|
const createInactivityTimer = require('inactivity-timer')
|
||||||
|
const debug = require('debug')('etcher:update-lock')
|
||||||
|
const analytics = require('./analytics')
|
||||||
|
|
||||||
|
/* eslint-disable no-magic-numbers, callback-return */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interaction timeout in milliseconds (defaults to 5 minutes)
|
||||||
|
* @type {Number}
|
||||||
|
* @constant
|
||||||
|
*/
|
||||||
|
const INTERACTION_TIMEOUT_MS = process.env.ETCHER_INTERACTION_TIMEOUT_MS
|
||||||
|
? parseInt(process.env.ETCHER_INTERACTION_TIMEOUT_MS, 10)
|
||||||
|
: 5 * 60 * 1000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resin Update Lock
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class UpdateLock extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* @summary Resin Update Lock
|
||||||
|
* @example
|
||||||
|
* new UpdateLock()
|
||||||
|
*/
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
this.paused = false
|
||||||
|
this.on('inactive', UpdateLock.onInactive)
|
||||||
|
this.lockTimer = createInactivityTimer(INTERACTION_TIMEOUT_MS, () => {
|
||||||
|
debug('inactive')
|
||||||
|
this.emit('inactive')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Inactivity event handler, releases the resin update lock on inactivity
|
||||||
|
* @private
|
||||||
|
* @example
|
||||||
|
* this.on('inactive', onInactive)
|
||||||
|
*/
|
||||||
|
static onInactive () {
|
||||||
|
if (process.env.ELECTRON_RESIN_UPDATE_LOCK) {
|
||||||
|
UpdateLock.check((checkError, isLocked) => {
|
||||||
|
debug('inactive-check', Boolean(checkError))
|
||||||
|
if (checkError) {
|
||||||
|
analytics.logException(checkError)
|
||||||
|
}
|
||||||
|
if (isLocked) {
|
||||||
|
UpdateLock.release((error) => {
|
||||||
|
debug('inactive-release', Boolean(error))
|
||||||
|
if (error) {
|
||||||
|
analytics.logException(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Acquire the update lock
|
||||||
|
* @private
|
||||||
|
* @param {Function} callback - callback(error)
|
||||||
|
* @example
|
||||||
|
* UpdateLock.acquire((error) => {
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
static acquire (callback) {
|
||||||
|
debug('lock')
|
||||||
|
if (process.env.ELECTRON_RESIN_UPDATE_LOCK) {
|
||||||
|
electron.ipcRenderer.once('resin-update-lock', (event, error) => {
|
||||||
|
callback(error)
|
||||||
|
})
|
||||||
|
electron.ipcRenderer.send('resin-update-lock', 'lock')
|
||||||
|
} else {
|
||||||
|
callback(new Error('Update lock disabled'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Release the update lock
|
||||||
|
* @private
|
||||||
|
* @param {Function} callback - callback(error)
|
||||||
|
* @example
|
||||||
|
* UpdateLock.release((error) => {
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
static release (callback) {
|
||||||
|
debug('unlock')
|
||||||
|
if (process.env.ELECTRON_RESIN_UPDATE_LOCK) {
|
||||||
|
electron.ipcRenderer.once('resin-update-lock', (event, error) => {
|
||||||
|
callback(error)
|
||||||
|
})
|
||||||
|
electron.ipcRenderer.send('resin-update-lock', 'unlock')
|
||||||
|
} else {
|
||||||
|
callback(new Error('Update lock disabled'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Check the state of the update lock
|
||||||
|
* @private
|
||||||
|
* @param {Function} callback - callback(error, isLocked)
|
||||||
|
* @example
|
||||||
|
* UpdateLock.check((error, isLocked) => {
|
||||||
|
* if (isLocked) {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
static check (callback) {
|
||||||
|
debug('check')
|
||||||
|
if (process.env.ELECTRON_RESIN_UPDATE_LOCK) {
|
||||||
|
electron.ipcRenderer.once('resin-update-lock', (event, error, isLocked) => {
|
||||||
|
callback(error, isLocked)
|
||||||
|
})
|
||||||
|
electron.ipcRenderer.send('resin-update-lock', 'check')
|
||||||
|
} else {
|
||||||
|
callback(new Error('Update lock disabled'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Extend the lock timer
|
||||||
|
* @example
|
||||||
|
* updateLock.extend()
|
||||||
|
*/
|
||||||
|
extend () {
|
||||||
|
debug('extend')
|
||||||
|
|
||||||
|
if (this.paused) {
|
||||||
|
debug('extend:paused')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lockTimer.signal()
|
||||||
|
|
||||||
|
// When extending, check that we have the lock,
|
||||||
|
// and acquire it, if not
|
||||||
|
if (process.env.ELECTRON_RESIN_UPDATE_LOCK) {
|
||||||
|
UpdateLock.check((checkError, isLocked) => {
|
||||||
|
if (checkError) {
|
||||||
|
analytics.logException(checkError)
|
||||||
|
}
|
||||||
|
if (!isLocked) {
|
||||||
|
UpdateLock.acquire((error) => {
|
||||||
|
if (error) {
|
||||||
|
analytics.logException(error)
|
||||||
|
}
|
||||||
|
debug('extend-acquire', Boolean(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Clear the lock timer
|
||||||
|
* @example
|
||||||
|
* updateLock.clearTimer()
|
||||||
|
*/
|
||||||
|
clearTimer () {
|
||||||
|
debug('clear')
|
||||||
|
this.lockTimer.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Clear the lock timer, and pause extension, avoiding triggering until resume()d
|
||||||
|
* @example
|
||||||
|
* updateLock.pause()
|
||||||
|
*/
|
||||||
|
pause () {
|
||||||
|
debug('pause')
|
||||||
|
this.paused = true
|
||||||
|
this.clearTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Un-pause lock extension, and restart the timer
|
||||||
|
* @example
|
||||||
|
* updateLock.resume()
|
||||||
|
*/
|
||||||
|
resume () {
|
||||||
|
debug('resume')
|
||||||
|
this.paused = false
|
||||||
|
this.extend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new UpdateLock()
|
@ -21,6 +21,7 @@ const settings = require('../../../models/settings')
|
|||||||
const flashState = require('../../../../../shared/models/flash-state')
|
const flashState = require('../../../../../shared/models/flash-state')
|
||||||
const selectionState = require('../../../../../shared/models/selection-state')
|
const selectionState = require('../../../../../shared/models/selection-state')
|
||||||
const analytics = require('../../../modules/analytics')
|
const analytics = require('../../../modules/analytics')
|
||||||
|
const updateLock = require('../../../modules/update-lock')
|
||||||
const messages = require('../../../../../shared/messages')
|
const messages = require('../../../../../shared/messages')
|
||||||
|
|
||||||
module.exports = function ($state) {
|
module.exports = function ($state) {
|
||||||
@ -57,6 +58,9 @@ module.exports = function ($state) {
|
|||||||
}
|
}
|
||||||
selectionState.deselectAllDrives()
|
selectionState.deselectAllDrives()
|
||||||
analytics.logEvent('Restart', options)
|
analytics.logEvent('Restart', options)
|
||||||
|
|
||||||
|
// Re-enable lock release on inactivity
|
||||||
|
updateLock.resume()
|
||||||
$state.go('main')
|
$state.go('main')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
npm-shrinkwrap.json
generated
10
npm-shrinkwrap.json
generated
@ -4363,6 +4363,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"inactivity-timer": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/inactivity-timer/-/inactivity-timer-1.0.0.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"indent-string": {
|
"indent-string": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz"
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz"
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"flexboxgrid": "6.3.0",
|
"flexboxgrid": "6.3.0",
|
||||||
"gpt": "1.0.0",
|
"gpt": "1.0.0",
|
||||||
"immutable": "3.8.1",
|
"immutable": "3.8.1",
|
||||||
|
"inactivity-timer": "1.0.0",
|
||||||
"lodash": "4.13.1",
|
"lodash": "4.13.1",
|
||||||
"lzma-native": "1.5.2",
|
"lzma-native": "1.5.2",
|
||||||
"mbr": "1.1.2",
|
"mbr": "1.1.2",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user