mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-16 07:46:31 +00:00
Handle the last fail as an error if all devices failed
This commit is contained in:
parent
d07d535993
commit
268c5302e8
@ -33,7 +33,6 @@ const _ = require('lodash')
|
||||
const semver = require('semver')
|
||||
const uuidV4 = require('uuid/v4')
|
||||
|
||||
>>>>>>> Show raspberry pi usbboot update progress in devices list
|
||||
const EXIT_CODES = require('../../shared/exit-codes')
|
||||
const messages = require('../../shared/messages')
|
||||
const s3Packages = require('../../shared/s3-packages')
|
||||
|
@ -86,21 +86,20 @@ const handleError = (error) => {
|
||||
terminate(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
function runVerifier(verifier, onFail) {
|
||||
return new Promise((resolve, reject) => {
|
||||
verifier.on('error', onFail);
|
||||
verifier.on('finish', resolve);
|
||||
verifier.run();
|
||||
});
|
||||
function lastMapValue(map) {
|
||||
let value
|
||||
for (value of map.values()){
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function pipeRegularSourceToDestination(source, destination, verify, onProgress, onFail) {
|
||||
function writeAndValidate(source, destination, verify, onProgress, onFail, onFinish, onError) {
|
||||
let checksum
|
||||
let sparse
|
||||
let sourceMetadata
|
||||
let step = 'flashing'
|
||||
let lastPosition = 0
|
||||
const errors = new Map() // destination -> error map
|
||||
const errors = new Map() // destination -> error map TODO: include open and close errors in it
|
||||
const state = {
|
||||
active: destination.destinations.size,
|
||||
flashing: destination.destinations.size,
|
||||
@ -109,6 +108,9 @@ function pipeRegularSourceToDestination(source, destination, verify, onProgress,
|
||||
successful: 0,
|
||||
type: step
|
||||
}
|
||||
function allDestinationsFailed() {
|
||||
return (errors.size === destination.destinations.size)
|
||||
}
|
||||
function updateState() {
|
||||
state.type = step
|
||||
state.failed = errors.size
|
||||
@ -140,7 +142,17 @@ function pipeRegularSourceToDestination(source, destination, verify, onProgress,
|
||||
Object.assign(progressEvent, state)
|
||||
onProgress(progressEvent)
|
||||
}
|
||||
function onFail2(error) {
|
||||
errors.set(error.destination, error.error)
|
||||
updateState()
|
||||
onFail(error)
|
||||
}
|
||||
destination.on('fail', onFail2)
|
||||
return Promise.all([ source.getInnerSource(), destination.open() ])
|
||||
.then(([ _source ]) => {
|
||||
source = _source
|
||||
return source.canCreateSparseReadStream()
|
||||
})
|
||||
.then((_sparse) => {
|
||||
sparse = _sparse
|
||||
let sourceStream
|
||||
@ -156,18 +168,15 @@ function pipeRegularSourceToDestination(source, destination, verify, onProgress,
|
||||
return Promise.all([ sourceStream, destinationStream, source.getMetadata() ])
|
||||
})
|
||||
.then(([ sourceStream, destinationStream, metadata ]) => {
|
||||
destinationStream.on('fail', (error) => {
|
||||
errors.set(error.destination, error.error)
|
||||
updateState()
|
||||
onFail({ device: error.destination.drive, error: error.error }) // TODO: device should be error.destination
|
||||
})
|
||||
destinationStream.on('fail', onFail2)
|
||||
sourceMetadata = metadata
|
||||
return new Promise((resolve, reject) => {
|
||||
let done = false
|
||||
let hasher
|
||||
sourceStream.on('error', reject)
|
||||
destinationStream.on('progress', onProgress2)
|
||||
if (verify && !sparse) {
|
||||
const hasher = sdk.sourceDestination.createHasher()
|
||||
hasher = sdk.sourceDestination.createHasher()
|
||||
hasher.on('checksum', (cs) => {
|
||||
checksum = cs
|
||||
if (done) {
|
||||
@ -178,6 +187,13 @@ function pipeRegularSourceToDestination(source, destination, verify, onProgress,
|
||||
}
|
||||
destinationStream.on('done', () => {
|
||||
done = true;
|
||||
if (allDestinationsFailed() && (hasher !== undefined)) {
|
||||
sourceStream.unpipe(hasher)
|
||||
verify = false
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
if (sparse || !verify || (checksum !== undefined)) {
|
||||
resolve()
|
||||
}
|
||||
@ -196,15 +212,24 @@ function pipeRegularSourceToDestination(source, destination, verify, onProgress,
|
||||
updateState()
|
||||
const verifier = destination.createVerifier(sparse ? sourceMetadata.blockMap : checksum, sourceMetadata.size) // TODO: ensure blockMap exists
|
||||
verifier.on('progress', onProgress2)
|
||||
return runVerifier(verifier, onFail)
|
||||
verifier.on('fail', onFail2)
|
||||
return new Promise((resolve) => {
|
||||
verifier.on('finish', resolve);
|
||||
verifier.run();
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
step = 'finished'
|
||||
updateState()
|
||||
//onProgress2({ speed: 0, position: sourceMetadata.size })
|
||||
return Promise.all([ source.close(), destination.close() ])
|
||||
})
|
||||
.then(() => {
|
||||
// If all destinations errored, treat the last fail as an error
|
||||
if (allDestinationsFailed()) {
|
||||
const lastError = lastMapValue(errors)
|
||||
throw lastError
|
||||
}
|
||||
const result = {
|
||||
bytesWritten: lastPosition,
|
||||
devices: {
|
||||
@ -220,16 +245,9 @@ function pipeRegularSourceToDestination(source, destination, verify, onProgress,
|
||||
error.device = destination.drive.device
|
||||
result.errors.push(error)
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
function sourceDestinationDisposer(sourceDestination) {
|
||||
return Bluebird.resolve(sourceDestination.open())
|
||||
.return(sourceDestination)
|
||||
.disposer(() => {
|
||||
return Bluebird.resolve(sourceDestination.close()).catchReturn()
|
||||
onFinish(result)
|
||||
})
|
||||
.catch(onError)
|
||||
}
|
||||
|
||||
ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
@ -315,39 +333,32 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
|
||||
/**
|
||||
* @summary Failure handler (non-fatal errors)
|
||||
* @param {Object} event - event data (error & device)
|
||||
* @param {Object} error - MultiDestinationError
|
||||
* @example
|
||||
* writer.on('fail', onFail)
|
||||
*/
|
||||
const onFail = (event) => {
|
||||
const onFail = (error) => {
|
||||
ipc.of[IPC_SERVER_ID].emit('fail', {
|
||||
device: event.device,
|
||||
error: errors.toJSON(event.error)
|
||||
device: error.destination.drive, // TODO: device should be error.destination
|
||||
error: errors.toJSON(error.error)
|
||||
})
|
||||
}
|
||||
|
||||
const destinations = _.map(options.destinations, 'drive.device')
|
||||
const destinations = _.map(options.destinations, 'device')
|
||||
const dests = options.destinations.map((destination) => {
|
||||
return new sdk.sourceDestination.BlockDevice(destination, options.unmountOnSuccess)
|
||||
})
|
||||
const destination = new sdk.sourceDestination.MultiDestination(dests)
|
||||
const source = new sdk.sourceDestination.File(options.imagePath, sdk.sourceDestination.File.OpenFlags.Read)
|
||||
source.getInnerSource()
|
||||
.then((innerSource) => {
|
||||
return Bluebird.using(
|
||||
sourceDestinationDisposer(innerSource),
|
||||
sourceDestinationDisposer(new sdk.sourceDestination.MultiDestination(dests)),
|
||||
(innerSource, destination) => {
|
||||
destination.on('fail', onFail)
|
||||
return pipeRegularSourceToDestination(innerSource, destination, options.validateWriteOnSuccess, onProgress, onFail)
|
||||
}
|
||||
writeAndValidate(
|
||||
source,
|
||||
destination,
|
||||
options.validateWriteOnSuccess,
|
||||
onProgress,
|
||||
onFail,
|
||||
onFinish,
|
||||
onError
|
||||
)
|
||||
})
|
||||
.then((results) => {
|
||||
onFinish(results)
|
||||
})
|
||||
.catch((error) => {
|
||||
onError(error)
|
||||
})
|
||||
|
||||
log(`Image: ${options.imagePath}`)
|
||||
log(`Devices: ${destinations.join(', ')}`)
|
||||
|
6
npm-shrinkwrap.json
generated
6
npm-shrinkwrap.json
generated
@ -3068,7 +3068,7 @@
|
||||
},
|
||||
"etcher-sdk": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "git://github.com/resin-io-modules/etcher-sdk.git#bda51535715edb3691b783973801434bb3d78b30",
|
||||
"resolved": "git://github.com/resin-io-modules/etcher-sdk.git#9d483eec059a9e149d0f1c2e2746b8a816f87bb4",
|
||||
"dependencies": {
|
||||
"@types/lodash": {
|
||||
"version": "4.14.110",
|
||||
@ -3126,6 +3126,10 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
|
||||
|
@ -63,7 +63,7 @@
|
||||
"debug": "3.1.0",
|
||||
"drivelist": "6.4.6",
|
||||
"electron-is-running-in-asar": "1.0.0",
|
||||
"etcher-sdk": "github:resin-io-modules/etcher-sdk#bda51535715edb3691b783973801434bb3d78b30",
|
||||
"etcher-sdk": "github:resin-io-modules/etcher-sdk#9d483eec059a9e149d0f1c2e2746b8a816f87bb4",
|
||||
"file-type": "4.1.0",
|
||||
"flexboxgrid": "6.3.0",
|
||||
"gpt": "1.0.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user