mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 07:17:18 +00:00
fix(GUI): log known user errors when querying S3 for updates (#1655)
We send an HTTP request to S3 to determine the latest available version. There are various error that can happen that we don't have control over (like `ETIMEDOUT`). The current approach is to whitelist certain errors and pretend there is no update available, however this commit improves that whole situation. Instead of swallowing these errors, we throw a user error from the function that determines the latest available version. From the application code, we check if that function throws a user error, and if so, instead of showing it to the user, we log a mixpanel event and carry on. This change is motivated by the latest reporter error we can't do anything about: `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`. Fixes: https://github.com/resin-io/etcher/issues/1525 Change-Type: patch Changelog-Entry: Fix `UNABLE_TO_GET_ISSUER_CERT_LOCALLY` error at startup when behind certain proxies. Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
This commit is contained in:
parent
d8e31665a0
commit
269aafd625
@ -34,6 +34,7 @@ const messages = require('../shared/messages')
|
||||
const s3Packages = require('../shared/s3-packages')
|
||||
const release = require('../shared/release')
|
||||
const store = require('../shared/store')
|
||||
const errors = require('../shared/errors')
|
||||
const packageJSON = require('../../package.json')
|
||||
const flashState = require('../shared/models/flash-state')
|
||||
const settings = require('./models/settings')
|
||||
@ -153,6 +154,17 @@ app.run(() => {
|
||||
return updateNotifier.notify(latestVersion, {
|
||||
allowSleepUpdateCheck: currentReleaseType === release.RELEASE_TYPE.PRODUCTION
|
||||
})
|
||||
|
||||
// If the error is an update user error, then we don't want
|
||||
// to bother users each time they open the app.
|
||||
// See: https://github.com/resin-io/etcher/issues/1525
|
||||
}).catch((error) => {
|
||||
return errors.isUserError(error) && error.code === 'UPDATE_USER_ERROR'
|
||||
}, (error) => {
|
||||
analytics.logEvent('Update check user error', {
|
||||
title: errors.getTitle(error),
|
||||
description: errors.getDescription(error)
|
||||
})
|
||||
})
|
||||
}).catch(exceptionReporter.report)
|
||||
})
|
||||
|
@ -257,6 +257,10 @@ exports.createError = (options) => {
|
||||
error.report = false
|
||||
}
|
||||
|
||||
if (!_.isNil(options.code)) {
|
||||
error.code = options.code
|
||||
}
|
||||
|
||||
return error
|
||||
}
|
||||
|
||||
@ -288,7 +292,8 @@ exports.createUserError = (options) => {
|
||||
return exports.createError({
|
||||
title: options.title,
|
||||
description: options.description,
|
||||
report: false
|
||||
report: false,
|
||||
code: options.code
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ const Bluebird = require('bluebird')
|
||||
const request = Bluebird.promisifyAll(require('request'))
|
||||
const xml = Bluebird.promisifyAll(require('xml2js'))
|
||||
const release = require('./release')
|
||||
const errors = require('./errors')
|
||||
|
||||
/**
|
||||
* @summary Etcher S3 bucket URLs
|
||||
@ -158,13 +159,22 @@ exports.getRemoteVersions = _.memoize((bucketUrl) => {
|
||||
code: 'ETIMEDOUT'
|
||||
}, {
|
||||
code: 'EHOSTDOWN'
|
||||
}, {
|
||||
|
||||
// May happen when behind a proxy
|
||||
code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
|
||||
|
||||
}, {
|
||||
|
||||
// May happen when behind a proxy
|
||||
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
|
||||
|
||||
}, () => {
|
||||
return []
|
||||
}, (error) => {
|
||||
throw errors.createUserError({
|
||||
title: 'Unable to check for updates',
|
||||
description: `We got an ${error.code} error in response`,
|
||||
code: 'UPDATE_USER_ERROR'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -445,6 +445,16 @@ describe('Shared: Errors', function () {
|
||||
m.chai.expect(errors.getDescription(error)).to.equal('Something happened')
|
||||
})
|
||||
|
||||
it('should correctly add a code', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo',
|
||||
description: 'Something happened',
|
||||
code: 'HELLO'
|
||||
})
|
||||
|
||||
m.chai.expect(error.code).to.equal('HELLO')
|
||||
})
|
||||
|
||||
it('should correctly add only a title', function () {
|
||||
const error = errors.createError({
|
||||
title: 'Foo'
|
||||
@ -541,6 +551,15 @@ describe('Shared: Errors', function () {
|
||||
m.chai.expect(errors.getDescription(error)).to.equal(error.stack)
|
||||
})
|
||||
|
||||
it('should correctly add a code', function () {
|
||||
const error = errors.createUserError({
|
||||
title: 'Foo',
|
||||
code: 'HELLO'
|
||||
})
|
||||
|
||||
m.chai.expect(error.code).to.equal('HELLO')
|
||||
})
|
||||
|
||||
it('should ignore an empty description', function () {
|
||||
const error = errors.createUserError({
|
||||
title: 'Foo',
|
||||
|
@ -22,6 +22,7 @@ const request = Bluebird.promisifyAll(require('request'))
|
||||
const nock = require('nock')
|
||||
const s3Packages = require('../../lib/shared/s3-packages')
|
||||
const release = require('../../lib/shared/release')
|
||||
const errors = require('../../lib/shared/errors')
|
||||
|
||||
describe('Shared: s3Packages', function () {
|
||||
describe('.getBucketUrlFromReleaseType()', function () {
|
||||
@ -573,9 +574,10 @@ describe('Shared: s3Packages', function () {
|
||||
nock.cleanAll()
|
||||
})
|
||||
|
||||
it('should be rejected with an error', function (done) {
|
||||
it('should be rejected with a non-user error', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(error).to.be.an.instanceof(Error)
|
||||
m.chai.expect(errors.isUserError(error)).to.be.false
|
||||
done()
|
||||
})
|
||||
})
|
||||
@ -594,11 +596,12 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -615,11 +618,12 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -636,11 +640,12 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -657,11 +662,12 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -678,11 +684,12 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -699,11 +706,34 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('given UNABLE_TO_GET_ISSUER_CERT_LOCALLY', function () {
|
||||
beforeEach(function () {
|
||||
const error = new Error('UNABLE_TO_GET_ISSUER_CERT_LOCALLY')
|
||||
error.code = 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
|
||||
|
||||
this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync')
|
||||
this.requestGetAsyncStub.returns(Bluebird.reject(error))
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -720,11 +750,12 @@ describe('Shared: s3Packages', function () {
|
||||
this.requestGetAsyncStub.restore()
|
||||
})
|
||||
|
||||
it('should resolve an empty array', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => {
|
||||
m.chai.expect(versions).to.deep.equal([])
|
||||
it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) {
|
||||
s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => {
|
||||
m.chai.expect(errors.isUserError(error)).to.be.true
|
||||
m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user