mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-25 15:57:18 +00:00
feat(GUI): warn the user on large drive selection (#2045)
We warn the user when they select a large drive to confirm they want to flash in case the device is important. Fixes: https://github.com/resin-io/etcher/issues/1916 Change-Type: patch Changelog-Entry: Warn the user on selection of large drives.
This commit is contained in:
parent
b1b82301d9
commit
91719435d9
@ -75,10 +75,7 @@ module.exports = function (
|
|||||||
return $q.resolve(false)
|
return $q.resolve(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constraints.isDriveSizeRecommended(drive, selectionState.getImage())) {
|
if (!constraints.isDriveSizeRecommended(drive, selectionState.getImage())) {
|
||||||
return $q.resolve(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return WarningModalService.display({
|
return WarningModalService.display({
|
||||||
confirmationLabel: 'Yes, continue',
|
confirmationLabel: 'Yes, continue',
|
||||||
description: [
|
description: [
|
||||||
@ -88,6 +85,16 @@ module.exports = function (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (constraints.isDriveSizeLarge(drive)) {
|
||||||
|
return WarningModalService.display({
|
||||||
|
confirmationLabel: 'Yes, continue',
|
||||||
|
description: messages.warning.largeDriveSize(drive)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return $q.resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Toggle a drive selection
|
* @summary Toggle a drive selection
|
||||||
* @function
|
* @function
|
||||||
|
@ -276,6 +276,29 @@ exports.isDriveSizeRecommended = (drive, image) => {
|
|||||||
return _.get(drive, [ 'size' ], UNKNOWN_SIZE) >= _.get(image, [ 'recommendedDriveSize' ], UNKNOWN_SIZE)
|
return _.get(drive, [ 'size' ], UNKNOWN_SIZE) >= _.get(image, [ 'recommendedDriveSize' ], UNKNOWN_SIZE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 64GB
|
||||||
|
* @private
|
||||||
|
* @constant
|
||||||
|
*/
|
||||||
|
exports.LARGE_DRIVE_SIZE = 64e9
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Check whether a drive's size is 'large'
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @param {Object} drive - drive
|
||||||
|
* @returns {Boolean} whether drive size is large
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* if (constraints.isDriveSizeLarge(drive)) {
|
||||||
|
* console.log('Impressive')
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.isDriveSizeLarge = (drive) => {
|
||||||
|
return _.get(drive, [ 'size' ], UNKNOWN_SIZE) > exports.LARGE_DRIVE_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Drive/image compatibility status messages.
|
* @summary Drive/image compatibility status messages.
|
||||||
* @public
|
* @public
|
||||||
@ -330,7 +353,16 @@ exports.COMPATIBILITY_STATUS_MESSAGES = {
|
|||||||
* @description
|
* @description
|
||||||
* The drive contains the image and therefore cannot be written to.
|
* The drive contains the image and therefore cannot be written to.
|
||||||
*/
|
*/
|
||||||
CONTAINS_IMAGE: 'Drive Contains Image'
|
CONTAINS_IMAGE: 'Drive Contains Image',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {String} LARGE_DRIVE
|
||||||
|
* @memberof COMPATIBILITY_STATUS_MESSAGES
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* The drive is large and therefore likely not a medium you want to write to.
|
||||||
|
*/
|
||||||
|
LARGE_DRIVE: 'Large Drive'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -416,6 +448,13 @@ exports.getDriveImageCompatibilityStatuses = (drive, image) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exports.isDriveSizeLarge(drive)) {
|
||||||
|
statusList.push({
|
||||||
|
type: exports.COMPATIBILITY_STATUS_TYPES.WARNING,
|
||||||
|
message: exports.COMPATIBILITY_STATUS_MESSAGES.LARGE_DRIVE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!_.isNil(drive) && !exports.isDriveSizeRecommended(drive, image)) {
|
if (!_.isNil(drive) && !exports.isDriveSizeRecommended(drive, image)) {
|
||||||
statusList.push({
|
statusList.push({
|
||||||
type: exports.COMPATIBILITY_STATUS_TYPES.WARNING,
|
type: exports.COMPATIBILITY_STATUS_TYPES.WARNING,
|
||||||
|
@ -78,6 +78,13 @@ module.exports = {
|
|||||||
'The image does not appear to contain a partition table,',
|
'The image does not appear to contain a partition table,',
|
||||||
'and might not be recognized or bootable by your device.'
|
'and might not be recognized or bootable by your device.'
|
||||||
].join(' ')
|
].join(' ')
|
||||||
|
},
|
||||||
|
|
||||||
|
largeDriveSize: (drive) => {
|
||||||
|
return [
|
||||||
|
`Drive ${drive.description} (${drive.device}) is unusually large for an SD card or USB stick.`,
|
||||||
|
'\n\nAre you sure you want to flash this drive?'
|
||||||
|
].join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -144,6 +144,9 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
constraints.isDriveValid(drive, image),
|
constraints.isDriveValid(drive, image),
|
||||||
constraints.isDriveSizeRecommended(drive, image),
|
constraints.isDriveSizeRecommended(drive, image),
|
||||||
|
|
||||||
|
// We don't want to auto-select large drives
|
||||||
|
!constraints.isDriveSizeLarge(drive),
|
||||||
|
|
||||||
// We don't want to auto-select system drives,
|
// We don't want to auto-select system drives,
|
||||||
// even when "unsafe mode" is enabled
|
// even when "unsafe mode" is enabled
|
||||||
!constraints.isSystemDrive(drive)
|
!constraints.isSystemDrive(drive)
|
||||||
|
@ -946,6 +946,48 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('.isDriveSizeLarge()', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.drive = {
|
||||||
|
device: '/dev/disk2',
|
||||||
|
name: 'My Drive',
|
||||||
|
isReadonly: false,
|
||||||
|
isSystem: false,
|
||||||
|
disabled: false,
|
||||||
|
mountpoints: [
|
||||||
|
{
|
||||||
|
path: this.mountpoint
|
||||||
|
}
|
||||||
|
],
|
||||||
|
size: constraints.LARGE_DRIVE_SIZE + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
this.image = {
|
||||||
|
path: path.join(__dirname, 'rpi.img'),
|
||||||
|
size: {
|
||||||
|
original: this.drive.size - 1,
|
||||||
|
final: {
|
||||||
|
estimation: false,
|
||||||
|
value: this.drive.size - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given a drive bigger than the unusually large drive size', function () {
|
||||||
|
it('should return true', function () {
|
||||||
|
m.chai.expect(constraints.isDriveSizeLarge(this.drive)).to.be.true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given a drive smaller than the unusually large drive size', function () {
|
||||||
|
it('should return false', function () {
|
||||||
|
this.drive.size = constraints.LARGE_DRIVE_SIZE - 1
|
||||||
|
m.chai.expect(constraints.isDriveSizeLarge(this.drive)).to.be.false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('.getDriveImageCompatibilityStatuses', function () {
|
describe('.getDriveImageCompatibilityStatuses', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
@ -1080,6 +1122,17 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('given the drive is unusually large', function () {
|
||||||
|
it('should return the large drive size warning', function () {
|
||||||
|
this.drive.size = constraints.LARGE_DRIVE_SIZE + 1
|
||||||
|
|
||||||
|
const result = constraints.getDriveImageCompatibilityStatuses(this.drive, this.image)
|
||||||
|
const expectedTuples = [ [ 'WARNING', 'LARGE_DRIVE' ] ]
|
||||||
|
|
||||||
|
expectStatusTypesAndMessagesToBe(result, expectedTuples)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('given the image is null', () => {
|
describe('given the image is null', () => {
|
||||||
it('should return an empty list', function () {
|
it('should return an empty list', function () {
|
||||||
const result = constraints.getDriveImageCompatibilityStatuses(this.drive, null)
|
const result = constraints.getDriveImageCompatibilityStatuses(this.drive, null)
|
||||||
|
@ -20,6 +20,7 @@ const m = require('mochainon')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const availableDrives = require('../../../lib/shared/models/available-drives')
|
const availableDrives = require('../../../lib/shared/models/available-drives')
|
||||||
const selectionState = require('../../../lib/shared/models/selection-state')
|
const selectionState = require('../../../lib/shared/models/selection-state')
|
||||||
|
const constraints = require('../../../lib/shared/drive-constraints')
|
||||||
|
|
||||||
describe('Model: availableDrives', function () {
|
describe('Model: availableDrives', function () {
|
||||||
describe('availableDrives', function () {
|
describe('availableDrives', function () {
|
||||||
@ -347,6 +348,27 @@ describe('Model: availableDrives', function () {
|
|||||||
|
|
||||||
m.chai.expect(selectionState.hasDrive()).to.be.false
|
m.chai.expect(selectionState.hasDrive()).to.be.false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not auto-select a single large size drive', function () {
|
||||||
|
m.chai.expect(selectionState.hasDrive()).to.be.false
|
||||||
|
|
||||||
|
availableDrives.setDrives([
|
||||||
|
{
|
||||||
|
device: '/dev/sdb',
|
||||||
|
name: 'Foo',
|
||||||
|
size: constraints.LARGE_DRIVE_SIZE + 1,
|
||||||
|
mountpoints: [
|
||||||
|
{
|
||||||
|
path: '/mnt/foo'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
system: false,
|
||||||
|
protected: false
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
m.chai.expect(selectionState.hasDrive()).to.be.false
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user