refactor: ExternalHostError (#6563)

This commit is contained in:
Rhys Arkins 2020-06-22 21:28:02 +02:00 committed by GitHub
parent cb84fa9d9b
commit 52a074e041
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 211 additions and 223 deletions

View file

@ -1,6 +1,6 @@
import { PLATFORM_FAILURE } from '../../../constants/error-messages';
import { PLATFORM_TYPE_GITHUB } from '../../../constants/platforms'; import { PLATFORM_TYPE_GITHUB } from '../../../constants/platforms';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { ExternalHostError } from '../../../types/error';
import { Http, HttpOptions } from '../../../util/http'; import { Http, HttpOptions } from '../../../util/http';
import { Preset, PresetConfig } from '../common'; import { Preset, PresetConfig } from '../common';
import { PRESET_DEP_NOT_FOUND, fetchPreset } from '../util'; import { PRESET_DEP_NOT_FOUND, fetchPreset } from '../util';
@ -27,7 +27,7 @@ export async function fetchJSONFile(
res = await http.getJson(url, opts); res = await http.getJson(url, opts);
} catch (err) { } catch (err) {
// istanbul ignore if: not testable with nock // istanbul ignore if: not testable with nock
if (err.message === PLATFORM_FAILURE) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
logger.debug( logger.debug(

View file

@ -70,7 +70,7 @@ Array [
] ]
`; `;
exports[`config/presets/gitlab/index getPreset() throws platform-failure 1`] = ` exports[`config/presets/gitlab/index getPreset() throws EXTERNAL_HOST_ERROR 1`] = `
Array [ Array [
Object { Object {
"headers": Object { "headers": Object {

View file

@ -1,6 +1,6 @@
import * as httpMock from '../../../../test/httpMock'; import * as httpMock from '../../../../test/httpMock';
import { getName } from '../../../../test/util'; import { getName } from '../../../../test/util';
import { PLATFORM_FAILURE } from '../../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../../constants/error-messages';
import { PRESET_DEP_NOT_FOUND } from '../util'; import { PRESET_DEP_NOT_FOUND } from '../util';
import * as gitlab from '.'; import * as gitlab from '.';
@ -18,14 +18,14 @@ describe(getName(__filename), () => {
}); });
describe('getPreset()', () => { describe('getPreset()', () => {
it('throws platform-failure', async () => { it('throws EXTERNAL_HOST_ERROR', async () => {
httpMock.scope(gitlabApiHost).get(`${basePath}/branches`).reply(500); httpMock.scope(gitlabApiHost).get(`${basePath}/branches`).reply(500);
await expect( await expect(
gitlab.getPreset({ gitlab.getPreset({
packageName: 'some/repo', packageName: 'some/repo',
presetName: 'non-default', presetName: 'non-default',
}) })
).rejects.toThrow(PLATFORM_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });

View file

@ -1,5 +1,5 @@
import { PLATFORM_FAILURE } from '../../../constants/error-messages';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { ExternalHostError } from '../../../types/error';
import type { GitLabBranch } from '../../../types/platform/gitlab'; import type { GitLabBranch } from '../../../types/platform/gitlab';
import { GitlabHttp } from '../../../util/http/gitlab'; import { GitlabHttp } from '../../../util/http/gitlab';
import { Preset, PresetConfig } from '../common'; import { Preset, PresetConfig } from '../common';
@ -42,7 +42,7 @@ export async function fetchJSONFile(
const url = `${endpoint}projects/${urlEncodedRepo}/repository/files/${urlEncodedPkgName}/raw?ref=${defautlBranchName}`; const url = `${endpoint}projects/${urlEncodedRepo}/repository/files/${urlEncodedPkgName}/raw?ref=${defautlBranchName}`;
return (await gitlabApi.getJson<Preset>(url)).body; return (await gitlabApi.getJson<Preset>(url)).body;
} catch (err) { } catch (err) {
if (err.message === PLATFORM_FAILURE) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
logger.debug( logger.debug(

View file

@ -1,10 +1,7 @@
import is from '@sindresorhus/is'; import is from '@sindresorhus/is';
import { import { CONFIG_VALIDATION } from '../../constants/error-messages';
CONFIG_VALIDATION,
DATASOURCE_FAILURE,
PLATFORM_FAILURE,
} from '../../constants/error-messages';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { regEx } from '../../util/regex'; import { regEx } from '../../util/regex';
import { RenovateConfig } from '../common'; import { RenovateConfig } from '../common';
import * as massage from '../massage'; import * as massage from '../massage';
@ -207,10 +204,7 @@ export async function resolveConfigPresets(
} catch (err) { } catch (err) {
logger.debug({ preset, err }, 'Preset fetch error'); logger.debug({ preset, err }, 'Preset fetch error');
// istanbul ignore if // istanbul ignore if
if ( if (err instanceof ExternalHostError) {
err.message === PLATFORM_FAILURE ||
err.message === DATASOURCE_FAILURE
) {
throw err; throw err;
} }
const error = new Error(CONFIG_VALIDATION); const error = new Error(CONFIG_VALIDATION);

View file

@ -5,7 +5,6 @@ export const SYSTEM_INSUFFICIENT_MEMORY = 'out-of-memory';
// Platform Error // Platform Error
export const PLATFORM_AUTHENTICATION_ERROR = 'authentication-error'; export const PLATFORM_AUTHENTICATION_ERROR = 'authentication-error';
export const PLATFORM_BAD_CREDENTIALS = 'bad-credentials'; export const PLATFORM_BAD_CREDENTIALS = 'bad-credentials';
export const PLATFORM_FAILURE = 'platform-failure';
export const PLATFORM_GPG_FAILED = 'gpg-failed'; export const PLATFORM_GPG_FAILED = 'gpg-failed';
export const PLATFORM_INTEGRATION_UNAUTHORIZED = 'integration-unauthorized'; export const PLATFORM_INTEGRATION_UNAUTHORIZED = 'integration-unauthorized';
export const PLATFORM_NOT_FOUND = 'platform-not-found'; export const PLATFORM_NOT_FOUND = 'platform-not-found';
@ -34,8 +33,8 @@ export const REPOSITORY_UNINITIATED = 'uninitiated';
export const MANAGER_LOCKFILE_ERROR = 'lockfile-error'; export const MANAGER_LOCKFILE_ERROR = 'lockfile-error';
export const MANAGER_NO_PACKAGE_FILES = 'no-package-files'; export const MANAGER_NO_PACKAGE_FILES = 'no-package-files';
// Datasource error // Host error
export const DATASOURCE_FAILURE = 'registry-failure'; export const EXTERNAL_HOST_ERROR = 'external-host-error';
// Worker Error // Worker Error
export const WORKER_FILE_UPDATE_FAILED = 'update-failure'; export const WORKER_FILE_UPDATE_FAILED = 'update-failure';

View file

@ -1,7 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import { getPkgReleases } from '..'; import { getPkgReleases } from '..';
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import { id as datasource } from '.'; import { id as datasource } from '.';
let res1 = fs.readFileSync( let res1 = fs.readFileSync(
@ -36,14 +36,14 @@ describe('datasource/cdnjs', () => {
httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(200, null); httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(200, null);
await expect( await expect(
getPkgReleases({ datasource, depName: 'foo/bar' }) getPkgReleases({ datasource, depName: 'foo/bar' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('throws for error', async () => { it('throws for error', async () => {
httpMock.scope(baseUrl).get(pathFor('foo/bar')).replyWithError('error'); httpMock.scope(baseUrl).get(pathFor('foo/bar')).replyWithError('error');
await expect( await expect(
getPkgReleases({ datasource, depName: 'foo/bar' }) getPkgReleases({ datasource, depName: 'foo/bar' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('returns null for 404', async () => { it('returns null for 404', async () => {
@ -70,28 +70,28 @@ describe('datasource/cdnjs', () => {
httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(401); httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(401);
await expect( await expect(
getPkgReleases({ datasource, depName: 'foo/bar' }) getPkgReleases({ datasource, depName: 'foo/bar' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('throws for 429', async () => { it('throws for 429', async () => {
httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(429); httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(429);
await expect( await expect(
getPkgReleases({ datasource, depName: 'foo/bar' }) getPkgReleases({ datasource, depName: 'foo/bar' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('throws for 5xx', async () => { it('throws for 5xx', async () => {
httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(502); httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(502);
await expect( await expect(
getPkgReleases({ datasource, depName: 'foo/bar' }) getPkgReleases({ datasource, depName: 'foo/bar' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('returns null for unknown error', async () => { it('returns null for unknown error', async () => {
httpMock.scope(baseUrl).get(pathFor('foo/bar')).replyWithError('error'); httpMock.scope(baseUrl).get(pathFor('foo/bar')).replyWithError('error');
await expect( await expect(
getPkgReleases({ datasource, depName: 'foo/bar' }) getPkgReleases({ datasource, depName: 'foo/bar' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('processes real data', async () => { it('processes real data', async () => {

View file

@ -1,7 +1,8 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { CachePromise, cacheAble } from '../cache'; import { CachePromise, cacheAble } from '../cache';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'cdnjs'; export const id = 'cdnjs';
@ -60,7 +61,7 @@ export async function getReleases({
logger.debug({ library }, 'cdnjs library not found'); logger.debug({ library }, 'cdnjs library not found');
return null; return null;
} }
// Throw a DatasourceError for all other types of errors // Throw an ExternalHostError for all other types of errors
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
} }

View file

@ -1,5 +1,3 @@
import { DATASOURCE_FAILURE } from '../constants/error-messages';
export interface Config { export interface Config {
datasource?: string; datasource?: string;
depName?: string; depName?: string;
@ -80,18 +78,3 @@ export interface Datasource {
defaultConfig?: object; defaultConfig?: object;
registryStrategy?: 'first' | 'hunt' | 'merge'; registryStrategy?: 'first' | 'hunt' | 'merge';
} }
export class DatasourceError extends Error {
err: Error;
datasource?: string;
lookupName?: string;
constructor(err: Error) {
super(DATASOURCE_FAILURE);
// Set the prototype explicitly: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
Object.setPrototypeOf(this, DatasourceError.prototype);
this.err = err;
}
}

View file

@ -764,7 +764,7 @@ Array [
] ]
`; `;
exports[`datasource/crate getReleases throws for 5xx 1`] = `[Error: registry-failure]`; exports[`datasource/crate getReleases throws for 5xx 1`] = `[Error: external-host-error]`;
exports[`datasource/crate getReleases throws for 5xx 2`] = ` exports[`datasource/crate getReleases throws for 5xx 2`] = `
Array [ Array [

View file

@ -1,12 +1,8 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { import { GetReleasesConfig, Release, ReleaseResult } from '../common';
DatasourceError,
GetReleasesConfig,
Release,
ReleaseResult,
} from '../common';
export const id = 'crate'; export const id = 'crate';
@ -105,7 +101,7 @@ export async function getReleases({
err.statusCode === 429 || err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
logger.warn({ err, lookupName }, 'crates.io lookup failure: Unknown error'); logger.warn({ err, lookupName }, 'crates.io lookup failure: Unknown error');
return null; return null;

View file

@ -144,7 +144,7 @@ Array [
] ]
`; `;
exports[`datasource/dart getReleases throws for 5xx 1`] = `[Error: registry-failure]`; exports[`datasource/dart getReleases throws for 5xx 1`] = `[Error: external-host-error]`;
exports[`datasource/dart getReleases throws for 5xx 2`] = ` exports[`datasource/dart getReleases throws for 5xx 2`] = `
Array [ Array [

View file

@ -1,6 +1,7 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http, HttpResponse } from '../../util/http'; import { Http, HttpResponse } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'dart'; export const id = 'dart';
@ -33,7 +34,7 @@ export async function getReleases({
err.statusCode === 429 || err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
logger.warn( logger.warn(
{ err, lookupName }, { err, lookupName },

View file

@ -2,7 +2,7 @@ import AWS from 'aws-sdk';
import AWSMock from 'aws-sdk-mock'; import AWSMock from 'aws-sdk-mock';
import { getDigest, getPkgReleases } from '..'; import { getDigest, getPkgReleases } from '..';
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import * as _hostRules from '../../util/host-rules'; import * as _hostRules from '../../util/host-rules';
import * as docker from '.'; import * as docker from '.';
@ -331,13 +331,13 @@ describe('api/docker', () => {
httpMock.scope(baseUrl).get('/').replyWithError({ statusCode: 429 }); httpMock.scope(baseUrl).get('/').replyWithError({ statusCode: 429 });
await expect( await expect(
getDigest({ datasource: 'docker', depName: 'some-dep' }, 'latest') getDigest({ datasource: 'docker', depName: 'some-dep' }, 'latest')
).rejects.toThrow(Error(DATASOURCE_FAILURE)); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
it('should throw error for 5xx', async () => { it('should throw error for 5xx', async () => {
httpMock.scope(baseUrl).get('/').replyWithError({ statusCode: 504 }); httpMock.scope(baseUrl).get('/').replyWithError({ statusCode: 504 });
await expect( await expect(
getDigest({ datasource: 'docker', depName: 'some-dep' }, 'latest') getDigest({ datasource: 'docker', depName: 'some-dep' }, 'latest')
).rejects.toThrow(Error(DATASOURCE_FAILURE)); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
}); });
describe('getReleases', () => { describe('getReleases', () => {

View file

@ -6,10 +6,11 @@ import parseLinkHeader from 'parse-link-header';
import wwwAuthenticate from 'www-authenticate'; import wwwAuthenticate from 'www-authenticate';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { HostRule } from '../../types'; import { HostRule } from '../../types';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import * as hostRules from '../../util/host-rules'; import * as hostRules from '../../util/host-rules';
import { Http, HttpResponse } from '../../util/http'; import { Http, HttpResponse } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
// TODO: add got typings when available // TODO: add got typings when available
// TODO: replace www-authenticate with https://www.npmjs.com/package/auth-header ? // TODO: replace www-authenticate with https://www.npmjs.com/package/auth-header ?
@ -220,14 +221,14 @@ async function getAuthHeaders(
} }
// prettier-ignore // prettier-ignore
if (err.name === 'RequestError' && registry.endsWith('docker.io')) { // lgtm [js/incomplete-url-substring-sanitization] if (err.name === 'RequestError' && registry.endsWith('docker.io')) { // lgtm [js/incomplete-url-substring-sanitization]
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
// prettier-ignore // prettier-ignore
if (err.statusCode === 429 && registry.endsWith('docker.io')) { // lgtm [js/incomplete-url-substring-sanitization] if (err.statusCode === 429 && registry.endsWith('docker.io')) { // lgtm [js/incomplete-url-substring-sanitization]
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.statusCode >= 500 && err.statusCode < 600) { if (err.statusCode >= 500 && err.statusCode < 600) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
logger.warn( logger.warn(
{ registry, dockerRepository: repository, err }, { registry, dockerRepository: repository, err },
@ -267,7 +268,7 @@ async function getManifestResponse(
}); });
return manifestResponse; return manifestResponse;
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
if (err.statusCode === 401) { if (err.statusCode === 401) {
@ -292,10 +293,10 @@ async function getManifestResponse(
} }
// prettier-ignore // prettier-ignore
if (err.statusCode === 429 && registry.endsWith('docker.io')) { // lgtm [js/incomplete-url-substring-sanitization] if (err.statusCode === 429 && registry.endsWith('docker.io')) { // lgtm [js/incomplete-url-substring-sanitization]
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.statusCode >= 500 && err.statusCode < 600) { if (err.statusCode >= 500 && err.statusCode < 600) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.code === 'ETIMEDOUT') { if (err.code === 'ETIMEDOUT') {
logger.debug( logger.debug(
@ -356,7 +357,7 @@ export async function getDigest(
logger.debug({ digest }, 'Got docker digest'); logger.debug({ digest }, 'Got docker digest');
} }
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
logger.debug( logger.debug(
@ -413,7 +414,7 @@ async function getTags(
await globalCache.set(cacheNamespace, cacheKey, tags, cacheMinutes); await globalCache.set(cacheNamespace, cacheKey, tags, cacheMinutes);
return tags; return tags;
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
logger.debug( logger.debug(
@ -441,14 +442,14 @@ async function getTags(
{ registry, dockerRepository: repository, err }, { registry, dockerRepository: repository, err },
'docker registry failure: too many requests' 'docker registry failure: too many requests'
); );
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.statusCode >= 500 && err.statusCode < 600) { if (err.statusCode >= 500 && err.statusCode < 600) {
logger.warn( logger.warn(
{ registry, dockerRepository: repository, err }, { registry, dockerRepository: repository, err },
'docker registry failure: internal error' 'docker registry failure: internal error'
); );
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.code === 'ETIMEDOUT') { if (err.code === 'ETIMEDOUT') {
logger.debug( logger.debug(
@ -543,7 +544,7 @@ async function getLabels(
await globalCache.set(cacheNamespace, cacheKey, labels, cacheMinutes); await globalCache.set(cacheNamespace, cacheKey, labels, cacheMinutes);
return labels; return labels;
} catch (err) { } catch (err) {
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
if (err.statusCode === 400 || err.statusCode === 401) { if (err.statusCode === 400 || err.statusCode === 401) {

View file

@ -119,7 +119,7 @@ Array [
] ]
`; `;
exports[`datasource/galaxy getReleases throws for 5xx 1`] = `[Error: registry-failure]`; exports[`datasource/galaxy getReleases throws for 5xx 1`] = `[Error: external-host-error]`;
exports[`datasource/galaxy getReleases throws for 5xx 2`] = ` exports[`datasource/galaxy getReleases throws for 5xx 2`] = `
Array [ Array [

View file

@ -1,12 +1,8 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { import { GetReleasesConfig, Release, ReleaseResult } from '../common';
DatasourceError,
GetReleasesConfig,
Release,
ReleaseResult,
} from '../common';
export const id = 'galaxy'; export const id = 'galaxy';
@ -101,7 +97,7 @@ export async function getReleases({
err.statusCode === 429 || err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
return null; return null;
} }

View file

@ -1,7 +1,8 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { regEx } from '../../util/regex'; import { regEx } from '../../util/regex';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'gradle-version'; export const id = 'gradle-version';
export const defaultRegistryUrls = ['https://services.gradle.org/versions/all']; export const defaultRegistryUrls = ['https://services.gradle.org/versions/all'];
@ -50,7 +51,7 @@ export async function getReleases({
})); }));
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.host === 'services.gradle.org') { if (err.host === 'services.gradle.org') {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
logger.debug({ err }, 'gradle-version err'); logger.debug({ err }, 'gradle-version err');
return null; return null;

View file

@ -244,7 +244,7 @@ Array [
] ]
`; `;
exports[`datasource/helm getReleases throws for 5xx 1`] = `[Error: registry-failure]`; exports[`datasource/helm getReleases throws for 5xx 1`] = `[Error: external-host-error]`;
exports[`datasource/helm getReleases throws for 5xx 2`] = ` exports[`datasource/helm getReleases throws for 5xx 2`] = `
Array [ Array [

View file

@ -1,10 +1,11 @@
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { ensureTrailingSlash } from '../../util/url'; import { ensureTrailingSlash } from '../../util/url';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'helm'; export const id = 'helm';
@ -60,7 +61,7 @@ export async function getRepositoryData(
err.statusCode === 429 || err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
// istanbul ignore if // istanbul ignore if
if (err.name === 'UnsupportedProtocolError') { if (err.name === 'UnsupportedProtocolError') {

View file

@ -1,7 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import { getPkgReleases } from '..'; import { getPkgReleases } from '..';
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import * as _hostRules from '../../util/host-rules'; import * as _hostRules from '../../util/host-rules';
import { id as datasource } from '.'; import { id as datasource } from '.';
@ -67,14 +67,14 @@ describe('datasource/hex', () => {
httpMock.scope(baseUrl).get('/some_crate').reply(429); httpMock.scope(baseUrl).get('/some_crate').reply(429);
await expect( await expect(
getPkgReleases({ datasource, depName: 'some_crate' }) getPkgReleases({ datasource, depName: 'some_crate' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('throws for 5xx', async () => { it('throws for 5xx', async () => {
httpMock.scope(baseUrl).get('/some_crate').reply(502); httpMock.scope(baseUrl).get('/some_crate').reply(502);
await expect( await expect(
getPkgReleases({ datasource, depName: 'some_crate' }) getPkgReleases({ datasource, depName: 'some_crate' })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('returns null for unknown error', async () => { it('returns null for unknown error', async () => {

View file

@ -1,6 +1,7 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'hex'; export const id = 'hex';
@ -69,7 +70,7 @@ export async function getReleases({
err.statusCode === 429 || err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.statusCode === 401) { if (err.statusCode === 401) {

View file

@ -1,7 +1,7 @@
import { mocked } from '../../test/util'; import { mocked } from '../../test/util';
import { DATASOURCE_FAILURE } from '../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../constants/error-messages';
import { ExternalHostError } from '../types/error';
import { loadModules } from '../util/modules'; import { loadModules } from '../util/modules';
import { DatasourceError } from './common';
import * as datasourceDocker from './docker'; import * as datasourceDocker from './docker';
import * as datasourceGithubTags from './github-tags'; import * as datasourceGithubTags from './github-tags';
import * as datasourceMaven from './maven'; import * as datasourceMaven from './maven';
@ -131,9 +131,9 @@ describe('datasource/index', () => {
}); });
expect(res).not.toBeNull(); expect(res).not.toBeNull();
}); });
it('hunts registries and aborts on DatasourceError', async () => { it('hunts registries and aborts on ExternalHostError', async () => {
packagistDatasource.getReleases.mockImplementationOnce(() => { packagistDatasource.getReleases.mockImplementationOnce(() => {
throw new DatasourceError(new Error()); throw new ExternalHostError(new Error());
}); });
await expect( await expect(
datasource.getPkgReleases({ datasource.getPkgReleases({
@ -141,7 +141,7 @@ describe('datasource/index', () => {
depName: 'something', depName: 'something',
registryUrls: ['https://reg1.com', 'https://reg2.io'], registryUrls: ['https://reg1.com', 'https://reg2.io'],
}) })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
it('hunts registries and passes on error', async () => { it('hunts registries and passes on error', async () => {
packagistDatasource.getReleases.mockImplementationOnce(() => { packagistDatasource.getReleases.mockImplementationOnce(() => {
@ -173,9 +173,9 @@ describe('datasource/index', () => {
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
expect(res.releases).toHaveLength(2); expect(res.releases).toHaveLength(2);
}); });
it('merges registries and aborts on DatasourceError', async () => { it('merges registries and aborts on ExternalHostError', async () => {
mavenDatasource.getReleases.mockImplementationOnce(() => { mavenDatasource.getReleases.mockImplementationOnce(() => {
throw new DatasourceError(new Error()); throw new ExternalHostError(new Error());
}); });
await expect( await expect(
datasource.getPkgReleases({ datasource.getPkgReleases({
@ -183,7 +183,7 @@ describe('datasource/index', () => {
depName: 'something', depName: 'something',
registryUrls: ['https://reg1.com', 'https://reg2.io'], registryUrls: ['https://reg1.com', 'https://reg2.io'],
}) })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
it('merges registries and passes on error', async () => { it('merges registries and passes on error', async () => {
mavenDatasource.getReleases.mockImplementationOnce(() => { mavenDatasource.getReleases.mockImplementationOnce(() => {

View file

@ -1,13 +1,13 @@
import is from '@sindresorhus/is'; import is from '@sindresorhus/is';
import _ from 'lodash'; import _ from 'lodash';
import { logger } from '../logger'; import { logger } from '../logger';
import { ExternalHostError } from '../types/error';
import * as runCache from '../util/cache/run'; import * as runCache from '../util/cache/run';
import { clone } from '../util/clone'; import { clone } from '../util/clone';
import * as allVersioning from '../versioning'; import * as allVersioning from '../versioning';
import datasources from './api.generated'; import datasources from './api.generated';
import { import {
Datasource, Datasource,
DatasourceError,
DigestConfig, DigestConfig,
GetPkgReleasesConfig, GetPkgReleasesConfig,
GetReleasesConfig, GetReleasesConfig,
@ -65,7 +65,7 @@ async function huntRegistries(
break; break;
} }
} catch (err) { } catch (err) {
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
// We'll always save the last-thrown error // We'll always save the last-thrown error
@ -100,7 +100,7 @@ async function mergeRegistries(
combinedRes = res; combinedRes = res;
} }
} catch (err) { } catch (err) {
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
// We'll always save the last-thrown error // We'll always save the last-thrown error
@ -221,8 +221,8 @@ export async function getPkgReleases(
}) })
); );
} catch (e) /* istanbul ignore next */ { } catch (e) /* istanbul ignore next */ {
if (e instanceof DatasourceError) { if (e instanceof ExternalHostError) {
e.datasource = config.datasource; e.hostType = config.datasource;
e.lookupName = lookupName; e.lookupName = lookupName;
} }
throw e; throw e;

View file

@ -2,7 +2,7 @@ import fs from 'fs';
import { resolve } from 'path'; import { resolve } from 'path';
import nock from 'nock'; import nock from 'nock';
import { getPkgReleases } from '..'; import { getPkgReleases } from '..';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import * as hostRules from '../../util/host-rules'; import * as hostRules from '../../util/host-rules';
import * as mavenVersioning from '../../versioning/maven'; import * as mavenVersioning from '../../versioning/maven';
import { id as datasource } from '.'; import { id as datasource } from '.';
@ -182,7 +182,7 @@ describe('datasource/maven', () => {
expect(releases.releases).toEqual(generateReleases(MYSQL_VERSIONS)); expect(releases.releases).toEqual(generateReleases(MYSQL_VERSIONS));
}); });
it('should throw registry-failure if default maven repo fails', async () => { it('should throw external-host-error if default maven repo fails', async () => {
nock('https://repo.maven.apache.org') nock('https://repo.maven.apache.org')
.get('/maven2/org/artifact/maven-metadata.xml') .get('/maven2/org/artifact/maven-metadata.xml')
.times(4) .times(4)
@ -195,7 +195,7 @@ describe('datasource/maven', () => {
depName: 'org:artifact', depName: 'org:artifact',
registryUrls: ['https://repo.maven.apache.org/maven2/'], registryUrls: ['https://repo.maven.apache.org/maven2/'],
}) })
).rejects.toThrow(Error(DATASOURCE_FAILURE)); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
it('should return all versions of a specific library if a repository fails because invalid protocol', async () => { it('should return all versions of a specific library if a repository fails because invalid protocol', async () => {

View file

@ -1,7 +1,7 @@
import url from 'url'; import url from 'url';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { DatasourceError } from '../common';
import { MAVEN_REPO, id } from './common'; import { MAVEN_REPO, id } from './common';
@ -77,7 +77,7 @@ export async function downloadHttpProtocol(
} else if (isTemporalError(err)) { } else if (isTemporalError(err)) {
logger.debug({ failedUrl, err }, 'Temporary error'); logger.debug({ failedUrl, err }, 'Temporary error');
if (isMavenCentral(pkgUrl)) { if (isMavenCentral(pkgUrl)) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
} else if (isConnectionError(err)) { } else if (isConnectionError(err)) {
// istanbul ignore next // istanbul ignore next

View file

@ -1,6 +1,6 @@
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { getName } from '../../../test/util'; import { getName } from '../../../test/util';
import { DatasourceError } from '../common'; import { ExternalHostError } from '../../types/error';
import { getDependency, resetMemCache } from './get'; import { getDependency, resetMemCache } from './get';
import { setNpmrc } from './npmrc'; import { setNpmrc } from './npmrc';
@ -150,7 +150,7 @@ describe(getName(__filename), () => {
.get('/npm-parse-error') .get('/npm-parse-error')
.reply(200, 'not-a-json'); .reply(200, 'not-a-json');
await expect(getDependency('npm-parse-error', 0)).rejects.toThrow( await expect(getDependency('npm-parse-error', 0)).rejects.toThrow(
DatasourceError ExternalHostError
); );
httpMock httpMock

View file

@ -6,11 +6,12 @@ import moment from 'moment';
import registryAuthToken from 'registry-auth-token'; import registryAuthToken from 'registry-auth-token';
import getRegistryUrl from 'registry-auth-token/registry-url'; import getRegistryUrl from 'registry-auth-token/registry-url';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { find } from '../../util/host-rules'; import { find } from '../../util/host-rules';
import { Http, HttpOptions } from '../../util/http'; import { Http, HttpOptions } from '../../util/http';
import { maskToken } from '../../util/mask'; import { maskToken } from '../../util/mask';
import { DatasourceError, Release, ReleaseResult } from '../common'; import { Release, ReleaseResult } from '../common';
import { id } from './common'; import { id } from './common';
import { getNpmrc } from './npmrc'; import { getNpmrc } from './npmrc';
@ -280,7 +281,7 @@ export async function getDependency(
if (err.name === 'ParseError' && err.body) { if (err.name === 'ParseError' && err.body) {
err.body = 'err.body deleted by Renovate'; err.body = 'err.body deleted by Renovate';
} }
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
return null; return null;
} }

View file

@ -3,7 +3,7 @@ import nock from 'nock';
import _registryAuthToken from 'registry-auth-token'; import _registryAuthToken from 'registry-auth-token';
import { getPkgReleases } from '..'; import { getPkgReleases } from '..';
import { getName } from '../../../test/util'; import { getName } from '../../../test/util';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import * as hostRules from '../../util/host-rules'; import * as hostRules from '../../util/host-rules';
import { id as datasource, getNpmrc, resetCache, setNpmrc } from '.'; import { id as datasource, getNpmrc, resetCache, setNpmrc } from '.';
@ -204,13 +204,13 @@ describe(getName(__filename), () => {
nock('https://registry.npmjs.org').get('/foobar').reply(503); nock('https://registry.npmjs.org').get('/foobar').reply(503);
await expect( await expect(
getPkgReleases({ datasource, depName: 'foobar' }) getPkgReleases({ datasource, depName: 'foobar' })
).rejects.toThrow(Error(DATASOURCE_FAILURE)); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
it('should throw error for 408', async () => { it('should throw error for 408', async () => {
nock('https://registry.npmjs.org').get('/foobar').reply(408); nock('https://registry.npmjs.org').get('/foobar').reply(408);
await expect( await expect(
getPkgReleases({ datasource, depName: 'foobar' }) getPkgReleases({ datasource, depName: 'foobar' })
).rejects.toThrow(Error(DATASOURCE_FAILURE)); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
}); });
it('should throw error for others', async () => { it('should throw error for others', async () => {
nock('https://registry.npmjs.org').get('/foobar').reply(451); nock('https://registry.npmjs.org').get('/foobar').reply(451);

View file

@ -2,11 +2,12 @@ import URL from 'url';
import pAll from 'p-all'; import pAll from 'p-all';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import * as runCache from '../../util/cache/run'; import * as runCache from '../../util/cache/run';
import * as hostRules from '../../util/host-rules'; import * as hostRules from '../../util/host-rules';
import { Http, HttpOptions } from '../../util/http'; import { Http, HttpOptions } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'packagist'; export const id = 'packagist';
export const defaultRegistryUrls = ['https://packagist.org']; export const defaultRegistryUrls = ['https://packagist.org'];
@ -313,10 +314,10 @@ async function packageLookup(
} }
if (err.host === 'packagist.org') { if (err.host === 'packagist.org') {
if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT') { if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT') {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
if (err.statusCode && err.statusCode >= 500 && err.statusCode < 600) { if (err.statusCode && err.statusCode >= 500 && err.statusCode < 600) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
} }
logger.warn({ err, name }, 'packagist registry failure: Unknown error'); logger.warn({ err, name }, 'packagist registry failure: Unknown error');

View file

@ -1,5 +1,6 @@
import { getPkgReleases } from '..'; import { getPkgReleases } from '..';
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import * as rubyVersioning from '../../versioning/ruby'; import * as rubyVersioning from '../../versioning/ruby';
import * as pod from '.'; import * as pod from '.';
@ -63,7 +64,7 @@ describe('datasource/cocoapods', () => {
.scope(cocoapodsHost) .scope(cocoapodsHost)
.get('/all_pods_versions_a_c_b.txt') .get('/all_pods_versions_a_c_b.txt')
.reply(429); .reply(429);
await expect(getPkgReleases(config)).rejects.toThrow('registry-failure'); await expect(getPkgReleases(config)).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('returns null for unknown error', async () => { it('returns null for unknown error', async () => {

View file

@ -1,5 +1,6 @@
import crypto from 'crypto'; import crypto from 'crypto';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { GithubHttp } from '../../util/http/github'; import { GithubHttp } from '../../util/http/github';
@ -44,7 +45,7 @@ function handleError(lookupName: string, err: Error): void {
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
logger.warn({ lookupName, err }, `CocoaPods registry failure`); logger.warn({ lookupName, err }, `CocoaPods registry failure`);
throw new Error('registry-failure'); throw new ExternalHostError(err);
} }
if (err.statusCode === 401) { if (err.statusCode === 401) {

View file

@ -2,7 +2,7 @@ import fs from 'fs';
import { getPkgReleases } from '..'; import { getPkgReleases } from '..';
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { getName } from '../../../test/util'; import { getName } from '../../../test/util';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import { id as versioning } from '../../versioning/loose'; import { id as versioning } from '../../versioning/loose';
import { RepologyPackage, id as datasource } from '.'; import { RepologyPackage, id as datasource } from '.';
@ -127,7 +127,7 @@ describe(getName(__filename), () => {
versioning, versioning,
depName: 'debian_stable/nginx', depName: 'debian_stable/nginx',
}) })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
@ -140,7 +140,7 @@ describe(getName(__filename), () => {
versioning, versioning,
depName: 'debian_stable/nginx', depName: 'debian_stable/nginx',
}) })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
@ -151,7 +151,7 @@ describe(getName(__filename), () => {
versioning, versioning,
depName: 'invalid-lookup-name', depName: 'invalid-lookup-name',
}) })
).rejects.toThrow(DATASOURCE_FAILURE); ).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });

View file

@ -1,8 +1,9 @@
import { URLSearchParams } from 'url'; import { URLSearchParams } from 'url';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'repology'; export const id = 'repology';
@ -117,7 +118,7 @@ export async function getReleases({
// Ensure lookup name contains both repository and package // Ensure lookup name contains both repository and package
const [repoName, pkgName] = lookupName.split('/', 2); const [repoName, pkgName] = lookupName.split('/', 2);
if (!repoName || !pkgName) { if (!repoName || !pkgName) {
throw new DatasourceError( throw new ExternalHostError(
new Error( new Error(
'Repology lookup name must contain repository and package separated by slash (<repo>/<pkg>)' 'Repology lookup name must contain repository and package separated by slash (<repo>/<pkg>)'
) )
@ -142,6 +143,6 @@ export async function getReleases({
{ lookupName, err }, { lookupName, err },
'Repology lookup failed with unexpected error' 'Repology lookup failed with unexpected error'
); );
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
} }

View file

@ -1,9 +1,10 @@
import { parse } from 'node-html-parser'; import { parse } from 'node-html-parser';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { isVersion } from '../../versioning/ruby'; import { isVersion } from '../../versioning/ruby';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'ruby-version'; export const id = 'ruby-version';
@ -51,6 +52,6 @@ export async function getReleases(
await globalCache.set(cacheNamespace, 'all', res, 15); await globalCache.set(cacheNamespace, 'all', res, 15);
return res; return res;
} catch (err) { } catch (err) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
} }

View file

@ -1,6 +1,7 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { DatasourceError, ReleaseResult } from '../common'; import { ReleaseResult } from '../common';
import { id } from './common'; import { id } from './common';
const http = new Http(id); const http = new Http(id);
@ -38,7 +39,7 @@ async function updateRubyGemsVersions(): Promise<void> {
if (err.statusCode !== 416) { if (err.statusCode !== 416) {
contentLength = 0; contentLength = 0;
packageReleases = Object.create(null); // Because we might need a "constructor" key packageReleases = Object.create(null); // Because we might need a "constructor" key
throw new DatasourceError( throw new ExternalHostError(
new Error('Rubygems fetch error - need to reset cache') new Error('Rubygems fetch error - need to reset cache')
); );
} }

View file

@ -1,7 +1,8 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global'; import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http'; import { Http } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'terraform-module'; export const id = 'terraform-module';
export const defaultRegistryUrls = ['https://registry.terraform.io']; export const defaultRegistryUrls = ['https://registry.terraform.io'];
@ -113,7 +114,7 @@ export async function getReleases({
const failureCodes = ['EAI_AGAIN']; const failureCodes = ['EAI_AGAIN'];
// istanbul ignore if // istanbul ignore if
if (failureCodes.includes(err.code)) { if (failureCodes.includes(err.code)) {
throw new DatasourceError(err); throw new ExternalHostError(err);
} }
logger.warn( logger.warn(
{ err, lookupName }, { err, lookupName },

View file

@ -3,9 +3,9 @@ import * as os from 'os';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import upath from 'upath'; import upath from 'upath';
import { LANGUAGE_JAVA } from '../../constants/languages'; import { LANGUAGE_JAVA } from '../../constants/languages';
import { DatasourceError } from '../../datasource';
import * as datasourceMaven from '../../datasource/maven'; import * as datasourceMaven from '../../datasource/maven';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { ExecOptions, exec } from '../../util/exec'; import { ExecOptions, exec } from '../../util/exec';
import { BinarySource } from '../../util/exec/common'; import { BinarySource } from '../../util/exec/common';
import { readLocalFile } from '../../util/fs'; import { readLocalFile } from '../../util/fs';
@ -106,9 +106,7 @@ export async function executeGradle(
({ stdout, stderr } = await exec(cmd, execOptions)); ({ stdout, stderr } = await exec(cmd, execOptions));
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.code === TIMEOUT_CODE) { if (err.code === TIMEOUT_CODE) {
const error = new DatasourceError(err); throw new ExternalHostError(err, 'gradle');
error.datasource = 'gradle';
throw error;
} }
logger.warn({ errMessage: err.message }, 'Gradle extraction failed'); logger.warn({ errMessage: err.message }, 'Gradle extraction failed');
return; return;

View file

@ -3,9 +3,10 @@ import fs from 'fs-extra';
import upath from 'upath'; import upath from 'upath';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages'; import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages';
import { DatasourceError } from '../../../datasource/common'; import { id as npmId } from '../../../datasource/npm';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { platform } from '../../../platform'; import { platform } from '../../../platform';
import { ExternalHostError } from '../../../types/error';
import { getChildProcessEnv } from '../../../util/exec/env'; import { getChildProcessEnv } from '../../../util/exec/env';
import { deleteLocalFile } from '../../../util/fs'; import { deleteLocalFile } from '../../../util/fs';
import * as hostRules from '../../../util/host-rules'; import * as hostRules from '../../../util/host-rules';
@ -432,7 +433,7 @@ export async function getAdditionalFiles(
const err = new Error( const err = new Error(
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
); );
throw new DatasourceError(err); throw new ExternalHostError(err, npmId);
} }
} }
} }
@ -492,10 +493,11 @@ export async function getAdditionalFiles(
{ dependency: upgrade.depName, type: 'yarn' }, { dependency: upgrade.depName, type: 'yarn' },
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
); );
throw new DatasourceError( throw new ExternalHostError(
new Error( new Error(
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
) ),
npmId
); );
} }
/* eslint-enable no-useless-escape */ /* eslint-enable no-useless-escape */
@ -594,10 +596,11 @@ export async function getAdditionalFiles(
{ dependency: upgrade.depName, type: 'pnpm' }, { dependency: upgrade.depName, type: 'pnpm' },
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
); );
throw new DatasourceError( throw new ExternalHostError(
Error( Error(
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
) ),
npmId
); );
} }
} }
@ -672,10 +675,11 @@ export async function getAdditionalFiles(
{ dependency: upgrade.depName, type: 'yarn' }, { dependency: upgrade.depName, type: 'yarn' },
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
); );
throw new DatasourceError( throw new ExternalHostError(
Error( Error(
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
) ),
npmId
); );
} }
/* eslint-enable no-useless-escape */ /* eslint-enable no-useless-escape */
@ -688,10 +692,11 @@ export async function getAdditionalFiles(
{ dependency: upgrade.depName, type: 'npm' }, { dependency: upgrade.depName, type: 'npm' },
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
); );
throw new DatasourceError( throw new ExternalHostError(
Error( Error(
'lock file failed for the dependency being updated - skipping branch creation' 'lock file failed for the dependency being updated - skipping branch creation'
) ),
npmId
); );
} }
} }

View file

@ -4,8 +4,9 @@ import { validRange } from 'semver';
import { quote } from 'shlex'; import { quote } from 'shlex';
import { join } from 'upath'; import { join } from 'upath';
import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages'; import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages';
import { DatasourceError } from '../../../datasource'; import { id as npmId } from '../../../datasource/npm';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { ExternalHostError } from '../../../types/error';
import { ExecOptions, exec } from '../../../util/exec'; import { ExecOptions, exec } from '../../../util/exec';
import { PostUpdateConfig, Upgrade } from '../../common'; import { PostUpdateConfig, Upgrade } from '../../common';
import { getNodeConstraint } from './node-version'; import { getNodeConstraint } from './node-version';
@ -151,7 +152,7 @@ export async function generateLockFile(
err.stderr.includes('getaddrinfo ENOTFOUND registry.yarnpkg.com') || err.stderr.includes('getaddrinfo ENOTFOUND registry.yarnpkg.com') ||
err.stderr.includes('getaddrinfo ENOTFOUND registry.npmjs.org') err.stderr.includes('getaddrinfo ENOTFOUND registry.npmjs.org')
) { ) {
throw new DatasourceError(err); throw new ExternalHostError(err, npmId);
} }
} }
return { error: true, stderr: err.stderr }; return { error: true, stderr: err.stderr };

View file

@ -4,13 +4,13 @@ import fs from 'fs-extra';
import Git from 'simple-git/promise'; import Git from 'simple-git/promise';
import { import {
CONFIG_VALIDATION, CONFIG_VALIDATION,
PLATFORM_FAILURE,
REPOSITORY_CHANGED, REPOSITORY_CHANGED,
REPOSITORY_EMPTY, REPOSITORY_EMPTY,
REPOSITORY_TEMPORARY_ERROR, REPOSITORY_TEMPORARY_ERROR,
SYSTEM_INSUFFICIENT_DISK_SPACE, SYSTEM_INSUFFICIENT_DISK_SPACE,
} from '../../constants/error-messages'; } from '../../constants/error-messages';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as limits from '../../workers/global/limits'; import * as limits from '../../workers/global/limits';
import { CommitFilesConfig } from '../common'; import { CommitFilesConfig } from '../common';
import { writePrivateKey } from './private-key'; import { writePrivateKey } from './private-key';
@ -54,7 +54,7 @@ function checkForPlatformFailure(err: Error): void {
]; ];
for (const errorStr of platformFailureStrings) { for (const errorStr of platformFailureStrings) {
if (err.message.includes(errorStr)) { if (err.message.includes(errorStr)) {
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, 'git');
} }
} }
} }
@ -176,7 +176,7 @@ export class Storage {
if (err.message?.includes('write error: No space left on device')) { if (err.message?.includes('write error: No space left on device')) {
throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE); throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE);
} }
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, 'git');
} }
const durationMs = Math.round(Date.now() - cloneStart); const durationMs = Math.round(Date.now() - cloneStart);
logger.debug({ durationMs }, 'git clone completed'); logger.debug({ durationMs }, 'git clone completed');

View file

@ -3,7 +3,6 @@ import is from '@sindresorhus/is';
import delay from 'delay'; import delay from 'delay';
import { configFileNames } from '../../config/app-strings'; import { configFileNames } from '../../config/app-strings';
import { import {
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
REPOSITORY_ACCESS_FORBIDDEN, REPOSITORY_ACCESS_FORBIDDEN,
REPOSITORY_ARCHIVED, REPOSITORY_ARCHIVED,
@ -24,6 +23,7 @@ import {
} from '../../constants/pull-requests'; } from '../../constants/pull-requests';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { BranchStatus } from '../../types'; import { BranchStatus } from '../../types';
import { ExternalHostError } from '../../types/error';
import * as hostRules from '../../util/host-rules'; import * as hostRules from '../../util/host-rules';
import * as githubHttp from '../../util/http/github'; import * as githubHttp from '../../util/http/github';
import { sanitize } from '../../util/sanitize'; import { sanitize } from '../../util/sanitize';
@ -397,7 +397,7 @@ export async function initRepo({
} }
); );
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
if ( if (
@ -965,7 +965,7 @@ export async function getPrList(): Promise<Pr[]> {
); );
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
logger.debug({ err }, 'getPrList err'); logger.debug({ err }, 'getPrList err');
throw new Error('platform-failure'); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
config.prList = res.body.map((pr) => ({ config.prList = res.body.map((pr) => ({
number: pr.number, number: pr.number,
@ -1094,7 +1094,7 @@ export async function getBranchStatus(
logger.debug({ result: checkRunsRaw }, 'No check runs found'); logger.debug({ result: checkRunsRaw }, 'No check runs found');
} }
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
if ( if (
@ -1506,7 +1506,7 @@ async function getComments(issueNo: number): Promise<Comment[]> {
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.statusCode === 404) { if (err.statusCode === 404) {
logger.debug('404 respose when retrieving comments'); logger.debug('404 respose when retrieving comments');
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
throw err; throw err;
} }
@ -1559,7 +1559,7 @@ export async function ensureComment({
} }
return true; return true;
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
if (err.body?.message?.includes('is locked')) { if (err.body?.message?.includes('is locked')) {
@ -1690,7 +1690,7 @@ export async function updatePr(
); );
logger.debug({ pr: prNo }, 'PR updated'); logger.debug({ pr: prNo }, 'PR updated');
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) { if (err instanceof ExternalHostError) {
throw err; throw err;
} }
logger.warn({ err }, 'Error updating PR'); logger.warn({ err }, 'Error updating PR');

19
lib/types/error.ts Normal file
View file

@ -0,0 +1,19 @@
import { EXTERNAL_HOST_ERROR } from '../constants/error-messages';
export class ExternalHostError extends Error {
hostType: string;
err: Error;
lookupName?: string;
reason?: string;
constructor(err: Error, hostType?: string) {
super(EXTERNAL_HOST_ERROR);
// Set the prototype explicitly: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
Object.setPrototypeOf(this, ExternalHostError.prototype);
this.hostType = hostType;
this.err = err;
}
}

View file

@ -2,8 +2,8 @@ import delay from 'delay';
import * as httpMock from '../../../test/httpMock'; import * as httpMock from '../../../test/httpMock';
import { getName } from '../../../test/util'; import { getName } from '../../../test/util';
import { import {
EXTERNAL_HOST_ERROR,
PLATFORM_BAD_CREDENTIALS, PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED, REPOSITORY_CHANGED,
@ -126,7 +126,7 @@ describe(getName(__filename), () => {
}, },
}); });
expect(e).toBeDefined(); expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE); expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('should throw platform failure for ENOTFOUND, ETIMEDOUT or EAI_AGAIN', () => { it('should throw platform failure for ENOTFOUND, ETIMEDOUT or EAI_AGAIN', () => {
const codes = ['ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN']; const codes = ['ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN'];
@ -137,7 +137,7 @@ describe(getName(__filename), () => {
code, code,
}); });
expect(e).toBeDefined(); expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE); expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
} }
}); });
it('should throw platform failure for 500', () => { it('should throw platform failure for 500', () => {
@ -146,14 +146,14 @@ describe(getName(__filename), () => {
message: 'Internal Server Error', message: 'Internal Server Error',
}); });
expect(e).toBeDefined(); expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE); expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('should throw platform failure ParseError', () => { it('should throw platform failure ParseError', () => {
const e = getError({ const e = getError({
name: 'ParseError', name: 'ParseError',
}); });
expect(e).toBeDefined(); expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE); expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('should throw for unauthorized integration', () => { it('should throw for unauthorized integration', () => {
const e = getError({ const e = getError({
@ -200,7 +200,7 @@ describe(getName(__filename), () => {
}; };
const e = getError(gotErr); const e = getError(gotErr);
expect(e).toBeDefined(); expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE); expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('should throw original error when failed to add reviewers', () => { it('should throw original error when failed to add reviewers', () => {
const gotErr = { const gotErr = {

View file

@ -4,13 +4,13 @@ import pAll from 'p-all';
import parseLinkHeader from 'parse-link-header'; import parseLinkHeader from 'parse-link-header';
import { import {
PLATFORM_BAD_CREDENTIALS, PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED, REPOSITORY_CHANGED,
} from '../../constants/error-messages'; } from '../../constants/error-messages';
import { PLATFORM_TYPE_GITHUB } from '../../constants/platforms'; import { PLATFORM_TYPE_GITHUB } from '../../constants/platforms';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { maskToken } from '../mask'; import { maskToken } from '../mask';
import { Http, HttpPostOptions, HttpResponse, InternalHttpOptions } from '.'; import { Http, HttpPostOptions, HttpResponse, InternalHttpOptions } from '.';
@ -61,15 +61,15 @@ export function handleGotError(
err.code === 'EAI_AGAIN') err.code === 'EAI_AGAIN')
) { ) {
logger.debug({ err }, 'GitHub failure: RequestError'); logger.debug({ err }, 'GitHub failure: RequestError');
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
if (err.name === 'ParseError') { if (err.name === 'ParseError') {
logger.debug({ err }, ''); logger.debug({ err }, '');
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
if (err.statusCode >= 500 && err.statusCode < 600) { if (err.statusCode >= 500 && err.statusCode < 600) {
logger.debug({ err }, 'GitHub failure: 5xx'); logger.debug({ err }, 'GitHub failure: 5xx');
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
if ( if (
err.statusCode === 403 && err.statusCode === 403 &&
@ -106,7 +106,7 @@ export function handleGotError(
'GitHub failure: Bad credentials' 'GitHub failure: Bad credentials'
); );
if (rateLimit === '60') { if (rateLimit === '60') {
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
throw new Error(PLATFORM_BAD_CREDENTIALS); throw new Error(PLATFORM_BAD_CREDENTIALS);
} }
@ -123,7 +123,7 @@ export function handleGotError(
throw new Error(REPOSITORY_CHANGED); throw new Error(REPOSITORY_CHANGED);
} }
logger.debug({ err }, '422 Error thrown from GitHub'); logger.debug({ err }, '422 Error thrown from GitHub');
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
} }
if (err.statusCode === 404) { if (err.statusCode === 404) {
logger.debug({ url: err.url }, 'GitHub 404'); logger.debug({ url: err.url }, 'GitHub 404');

View file

@ -1,8 +1,8 @@
import { URL } from 'url'; import { URL } from 'url';
import parseLinkHeader from 'parse-link-header'; import parseLinkHeader from 'parse-link-header';
import { PLATFORM_FAILURE } from '../../constants/error-messages';
import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms'; import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http, HttpResponse, InternalHttpOptions } from '.'; import { Http, HttpResponse, InternalHttpOptions } from '.';
let baseUrl = 'https://gitlab.com/api/v4/'; let baseUrl = 'https://gitlab.com/api/v4/';
@ -63,7 +63,7 @@ export class GitlabHttp extends Http<GitlabHttpOptions, GitlabHttpOptions> {
err.statusCode === 429 || err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600) (err.statusCode >= 500 && err.statusCode < 600)
) { ) {
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITLAB);
} }
const platformFailureCodes = [ const platformFailureCodes = [
'EAI_AGAIN', 'EAI_AGAIN',
@ -72,10 +72,10 @@ export class GitlabHttp extends Http<GitlabHttpOptions, GitlabHttpOptions> {
'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
]; ];
if (platformFailureCodes.includes(err.code)) { if (platformFailureCodes.includes(err.code)) {
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITLAB);
} }
if (err.name === 'ParseError') { if (err.name === 'ParseError') {
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(err, PLATFORM_TYPE_GITLAB);
} }
throw err; throw err;
} }

View file

@ -4,11 +4,9 @@ import { DateTime } from 'luxon';
import minimatch from 'minimatch'; import minimatch from 'minimatch';
import { RenovateConfig } from '../../config'; import { RenovateConfig } from '../../config';
import { import {
DATASOURCE_FAILURE,
MANAGER_LOCKFILE_ERROR, MANAGER_LOCKFILE_ERROR,
PLATFORM_AUTHENTICATION_ERROR, PLATFORM_AUTHENTICATION_ERROR,
PLATFORM_BAD_CREDENTIALS, PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED, REPOSITORY_CHANGED,
@ -24,6 +22,7 @@ import { logger } from '../../logger';
import { getAdditionalFiles } from '../../manager/npm/post-update'; import { getAdditionalFiles } from '../../manager/npm/post-update';
import { platform } from '../../platform'; import { platform } from '../../platform';
import { BranchStatus } from '../../types'; import { BranchStatus } from '../../types';
import { ExternalHostError } from '../../types/error';
import { emojify } from '../../util/emoji'; import { emojify } from '../../util/emoji';
import { exec } from '../../util/exec'; import { exec } from '../../util/exec';
import { readLocalFile, writeLocalFile } from '../../util/fs'; import { readLocalFile, writeLocalFile } from '../../util/fs';
@ -541,10 +540,7 @@ export async function processBranch(
err.message.includes('fatal: Authentication failed') err.message.includes('fatal: Authentication failed')
) { ) {
throw new Error(PLATFORM_AUTHENTICATION_ERROR); throw new Error(PLATFORM_AUTHENTICATION_ERROR);
} else if ( } else if (!(err instanceof ExternalHostError)) {
err.message !== DATASOURCE_FAILURE &&
err.message !== PLATFORM_FAILURE
) {
logger.error({ err }, `Error updating branch: ${err.message}`); logger.error({ err }, `Error updating branch: ${err.message}`);
} }
// Don't throw here - we don't want to stop the other renovations // Don't throw here - we don't want to stop the other renovations
@ -655,11 +651,8 @@ export async function processBranch(
} }
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
if ( if (
[ err instanceof ExternalHostError ||
PLATFORM_RATE_LIMIT_EXCEEDED, [PLATFORM_RATE_LIMIT_EXCEEDED, REPOSITORY_CHANGED].includes(err.message)
PLATFORM_FAILURE,
REPOSITORY_CHANGED,
].includes(err.message)
) { ) {
logger.debug('Passing PR error up'); logger.debug('Passing PR error up');
throw err; throw err;

View file

@ -2,7 +2,6 @@ import sampleSize from 'lodash/sampleSize';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import { RenovateConfig } from '../../config/common'; import { RenovateConfig } from '../../config/common';
import { import {
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED, REPOSITORY_CHANGED,
@ -10,6 +9,7 @@ import {
import { logger } from '../../logger'; import { logger } from '../../logger';
import { PlatformPrOptions, Pr, platform } from '../../platform'; import { PlatformPrOptions, Pr, platform } from '../../platform';
import { BranchStatus } from '../../types'; import { BranchStatus } from '../../types';
import { ExternalHostError } from '../../types/error';
import { BranchConfig, PrResult } from '../common'; import { BranchConfig, PrResult } from '../common';
import { getPrBody } from './body'; import { getPrBody } from './body';
import { ChangeLogError } from './changelog'; import { ChangeLogError } from './changelog';
@ -427,9 +427,9 @@ export async function ensurePr(
} catch (err) { } catch (err) {
// istanbul ignore if // istanbul ignore if
if ( if (
err instanceof ExternalHostError ||
err.message === REPOSITORY_CHANGED || err.message === REPOSITORY_CHANGED ||
err.message === PLATFORM_RATE_LIMIT_EXCEEDED || err.message === PLATFORM_RATE_LIMIT_EXCEEDED ||
err.message === PLATFORM_FAILURE ||
err.message === PLATFORM_INTEGRATION_UNAUTHORIZED err.message === PLATFORM_INTEGRATION_UNAUTHORIZED
) { ) {
logger.debug('Passing error up'); logger.debug('Passing error up');

View file

@ -1,12 +1,11 @@
import { RenovateConfig, getConfig } from '../../../test/util'; import { RenovateConfig, getConfig } from '../../../test/util';
import { import {
CONFIG_VALIDATION, CONFIG_VALIDATION,
DATASOURCE_FAILURE, EXTERNAL_HOST_ERROR,
MANAGER_LOCKFILE_ERROR, MANAGER_LOCKFILE_ERROR,
MANAGER_NO_PACKAGE_FILES, MANAGER_NO_PACKAGE_FILES,
PLATFORM_AUTHENTICATION_ERROR, PLATFORM_AUTHENTICATION_ERROR,
PLATFORM_BAD_CREDENTIALS, PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_ACCESS_FORBIDDEN, REPOSITORY_ACCESS_FORBIDDEN,
@ -27,7 +26,7 @@ import {
SYSTEM_INSUFFICIENT_MEMORY, SYSTEM_INSUFFICIENT_MEMORY,
UNKNOWN_ERROR, UNKNOWN_ERROR,
} from '../../constants/error-messages'; } from '../../constants/error-messages';
import { DatasourceError } from '../../datasource/common'; import { ExternalHostError } from '../../types/error';
import handleError from './error'; import handleError from './error';
jest.mock('./error-config'); jest.mock('./error-config');
@ -48,7 +47,6 @@ describe('workers/repository/error', () => {
REPOSITORY_FORKED, REPOSITORY_FORKED,
MANAGER_NO_PACKAGE_FILES, MANAGER_NO_PACKAGE_FILES,
CONFIG_VALIDATION, CONFIG_VALIDATION,
DATASOURCE_FAILURE,
REPOSITORY_ARCHIVED, REPOSITORY_ARCHIVED,
REPOSITORY_MIRRORED, REPOSITORY_MIRRORED,
REPOSITORY_RENAMED, REPOSITORY_RENAMED,
@ -60,7 +58,6 @@ describe('workers/repository/error', () => {
MANAGER_LOCKFILE_ERROR, MANAGER_LOCKFILE_ERROR,
SYSTEM_INSUFFICIENT_DISK_SPACE, SYSTEM_INSUFFICIENT_DISK_SPACE,
SYSTEM_INSUFFICIENT_MEMORY, SYSTEM_INSUFFICIENT_MEMORY,
PLATFORM_FAILURE,
REPOSITORY_NO_VULNERABILITY, REPOSITORY_NO_VULNERABILITY,
REPOSITORY_CANNOT_FORK, REPOSITORY_CANNOT_FORK,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
@ -73,23 +70,26 @@ describe('workers/repository/error', () => {
expect(res).toEqual(err); expect(res).toEqual(err);
}); });
}); });
it(`handles DatasourceError`, async () => { it(`handles ExternalHostError`, async () => {
const res = await handleError(config, new DatasourceError(new Error())); const res = await handleError(
expect(res).toEqual(DATASOURCE_FAILURE); config,
new ExternalHostError(new Error(), 'some-host-type')
);
expect(res).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('rewrites git 5xx error', async () => { it('rewrites git 5xx error', async () => {
const gitError = new Error( const gitError = new Error(
"fatal: unable to access 'https://**redacted**@gitlab.com/learnox/learnox.git/': The requested URL returned error: 500\n" "fatal: unable to access 'https://**redacted**@gitlab.com/learnox/learnox.git/': The requested URL returned error: 500\n"
); );
const res = await handleError(config, gitError); const res = await handleError(config, gitError);
expect(res).toEqual(PLATFORM_FAILURE); expect(res).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('rewrites git remote error', async () => { it('rewrites git remote error', async () => {
const gitError = new Error( const gitError = new Error(
'fatal: remote error: access denied or repository not exported: /b/nw/bd/27/47/159945428/108610112.git\n' 'fatal: remote error: access denied or repository not exported: /b/nw/bd/27/47/159945428/108610112.git\n'
); );
const res = await handleError(config, gitError); const res = await handleError(config, gitError);
expect(res).toEqual(PLATFORM_FAILURE); expect(res).toEqual(EXTERNAL_HOST_ERROR);
}); });
it('handles unknown error', async () => { it('handles unknown error', async () => {
const res = await handleError(config, new Error('abcdefg')); const res = await handleError(config, new Error('abcdefg'));

View file

@ -2,12 +2,11 @@ import { RenovateConfig } from '../../config';
import { import {
CONFIG_VALIDATION, CONFIG_VALIDATION,
DATASOURCE_FAILURE, EXTERNAL_HOST_ERROR,
MANAGER_LOCKFILE_ERROR, MANAGER_LOCKFILE_ERROR,
MANAGER_NO_PACKAGE_FILES, MANAGER_NO_PACKAGE_FILES,
PLATFORM_AUTHENTICATION_ERROR, PLATFORM_AUTHENTICATION_ERROR,
PLATFORM_BAD_CREDENTIALS, PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED, PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_ACCESS_FORBIDDEN, REPOSITORY_ACCESS_FORBIDDEN,
@ -28,8 +27,8 @@ import {
SYSTEM_INSUFFICIENT_MEMORY, SYSTEM_INSUFFICIENT_MEMORY,
UNKNOWN_ERROR, UNKNOWN_ERROR,
} from '../../constants/error-messages'; } from '../../constants/error-messages';
import { DatasourceError } from '../../datasource/common';
import { logger } from '../../logger'; import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { raiseConfigWarningIssue } from './error-config'; import { raiseConfigWarningIssue } from './error-config';
export default async function handleError( export default async function handleError(
@ -107,22 +106,12 @@ export default async function handleError(
await raiseConfigWarningIssue(config, err); await raiseConfigWarningIssue(config, err);
return err.message; return err.message;
} }
if (err instanceof DatasourceError) { if (err instanceof ExternalHostError) {
logger.warn( logger.warn(
{ datasource: err.datasource, lookupName: err.lookupName, err: err.err }, { hostType: err.hostType, lookupName: err.lookupName, err: err.err },
'Datasource failure' 'Host error'
); );
logger.info('Registry error - skipping'); logger.info('External host error causing abort - skipping');
delete config.branchList; // eslint-disable-line no-param-reassign
return err.message;
}
if (err.message === DATASOURCE_FAILURE) {
logger.info({ err }, 'Registry error - skipping');
delete config.branchList; // eslint-disable-line no-param-reassign
return err.message;
}
if (err.message === PLATFORM_FAILURE) {
logger.info('Platform error - skipping');
delete config.branchList; // eslint-disable-line no-param-reassign delete config.branchList; // eslint-disable-line no-param-reassign
return err.message; return err.message;
} }
@ -174,7 +163,7 @@ export default async function handleError(
logger.warn({ err }, 'Git error - aborting'); logger.warn({ err }, 'Git error - aborting');
delete config.branchList; // eslint-disable-line no-param-reassign delete config.branchList; // eslint-disable-line no-param-reassign
// rewrite this error // rewrite this error
return PLATFORM_FAILURE; return EXTERNAL_HOST_ERROR;
} }
if ( if (
err.message.includes('The remote end hung up unexpectedly') || err.message.includes('The remote end hung up unexpectedly') ||
@ -183,7 +172,7 @@ export default async function handleError(
logger.warn({ err }, 'Git error - aborting'); logger.warn({ err }, 'Git error - aborting');
delete config.branchList; // eslint-disable-line no-param-reassign delete config.branchList; // eslint-disable-line no-param-reassign
// rewrite this error // rewrite this error
return PLATFORM_FAILURE; return EXTERNAL_HOST_ERROR;
} }
// Swallow this error so that other repositories can be processed // Swallow this error so that other repositories can be processed
logger.error({ err }, `Repository has unknown error`); logger.error({ err }, `Repository has unknown error`);

View file

@ -7,13 +7,11 @@ import { configFileNames } from '../../../config/app-strings';
import { decryptConfig } from '../../../config/decrypt'; import { decryptConfig } from '../../../config/decrypt';
import { migrateAndValidate } from '../../../config/migrate-validate'; import { migrateAndValidate } from '../../../config/migrate-validate';
import * as presets from '../../../config/presets'; import * as presets from '../../../config/presets';
import { import { CONFIG_VALIDATION } from '../../../constants/error-messages';
CONFIG_VALIDATION,
PLATFORM_FAILURE,
} from '../../../constants/error-messages';
import * as npmApi from '../../../datasource/npm'; import * as npmApi from '../../../datasource/npm';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { platform } from '../../../platform'; import { platform } from '../../../platform';
import { ExternalHostError } from '../../../types/error';
import { readLocalFile } from '../../../util/fs'; import { readLocalFile } from '../../../util/fs';
import * as hostRules from '../../../util/host-rules'; import * as hostRules from '../../../util/host-rules';
import { flattenPackageRules } from './flatten'; import { flattenPackageRules } from './flatten';
@ -59,7 +57,10 @@ export async function mergeRenovateConfig(
// istanbul ignore if // istanbul ignore if
if (renovateConfig === null) { if (renovateConfig === null) {
logger.warn('Fetching renovate config returns null'); logger.warn('Fetching renovate config returns null');
throw new Error(PLATFORM_FAILURE); throw new ExternalHostError(
Error('Fetching renovate config returns null'),
config.platform
);
} }
// istanbul ignore if // istanbul ignore if
if (!renovateConfig.length) { if (!renovateConfig.length) {